import { useState } from 'react' import { Alert, Button, Checkbox, Group, Modal, Select, Stack, TextInput } from '@mantine/core' import { ApiError } from '../api/client' import { useCreateSource, useSourceProfiles, useUpdateSource, type MeterSourceResponse } from './hooks' export function SourceForm({ source, onClose }: { source?: MeterSourceResponse; onClose: () => void }) { const profiles = useSourceProfiles(); const create = useCreateSource(); const update = useUpdateSource() const [name, setName] = useState(source?.name ?? ''); const [kind, setKind] = useState(source?.kind ?? null) const [enabled, setEnabled] = useState(source?.enabled ?? true); const [config, setConfig] = useState>({}); const [error, setError] = useState(null) const profile = profiles.data?.items.find((item) => item.kind === kind) async function submit(e: React.FormEvent) { e.preventDefault(); setError(null); if (!name.trim() || !kind) return setError('Name and source type are required.') const values: Record = {}; profile?.fields.forEach((field) => { const changed = Object.prototype.hasOwnProperty.call(config, field.name) const raw = changed ? config[field.name] : (source?.config[field.name] ?? field.default ?? '') // A masked secret is deliberately absent from edit PATCHes until the user // explicitly enters a replacement; sending an empty/masked value is unsafe. if (field.secret && source && (!changed || raw === '')) return if (field.value_type === 'bool' || field.value_type === 'boolean') values[field.name] = typeof raw === 'boolean' ? raw : raw === 'true' else if (field.value_type === 'int' || field.value_type === 'integer') values[field.name] = typeof raw === 'number' ? raw : Number(raw) else values[field.name] = typeof raw === 'string' ? raw : String(raw) }) try { if (source) await update.mutateAsync({ uuid: source.uuid, body: { name: name.trim(), enabled, config: values } }); else await create.mutateAsync({ name: name.trim(), kind, enabled, config: values }); onClose() } catch (err) { setError(err instanceof ApiError ? String((err.body as { detail?: string })?.detail ?? `Error ${err.status}`) : 'Could not save source.') } } return
setName(e.currentTarget.value)} /> {profiles.isLoading && Loading source profiles…}{profiles.isError && Failed to load source profiles.}