feat: add vendor optional fields and configuration hooks
- Implemented VendorOptionalFields component to handle optional vendor details (mobile, email, address) with validation. - Created useCustomerConfig hook to load optional customer field settings from Sales configuration. - Developed useProcurementConfig hook to manage Procurement configuration flags. - Introduced accessConfigApi for managing access configuration settings. - Added openingBalancesApi for handling opening balances operations. - Created procurementConfigApi for managing procurement configuration settings. - Implemented nav-routes for permission-based routing. - Developed PDF generation for purchase orders with dynamic vendor and item details. - Added purchase-order tax helpers for tax calculations and formatting. - Defined types for opening balances and related operations.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"use client"
|
||||
|
||||
import { CalendarDays } from "lucide-react"
|
||||
|
||||
import { DateRangePicker } from "@/components/ui/date-picker"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
|
||||
export type DateRangePreset = "All" | "Today" | "7d" | "30d" | "Month" | "Custom"
|
||||
export type CustomDateRange = { from: Date | undefined; to?: Date | undefined }
|
||||
|
||||
export interface DateRangeValue {
|
||||
preset: DateRangePreset
|
||||
custom?: CustomDateRange
|
||||
}
|
||||
|
||||
const PRESETS: { value: DateRangePreset; label: string }[] = [
|
||||
{ value: "All", label: "All time" },
|
||||
{ value: "Today", label: "Today" },
|
||||
{ value: "7d", label: "Last 7 days" },
|
||||
{ value: "30d", label: "Last 30 days" },
|
||||
{ value: "Month", label: "This month" },
|
||||
{ value: "Custom", label: "Custom range" },
|
||||
]
|
||||
|
||||
export function toIsoDate(d: Date): string {
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`
|
||||
}
|
||||
|
||||
/** Inclusive `YYYY-MM-DD` bounds for a filter value; empty (no bounds) for "All time" or a custom range with no start yet. */
|
||||
export function dateRangeBounds(value: DateRangeValue): { from?: string; to?: string } {
|
||||
const today = new Date()
|
||||
switch (value.preset) {
|
||||
case "All":
|
||||
return {}
|
||||
case "Custom":
|
||||
return value.custom?.from ? { from: toIsoDate(value.custom.from), to: toIsoDate(value.custom.to ?? value.custom.from) } : {}
|
||||
case "Month":
|
||||
return { from: toIsoDate(new Date(today.getFullYear(), today.getMonth(), 1)), to: toIsoDate(today) }
|
||||
default: {
|
||||
const days = value.preset === "Today" ? 1 : value.preset === "7d" ? 7 : 30
|
||||
const from = new Date(today)
|
||||
from.setDate(today.getDate() - (days - 1))
|
||||
return { from: toIsoDate(from), to: toIsoDate(today) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A date-range dropdown (presets, plus a from–to picker for "Custom range") for list-page filter bars. */
|
||||
export function DateRangeFilter({
|
||||
value,
|
||||
onChange,
|
||||
label = "Date range",
|
||||
}: {
|
||||
value: DateRangeValue
|
||||
onChange: (next: DateRangeValue) => void
|
||||
label?: string
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Select<DateRangePreset> value={value.preset} onValueChange={(v) => onChange({ ...value, preset: v ?? "All" })}>
|
||||
<SelectTrigger className="h-10! w-40 text-sm" aria-label={label}>
|
||||
<CalendarDays className="size-4 text-muted-foreground" />
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PRESETS.map((p) => (
|
||||
<SelectItem key={p.value} value={p.value}>
|
||||
{p.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{value.preset === "Custom" && (
|
||||
<DateRangePicker
|
||||
value={value.custom}
|
||||
onChange={(custom) => onChange({ ...value, custom })}
|
||||
placeholder="Pick from – to dates"
|
||||
className="sm:w-72"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user