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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user