diff --git a/Frontend/erp-system/app/dashboard/sales/bundles/[id]/page.tsx b/Frontend/erp-system/app/dashboard/sales/bundles/[id]/page.tsx index 28c302c..b2e534b 100644 --- a/Frontend/erp-system/app/dashboard/sales/bundles/[id]/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/bundles/[id]/page.tsx @@ -15,7 +15,9 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { formatUomName } from "@/lib/format-uom" import { cn } from "@/lib/utils" +import { validateBundleSale } from "@/lib/sales-validation" import { BundleSale, BundleSaleTemplateSummary, BundleSaleTemplateLine, UpdateBundleSaleRequest } from "@/types/bundles" import { customersApi } from "@/lib/api/customers" import { warehousesApi } from "@/lib/api/warehouses" @@ -138,7 +140,20 @@ export default function BundleSaleDetailPage() { } async function saveBundle() { - if (!bundle || !customerId || !warehouseId || !cashierUserId || !templateId) return + if (!bundle) return + const validationError = validateBundleSale({ + customerId, + warehouseId, + cashierUserId, + templateId, + bundleName, + bundlePrice, + lines, + }) + if (validationError) { + setError(validationError) + return + } setBusy(true) setError(null) try { @@ -393,7 +408,7 @@ export default function BundleSaleDetailPage() { {uoms.map((uom) => ( - {uom.name} + {formatUomName(uom.name)} ))} diff --git a/Frontend/erp-system/app/dashboard/sales/bundles/new/page.tsx b/Frontend/erp-system/app/dashboard/sales/bundles/new/page.tsx index ce49664..655d326 100644 --- a/Frontend/erp-system/app/dashboard/sales/bundles/new/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/bundles/new/page.tsx @@ -18,8 +18,10 @@ import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Skeleton } from "@/components/ui/skeleton" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { formatUomName } from "@/lib/format-uom" import { cn } from "@/lib/utils" import { toast } from "@/components/ui/toast" +import { validateBundleSale } from "@/lib/sales-validation" import { Customer } from "@/types/customers" import { ItemListItem, Uom, Warehouse } from "@/types/master-data" import { ManagedUser } from "@/types/users" @@ -54,7 +56,7 @@ function NewBundleSaleContent() { const [warehouseId, setWarehouseId] = useState(null) const [cashierUserId, setCashierUserId] = useState(null) const [templateId, setTemplateId] = useState(templateFromQuery ? Number(templateFromQuery) : null) - const [bundleName, setBundleName] = useState("Demo Bundle") + const [bundleName, setBundleName] = useState("") const [bundlePrice, setBundlePrice] = useState(0) const [allowPriceOverride, setAllowPriceOverride] = useState(false) const [lines, setLines] = useState([]) @@ -78,10 +80,10 @@ function NewBundleSaleContent() { setWarehouses(whRes.items) setUsers(userRes.items) setTemplates(templateRes.items) - setCustomerId(cust.items[0]?.customerId ?? null) - setWarehouseId(whRes.items[0]?.warehouseId ?? null) - setCashierUserId(userRes.items[0]?.userId ?? null) - setTemplateId((current) => current ?? templateRes.items[0]?.bundleSaleTemplateId ?? null) + setCustomerId(null) + setWarehouseId(null) + setCashierUserId(null) + setTemplateId((current) => current ?? null) }) .catch((err) => setSubmitError(errorMessage(err))) .finally(() => setLoading(false)) @@ -122,12 +124,21 @@ function NewBundleSaleContent() { } async function submit() { - if (!customerId || !warehouseId || !cashierUserId || !templateId || !template) { - setSubmitError("Select customer, warehouse, cashier, and bundle template.") + const validationError = validateBundleSale({ + customerId, + warehouseId, + cashierUserId, + templateId, + bundleName, + bundlePrice, + lines, + }) + if (validationError) { + setSubmitError(validationError) return } - if (lines.length === 0) { - setSubmitError("Add at least one bundle component line.") + if (!template) { + setSubmitError("Load a bundle template before saving.") return } setSaving(true) @@ -189,28 +200,28 @@ function NewBundleSaleContent() {
- setCustomerId(v ? Number(v) : null)}> {customers.map((c) => {c.customerCode} - {c.displayName ?? c.name})}
- setWarehouseId(v ? Number(v) : null)}> {warehouses.map((w) => {w.code} - {w.name})}
- setCashierUserId(v ? Number(v) : null)}> {users.map((u) => {u.displayName})}
- setTemplateId(v ? Number(v) : null)}> {templates.map((t) => {t.templateCode} - {t.templateName})} @@ -256,7 +267,7 @@ function NewBundleSaleContent() { {lines.map((line) => ( - { const itemId = Number(v) const item = items.find((candidate) => candidate.itemId === itemId) updateLine(line.key, { @@ -278,14 +289,14 @@ function NewBundleSaleContent() { - updateLine(line.key, { uomId: v ? Number(v) : 0 })} disabled> {uoms.map((uom) => ( - {uom.name} + {formatUomName(uom.name)} ))} diff --git a/Frontend/erp-system/app/dashboard/sales/bundles/page.tsx b/Frontend/erp-system/app/dashboard/sales/bundles/page.tsx index 5d84432..3fbcf36 100644 --- a/Frontend/erp-system/app/dashboard/sales/bundles/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/bundles/page.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react" import Link from "next/link" -import { ChevronLeft, ChevronRight, Eye, Filter, FileText, Plus, Printer } from "lucide-react" +import { ChevronLeft, ChevronRight, Eye, FileText, Plus, Printer, Search } from "lucide-react" import { bundleApi } from "@/lib/api/bundles" import { customersApi } from "@/lib/api/customers" @@ -10,6 +10,7 @@ import { warehousesApi } from "@/lib/api/warehouses" import { errorMessage } from "@/lib/error-map" import { Button, buttonVariants } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" +import { Card, CardContent, CardHeader } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Skeleton } from "@/components/ui/skeleton" @@ -23,7 +24,6 @@ import { cn } from "@/lib/utils" type StatusFilter = BundleSaleStatus | "All" const PAGE_SIZE = 10 -const tabs = ["All", "Draft", "Posted", "Cancelled"] as const function statusClass(status: BundleSaleStatus) { switch (status) { @@ -46,7 +46,6 @@ export default function BundleSalesPage() { const [searchInput, setSearchInput] = useState("") const [query, setQuery] = useState("") const [page, setPage] = useState(1) - const [showFilters, setShowFilters] = useState(false) const [customers, setCustomers] = useState([]) const [warehouses, setWarehouses] = useState([]) @@ -119,88 +118,74 @@ export default function BundleSalesPage() {
-
-
-
- {tabs.map((t) => ( - - ))} + + +
+
+ + setSearchInput(e.target.value)} + placeholder="Search bundle, code, or customer" + className="h-14 w-full pl-11 text-base" + aria-label="Search bundles" + /> +
+ value={status} onValueChange={(v) => setStatus(v ?? "All")}> + + + + + All statuses + Draft + Posted + Cancelled + + + +
-
- setSearchInput(e.target.value)} - placeholder="Filter by bundle, code, or customer" - className="h-12 w-full lg:max-w-sm" - /> -
-
- - {showFilters && ( -
-
-
Customer
- -
-
-
Warehouse
- -
-
- -
-
- )} + {error &&
{error}
} @@ -221,6 +206,7 @@ export default function BundleSalesPage() { {!error && rows !== null && visibleRows.length > 0 && ( <> +
@@ -266,6 +252,7 @@ export default function BundleSalesPage() {
+
@@ -302,7 +289,7 @@ export default function BundleSalesPage() { )} )} -
+
) } diff --git a/Frontend/erp-system/app/dashboard/sales/free-issues/new/page.tsx b/Frontend/erp-system/app/dashboard/sales/free-issues/new/page.tsx index 2dced6d..5e37842 100644 --- a/Frontend/erp-system/app/dashboard/sales/free-issues/new/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/free-issues/new/page.tsx @@ -6,9 +6,12 @@ import { ArrowLeft, Pencil, Plus, Save, Trash2, X } from "lucide-react" import { Button, buttonVariants } from "@/components/ui/button" import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { formatUomName } from "@/lib/format-uom" import { cn } from "@/lib/utils" +import { validateSalesDocument } from "@/lib/sales-validation" import { errorMessage } from "@/lib/error-map" import { salesApi } from "@/lib/api/sales" import { customersApi } from "@/lib/api/customers" @@ -88,7 +91,7 @@ export default function NewFreeIssuePage() { warehouseName: warehouse?.name ?? `Warehouse ${detail.data.warehouseId}`, itemName: item?.name ?? firstLine?.description ?? "—", itemSku: item?.sku ?? `SKU-${firstLine?.itemId ?? 0}`, - uomName: uom?.name ?? `UOM ${firstLine?.uomId ?? 0}`, + uomName: uom?.name ? formatUomName(uom.name) : `UOM ${firstLine?.uomId ?? 0}`, qty: firstLine?.qty ?? 0, freeQty: firstLine?.freeQty ?? 0, } satisfies FreeIssueRow @@ -111,17 +114,10 @@ export default function NewFreeIssuePage() { setUoms(uomRes.items) setWarehouses(whRes.items) setUsers(userRes.items) - setCustomerId(cust.items[0]?.customerId ?? null) - setWarehouseId(whRes.items[0]?.warehouseId ?? null) - setCashierUserId(userRes.items[0]?.userId ?? null) - setLines([ - { - ...blankLine("line-1"), - itemId: itemRes.items[0]?.itemId ?? 0, - uomId: itemRes.items[0]?.baseUomId ?? uomRes.items[0]?.uomId ?? 0, - warehouseId: whRes.items[0]?.warehouseId ?? 0, - }, - ]) + setCustomerId(null) + setWarehouseId(null) + setCashierUserId(null) + setLines([blankLine("line-1")]) await refreshRows() }) .catch((err) => setError(errorMessage(err))) @@ -132,12 +128,18 @@ export default function NewFreeIssuePage() { setLines((prev) => prev.map((line) => (line.key === key ? { ...line, ...patch } : line))) } + function updateHeaderWarehouse(nextWarehouseId: number | null) { + setWarehouseId(nextWarehouseId) + setLines((prev) => prev.map((line) => ({ ...line, warehouseId: nextWarehouseId ?? line.warehouseId }))) + setEditingLines((prev) => prev.map((line) => ({ ...line, warehouseId: nextWarehouseId ?? line.warehouseId }))) + } + function updateEditingLine(key: string, patch: Partial) { setEditingLines((prev) => prev.map((line) => (line.key === key ? { ...line, ...patch } : line))) } function addLine() { - setLines((prev) => [...prev, blankLine(`line-${Date.now()}`)]) + setLines((prev) => [...prev, { ...blankLine(`line-${Date.now()}`), warehouseId: warehouseId ?? 0 }]) } function removeLine(key: string) { @@ -146,20 +148,25 @@ export default function NewFreeIssuePage() { function selectItem(key: string, itemId: number) { const item = items.find((candidate) => candidate.itemId === itemId) - updateLine(key, { itemId, uomId: item?.baseUomId ?? 0 }) + updateLine(key, { itemId, uomId: item?.baseUomId ?? 0, warehouseId: warehouseId ?? 0 }) } function selectEditingItem(key: string, itemId: number) { const item = items.find((candidate) => candidate.itemId === itemId) - updateEditingLine(key, { itemId, uomId: item?.baseUomId ?? 0 }) + updateEditingLine(key, { itemId, uomId: item?.baseUomId ?? 0, warehouseId: warehouseId ?? 0 }) } async function submit() { const activeLines = editingRowId ? editingLines : lines - if (!customerId || !warehouseId || !cashierUserId) return setError("Select customer, warehouse, and cashier.") - if (activeLines.some((line) => !line.itemId)) return setError("Select an item for every line.") - if (activeLines.some((line) => !line.uomId)) return setError("Select a valid UOM for every line.") - if (activeLines.some((line) => !line.warehouseId)) return setError("Select a warehouse for every line.") + const validationError = validateSalesDocument({ + customerId, + warehouseId, + cashierUserId, + requireCashierUser: true, + lines: activeLines, + lineLabel: "free issue line", + }) + if (validationError) return setError(validationError) setSaving(true) setError(null) @@ -198,6 +205,11 @@ export default function NewFreeIssuePage() { toast.success("Free issue created", created.data.slipNo) } + if (!editingRowId) { + setCustomerId(null) + setWarehouseId(null) + setCashierUserId(null) + } setLines([blankLine("line-1")]) await refreshRows() } catch (err) { @@ -281,6 +293,57 @@ export default function NewFreeIssuePage() { {error ?
{error}
: null} +
+

{editingRowId ? "Free issue header" : "Create free issue header"}

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ {editingRowId ? (
@@ -327,7 +390,7 @@ export default function NewFreeIssuePage() { {uoms.map((u) => ( - {u.name} + {formatUomName(u.name)} ))} @@ -392,7 +455,7 @@ export default function NewFreeIssuePage() { {uoms.map((u) => ( - {u.name} + {formatUomName(u.name)} ))} diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx index 94793fb..1f57856 100644 --- a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/page.tsx @@ -11,8 +11,10 @@ import { itemsApi } from "@/lib/api/items" import { uomsApi } from "@/lib/api/uoms" import { warehousesApi } from "@/lib/api/warehouses" import { errorMessage } from "@/lib/error-map" +import { findUomLabel, formatUomName } from "@/lib/format-uom" import { cn } from "@/lib/utils" import { getSuggestedUnitPrice } from "@/lib/sales-line-utils" +import { validateSalesDocument } from "@/lib/sales-validation" import { Customer } from "@/types/customers" import { ItemListItem, Uom, Warehouse } from "@/types/master-data" import { CreateSalesInvoiceLineRequest, SalesInvoice, SalesInvoicePostingCheck, SalesInvoiceStatus, SalesInvoiceType } from "@/types/sales" @@ -171,9 +173,15 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i } async function save() { - if (!customerId || !warehouseId || !etag) return - if (lines.some((line) => !line.itemId || !line.uomId || !line.warehouseId)) { - setError("Select item, UOM and warehouse for every line.") + if (!etag) return + const validationError = validateSalesDocument({ + customerId, + warehouseId, + lines, + lineLabel: "invoice line", + }) + if (validationError) { + setError(validationError) return } @@ -353,7 +361,7 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i
{line.description}
SKU {items.find((i) => i.itemId === line.itemId)?.sku ?? `#${line.itemId}`}
- {uoms.find((u) => u.uomId === line.uomId)?.name ?? `#${line.uomId}`} + {findUomLabel(uoms, line.uomId)} {line.qty.toFixed(0)} {line.freeQty > 0 ? line.freeQty.toFixed(0) : "—"} {money.format(line.unitPrice)} @@ -505,7 +513,7 @@ export default function SalesInvoiceDetailPage({ params }: { params: Promise<{ i {uoms.map((u) => ( ))} diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx index c86302d..f1ef563 100644 --- a/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/invoices/[id]/print/page.tsx @@ -10,6 +10,7 @@ import { itemsApi } from "@/lib/api/items" import { uomsApi } from "@/lib/api/uoms" import { warehousesApi } from "@/lib/api/warehouses" import { errorMessage } from "@/lib/error-map" +import { findUomLabel } from "@/lib/format-uom" import { Button, buttonVariants } from "@/components/ui/button" import { Skeleton } from "@/components/ui/skeleton" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" @@ -143,7 +144,7 @@ export default function SalesInvoicePrintPage({ params }: { params: { id: string
{line.description}
SKU {items.find((i) => i.itemId === line.itemId)?.sku ?? `#${line.itemId}`}
- {uoms.find((u) => u.uomId === line.uomId)?.name ?? `#${line.uomId}`} + {findUomLabel(uoms, line.uomId)} {line.qty.toFixed(2)} {line.freeQty > 0 ? line.freeQty.toFixed(2) : "—"} {line.unitPrice.toFixed(2)} diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx index ad3e323..fd667b2 100644 --- a/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/invoices/new/page.tsx @@ -11,8 +11,10 @@ import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { formatUomName } from "@/lib/format-uom" import { cn } from "@/lib/utils" import { getSuggestedUnitPrice } from "@/lib/sales-line-utils" +import { validateSalesDocument } from "@/lib/sales-validation" import { errorMessage } from "@/lib/error-map" import { salesApi } from "@/lib/api/sales" import { customersApi } from "@/lib/api/customers" @@ -85,18 +87,9 @@ export default function NewSalesInvoicePage() { setItems(itemRes.items) setUoms(uomRes.items) setWarehouses(whRes.items) - const defaultWarehouseId = whRes.items[0]?.warehouseId ?? null - setCustomerId(cust.items[0]?.customerId ?? null) - setWarehouseId(defaultWarehouseId) - setLines([ - { - ...blankLine("line-1"), - itemId: itemRes.items[0]?.itemId ?? 0, - uomId: itemRes.items[0]?.baseUomId ?? uomRes.items[0]?.uomId ?? 0, - warehouseId: defaultWarehouseId ?? 0, - unitPrice: getSuggestedUnitPrice(itemRes.items, itemRes.items[0]?.itemId), - }, - ]) + setCustomerId(null) + setWarehouseId(null) + setLines([blankLine("line-1")]) setActiveFocSchemes( freeIssueRes.items.flatMap((issue) => { if (!issue.itemId) return [] @@ -132,6 +125,7 @@ export default function NewSalesInvoicePage() { updateLine(key, { itemId, uomId: item?.baseUomId ?? 0, + warehouseId: warehouseId ?? 0, unitPrice: getSuggestedUnitPrice(items, itemId), }) } @@ -178,10 +172,13 @@ export default function NewSalesInvoicePage() { const payableTotal = netTotal + taxTotal async function submit() { - if (!customerId || !warehouseId) return setSubmitError("Select a customer and warehouse.") - if (lines.some((line) => !line.itemId)) return setSubmitError("Select an item for every line.") - if (lines.some((line) => !line.warehouseId)) return setSubmitError("Select a warehouse for every line.") - if (lines.some((line) => !line.uomId)) return setSubmitError("Select a valid UOM for every line.") + const validationError = validateSalesDocument({ + customerId, + warehouseId, + lines, + lineLabel: "invoice line", + }) + if (validationError) return setSubmitError(validationError) const payload: CreateSalesInvoiceRequest = { customerId, @@ -365,7 +362,7 @@ export default function NewSalesInvoicePage() { {uoms.map((u) => ( - {u.name} + {formatUomName(u.name)} ))} diff --git a/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx b/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx index d439ef7..9f09f1f 100644 --- a/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/invoices/page.tsx @@ -2,13 +2,15 @@ import { useEffect, useMemo, useState } from "react" import Link from "next/link" -import { ChevronLeft, ChevronRight, Eye, Filter, FileText, Plus, Printer } from "lucide-react" +import { ChevronLeft, ChevronRight, Eye, FileText, Plus, Printer, Search } from "lucide-react" import { salesApi } from "@/lib/api/sales" import { errorMessage } from "@/lib/error-map" import { Button, buttonVariants } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" +import { Card, CardContent, CardHeader } from "@/components/ui/card" import { Input } from "@/components/ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Skeleton } from "@/components/ui/skeleton" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { PaginationMeta } from "@/types/common" @@ -18,7 +20,6 @@ import { cn } from "@/lib/utils" type StatusFilter = SalesInvoiceStatus | "All" const PAGE_SIZE = 10 -const tabs = ["All", "Draft", "Posted", "Cancelled"] as const function statusClass(status: SalesInvoiceStatus) { switch (status) { @@ -95,36 +96,32 @@ export default function SalesInvoicesPage() {
-
-
-
- {tabs.map((t) => ( - - ))} + + +
+
+ + setSearchInput(e.target.value)} + placeholder="Search customer or invoice number" + className="h-14 w-full pl-11 text-base" + aria-label="Search invoices" + /> +
+ value={status} onValueChange={(v) => setStatus(v ?? "All")}> + + + + + All statuses + Draft + Posted + Cancelled + +
- -
- setSearchInput(e.target.value)} - placeholder="Filter by customer or invoice number" - className="h-12 w-full lg:max-w-sm" - /> - -
-
+ {error &&
{error}
} @@ -145,6 +142,7 @@ export default function SalesInvoicesPage() { {!error && rows !== null && visibleRows.length > 0 && ( <> +
@@ -195,6 +193,7 @@ export default function SalesInvoicesPage() {
+
@@ -233,7 +232,7 @@ export default function SalesInvoicesPage() { )} )} -
+
) } diff --git a/Frontend/erp-system/app/dashboard/sales/page.tsx b/Frontend/erp-system/app/dashboard/sales/page.tsx index ad9c562..98982c9 100644 --- a/Frontend/erp-system/app/dashboard/sales/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/page.tsx @@ -1,7 +1,8 @@ import Link from "next/link" -import { FileBarChart, FileText, PackageX, ReceiptText, ShoppingCart } from "lucide-react" +import { FileText, PackageX, ReceiptText, ShoppingCart } from "lucide-react" import { buttonVariants } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { cn } from "@/lib/utils" const sections = [ @@ -34,36 +35,38 @@ const sections = [ export default function SalesHubPage() { return (
-
+
Sales

Sales

-

- Invoices, slips, and free issues in one place. -

+

Invoices, slips, and free issues in one place.

-
+
{sections.map((section) => { const Icon = section.icon return ( -
- -
-

{section.title}

-

{section.description}

-
- Open -
+ + +
+ +
+ {section.title} + {section.description} +
+ +
Open
+
+
) })} diff --git a/Frontend/erp-system/app/dashboard/sales/slips/page.tsx b/Frontend/erp-system/app/dashboard/sales/slips/page.tsx index ad31b4a..0787b3e 100644 --- a/Frontend/erp-system/app/dashboard/sales/slips/page.tsx +++ b/Frontend/erp-system/app/dashboard/sales/slips/page.tsx @@ -2,13 +2,15 @@ import { useEffect, useMemo, useState } from "react" import Link from "next/link" -import { ChevronLeft, ChevronRight, Eye, Filter, Package2, Plus, Printer } from "lucide-react" +import { ChevronLeft, ChevronRight, Eye, Package2, Plus, Printer, Search } from "lucide-react" import { salesApi } from "@/lib/api/sales" import { errorMessage } from "@/lib/error-map" import { Button, buttonVariants } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" +import { Card, CardContent, CardHeader } from "@/components/ui/card" import { Input } from "@/components/ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Skeleton } from "@/components/ui/skeleton" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { cn } from "@/lib/utils" @@ -18,7 +20,6 @@ import { SalesSlipStatus, SalesSlipSummary } from "@/types/sales" type StatusFilter = SalesSlipStatus | "All" const PAGE_SIZE = 10 -const tabs = ["All", "Draft", "Posted", "Cancelled"] as const function statusClass(status: SalesSlipStatus) { switch (status) { @@ -95,36 +96,32 @@ export default function SalesSlipsPage() {
-
-
-
- {tabs.map((t) => ( - - ))} + + +
+
+ + setSearchInput(e.target.value)} + placeholder="Search customer or slip number" + className="h-14 w-full pl-11 text-base" + aria-label="Search slips" + /> +
+ value={status} onValueChange={(v) => setStatus(v ?? "All")}> + + + + + All statuses + Draft + Posted + Cancelled + +
- -
- setSearchInput(e.target.value)} - placeholder="Filter by customer or slip number" - className="h-12 w-full lg:max-w-sm" - /> - -
-
+ {error &&
{error}
} @@ -145,6 +142,7 @@ export default function SalesSlipsPage() { {!error && rows !== null && visibleRows.length > 0 && ( <> +
@@ -191,6 +189,7 @@ export default function SalesSlipsPage() {
+
@@ -229,7 +228,7 @@ export default function SalesSlipsPage() { )} )} -
+
) }