diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx
index 74d8040..4548557 100644
--- a/app/components/Navbar.tsx
+++ b/app/components/Navbar.tsx
@@ -25,6 +25,12 @@ export default function Navbar() {
>
Analyze
+ {/* If you have an isAdmin helper, show Settings only for admins. Example:
+ {isAdmin(user) && (
+ Settings
+ )}
+ */}
+ Settings
diff --git a/app/routes/settings.tsx b/app/routes/settings.tsx
new file mode 100644
index 0000000..6d5037a
--- /dev/null
+++ b/app/routes/settings.tsx
@@ -0,0 +1,61 @@
+import type { LoaderFunction } from '@remix-run/node';
+import { json } from '@remix-run/node';
+import React, { useEffect, useState } from 'react';
+import { requireAdmin } from '~/lib/auth.server';
+import { settingsService } from '~/lib/settings.server';
+
+export const loader: LoaderFunction = async ({ request }) => {
+ await requireAdmin(request);
+ await settingsService.init?.();
+ const entries: any[] = [];
+ // @ts-ignore
+ for (const key of (settingsService as any).cache.keys()) {
+ entries.push({ key, value: await settingsService.get(key) });
+ }
+ return json({ entries });
+};
+
+export default function SettingsPage() {
+ const [items, setItems] = useState>([]);
+ useEffect(() => {
+ fetch('/api/admin/settings')
+ .then(r => r.json())
+ .then(j => setItems(j));
+ }, []);
+
+ async function save(key: string, value: any) {
+ await fetch(`/api/admin/settings/${encodeURIComponent(key)}`, {
+ method: 'PUT',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ value }),
+ });
+ setItems(s => s.map(i => (i.key === key ? { ...i, value } : i)));
+ }
+
+ return (
+
+
Settings
+
+ {items.map(it => (
+ -
+
+
+ ))}
+
+
+ );
+}
diff --git a/tests/e2e/settings.spec.ts b/tests/e2e/settings.spec.ts
new file mode 100644
index 0000000..29f5744
--- /dev/null
+++ b/tests/e2e/settings.spec.ts
@@ -0,0 +1,6 @@
+import { test, expect } from '@playwright/test';
+
+test('admin can view settings page', async ({ page }) => {
+ await page.goto('http://localhost:5173/settings');
+ await expect(page.locator('text=Settings')).toBeVisible();
+});