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:
Dhananjaya99
2026-10-18 12:46:31 +05:30
parent c27db06880
commit e7b1f270a5
202 changed files with 99906 additions and 1894 deletions
@@ -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"
/>
)}
</>
)
}