+
+
navigate('/snippets') },
+ { label: snippet?.name ?? `#${id}` },
+ ]}
+ />
+ {snippet && (
+
+
+
+
+ )}
+
+
+ {error && (
+
+ {error.startsWith('404') ? t('errors.not_found_snippet', { id }) : error}
+
+ )}
+
+ {loading &&
{t('snippets.loading')}
}
+
+ {snippet && (
+ <>
+
+ ,
+ },
+ {
+ term: t('snippets.field_updated'),
+ detail: ,
+ },
+ ]}
+ />
+
+
+
+
+
{t('snippets.section_code')}
+
+
+ {snippet.code}
+
+
+ >
+ )}
+
+ );
+}
diff --git a/client/src/pages/snippet/SnippetsPage.tsx b/client/src/pages/snippet/SnippetsPage.tsx
new file mode 100644
index 0000000..569490e
--- /dev/null
+++ b/client/src/pages/snippet/SnippetsPage.tsx
@@ -0,0 +1,129 @@
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { Code, Pencil, Trash2, Plus } from 'lucide-react';
+import { snippets } from '../../api';
+import type { Snippet } from '../../api';
+import { Breadcrumbs, Button, Card, ContextMenu, Timestamp } from '../../ui';
+import styles from '../Page.module.css';
+
+function SnippetCard({
+ snippet,
+ onDelete,
+}: {
+ snippet: Snippet;
+ onDelete: (id: number) => void;
+}) {
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+
+ const header = (
+