feat: add logo field to settings schema and update settings client to handle file uploads

feat: integrate dialog close context in vendor select field and CRUD dialog components

feat: enhance vendor general info to format status using utility function

feat: implement form dialog context for managing dialog close actions

feat: add async select field dialog close context for better form handling

fix: update form mutation hook to close dialog on successful submission

feat: extend document print types to include expense and credit note

feat: add settings update payload type to include logo and other fields

feat: create employee attendance and work history pages with resource management

feat: implement payment made and received detail pages with actions

feat: add quick shortcuts component for easy navigation in the dashboard

feat: create actions for payment made and received with print and delete options

feat: implement dialog close context for better dialog management

feat: add error parsing utility for improved error handling in API responses
This commit is contained in:
2026-05-19 17:56:39 +04:00
parent 2d6e3c5734
commit 4f0a2f790f
56 changed files with 1220 additions and 92 deletions
@@ -17,9 +17,11 @@ import {
RhfAsyncSelectField,
RhfTextareaField,
} from "@/shared/components/form"
import { RhfImageField } from "@/shared/components/form/fields/rhf-image-field"
import { useAuthApi } from "@/shared/useApi"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import { toId } from "@/shared/lib/utils"
import { CONSTANTS } from "@/config/constants"
import { FirstDayOfWork } from "@garage/api"
import { SETTINGS_ROUTES } from "@garage/api"
@@ -58,6 +60,7 @@ const DEFAULT_VALUES: SettingsFormValues = {
description: "",
security: "",
privacy_policy: "",
logo: null,
}
// ── Mapping helpers ──
@@ -85,6 +88,7 @@ function mapToFormValues(data: unknown): SettingsFormValues {
description: d.description ?? "",
security: d.security ?? "",
privacy_policy: d.privacy_policy ?? "",
logo: null,
}
}
@@ -93,11 +97,24 @@ function mapFormToPayload(values: SettingsFormValues) {
name: values.name,
email: values.email || undefined,
phone: values.phone || undefined,
alternative_phone: values.alternative_phone || undefined,
website: values.website || undefined,
time_zone: values.time_zone || undefined,
upi_id: values.upi_id || undefined,
first_day_of_work: values.first_day_of_work || undefined,
latitude: values.latitude || undefined,
longitude: values.longitude || undefined,
bank_details: values.bank_details || undefined,
first_address_line: values.first_address_line || undefined,
second_address_line: values.second_address_line || undefined,
country_id: toId(values.country),
state_id: toId(values.state),
city: values.city || undefined,
zip_code: values.zip_code || undefined,
description: values.description || undefined,
security: values.security || undefined,
privacy_policy: values.privacy_policy || undefined,
logo: values.logo instanceof File ? values.logo : undefined,
}
}
@@ -116,6 +133,14 @@ export function SettingsForm() {
queryFn: () => api.settings.fetch(),
})
const existingLogoPath: string | null = (() => {
const raw = (data as any)?.data
const record = Array.isArray(raw) ? raw[0] : raw
const logo = record?.logo
if (!logo || typeof logo !== "string") return null
return logo.startsWith("http") ? logo : CONSTANTS.getAssetUrl(`storage/${logo.replace(/^\/+/, "")}`)
})()
useEffect(() => {
if (!data) return
const raw = (data as any)?.data
@@ -286,6 +311,20 @@ export function SettingsForm() {
{/* Sidebar - 4/12 */}
<div className="lg:col-span-4">
<FieldGroup className="sticky top-24 space-y-6">
{/* Logo Section */}
<div className="rounded-lg border bg-card p-4">
<h3 className="mb-1 text-base font-semibold">Workshop Logo</h3>
<p className="text-muted-foreground mb-3 text-xs">
Shown on printed invoices, estimates, job cards and other documents.
</p>
<RhfImageField
name="logo"
accept="image/png,image/jpeg,image/jpg,image/webp,image/svg+xml"
disabled={isLoading}
initialPreviewUrl={existingLogoPath}
/>
</div>
{/* Location & Time Section */}
<div className="rounded-lg border bg-card p-4">
<h3 className="mb-4 text-base font-semibold">Location & Time</h3>
@@ -340,8 +379,8 @@ export function SettingsForm() {
<div className="mt-4">
<RhfTextareaField
name="privacy_policy"
label="Privacy Policy"
placeholder="Privacy policy text..."
label="Terms and Conditions"
placeholder="Terms and conditions text..."
disabled={isLoading}
/>
</div>
@@ -51,6 +51,14 @@ export const settingsFormSchema = z.object({
description: z.string().optional(),
security: z.string().optional(),
privacy_policy: z.string().optional(),
logo: z
.any()
.nullable()
.optional()
.refine(
(v) => v == null || v instanceof File,
"Logo must be an image file",
),
})
export type SettingsFormValues = z.infer<typeof settingsFormSchema>