'use client';
import { useState, useEffect, useCallback } from 'react';
import { api } from '@/lib/api';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { DataTable } from '@/components/ui/data-table';
import { ActionIcon } from '@/components/ui/action-icon';
import { Modal } from '@/components/ui/modal';
import { FormModal } from '@/components/ui/form-modal';
import { useToast } from '@/components/ui/toast';
interface Area {
id: string;
name: string;
description: string | null;
isActive: boolean;
_count: { clients: number };
}
export default function AreasPage() {
const { toast } = useToast();
const [areas, setAreas] = useState([]);
const [loading, setLoading] = useState(true);
const [showCreate, setShowCreate] = useState(false);
const [deleteTarget, setDeleteTarget] = useState(null);
const [deleting, setDeleting] = useState(false);
const [search, setSearch] = useState('');
const loadAreas = useCallback(async () => {
try {
const res = await api.get<{ data: Area[] }>('/areas');
setAreas(res.data.data);
} catch {
toast('Failed to load areas', 'error');
} finally {
setLoading(false);
}
}, [toast]);
useEffect(() => { loadAreas(); }, [loadAreas]);
async function handleDelete() {
if (!deleteTarget) return;
setDeleting(true);
try {
await api.delete(`/areas/${deleteTarget.id}`);
toast(`Area "${deleteTarget.name}" deleted`, 'success');
setDeleteTarget(null);
loadAreas();
} catch (err: any) {
toast(err.response?.data?.error || 'Failed to delete area', 'error');
} finally {
setDeleting(false);
}
}
const filtered = search
? areas.filter((a) => a.name.toLowerCase().includes(search.toLowerCase()) || (a.description || '').toLowerCase().includes(search.toLowerCase()))
: areas;
return (
Define service areas and zones for client assignment
a.id}
emptyTitle="No areas yet"
emptyDescription="Create your first service area to start organizing clients by location."
searchPlaceholder="Search areas..."
searchValue={search}
onSearchChange={setSearch}
columns={[
{ key: 'name', label: 'Name', sortable: true, render: (a: Area) => {a.name} },
{ key: 'description', label: 'Description', render: (a: Area) => {a.description || '—'} },
{ key: 'isActive', label: 'Status', sortable: true, render: (a: Area) => },
{ key: 'clients', label: 'Clients', align: 'right' as const, render: (a: Area) => {a._count.clients} },
{
key: 'actions', label: '', align: 'right',
render: (a: Area) => (
e.stopPropagation()}>
{a._count.clients === 0 && (
setDeleteTarget(a)} />
)}
),
},
]}
/>
setDeleteTarget(null)}
title="Delete Area"
description={`Are you sure you want to delete "${deleteTarget?.name}"? This action cannot be undone.`}
variant="danger"
confirmLabel="Delete Area"
onConfirm={handleDelete}
loading={deleting}
/>
{showCreate && (
{ setShowCreate(false); loadAreas(); toast('Area created', 'success'); }} onClose={() => setShowCreate(false)} />
)}
);
}
function CreateAreaModal({ onCreated, onClose }: { onCreated: () => void; onClose: () => void }) {
const { toast } = useToast();
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [error, setError] = useState('');
const [submitting, setSubmitting] = useState(false);
const ic = 'mt-1 block w-full rounded-lg border border-surface-200 dark:border-surface-600 bg-white dark:bg-surface-800 px-3.5 py-2.5 text-sm text-surface-900 dark:text-surface-100 placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:border-primary-500 dark:focus:border-primary-400 focus:outline-none focus:ring-2 focus:ring-primary-500/20 transition-all duration-200';
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError('');
setSubmitting(true);
try {
await api.post('/areas', { name, description: description || undefined });
onCreated();
} catch (err: any) {
setError(err.response?.data?.error || 'Failed to create area');
toast(err.response?.data?.error || 'Failed to create area', 'error');
} finally {
setSubmitting(false);
}
}
return (
);
}