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
@@ -2,6 +2,7 @@
import { useEffect, useState } from "react"
import { useCustomerConfig } from "@/hooks/use-customer-config"
import { customersApi } from "@/lib/api/customers"
import { errorMessage, fieldErrors } from "@/lib/error-map"
import { Customer, CustomerType } from "@/types/customers"
@@ -28,6 +29,8 @@ export function EditCustomerDialog({
onClose: () => void
onSaved: (customer: Customer) => void
}) {
// A field switched off in Sales Configuration is hidden; its stored value is sent back unchanged.
const { fields } = useCustomerConfig()
const [customer, setCustomer] = useState<Customer | null>(null)
const [etag, setEtag] = useState<string | null>(null)
const [loadError, setLoadError] = useState<string | null>(null)
@@ -194,48 +197,70 @@ export function EditCustomerDialog({
</Select>
</Field>
</div>
<div className="grid grid-cols-2 gap-3">
<Field>
<FieldLabel htmlFor="ec-phone">Phone (optional)</FieldLabel>
<Input id="ec-phone" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="0771234567" />
</Field>
<Field>
<FieldLabel htmlFor="ec-email">Email (optional)</FieldLabel>
<Input id="ec-email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="customer@example.com" />
</Field>
</div>
<Field>
<FieldLabel htmlFor="ec-addr1">Address line 1 (optional)</FieldLabel>
<Input id="ec-addr1" value={addressLine1} onChange={(e) => setAddressLine1(e.target.value)} />
</Field>
<Field>
<FieldLabel htmlFor="ec-addr2">Address line 2 (optional)</FieldLabel>
<Input id="ec-addr2" value={addressLine2} onChange={(e) => setAddressLine2(e.target.value)} />
</Field>
<div className="grid grid-cols-2 gap-3">
<Field>
<FieldLabel htmlFor="ec-city">City (optional)</FieldLabel>
<Input id="ec-city" value={city} onChange={(e) => setCity(e.target.value)} />
</Field>
<Field>
<FieldLabel htmlFor="ec-country">Country (optional)</FieldLabel>
<Input id="ec-country" value={country} onChange={(e) => setCountry(e.target.value)} />
</Field>
</div>
{(fields.phone || fields.email) && (
<div className="grid grid-cols-2 gap-3">
{fields.phone && (
<Field>
<FieldLabel htmlFor="ec-phone">Phone (optional)</FieldLabel>
<Input id="ec-phone" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="0771234567" />
</Field>
)}
{fields.email && (
<Field>
<FieldLabel htmlFor="ec-email">Email (optional)</FieldLabel>
<Input id="ec-email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="customer@example.com" />
</Field>
)}
</div>
)}
{fields.address && (
<>
<Field>
<FieldLabel htmlFor="ec-addr1">Address line 1 (optional)</FieldLabel>
<Input id="ec-addr1" value={addressLine1} onChange={(e) => setAddressLine1(e.target.value)} />
</Field>
<Field>
<FieldLabel htmlFor="ec-addr2">Address line 2 (optional)</FieldLabel>
<Input id="ec-addr2" value={addressLine2} onChange={(e) => setAddressLine2(e.target.value)} />
</Field>
</>
)}
{(fields.city || fields.country) && (
<div className="grid grid-cols-2 gap-3">
{fields.city && (
<Field>
<FieldLabel htmlFor="ec-city">City (optional)</FieldLabel>
<Input id="ec-city" value={city} onChange={(e) => setCity(e.target.value)} />
</Field>
)}
{fields.country && (
<Field>
<FieldLabel htmlFor="ec-country">Country (optional)</FieldLabel>
<Input id="ec-country" value={country} onChange={(e) => setCountry(e.target.value)} />
</Field>
)}
</div>
)}
<Field>
<FieldLabel htmlFor="ec-tax">Tax registration (optional)</FieldLabel>
<Input id="ec-tax" value={taxRegistrationNo} onChange={(e) => setTaxRegistrationNo(e.target.value)} />
</Field>
<div className="grid grid-cols-2 gap-3">
<Field>
<FieldLabel htmlFor="ec-credit-limit">Credit limit (LKR)</FieldLabel>
<Input id="ec-credit-limit" type="number" min="0" step="0.01" placeholder="0" value={creditLimit} onChange={(e) => setCreditLimit(e.target.value)} />
</Field>
<Field>
<FieldLabel htmlFor="ec-credit-days">Credit days</FieldLabel>
<Input id="ec-credit-days" type="number" min="0" step="1" placeholder="0" value={creditDays} onChange={(e) => setCreditDays(e.target.value)} />
</Field>
</div>
{(fields.creditLimit || fields.creditDays) && (
<div className="grid grid-cols-2 gap-3">
{fields.creditLimit && (
<Field>
<FieldLabel htmlFor="ec-credit-limit">Credit limit (LKR)</FieldLabel>
<Input id="ec-credit-limit" type="number" min="0" step="0.01" placeholder="0" value={creditLimit} onChange={(e) => setCreditLimit(e.target.value)} />
</Field>
)}
{fields.creditDays && (
<Field>
<FieldLabel htmlFor="ec-credit-days">Credit days</FieldLabel>
<Input id="ec-credit-days" type="number" min="0" step="1" placeholder="0" value={creditDays} onChange={(e) => setCreditDays(e.target.value)} />
</Field>
)}
</div>
)}
</FieldGroup>
)}