diff --git a/app/.gitignore b/app/.gitignore
index e3471e4..38b6f41 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -10,3 +10,4 @@ vite.config.js.timestamp-*
vite.config.ts.timestamp-*
couchdb
shopping
+test-results
diff --git a/app/package.json b/app/package.json
index 616a6f1..4af4732 100644
--- a/app/package.json
+++ b/app/package.json
@@ -51,5 +51,9 @@
"vitest": "^1.2.0",
"yaml": "^2.4.1"
},
- "type": "module"
+ "type": "module",
+ "dependencies": {
+ "@types/speakingurl": "^13.0.6",
+ "speakingurl": "^14.0.1"
+ }
}
diff --git a/app/playwright.config.ts b/app/playwright.config.ts
index 3cf8326..94a4675 100644
--- a/app/playwright.config.ts
+++ b/app/playwright.config.ts
@@ -3,7 +3,8 @@ import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'pnpm run build && pnpm run preview',
- port: 4173
+ port: 4173,
+ reuseExistingServer: true
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/
diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml
index 6df9455..db7fc4b 100644
--- a/app/pnpm-lock.yaml
+++ b/app/pnpm-lock.yaml
@@ -4,6 +4,14 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+dependencies:
+ '@types/speakingurl':
+ specifier: ^13.0.6
+ version: 13.0.6
+ speakingurl:
+ specifier: ^14.0.1
+ version: 14.0.1
+
devDependencies:
'@iconify-json/flowbite':
specifier: ^1.1.2
@@ -904,6 +912,10 @@ packages:
resolution: {integrity: sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==}
dev: true
+ /@types/speakingurl@13.0.6:
+ resolution: {integrity: sha512-ywkRHNHBwq0mFs/2HRgW6TEBAzH66G8f2Txzh1aGR0UC9ZoAUHfHxLZGDhwMpck4BpSnB61eNFIFmlV+TJ+KUA==}
+ dev: false
+
/@typescript-eslint/eslint-plugin@7.0.2(@typescript-eslint/parser@7.0.2)(eslint@8.56.0)(typescript@5.3.3):
resolution: {integrity: sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -3205,6 +3217,11 @@ packages:
resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
dev: true
+ /speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
diff --git a/app/src/lib/components/ListeErstellenForm.svelte b/app/src/lib/components/ListeErstellenForm.svelte
deleted file mode 100644
index 1dc25ab..0000000
--- a/app/src/lib/components/ListeErstellenForm.svelte
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
diff --git a/app/src/lib/components/ListenItem.svelte b/app/src/lib/components/ListenItem.svelte
deleted file mode 100644
index 77b23c0..0000000
--- a/app/src/lib/components/ListenItem.svelte
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
{liste.titel}
-
- {#each liste.artikel as artikel}
- - {artikel.name} ({artikel.menge}) - {artikel.geschäft}
- {/each}
-
- // Buttons für Bearbeiten und Löschen hier einfügen
-
diff --git "a/app/src/lib/components/Listen\303\234bersicht.svelte" "b/app/src/lib/components/Listen\303\234bersicht.svelte"
deleted file mode 100644
index 2805a91..0000000
--- "a/app/src/lib/components/Listen\303\234bersicht.svelte"
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- {#each listen as liste}
-
- {/each}
-
diff --git a/app/src/lib/components/Product.svelte b/app/src/lib/components/Product.svelte
new file mode 100644
index 0000000..4f47108
--- /dev/null
+++ b/app/src/lib/components/Product.svelte
@@ -0,0 +1,39 @@
+
+
+
+
+ {product.name}
+
+
+ {product.count}
+ Stück
+
+
+
diff --git a/app/src/lib/components/ProductList.svelte b/app/src/lib/components/ProductList.svelte
index d7489eb..d8fd2d1 100644
--- a/app/src/lib/components/ProductList.svelte
+++ b/app/src/lib/components/ProductList.svelte
@@ -1,10 +1,48 @@
- {productlist.name}
+
+ {productlist.name}
+
+
+
+
+
+
+
+ Sicher, dass sie dieses Produkt löschen möchten?
+
+
+
+
+
diff --git a/app/src/lib/db.ts b/app/src/lib/db.ts
index 30e79c6..05bb489 100644
--- a/app/src/lib/db.ts
+++ b/app/src/lib/db.ts
@@ -20,4 +20,11 @@ export interface ProductList {
name: string;
}
-export const db = writable(new PouchDB('shopping'));
+export interface Product {
+ type: 'product';
+ list: string;
+ name: string;
+ count: number;
+}
+
+export const db = writable(new PouchDB('shopping'));
diff --git a/app/src/lib/index.ts b/app/src/lib/index.ts
index 856f2b6..cfe6c83 100644
--- a/app/src/lib/index.ts
+++ b/app/src/lib/index.ts
@@ -1 +1,2 @@
// place files you want to import through the `$lib` alias in this folder.
+export { default as getSlug } from 'speakingurl';
diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte
index a7a0b2f..14adb0e 100644
--- a/app/src/routes/+page.svelte
+++ b/app/src/routes/+page.svelte
@@ -1,16 +1,31 @@
@@ -62,4 +91,32 @@
{:catch error}
Ein Fehler ist aufgetreten: {error.message}
{/await}
+
+
+
+
+
+
+
diff --git a/app/src/routes/createlist/+page.svelte b/app/src/routes/createlist/+page.svelte
deleted file mode 100644
index c3356c0..0000000
--- a/app/src/routes/createlist/+page.svelte
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
Neue Einkaufsliste erstellen
-
-
-
diff --git a/app/src/routes/items/[id]/+layout.ts b/app/src/routes/items/[id]/+layout.ts
new file mode 100644
index 0000000..ae88a27
--- /dev/null
+++ b/app/src/routes/items/[id]/+layout.ts
@@ -0,0 +1,2 @@
+export const prerender = false;
+export const ssr = false;
diff --git a/app/src/routes/items/[id]/+page.svelte b/app/src/routes/items/[id]/+page.svelte
new file mode 100644
index 0000000..6e13c25
--- /dev/null
+++ b/app/src/routes/items/[id]/+page.svelte
@@ -0,0 +1,123 @@
+
+
+
+ G'schäft'lhaberer
+
+
+G'schäft'lHaberer
+
+
+ {#await data.products}
+
+ {:then}
+ {#each products as product}
+
+ {:else}
+
Keine Produkte vorhanden
+ {/each}
+ {:catch error}
+
Ein Fehler ist aufgetreten: {error.message}
+ {/await}
+
+
+
+
+
+
+
+ Produkt erstellen
+
+
diff --git a/app/src/routes/items/[id]/+page.ts b/app/src/routes/items/[id]/+page.ts
new file mode 100644
index 0000000..979fb47
--- /dev/null
+++ b/app/src/routes/items/[id]/+page.ts
@@ -0,0 +1,32 @@
+import { browser } from '$app/environment';
+import { db as DB } from '$lib/db';
+import { get } from 'svelte/store';
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async ({ params: { id } }) => {
+ if (!browser) {
+ return {
+ products: Promise.resolve({ docs: [] }),
+ id
+ };
+ }
+
+ const db = get(DB);
+ const products = db
+ .createIndex({
+ index: { fields: ['type', 'list'] }
+ })
+ .then(async () => {
+ const query = {
+ selector: {
+ type: 'product',
+ list: id
+ }
+ };
+ return await db.find(query);
+ });
+ return {
+ products,
+ id
+ };
+};
diff --git a/app/src/routes/listen/+page.svelte b/app/src/routes/listen/+page.svelte
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/routes/listen/erstellen/+page.svelte b/app/src/routes/listen/erstellen/+page.svelte
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/routes/settings/+page.svelte b/app/src/routes/settings/+page.svelte
index abc22d1..3fd45b4 100644
--- a/app/src/routes/settings/+page.svelte
+++ b/app/src/routes/settings/+page.svelte
@@ -1,3 +1,74 @@
+
+
Settings - G'schäft'lhaberer
+
+
diff --git a/app/tests/create-lists.test.ts b/app/tests/create-lists.test.ts
deleted file mode 100644
index 16cd7ae..0000000
--- a/app/tests/create-lists.test.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { expect, test } from '@playwright/test';
-
-test('createlist page has expected h1', async ({ page }) => {
- await page.goto('/createlist');
- await expect(page.getByRole('heading', { name: 'Neue Einkaufsliste erstellen' })).toBeVisible();
-});
-
-test('createlist page has expected input', async ({ page }) => {
- await page.goto('/createlist');
- await expect(page.getByRole('textbox', { name: 'Name' })).toBeVisible();
-});
diff --git a/app/tests/lists.test.ts b/app/tests/lists.test.ts
index ef80f88..9ae6105 100644
--- a/app/tests/lists.test.ts
+++ b/app/tests/lists.test.ts
@@ -1,14 +1,47 @@
import { expect, test } from '@playwright/test';
-import PouchDB from 'pouchdb';
-import PouchDBFind from 'pouchdb-find';
-PouchDB.plugin(PouchDBFind);
-
-// async function clearDatabase() {
-// const docs = await DB.allDocs();
-// docs.rows.forEach((row) => DB.remove(row.id, row.value.rev));
-// }
test('no shopping lists entries', async ({ page }) => {
await page.goto('/');
await expect(page.getByText('Keine Listen vorhanden')).toBeVisible();
});
+
+test('create a list', async ({ page }) => {
+ const listName = 'Test';
+
+ await page.goto('/');
+ const newButton = page.getByRole('button', { name: 'Neue Liste erstellen' });
+ await expect(newButton).toBeVisible();
+ await newButton.click();
+ const nameInput = page.getByRole('textbox', { name: 'Name' });
+ await expect(nameInput).toBeVisible();
+ await nameInput.fill('Test');
+ const confirmButton = page.getByRole('button', { name: 'Liste hinzufügen' });
+ await expect(confirmButton).toBeVisible();
+ await confirmButton.click();
+ const listLink = page.getByRole('link', { name: listName });
+ await expect(listLink).toBeVisible();
+ await expect(listLink).toHaveAttribute('href', '/items/test');
+});
+
+test('delete a list', async ({ page }) => {
+ const listName = 'Test';
+
+ await page.goto('/');
+ const newButton = page.getByRole('button', { name: 'Neue Liste erstellen' });
+ await newButton.click();
+ const nameInput = page.getByRole('textbox', { name: 'Name' });
+ await nameInput.fill('Test');
+ const confirmCreationButton = page.getByRole('button', { name: 'Liste hinzufügen' });
+ await confirmCreationButton.click();
+
+ const listLink = page.getByRole('link', { name: listName });
+ expect(listLink).toBeVisible();
+ const deleteButton = listLink.getByRole('button');
+ await deleteButton.waitFor();
+ await expect(deleteButton).toBeVisible();
+ await deleteButton.click();
+ const confirmDeletionButton = page.getByRole('button', { name: 'Ja, ich bin sicher' });
+ await expect(confirmDeletionButton).toBeVisible();
+ await confirmDeletionButton.click();
+ await expect(page.getByText('Keine Listen vorhanden')).toBeVisible();
+});
diff --git a/app/tests/products.test.ts b/app/tests/products.test.ts
new file mode 100644
index 0000000..c33050f
--- /dev/null
+++ b/app/tests/products.test.ts
@@ -0,0 +1,67 @@
+import { expect, test, type Page } from '@playwright/test';
+
+async function createList(page: Page) {
+ await page.goto('/');
+ const newButton = page.getByRole('button', { name: 'Neue Liste erstellen' });
+ await newButton.click();
+ const nameInput = page.getByRole('textbox', { name: 'Name' });
+ await nameInput.fill('Test');
+ const confirmCreationButton = page.getByRole('button', { name: 'Liste hinzufügen' });
+ await confirmCreationButton.click();
+}
+
+test('test no products', async ({ page }) => {
+ await createList(page);
+
+ await page.goto('/items/test');
+ await expect(page.getByText('Keine Produkte vorhanden')).toBeVisible();
+});
+
+test('create a product', async ({ page }) => {
+ const productName = 'Testprodukt';
+
+ await createList(page);
+
+ await page.goto('/items/test');
+ const newButton = page.getByRole('button', { name: 'Produkt erstellen' });
+ await expect(newButton).toBeVisible();
+ await newButton.click();
+
+ const nameInput = page.getByRole('textbox', { name: 'Produktname' });
+ await expect(nameInput).toBeVisible();
+ await nameInput.fill(productName);
+ const countInput = page.getByLabel('Anzahl');
+ await expect(countInput).toBeVisible();
+ await countInput.fill('4');
+ const confirmButton = page.getByRole('button', { name: 'Erstellen', exact: true });
+ await expect(confirmButton).toBeVisible();
+ await confirmButton.click();
+
+ const productLabel = page.getByRole('heading', { name: productName });
+ await expect(productLabel).toBeVisible();
+ const productCountLabel = page.getByText('4 Stück');
+ await expect(productCountLabel).toBeVisible();
+});
+
+test('delete a product', async ({ page }) => {
+ const productName = 'Testprodukt';
+
+ await createList(page);
+
+ await page.goto('/items/test');
+ const newButton = page.getByRole('button', { name: 'Produkt erstellen' });
+ await newButton.click();
+
+ const nameInput = page.getByRole('textbox', { name: 'Produktname' });
+ await nameInput.fill(productName);
+ const countInput = page.getByLabel('Anzahl');
+ await countInput.fill('4');
+ const confirmButton = page.getByRole('button', { name: 'Erstellen', exact: true });
+ await confirmButton.click();
+
+ const deleteButton = page.getByRole('button').first();
+ await expect(deleteButton).toBeVisible();
+ await deleteButton.click();
+
+ await expect(page.getByText('Keine Produkte vorhanden')).toBeVisible();
+});