From 3db3be1d06578d45661f30e5fe84340072bb25be Mon Sep 17 00:00:00 2001 From: Julian Lobbes Date: Tue, 23 May 2023 17:06:04 +0200 Subject: [PATCH] feat(frontend): create sortable data-table component --- backend/todo/crud/todos.py | 1 + backend/todo/routes/todos.py | 2 +- frontend/package-lock.json | 103 ++++--- frontend/src/lib/data-table/Table.svelte | 270 ++++++++++++++++++ frontend/src/lib/data-table/Td.svelte | 24 ++ frontend/src/lib/data-table/Th.svelte | 39 +++ frontend/src/lib/utils/utils.ts | 7 + frontend/src/routes/+layout.svelte | 2 +- frontend/src/routes/+page.svelte | 2 +- frontend/src/routes/[user]/+page.svelte | 5 + frontend/src/routes/[user]/todos/+page.svelte | 52 ++++ frontend/src/routes/[user]/todos/+page.ts | 15 + frontend/src/routes/users/+page.svelte | 260 +++-------------- frontend/vite.config.ts | 24 +- 14 files changed, 530 insertions(+), 276 deletions(-) create mode 100644 frontend/src/lib/data-table/Table.svelte create mode 100644 frontend/src/lib/data-table/Td.svelte create mode 100644 frontend/src/lib/data-table/Th.svelte create mode 100644 frontend/src/lib/utils/utils.ts create mode 100644 frontend/src/routes/[user]/+page.svelte create mode 100644 frontend/src/routes/[user]/todos/+page.svelte create mode 100644 frontend/src/routes/[user]/todos/+page.ts diff --git a/backend/todo/crud/todos.py b/backend/todo/crud/todos.py index 2fab703..03f1bdf 100644 --- a/backend/todo/crud/todos.py +++ b/backend/todo/crud/todos.py @@ -60,6 +60,7 @@ def read_todos_for_user( raise NotFoundException(f"User with id '{user_id}' not found.") db_todos = db.query(todomodel.TodoItem).filter(todomodel.TodoItem.user_id == user_id).order_by(sortorder.call(sortby.field)).offset(skip).limit(limit).all() + return [todoschema.TodoItem.from_orm(db_todo) for db_todo in db_todos] diff --git a/backend/todo/routes/todos.py b/backend/todo/routes/todos.py index 2d21580..79e872d 100644 --- a/backend/todo/routes/todos.py +++ b/backend/todo/routes/todos.py @@ -48,7 +48,7 @@ def read_todos( db: Session = Depends(get_db) ): try: - return todocrud.read_todos_for_user(db=db, user_id=user_id, skip=skip, limit=limit) + return todocrud.read_todos_for_user(db=db, user_id=user_id, skip=skip, limit=limit, sortby=sortby, sortorder=sortorder) except InvalidFilterParameterException as e: raise HTTPException(400, fmt(str(e))) except NotFoundException as e: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 964f7d9..5729702 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1971,11 +1971,12 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.2.0.tgz", - "integrity": "sha512-KDtdva+FZrZlyug15KlbXuubntAPKcBau0K7QhAIqC5SAy0uDbjZwoexDRx0L0J2T4niEfC6FnA9GuQQJKg+Aw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.3.0.tgz", + "integrity": "sha512-NbgDn5/auWfGYFip7DheDj49/JLE6VugdtdLJjnQASYxXqrQjl81xaZzQsoSAxWk+j2mOkmPFy56gV2i63FUnA==", "dev": true, "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^1.0.1", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", @@ -1991,6 +1992,23 @@ "vite": "^4.0.0" } }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.1.tgz", + "integrity": "sha512-8ZXgDbAL1b2o7WHxnPsbkxTzZiZhMwOsCI/GFti3zFlh8unqJtUsgwRQV/XSULFcqkbZXz5v6MqMLSUpl3VKaA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^14.18.0 || >= 16" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.2.0", + "svelte": "^3.54.0", + "vite": "^4.0.0" + } + }, "node_modules/@sveltejs/vite-plugin-svelte/node_modules/magic-string": { "version": "0.30.0", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", @@ -2201,9 +2219,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001488", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001488.tgz", - "integrity": "sha512-NORIQuuL4xGpIy6iCCQGN4iFjlBXtfKWIenlUuyZJumLRIindLb7wXM+GO8erEhb7vXfcnf4BAg2PrSDN5TNLQ==", + "version": "1.0.30001489", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", + "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", "dev": true, "funding": [ { @@ -2331,9 +2349,9 @@ } }, "node_modules/devalue": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.1.tgz", - "integrity": "sha512-Kc0TSP9IUU9eg55au5Q3YtqaYI2cgntVpunJV9Exbm9nvlBeTE5p2NqYHfpuXK6+VF2hF5PI+BPFPUti7e2N1g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", + "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", "dev": true }, "node_modules/didyoumean": { @@ -2357,9 +2375,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.402", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.402.tgz", - "integrity": "sha512-gWYvJSkohOiBE6ecVYXkrDgNaUjo47QEKK0kQzmWyhkH+yoYiG44bwuicTGNSIQRG3WDMsWVZJLRnJnLNkbWvA==", + "version": "1.4.405", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.405.tgz", + "integrity": "sha512-JdDgnwU69FMZURoesf9gNOej2Cms1XJFfLk24y1IBtnAdhTcJY/mXnokmpmxHN59PcykBP4bgUU98vLY44Lhuw==", "dev": true }, "node_modules/es6-promise": { @@ -3265,9 +3283,9 @@ } }, "node_modules/rollup": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.22.0.tgz", - "integrity": "sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==", + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz", + "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -3910,12 +3928,13 @@ "dev": true }, "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", + "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", "dev": true, "engines": { - "node": ">= 14" + "node": ">= 14", + "npm": ">= 7" } } }, @@ -5586,11 +5605,12 @@ } }, "@sveltejs/vite-plugin-svelte": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.2.0.tgz", - "integrity": "sha512-KDtdva+FZrZlyug15KlbXuubntAPKcBau0K7QhAIqC5SAy0uDbjZwoexDRx0L0J2T4niEfC6FnA9GuQQJKg+Aw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.3.0.tgz", + "integrity": "sha512-NbgDn5/auWfGYFip7DheDj49/JLE6VugdtdLJjnQASYxXqrQjl81xaZzQsoSAxWk+j2mOkmPFy56gV2i63FUnA==", "dev": true, "requires": { + "@sveltejs/vite-plugin-svelte-inspector": "^1.0.1", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", @@ -5610,6 +5630,15 @@ } } }, + "@sveltejs/vite-plugin-svelte-inspector": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.1.tgz", + "integrity": "sha512-8ZXgDbAL1b2o7WHxnPsbkxTzZiZhMwOsCI/GFti3zFlh8unqJtUsgwRQV/XSULFcqkbZXz5v6MqMLSUpl3VKaA==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, "@types/cookie": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz", @@ -5746,9 +5775,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001488", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001488.tgz", - "integrity": "sha512-NORIQuuL4xGpIy6iCCQGN4iFjlBXtfKWIenlUuyZJumLRIindLb7wXM+GO8erEhb7vXfcnf4BAg2PrSDN5TNLQ==", + "version": "1.0.30001489", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", + "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", "dev": true }, "chokidar": { @@ -5825,9 +5854,9 @@ "dev": true }, "devalue": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.1.tgz", - "integrity": "sha512-Kc0TSP9IUU9eg55au5Q3YtqaYI2cgntVpunJV9Exbm9nvlBeTE5p2NqYHfpuXK6+VF2hF5PI+BPFPUti7e2N1g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", + "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", "dev": true }, "didyoumean": { @@ -5848,9 +5877,9 @@ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, "electron-to-chromium": { - "version": "1.4.402", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.402.tgz", - "integrity": "sha512-gWYvJSkohOiBE6ecVYXkrDgNaUjo47QEKK0kQzmWyhkH+yoYiG44bwuicTGNSIQRG3WDMsWVZJLRnJnLNkbWvA==", + "version": "1.4.405", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.405.tgz", + "integrity": "sha512-JdDgnwU69FMZURoesf9gNOej2Cms1XJFfLk24y1IBtnAdhTcJY/mXnokmpmxHN59PcykBP4bgUU98vLY44Lhuw==", "dev": true }, "es6-promise": { @@ -6498,9 +6527,9 @@ } }, "rollup": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.22.0.tgz", - "integrity": "sha512-imsigcWor5Y/dC0rz2q0bBt9PabcL3TORry2hAa6O6BuMvY71bqHyfReAz5qyAqiQATD1m70qdntqBfBQjVWpQ==", + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz", + "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -6918,9 +6947,9 @@ "dev": true }, "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz", + "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==", "dev": true } } diff --git a/frontend/src/lib/data-table/Table.svelte b/frontend/src/lib/data-table/Table.svelte new file mode 100644 index 0000000..9adcf21 --- /dev/null +++ b/frontend/src/lib/data-table/Table.svelte @@ -0,0 +1,270 @@ + + + + {#if caption !== null} + + {/if} + + + {#each columns as column} + + + + {#if currentState === 'finished'} + {#each currentItems as item} + + {#each columns as column} + + {/each} + {:else if currentState === 'loading'} + + + + {:else if currentState === 'error'} + {#each errorMessages as message} + + + + {/each} + {/if} + +
{caption}
+ {/each} +
+ {/each} +
Loading...
{message}
+ + + +of {lastPage + 1} + + + diff --git a/frontend/src/lib/data-table/Td.svelte b/frontend/src/lib/data-table/Td.svelte new file mode 100644 index 0000000..93ad19f --- /dev/null +++ b/frontend/src/lib/data-table/Td.svelte @@ -0,0 +1,24 @@ + + + + {#if type === 'date'} + {#if data !== null && data !== undefined} + {new Date(data).toLocaleDateString()} + {:else} + + {/if} + {:else if type === 'boolean'} + {#if data} + + {:else} + + {/if} + {:else} + {data} + {/if} + diff --git a/frontend/src/lib/data-table/Th.svelte b/frontend/src/lib/data-table/Th.svelte new file mode 100644 index 0000000..0aeafb1 --- /dev/null +++ b/frontend/src/lib/data-table/Th.svelte @@ -0,0 +1,39 @@ + + + + {heading ? heading : ''} + {#if order === 'asc' && sortable} + + {:else if order === 'desc' && sortable} + + {/if} + diff --git a/frontend/src/lib/utils/utils.ts b/frontend/src/lib/utils/utils.ts new file mode 100644 index 0000000..096022d --- /dev/null +++ b/frontend/src/lib/utils/utils.ts @@ -0,0 +1,7 @@ +/** + * Generates an array containing each number from 0 up to and excluding n in sequence. + * Works like Python's range() function when using a single argument. + */ +export function range(n: number): number[] { + return Array.from(Array(n).keys()); +} diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 0e766c1..c18923b 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -21,7 +21,7 @@ - diff --git a/frontend/src/routes/[user]/+page.svelte b/frontend/src/routes/[user]/+page.svelte new file mode 100644 index 0000000..17068a1 --- /dev/null +++ b/frontend/src/routes/[user]/+page.svelte @@ -0,0 +1,5 @@ + + +

!

diff --git a/frontend/src/routes/[user]/todos/+page.svelte b/frontend/src/routes/[user]/todos/+page.svelte new file mode 100644 index 0000000..90465f4 --- /dev/null +++ b/frontend/src/routes/[user]/todos/+page.svelte @@ -0,0 +1,52 @@ + + + diff --git a/frontend/src/routes/[user]/todos/+page.ts b/frontend/src/routes/[user]/todos/+page.ts new file mode 100644 index 0000000..9a0960d --- /dev/null +++ b/frontend/src/routes/[user]/todos/+page.ts @@ -0,0 +1,15 @@ +import { error } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = ({ params }) => { + // TODO check if user exists + return { + userId: params.user + } + + //throw error(404, 'Not found'); +} + +export interface UserPage { + userId: number +} diff --git a/frontend/src/routes/users/+page.svelte b/frontend/src/routes/users/+page.svelte index 674068d..f192861 100644 --- a/frontend/src/routes/users/+page.svelte +++ b/frontend/src/routes/users/+page.svelte @@ -1,227 +1,39 @@ - -
- - - - - - - - - - - - - {#if currentState === 'finished'} - {#each currentItems as user} - - - - - - - - - {/each} - {:else if currentState === 'loading'} - - - - {:else if currentState === 'error'} - - {#each errorMessages as message} - - {/each} - - {/if} - -
List of users
IDEmailFirst NameLast NameCreatedUpdated
{user.id}{user.email}{user.first_name}{user.last_name}{new Date(user.created).toLocaleDateString()}{new Date(user.updated).toLocaleDateString()}
Loading...
{message}
- - - -of {lastPage + 1} - - - + diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index dff31fd..9dbeb72 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,16 +2,16 @@ import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - envPrefix: 'PUBLIC_', - plugins: [sveltekit()], - server: { - host: '0.0.0.0', - port: 3000, - strictPort: true, - }, - preview: { - host: '0.0.0.0', - port: 3000, - strictPort: true, - }, + envPrefix: 'PUBLIC_', + plugins: [sveltekit()], + server: { + host: '0.0.0.0', + port: 3000, + strictPort: true, + }, + preview: { + host: '0.0.0.0', + port: 3000, + strictPort: true, + }, });