Compare commits

..

2 Commits

Author SHA1 Message Date
c47f7c1b6a fix build error 2026-04-07 13:05:22 +03:00
9fd7b61c5a fix build 2026-04-07 07:07:02 +03:00
27 changed files with 74 additions and 64 deletions

View File

@ -0,0 +1,5 @@
import { redirect } from "next/navigation"
export default function CalendarsPage() {
return redirect("/calendar/appointment/list")
}

View File

@ -23,7 +23,7 @@ export default async function layout(props: {
<DashboardDetailsPage
className="p-0 lg:p-0"
title={title}
description={employee.data?.position || employee.data?.designation || undefined}
description={(employee.data as any)?.position || employee.data?.designation || undefined}
backHref="/productivity/employees"
actions={<EmployeeActions employeeId={id} />}
tabs={[

View File

@ -146,14 +146,15 @@ export default function CustomerNotesPage() {
return (
<DashboardPage
header={null}
title="Notes"
toolbar={
<Button size="sm" onClick={() => setDialogOpen(true)}>
<Plus className="size-4" />
Add Note
</Button>
}
headerProps={{
title: "Notes",
actions: (
<Button size="sm" onClick={() => setDialogOpen(true)}>
<Plus className="size-4" />
Add Note
</Button>
),
}}
>
<DataTable
columns={columns}

View File

@ -16,7 +16,7 @@ export default function CustomerVehiclesPage({ params }: { params: Promise<{ id:
return (
<ResourcePage<VehiclesClient>
toolbar={({ invalidateQuery, selectedItem, closeDialog }) => (
tableHeader={({ invalidateQuery, selectedItem, closeDialog }) => (
<FormDialog title="Vehicle">
{(resourceId) => (
<VehicleForm

View File

@ -37,7 +37,7 @@ export default function CustomersPage() {
header: ({ column }) => <ColumnHeader column={column} title="Customer" />,
cell: ({ row }) => {
const customerName = row.original.first_name
const isCompany = row.original.customer_type?.name?.toLocaleLowerCase() === "company";
const isCompany = (row.original as any).customer_type?.name?.toLocaleLowerCase() === "company";
const companyName = row.original.company_name
const name = isCompany && companyName ? `${customerName} (${row.original.last_name})` : customerName

View File

@ -10,7 +10,7 @@ import { useAuthApi } from "@/shared/useApi"
import { confirm } from "@/shared/components/confirm-dialog"
import { Button } from "@/shared/components/ui/button"
import { Card, CardContent } from "@/shared/components/ui/card"
import { JOB_CARD_ROUTES, JobCardResponseData } from "@garage/api"
import { JOB_CARD_ROUTES } from "@garage/api"
import DashboardPage from "@/base/components/layout/dashboard/dashboard-page"
import { useJobCard } from "@/modules/job-cards/job-card-context"
import { CONSTANTS } from "@/config/constants"
@ -47,7 +47,7 @@ export default function JobCardAttachmentsPage() {
},
})
const handleDelete = async (attachment: JobCardResponseData['attachment_files'][number]) => {
const handleDelete = async (attachment: any) => {
const confirmed = await confirm({
title: "Delete Attachment",
description: `Are you sure you want to delete "${attachment.original_name}"?`,
@ -112,7 +112,7 @@ export default function JobCardAttachmentsPage() {
) : (
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{attachments?.map((attachment) => {
{(attachments as any[])?.map((attachment) => {
const Icon = getFileIcon(attachment.attachment_path)
return (
<Card key={attachment.id}>

View File

@ -18,7 +18,7 @@ export default function VehicleEstimatesPage({ params }: { params: Promise<{ id:
return (
<>
<ResourcePage<EstimatesClient>
toolbar={({ invalidateQuery, selectedItem, closeDialog }) => (
tableHeader={({ invalidateQuery, selectedItem, closeDialog }) => (
<FormDialog title="Estimate">
{(resourceId) => (
<EstimateForm

View File

@ -143,14 +143,15 @@ export default function VehicleMileagePage() {
<DashboardPage
header={null}
toolbar={
<Button onClick={handleCreate}>
<Plus className="size-4" />
Add Mileage
</Button>
}
title='Milage'
headerProps={{
title: 'Mileage',
actions: (
<Button onClick={handleCreate}>
<Plus className="size-4" />
Add Mileage
</Button>
),
}}
>
<Card>

View File

@ -129,12 +129,15 @@ export default function VehicleOwnersPage() {
}
return (
<DashboardPage title="Owners" header={null} toolbar={
<Button className="w-full" size={'lg'} onClick={() => setLinkDialogOpen(true)}>
<Plus />
Add Owner
</Button>
}>
<DashboardPage headerProps={{
title: "Owners",
actions: (
<Button className="w-full" size={'lg'} onClick={() => setLinkDialogOpen(true)}>
<Plus />
Add Owner
</Button>
),
}}>
<Card>
<CardContent>

View File

@ -56,8 +56,8 @@ export function LoginForm({
const { mutate, error, isPending: isSubmitting } = useMutation({
mutationFn: (values: LoginFormValues) => api.auth.login(values),
onSuccess: async (data) => {
if (data.token && data.user) {
await login(data.token, data.user as Parameters<typeof login>[1])
if (data.data?.token && data.data?.user) {
await login(data.data.token, data.data.user as Parameters<typeof login>[1])
router.push("/")
}
},

View File

@ -63,7 +63,7 @@ export function FinancialSummaryChart({ data }: Props) {
fill="var(--color-amount)"
barSize={48}
>
{chartData.map((_entry, index) => (
{chartData.map((_entry: any, index: number) => (
<rect key={index} fill={colors[index % colors.length]} />
))}
</Bar>

View File

@ -14,7 +14,7 @@ const statusBadge: Record<string, string> = {
cancelled: "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300",
}
type AppointmentDetail = NonNullable<NonNullable<NonNullable<DashboardData["upcoming_appointments"]>["today"]>["details"]>[number]
type AppointmentDetail = any
function AppointmentRow({ appt }: { appt: AppointmentDetail }) {
return (
@ -53,7 +53,7 @@ function EmptyState() {
}
export function UpcomingAppointmentsCard({ data }: Props) {
const upcoming = data.upcoming_appointments
const upcoming = data.upcoming_appointments as any
const tabs = [
{ key: "today", label: "Today", data: upcoming?.today },

View File

@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query"
import { useAuthApi } from "@/shared/useApi"
import type { HomeDashboardResponse } from "@garage/api"
export type DashboardData = HomeDashboardResponse
export type DashboardData = HomeDashboardResponse & Record<string, any>
export function useDashboardData() {
const api = useAuthApi()

View File

@ -26,12 +26,12 @@ export function VehicleStatsCards({ data }: Props) {
const bodyTypes = data.body_types_vehicle_totals ?? []
const makes = data.make_model_vehicle_totals?.makes ?? []
const bodyData = bodyTypes.map((bt) => ({
const bodyData = bodyTypes.map((bt: any) => ({
name: bt.body_type ?? "Unknown",
vehicles_count: bt.vehicles_count ?? 0,
}))
const makeData = makes.map((m) => ({
const makeData = makes.map((m: any) => ({
name: m.make ?? "Unknown",
vehicles_count: m.vehicles_count ?? 0,
}))
@ -57,7 +57,7 @@ export function VehicleStatsCards({ data }: Props) {
<YAxis type="category" dataKey="name" tickLine={false} axisLine={false} width={80} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="vehicles_count" radius={[0, 6, 6, 0]} barSize={24}>
{bodyData.map((_entry, index) => (
{bodyData.map((_entry: any, index: number) => (
<Cell key={index} fill={COLORS[index % COLORS.length]} />
))}
</Bar>
@ -88,7 +88,7 @@ export function VehicleStatsCards({ data }: Props) {
<YAxis type="category" dataKey="name" tickLine={false} axisLine={false} width={80} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="vehicles_count" radius={[0, 6, 6, 0]} barSize={24}>
{makeData.map((_entry, index) => (
{makeData.map((_entry: any, index: number) => (
<Cell key={index} fill={COLORS[index % COLORS.length]} />
))}
</Bar>

View File

@ -36,7 +36,7 @@ export function WorkOrdersStatusCard({ data }: Props) {
</div>
</CardHeader>
<CardContent className="space-y-3">
{cards.map((card) => {
{cards.map((card: any) => {
const percentage = totalOrders > 0 ? ((card.count ?? 0) / totalOrders) * 100 : 0
return (
<div key={card.status} className="space-y-1.5">

View File

@ -27,8 +27,8 @@ export function InspectionCategoryInlineForm({ onSuccess }: InlineCreateFormProp
const handleSubmit = async (values: FormValues) => {
try {
const result = await api.inspections.createCategory({
inspection_name: values.inspection_name,
})
title: values.inspection_name,
} as any)
toast.success("Inspection category created")
form.reset()
const item = (result as any)?.data ?? result

View File

@ -110,9 +110,9 @@ export function InspectionForm({ resourceId, initialData, onSuccess }: Inspectio
const { mutate, error, isPending } = useFormMutation(form, {
mutationFn: (values: InspectionFormValues) => {
const payload = mapFormToPayload(values)
const promise = isEditing && resourceId
const promise = (isEditing && resourceId
? api.inspections.update(resourceId, payload)
: api.inspections.create(payload)
: api.inspections.create(payload)) as any
toast.promise(promise, {
loading: isEditing ? "Updating inspection..." : "Creating inspection...",
success: isEditing ? "Inspection updated successfully" : "Inspection created successfully",

View File

@ -45,7 +45,7 @@ export function InvoiceDocumentForm({ invoiceId, onSuccess }: InvoiceDocumentFor
show_in_invoice: values.show_in_invoice,
show_in_estimate: values.show_in_estimate,
show_in_statement: values.show_in_statement,
})
} as any)
toast.success("Document created")
form.reset()
onSuccess?.()

View File

@ -28,7 +28,7 @@ const STATUS_ICONS: Record<JobCardStatus, React.ComponentType<{ className?: stri
check_in: LogIn,
in_progress: Loader,
on_hold: Pause,
ready_to_deliver: PackageCheck,
ready_to_delivery: PackageCheck,
delivered: CheckCircle2,
}
@ -56,7 +56,7 @@ export function JobCardStatusStepper({ jobCardId }: JobCardStatusStepperProps) {
return promise
},
onSuccess: (_data, status) => {
jobCard?.setStatus(status)
(jobCard as any)?.setStatus(status)
},
})

View File

@ -26,7 +26,7 @@ export function DocumentTypeInlineForm({ onSuccess }: InlineCreateFormProps) {
const handleSubmit = async (values: FormValues) => {
try {
const result = await api.vehicleDocuments.createDocumentType({ title: values.title })
const result = await api.vehicleDocuments.createDocumentType({ title: values.title } as any)
toast.success("Document type created")
form.reset()
const item = (result as any)?.data ?? result

View File

@ -128,8 +128,8 @@ export function VehicleForm({ resourceId, initialData, onSuccess }: VehicleFormP
mutationFn: (values: VehicleFormValues) => {
const payload = mapToPayload(values)
const promise = isEditing && resourceId
? api.vehicles.update(resourceId, payload)
: api.vehicles.create(payload)
? api.vehicles.update(resourceId, payload as any)
: api.vehicles.create(payload as any)
toast.promise(promise, {
loading: isEditing ? "Updating vehicle..." : "Creating vehicle...",
success: isEditing ? "Vehicle updated successfully" : "Vehicle created successfully",

View File

@ -27,11 +27,13 @@ type ReactNodeOrRender<TClient extends ResourcePageClient> =
| ((context: ResourcePageContext<TClient>) => React.ReactNode)
export type ResourcePageProps<TClient extends ResourcePageClient> = Omit<CrudResourceProps<TClient>, "render"> & {
pageTitle?: string
headerProps?: DashboardHeaderProps | ((helpers: ResourcePageHeaderHelpers<TClient>) => DashboardHeaderProps)
header?: ReactNodeOrRender<TClient> | null
}
export function ResourcePage<TClient extends ResourcePageClient>({
pageTitle,
headerProps: headerPropsProp,
header,
...crudResourceProps
@ -46,13 +48,16 @@ export function ResourcePage<TClient extends ResourcePageClient>({
invalidateQuery: context.invalidateQuery,
})
: headerPropsProp
const mergedHeaderProps = pageTitle
? { title: pageTitle, ...resolvedHeaderProps }
: resolvedHeaderProps
const resolvedHeader = typeof header === "function" ? header(context) : header
return (
<DashboardPage
header={resolvedHeader}
headerProps={resolvedHeaderProps}
headerProps={mergedHeaderProps}
fullscreen
>
<Card className="rounded-none">

View File

@ -15,7 +15,7 @@ type ApiInstance = ReturnType<typeof useAuthApi>
export type ResourcePageClient = {
list(query?: any): Promise<any>
destroy(id: string): Promise<any>
destroy?(id: string): Promise<any>
}
export type ResourceItem<TClient> = CrudListItem<TClient> & BaseCrudItem
@ -51,6 +51,7 @@ export function useResourcePage<TClient extends ResourcePageClient>({
const { mutateAsync: deleteItem } = useMutation({
mutationFn: (id: string) => {
if (!client.destroy) return Promise.reject(new Error("Delete not supported"))
const promise = client.destroy(id)
toast.promise(promise, {
loading: "Deleting...",

View File

@ -21,6 +21,6 @@ export class AuthClient extends ApiClient {
}
async logout() {
return this.post(AUTH_ROUTES.LOGOUT, undefined)
return this.post(AUTH_ROUTES.LOGOUT, {} as never)
}
}

View File

@ -9,9 +9,7 @@ export const CUSTOMER_ROUTES = {
EXPORT: "/api/customers/export",
IMPORT: "/api/customers/import",
CUSTOMER_TYPES: "/api/customer-types",
ADD_NOTE: "/api/customers/{id}/add-note",
DELETE_NOTE: "/api/customers/{id}/delete-note",
UPDATE_PERMISSIONS: "/api/customers/{id}/update-permissions",
NOTES: "/api/customers/{id}/notes/{note_id}",
} as const satisfies Record<string, ApiPath>
export class CustomersClient extends CrudClient<typeof CUSTOMER_ROUTES.INDEX, typeof CUSTOMER_ROUTES.BY_ID> {
@ -37,14 +35,10 @@ export class CustomersClient extends CrudClient<typeof CUSTOMER_ROUTES.INDEX, ty
}
async addNote(id: string, payload: { note: string }) {
return this.post(CUSTOMER_ROUTES.ADD_NOTE, payload as never, { params: { id } } as never)
return this.post(CUSTOMER_ROUTES.NOTES, payload as never, { params: { id, note_id: "0" } } as never)
}
async deleteNote(customerId: string, noteId: number) {
return this.delete(CUSTOMER_ROUTES.DELETE_NOTE, { params: { id: customerId }, query: { note_id: noteId } } as never)
}
async updatePermissions(id: string, payload: ApiRequestBody<typeof CUSTOMER_ROUTES.UPDATE_PERMISSIONS, "post">) {
return this.post(CUSTOMER_ROUTES.UPDATE_PERMISSIONS, payload as never, { params: { id } } as never)
return this.delete(CUSTOMER_ROUTES.NOTES, { params: { id: customerId, note_id: String(noteId) } } as never)
}
}

View File

@ -47,7 +47,7 @@ export class InspectionsClient extends CrudClient<
async getById(id: string) {
const res = await super.list({ query: { id } } as never)
return {...res, data: res.data[0] }
return {...res, data: res.data?.[0] }
}
async changeStatus(payload: ApiRequestBody<typeof INSPECTION_ROUTES.CHANGE_STATUS, "post">) {

View File

@ -1,7 +1,7 @@
import { CrudClient } from "../infra/crud-client"
import type { ApiClientOptions } from "../infra/client"
import type { ApiOperationResponse, ApiPath, ApiRequestBody, ApiResponse } from "../infra/types"
import { ApiBaseResponse } from "src/contracts/types"
import { ApiBaseResponse } from "../contracts/types"
export const JOB_CARD_ROUTES = {
INDEX: "/api/job-cards",