diff --git a/distribution_fe/app/(dashboard)/network/regions/_components/region-sheet.tsx b/distribution_fe/app/(dashboard)/network/regions/_components/region-sheet.tsx index d31ed67..b68aa66 100644 --- a/distribution_fe/app/(dashboard)/network/regions/_components/region-sheet.tsx +++ b/distribution_fe/app/(dashboard)/network/regions/_components/region-sheet.tsx @@ -4,7 +4,6 @@ import { useEffect } from 'react' import { useForm, zodResolver, z } from '@/lib/form' import { FormField } from '@/components/shared/form-field' import { Input } from '@/components/ui/input' -import { Checkbox } from '@/components/ui/checkbox' import { Button } from '@/components/ui/button' import { Dialog, @@ -20,8 +19,8 @@ import { regions as regionsApi, getApiError } from '@/lib/api/index' import type { RegionResponse } from '@/types/api' const schema = z.object({ + code: z.string().max(50, 'Code must be under 50 characters').optional(), name: z.string().min(1, 'Name is required').max(100, 'Name must be under 100 characters'), - isActive: z.boolean(), }) type FormValues = z.infer @@ -30,30 +29,39 @@ interface RegionSheetProps { open: boolean onClose: () => void region: RegionResponse | null + readOnly?: boolean onSuccess: () => void } -export function RegionSheet({ open, onClose, region, onSuccess }: RegionSheetProps) { +export function RegionSheet({ open, onClose, region, readOnly = false, onSuccess }: RegionSheetProps) { const isEdit = !!region const form = useForm({ resolver: zodResolver(schema), - defaultValues: { name: '', isActive: true }, + defaultValues: { code: '', name: '' }, }) useEffect(() => { if (open) { - form.reset(region ? { name: region.name, isActive: region.isActive } : { name: '', isActive: true }) + form.reset(region ? { code: region.code, name: region.name } : { code: '', name: '' }) + if (!region) { + // Suggest the next code; the user can overwrite it, or clear it to let the server generate one. + regionsApi.nextCode() + .then((res) => { + if (!form.getValues('code')) form.setValue('code', res.code) + }) + .catch(() => {}) + } } }, [open, region]) async function onSubmit(values: FormValues) { try { if (isEdit) { - await regionsApi.update(region!.id, values) + await regionsApi.update(region!.id, { name: values.name, code: values.code?.trim() || undefined }) toast.add({ title: 'Region updated', type: 'success' }) } else { - await regionsApi.create(values) + await regionsApi.create({ name: values.name, code: values.code?.trim() || undefined }) toast.add({ title: 'Region created', type: 'success' }) } onSuccess() @@ -67,14 +75,37 @@ export function RegionSheet({ open, onClose, region, onSuccess }: RegionSheetPro !v && onClose()}> - {isEdit ? 'Edit Region' : 'Add Region'} + {readOnly ? 'View Region' : isEdit ? 'Edit Region' : 'Add Region'} - {isEdit ? 'Update the region details below.' : 'Add a new geographic region to the distribution network.'} + {readOnly ? 'Region details.' : isEdit ? 'Update the region details below.' : 'Add a new geographic region to the distribution network.'}
+ ( + + )} + /> + {!isEdit && !readOnly && ( +

+ Suggested automatically — edit it, or clear it to auto-generate on save. +

+ )} + )} /> - - ( -
- - -
- )} - />
- + )}
diff --git a/distribution_fe/app/(dashboard)/network/regions/_components/regions-client.tsx b/distribution_fe/app/(dashboard)/network/regions/_components/regions-client.tsx index 3017e36..cff5519 100644 --- a/distribution_fe/app/(dashboard)/network/regions/_components/regions-client.tsx +++ b/distribution_fe/app/(dashboard)/network/regions/_components/regions-client.tsx @@ -8,6 +8,7 @@ import { DataTable } from '@/components/shared/data-table' import type { TableConfig } from '@/components/shared/data-table' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog' import { toast } from '@/components/ui/toast' import { regions as regionsApi, getApiError, getPaginatedItems } from '@/lib/api/index' import type { RegionResponse } from '@/types/api' @@ -23,6 +24,9 @@ export function RegionsClient() { const [sheetOpen, setSheetOpen] = useState(false) const [editing, setEditing] = useState(null) + const [viewOnly, setViewOnly] = useState(false) + const [deleting, setDeleting] = useState(null) + const [deleteBusy, setDeleteBusy] = useState(false) const fetchData = useCallback(async (p: number, ps: number) => { setLoading(true) @@ -43,24 +47,34 @@ export function RegionsClient() { function openCreate() { setEditing(null) + setViewOnly(false) setSheetOpen(true) } function openEdit(region: RegionResponse) { setEditing(region) + setViewOnly(false) setSheetOpen(true) } - async function toggleActive(region: RegionResponse) { + function openView(region: RegionResponse) { + setEditing(region) + setViewOnly(true) + setSheetOpen(true) + } + + async function confirmDelete() { + if (!deleting) return + setDeleteBusy(true) try { - await regionsApi.update(region.id, { isActive: !region.isActive }) - toast.add({ - title: region.isActive ? 'Region deactivated' : 'Region activated', - type: 'success', - }) + await regionsApi.remove(deleting.id) + toast.add({ title: 'Region deleted', type: 'success' }) + setDeleting(null) fetchData(page, pageSize) } catch (err) { - toast.add({ title: 'Failed to update region', description: getApiError(err), type: 'error' }) + toast.add({ title: 'Failed to delete region', description: getApiError(err), type: 'error' }) + } finally { + setDeleteBusy(false) } } @@ -71,6 +85,13 @@ export function RegionsClient() { emptyMessage: 'No regions yet. Add one to get started.', pagination: { enabled: true, pageSize, pageSizeOptions: [10, 20, 50] }, columns: [ + { + key: 'code', + title: 'Code', + fields: [{ key: 'code', primary: true }], + sortable: true, + width: 120, + }, { key: 'name', title: 'Region', @@ -103,6 +124,11 @@ export function RegionsClient() { ], rowActions: { actions: () => [ + { + action: 'view', + label: 'View region', + onClick: openView, + }, { action: 'edit', label: 'Edit region', @@ -110,19 +136,12 @@ export function RegionsClient() { }, { action: 'deactivate', - label: 'Deactivate region', - onClick: toggleActive, - visible: (target) => target.isActive, - }, - { - action: 'activate', - label: 'Activate region', - onClick: toggleActive, - visible: (target) => !target.isActive, + label: 'Delete region', + onClick: (region: RegionResponse) => setDeleting(region), }, ], }, - }), [pageSize, openEdit, toggleActive]) + }), [pageSize, openEdit, openView]) return ( <> @@ -154,8 +173,28 @@ export function RegionsClient() { open={sheetOpen} onClose={() => setSheetOpen(false)} region={editing} + readOnly={viewOnly} onSuccess={() => fetchData(page, pageSize)} /> + !v && !deleteBusy && setDeleting(null)}> + + + Delete region? + + {deleting ? `${deleting.code} – ${deleting.name} will be removed from the regions list.` : null} + {' '}This action cannot be undone from this screen. + + + + + + + + ) } diff --git a/distribution_fe/lib/api/master.ts b/distribution_fe/lib/api/master.ts index b7c7656..0cf844e 100644 --- a/distribution_fe/lib/api/master.ts +++ b/distribution_fe/lib/api/master.ts @@ -84,6 +84,7 @@ export interface PageParams { export interface RegionBody { name: string + code?: string isActive?: boolean } @@ -91,6 +92,9 @@ export const regions = { list: (params?: PageParams) => api.get>('/regions', { params }).then(r => r.data), + nextCode: () => + api.get<{ code: string }>('/regions/next-code').then(r => r.data), + get: (id: string) => api.get(`/regions/${id}`).then(r => r.data), diff --git a/distribution_fe/types/api.ts b/distribution_fe/types/api.ts index 27f9c1c..7af0b3b 100644 --- a/distribution_fe/types/api.ts +++ b/distribution_fe/types/api.ts @@ -71,6 +71,7 @@ export interface PermissionResponse { export interface RegionResponse { id: string + code: string name: string isActive: boolean createdAt: string