diff --git a/frontend/package.json b/frontend/package.json index 1f7e91f..2da9cbc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,9 @@ "format:check": "prettier --check \"src/**/*.{ts,tsx,css,json,html}\"" }, "dependencies": { + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@fontsource/ibm-plex-sans": "^5.0.20", "@fontsource/jetbrains-mono": "^5.0.20", "@tanstack/react-query": "^5.51.0", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index db48353..44f8731 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,8 @@ import { RequireAdmin } from '@/components/RequireAdmin'; import { RequireAuth } from '@/components/RequireAuth'; import { AdminGroupsPage } from '@/pages/AdminGroupsPage'; import { AdminInvitationsPage } from '@/pages/AdminInvitationsPage'; +import { AdminScenariosPage } from '@/pages/AdminScenariosPage'; +import { AdminTestsPage } from '@/pages/AdminTestsPage'; import { AdminUsersPage } from '@/pages/AdminUsersPage'; import { HomePage } from '@/pages/HomePage'; import { MitrePage } from '@/pages/MitrePage'; @@ -82,6 +84,22 @@ function App() { } /> + + + + } + /> + + + + } + /> } /> diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 0eab999..a9b269b 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -42,6 +42,8 @@ export function Layout() { {navItem('/admin/users', 'Users')} {navItem('/admin/groups', 'Groups')} {navItem('/admin/invitations', 'Invitations')} + {navItem('/admin/tests', 'Tests')} + {navItem('/admin/scenarios', 'Scenarios')} )} @@ -69,7 +71,7 @@ export function Layout() {
- metamorph · M0 bootstrap · M1 db schema · M2 auth · M3 rbac · M4 mitre · design system from tasks/design.md + metamorph · M0 bootstrap · M1 db schema · M2 auth · M3 rbac · M4 mitre · M5 templates · design system from tasks/design.md
diff --git a/frontend/src/components/MarkdownField.tsx b/frontend/src/components/MarkdownField.tsx new file mode 100644 index 0000000..635793a --- /dev/null +++ b/frontend/src/components/MarkdownField.tsx @@ -0,0 +1,45 @@ +import { useId, type TextareaHTMLAttributes } from 'react'; + +import { cn } from '@/lib/cn'; + +interface MarkdownFieldProps extends Omit, 'value' | 'onChange'> { + label: string; + value: string; + onChange: (next: string) => void; + rows?: number; + hint?: string; +} + +/** + * Markdown-content textarea. We deliberately keep it textarea-only (no fancy + * WYSIWYG editor) — markdown lives well in plain text and the saved blob is + * rendered to HTML at display time (M6/M7 mission pages). The label exposes + * "markdown" so the user knows the field accepts MD syntax. + */ +export function MarkdownField({ label, value, onChange, rows = 6, hint, id, className, ...rest }: MarkdownFieldProps) { + const fallbackId = useId(); + const inputId = id ?? fallbackId; + return ( +
+ +