// ============================================================
// EXTENSIONS — first tab ported from vanilla JS to React.
// Read-only list view with a simple add form. The old vanilla
// version has richer UI (trash, restore, purge, search) — this
// minimum-viable port proves the migration pipeline works:
// shared types + api wrapper + React Query + Tailwind shadcn.
// The full CRUD UI lands in a follow-up when polish time arrives.
// ============================================================
import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api';
import type { ExtensionsListOk, Extension } from '@/shared/types';
import { ExtensionCreateSchema, type ExtensionCreate } from '@/shared/schemas';
function ExtensionRow({ ext }: { ext: Extension }) {
return (
{ext.name}
{ext.location}
{ext.number}
{ext.type}
);
}
function AddForm({ onDone }: { onDone: () => void }) {
const qc = useQueryClient();
const [form, setForm] = useState({
location: '',
name: '',
number: '',
type: 'extension',
notes: '',
});
const [error, setError] = useState(null);
const createMutation = useMutation({
mutationFn: (body: ExtensionCreate) => api.post<{ id: number }>('/api/extensions', body),
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['extensions'] });
onDone();
},
onError: (e: Error) => setError(e.message),
});
function submit(e: React.FormEvent) {
e.preventDefault();
setError(null);
const parsed = ExtensionCreateSchema.safeParse(form);
if (!parsed.success) {
setError(parsed.error.issues.map((i: { message: string }) => i.message).join(', '));
return;
}
createMutation.mutate(parsed.data);
}
const input = 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm';
return (
);
}
export default function Extensions() {
const [adding, setAdding] = useState(false);
const { data, isLoading, error } = useQuery({
queryKey: ['extensions'],
queryFn: () => api.get('/api/extensions'),
});
return (
{adding &&
setAdding(false)} />}
{isLoading && Loading…
}
{error && {(error as Error).message}
}
{data && data.items.length === 0 && (
No extensions yet. Click Add to create the first one.
)}
{data && data.items.length > 0 && (
{data.items.map((ext: Extension) => )}
)}
);
}