Skip to content
File

Blob: src/client/components/admin/app-form.tsx

typescript72 lines
1import { useState } from "react";
2 
3import { LauncherTilePicker } from "@/client/components/admin/launcher-tile-picker";
4import { FormActions } from "@/client/components/ui/form-actions";
5import { Input } from "@/client/components/ui/input";
6import type { LauncherIconName, LauncherTintName } from "@/client/lib/launcher-icons";
7 
8export interface AppFormValues {
9 name: string;
10 url: string;
11 icon: LauncherIconName | null;
12 tint: LauncherTintName | null;
13}
14 
15interface AppFormInitial {
16 name: string;
17 url: string;
18 icon: LauncherIconName | "";
19 tint: LauncherTintName | "";
20}
21 
22interface AppFormProps {
23 mode: "create" | "edit";
24 initial?: Partial<AppFormInitial>;
25 pending: boolean;
26 error?: string | null;
27 onDismissError?: () => void;
28 onSubmit: (values: AppFormValues) => Promise<void>;
29 onCancel: () => void;
30}
31 
32export const AppForm = ({ mode, initial, pending, error, onDismissError, onSubmit, onCancel }: AppFormProps) => {
33 const [name, setName] = useState(initial?.name ?? "");
34 const [url, setUrl] = useState(initial?.url ?? "");
35 const [icon, setIcon] = useState<LauncherIconName | "">(initial?.icon ?? "");
36 const [tint, setTint] = useState<LauncherTintName | "">(initial?.tint ?? "");
37 const isEdit = mode === "edit";
38 
39 const handleSubmit = async (e: React.FormEvent) => {
40 e.preventDefault();
41 if (pending) return;
42 await onSubmit({
43 name: name.trim(),
44 url: url.trim(),
45 icon: icon || null,
46 tint: tint || null,
47 });
48 };
49 
50 return (
51 <form onSubmit={handleSubmit} className="flex flex-col gap-4">
52 <Input label="Name" value={name} onChange={(e) => setName(e.target.value)} required autoFocus={!isEdit} />
53 <Input
54 label="URL"
55 placeholder="https://anvil.limic.dev"
56 value={url}
57 onChange={(e) => setUrl(e.target.value)}
58 type="url"
59 required
60 />
61 <LauncherTilePicker icon={icon} tint={tint} name={name} onIconChange={setIcon} onTintChange={setTint} />
62 <FormActions
63 pending={pending}
64 error={error}
65 onDismissError={onDismissError}
66 submitLabel={isEdit ? "Save changes" : "Add tile"}
67 onCancel={onCancel}
68 />
69 </form>
70 );
71};