From c817e004b70dd7254e430e5f6af51b303af4f3d4 Mon Sep 17 00:00:00 2001 From: BOHA Date: Mon, 23 Mar 2026 13:32:38 +0100 Subject: [PATCH] feat: supplier name autocomplete on received invoices - Added GET /api/admin/received-invoices/suppliers endpoint (distinct names) - Upload and edit forms use HTML datalist for browser-native autocomplete - Suggestions loaded once on page mount Co-Authored-By: Claude Opus 4.6 (1M context) --- src/admin/pages/ReceivedInvoices.tsx | 21 +++++++++++++++++++++ src/routes/admin/received-invoices.ts | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/admin/pages/ReceivedInvoices.tsx b/src/admin/pages/ReceivedInvoices.tsx index ee0dc14..4968848 100644 --- a/src/admin/pages/ReceivedInvoices.tsx +++ b/src/admin/pages/ReceivedInvoices.tsx @@ -135,6 +135,9 @@ export default function ReceivedInvoices({ statsMonth, statsYear, uploadOpen, se const [deleting, setDeleting] = useState(false) const [saving, setSaving] = useState(false) + // Supplier autocomplete + const [supplierNames, setSupplierNames] = useState([]) + // Upload state const [uploadFiles, setUploadFiles] = useState([]) const [uploadMeta, setUploadMeta] = useState([]) @@ -177,6 +180,14 @@ export default function ReceivedInvoices({ statsMonth, statsYear, uploadOpen, se useEffect(() => { fetchList() }, [fetchList]) + // Fetch supplier names for autocomplete + useEffect(() => { + apiFetch(`${API_BASE}/received-invoices/suppliers`) + .then(r => r.json()) + .then(d => { if (d.success) setSupplierNames(d.data || []) }) + .catch(() => {}) + }, []) + // Fetch stats (silent refresh without animation) const refreshStats = useCallback(async () => { try { @@ -737,9 +748,11 @@ export default function ReceivedInvoices({ statsMonth, statsYear, uploadOpen, se updateMeta(idx, 'supplier_name', e.target.value)} + autoComplete="off" /> @@ -860,10 +873,12 @@ export default function ReceivedInvoices({ statsMonth, statsYear, uploadOpen, se setEditInvoice(prev => prev ? { ...prev, supplier_name: e.target.value } : null)} readOnly={ro} + autoComplete="off" /> @@ -991,6 +1006,12 @@ export default function ReceivedInvoices({ statsMonth, statsYear, uploadOpen, se type="danger" loading={deleting} /> + + + {supplierNames.map(name => ( + ) } diff --git a/src/routes/admin/received-invoices.ts b/src/routes/admin/received-invoices.ts index cae08c0..b9cbedc 100644 --- a/src/routes/admin/received-invoices.ts +++ b/src/routes/admin/received-invoices.ts @@ -87,6 +87,16 @@ export default async function receivedInvoicesRoutes(fastify: FastifyInstance): }); }); + // GET /api/admin/received-invoices/suppliers — distinct supplier names for autocomplete + fastify.get('/suppliers', { preHandler: requirePermission('invoices.view') }, async (_request, reply) => { + const results = await prisma.received_invoices.findMany({ + select: { supplier_name: true }, + distinct: ['supplier_name'], + orderBy: { supplier_name: 'asc' }, + }); + return success(reply, results.map(r => r.supplier_name)); + }); + // GET /api/admin/received-invoices/:id/file fastify.get<{ Params: { id: string } }>('/:id/file', { preHandler: requirePermission('invoices.view') }, async (request, reply) => { const id = parseId(request.params.id, reply);