|
2 | 2 | import Plus from '@lucide/svelte/icons/plus'; |
3 | 3 | import RotateCcw from '@lucide/svelte/icons/rotate-ccw'; |
4 | 4 | import type { SortingState } from '@tanstack/table-core'; |
| 5 | + import type { ColumnDef } from '@tanstack/table-core'; |
| 6 | + import { apiTokensApi } from '$lib/api'; |
| 7 | + import type { TokenCreatedResponse, TokenResponse } from '$lib/api/internal/v1'; |
5 | 8 | import Container from '$lib/components/Container.svelte'; |
| 9 | + import { |
| 10 | + CreateSortableColumnDef, |
| 11 | + LocaleDateRenderer, |
| 12 | + RenderCell, |
| 13 | + TimeSinceRelativeOrNeverRenderer, |
| 14 | + } from '$lib/components/Table/ColumnUtils'; |
6 | 15 | import DataTable from '$lib/components/Table/DataTableTemplate.svelte'; |
7 | 16 | import Button from '$lib/components/ui/button/button.svelte'; |
8 | 17 | import * as Card from '$lib/components/ui/card'; |
9 | | - import { ApiTokensStore, refreshApiTokens } from '$lib/stores/ApiTokensStore'; |
| 18 | + import { renderComponent } from '$lib/components/ui/data-table'; |
| 19 | + import type { ProblemDetails } from '$lib/errorhandling/ProblemDetails'; |
| 20 | + import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; |
10 | 21 | import { onMount } from 'svelte'; |
11 | 22 | import { toast } from 'svelte-sonner'; |
12 | | - import { columns } from './columns'; |
13 | | - import TokenGenerateDialog from './dialog-token-generate.svelte'; |
| 23 | + import DataTableActions from './data-table-actions.svelte'; |
| 24 | + import TokenCreateDialog from './dialog-token-create.svelte'; |
| 25 | + import TokenCreatedDialog from './dialog-token-created.svelte'; |
14 | 26 |
|
15 | | - let data = $derived(Array.from($ApiTokensStore.values())); |
| 27 | + let loading = $state<boolean>(false); |
| 28 | + let data = $state<TokenResponse[]>([]); |
16 | 29 | let sorting = $state<SortingState>([]); |
17 | 30 |
|
| 31 | + function onCreated(token: TokenCreatedResponse) { |
| 32 | + data = [ |
| 33 | + ...data, |
| 34 | + { |
| 35 | + id: token.id, |
| 36 | + name: token.name, |
| 37 | + createdOn: token.createdAt, |
| 38 | + validUntil: token.validUntil, |
| 39 | + lastUsed: token.lastUsed, |
| 40 | + permissions: token.permissions, |
| 41 | + }, |
| 42 | + ]; |
| 43 | + createdTokenSecret = token.token; |
| 44 | + toast.success('Token created successfully'); |
| 45 | + } |
| 46 | +
|
| 47 | + function onEdit(id: string, updater: (token: TokenResponse) => TokenResponse) { |
| 48 | + data = data.map((token) => (token.id === id ? updater(token) : token)); |
| 49 | + } |
| 50 | +
|
| 51 | + async function onDeleted(id: string) { |
| 52 | + data = data.filter((token) => token.id !== id); |
| 53 | + } |
| 54 | +
|
| 55 | + const columns: ColumnDef<TokenResponse>[] = [ |
| 56 | + CreateSortableColumnDef('name', 'Name', RenderCell), |
| 57 | + CreateSortableColumnDef('createdOn', 'Created at', LocaleDateRenderer), |
| 58 | + CreateSortableColumnDef('validUntil', 'Expires at', TimeSinceRelativeOrNeverRenderer), |
| 59 | + CreateSortableColumnDef('lastUsed', 'Last used', TimeSinceRelativeOrNeverRenderer), |
| 60 | + { |
| 61 | + id: 'actions', |
| 62 | + cell: ({ row }) => { |
| 63 | + // You can pass whatever you need from `row.original` to the component |
| 64 | + return renderComponent(DataTableActions, { token: row.original, onEdit, onDeleted }); |
| 65 | + }, |
| 66 | + }, |
| 67 | + ]; |
| 68 | +
|
18 | 69 | let showGenerateTokenModal = $state<boolean>(false); |
| 70 | + let createdTokenSecret = $state<string | null>(null); |
| 71 | +
|
| 72 | + function handleProblem(problem: ProblemDetails): boolean { |
| 73 | + return false; |
| 74 | + } |
| 75 | +
|
| 76 | + async function loadTokens(successMessage?: string) { |
| 77 | + loading = true; |
| 78 | + try { |
| 79 | + data = await apiTokensApi.tokensListTokens(); |
| 80 | + if (successMessage) { |
| 81 | + toast.success(successMessage); |
| 82 | + } |
| 83 | + } catch (error) { |
| 84 | + await handleApiError(error, handleProblem); |
| 85 | + } finally { |
| 86 | + loading = false; |
| 87 | + } |
| 88 | + } |
19 | 89 |
|
20 | | - onMount(refreshApiTokens); |
| 90 | + onMount(loadTokens); |
21 | 91 | </script> |
22 | 92 |
|
23 | | -<TokenGenerateDialog bind:open={showGenerateTokenModal} /> |
| 93 | +<TokenCreateDialog bind:open={showGenerateTokenModal} {onCreated} /> |
| 94 | +<TokenCreatedDialog bind:token={createdTokenSecret} /> |
24 | 95 |
|
25 | 96 | <Container> |
26 | 97 | <Card.Header class="w-full"> |
|
31 | 102 | <Plus /> |
32 | 103 | Generate Token |
33 | 104 | </Button> |
34 | | - <Button |
35 | | - onclick={() => { |
36 | | - refreshApiTokens(); |
37 | | - toast.success('Tokens refreshed successfully'); |
38 | | - }} |
39 | | - > |
| 105 | + <Button onclick={() => loadTokens('Tokens refreshed successfully')}> |
40 | 106 | <RotateCcw /> |
41 | 107 | Refresh |
42 | 108 | </Button> |
|
0 commit comments