diff --git a/client/components/admin/AddUserCard.jsx b/client/components/admin/AddUserCard.tsx similarity index 89% rename from client/components/admin/AddUserCard.jsx rename to client/components/admin/AddUserCard.tsx index ccb2c92..16ae09e 100644 --- a/client/components/admin/AddUserCard.jsx +++ b/client/components/admin/AddUserCard.tsx @@ -1,18 +1,19 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; import { toast } from 'sonner'; import { api } from '@/api'; +import { errMessage } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; -export default function AddUserCard({ onCreated }) { +export default function AddUserCard({ onCreated }: { onCreated: () => void }) { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); - const handleCreate = async (e) => { + const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); setError(''); @@ -32,7 +33,7 @@ export default function AddUserCard({ onCreated }) { setError(''); onCreated(); } catch (err) { - const errorMessage = err.message || 'Failed to create user.'; + const errorMessage = errMessage(err, 'Failed to create user.'); setError(errorMessage); toast.error(errorMessage); } finally { diff --git a/client/components/admin/PrivacyAdminCard.jsx b/client/components/admin/PrivacyAdminCard.jsx deleted file mode 100644 index 2fffc99..0000000 --- a/client/components/admin/PrivacyAdminCard.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { toast } from 'sonner'; -import { api } from '@/api'; -import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; -import { FieldRow, Toggle } from './adminShared'; - -export default function PrivacyAdminCard() { - const [geoEnabled, setGeoEnabled] = useState(false); - const [loading, setLoading] = useState(true); - const [saving, setSaving] = useState(false); - - useEffect(() => { - api.privacySettings() - .then(d => { setGeoEnabled(!!d.geolocation_enabled); }) - .catch(() => toast.error('Failed to load privacy settings')) - .finally(() => setLoading(false)); - }, []); - - async function toggleGeo(next) { - setSaving(true); - try { - const d = await api.setPrivacySettings({ geolocation_enabled: next }); - setGeoEnabled(!!d.geolocation_enabled); - toast.success(next ? 'Geolocation enabled' : 'Geolocation disabled'); - } catch { - toast.error('Failed to update privacy settings'); - } finally { - setSaving(false); - } - } - - return ( - - - Privacy - - - -
- - - {geoEnabled ? 'On' : 'Off (default)'} - -
-
-

- When enabled, new-device logins resolve the login IP to a city/region via{' '} - ip-api.com over plain HTTP. Disable to keep - all login data on-device. Location data is encrypted at rest. -

-
-
- ); -} diff --git a/client/components/admin/adminShared.jsx b/client/components/admin/adminShared.tsx similarity index 73% rename from client/components/admin/adminShared.jsx rename to client/components/admin/adminShared.tsx index 0c99fb2..6cfd98f 100644 --- a/client/components/admin/adminShared.jsx +++ b/client/components/admin/adminShared.tsx @@ -1,12 +1,12 @@ -import React from 'react'; +import type { ReactNode } from 'react'; import { Badge } from '@/components/ui/badge'; import { Label } from '@/components/ui/label'; -export function SectionHeading({ children }) { +export function SectionHeading({ children }: { children?: ReactNode }) { return

{children}

; } -export function FieldRow({ label, children }) { +export function FieldRow({ label, children }: { label?: ReactNode; children?: ReactNode }) { return (
@@ -15,7 +15,12 @@ export function FieldRow({ label, children }) { ); } -export function Toggle({ checked, onChange, label, disabled = false }) { +export function Toggle({ checked, onChange, label, disabled = false }: { + checked: boolean; + onChange: (checked: boolean) => void; + label?: string; + disabled?: boolean; +}) { return (