Compare commits
No commits in common. "c47f7c1b6ad47a4b4e08bf264191deb795f3b01a" and "11db1e6941a1287141eb16c232ebe4028fb11b7c" have entirely different histories.
c47f7c1b6a
...
11db1e6941
@ -1,5 +0,0 @@
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
export default function CalendarsPage() {
|
||||
return redirect("/calendar/appointment/list")
|
||||
}
|
||||
@ -23,7 +23,7 @@ export default async function layout(props: {
|
||||
<DashboardDetailsPage
|
||||
className="p-0 lg:p-0"
|
||||
title={title}
|
||||
description={(employee.data as any)?.position || employee.data?.designation || undefined}
|
||||
description={employee.data?.position || employee.data?.designation || undefined}
|
||||
backHref="/productivity/employees"
|
||||
actions={<EmployeeActions employeeId={id} />}
|
||||
tabs={[
|
||||
|
||||
@ -146,15 +146,14 @@ export default function CustomerNotesPage() {
|
||||
|
||||
return (
|
||||
<DashboardPage
|
||||
headerProps={{
|
||||
title: "Notes",
|
||||
actions: (
|
||||
<Button size="sm" onClick={() => setDialogOpen(true)}>
|
||||
<Plus className="size-4" />
|
||||
Add Note
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
header={null}
|
||||
title="Notes"
|
||||
toolbar={
|
||||
<Button size="sm" onClick={() => setDialogOpen(true)}>
|
||||
<Plus className="size-4" />
|
||||
Add Note
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
|
||||
@ -16,7 +16,7 @@ export default function CustomerVehiclesPage({ params }: { params: Promise<{ id:
|
||||
|
||||
return (
|
||||
<ResourcePage<VehiclesClient>
|
||||
tableHeader={({ invalidateQuery, selectedItem, closeDialog }) => (
|
||||
toolbar={({ invalidateQuery, selectedItem, closeDialog }) => (
|
||||
<FormDialog title="Vehicle">
|
||||
{(resourceId) => (
|
||||
<VehicleForm
|
||||
|
||||
@ -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 as any).customer_type?.name?.toLocaleLowerCase() === "company";
|
||||
const isCompany = row.original.customer_type?.name?.toLocaleLowerCase() === "company";
|
||||
const companyName = row.original.company_name
|
||||
const name = isCompany && companyName ? `${customerName} (${row.original.last_name})` : customerName
|
||||
|
||||
|
||||
@ -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 } from "@garage/api"
|
||||
import { JOB_CARD_ROUTES, JobCardResponseData } 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: any) => {
|
||||
const handleDelete = async (attachment: JobCardResponseData['attachment_files'][number]) => {
|
||||
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 as any[])?.map((attachment) => {
|
||||
{attachments?.map((attachment) => {
|
||||
const Icon = getFileIcon(attachment.attachment_path)
|
||||
return (
|
||||
<Card key={attachment.id}>
|
||||
|
||||
@ -18,7 +18,7 @@ export default function VehicleEstimatesPage({ params }: { params: Promise<{ id:
|
||||
return (
|
||||
<>
|
||||
<ResourcePage<EstimatesClient>
|
||||
tableHeader={({ invalidateQuery, selectedItem, closeDialog }) => (
|
||||
toolbar={({ invalidateQuery, selectedItem, closeDialog }) => (
|
||||
<FormDialog title="Estimate">
|
||||
{(resourceId) => (
|
||||
<EstimateForm
|
||||
|
||||
@ -143,15 +143,14 @@ export default function VehicleMileagePage() {
|
||||
|
||||
|
||||
<DashboardPage
|
||||
headerProps={{
|
||||
title: 'Mileage',
|
||||
actions: (
|
||||
<Button onClick={handleCreate}>
|
||||
<Plus className="size-4" />
|
||||
Add Mileage
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
header={null}
|
||||
toolbar={
|
||||
<Button onClick={handleCreate}>
|
||||
<Plus className="size-4" />
|
||||
Add Mileage
|
||||
</Button>
|
||||
}
|
||||
title='Milage'
|
||||
>
|
||||
|
||||
<Card>
|
||||
|
||||
@ -129,15 +129,12 @@ export default function VehicleOwnersPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardPage headerProps={{
|
||||
title: "Owners",
|
||||
actions: (
|
||||
<Button className="w-full" size={'lg'} onClick={() => setLinkDialogOpen(true)}>
|
||||
<Plus />
|
||||
Add Owner
|
||||
</Button>
|
||||
),
|
||||
}}>
|
||||
<DashboardPage title="Owners" header={null} toolbar={
|
||||
<Button className="w-full" size={'lg'} onClick={() => setLinkDialogOpen(true)}>
|
||||
<Plus />
|
||||
Add Owner
|
||||
</Button>
|
||||
}>
|
||||
|
||||
<Card>
|
||||
<CardContent>
|
||||
|
||||
@ -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.data?.token && data.data?.user) {
|
||||
await login(data.data.token, data.data.user as Parameters<typeof login>[1])
|
||||
if (data.token && data.user) {
|
||||
await login(data.token, data.user as Parameters<typeof login>[1])
|
||||
router.push("/")
|
||||
}
|
||||
},
|
||||
|
||||
@ -63,7 +63,7 @@ export function FinancialSummaryChart({ data }: Props) {
|
||||
fill="var(--color-amount)"
|
||||
barSize={48}
|
||||
>
|
||||
{chartData.map((_entry: any, index: number) => (
|
||||
{chartData.map((_entry, index) => (
|
||||
<rect key={index} fill={colors[index % colors.length]} />
|
||||
))}
|
||||
</Bar>
|
||||
|
||||
@ -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 = any
|
||||
type AppointmentDetail = NonNullable<NonNullable<NonNullable<DashboardData["upcoming_appointments"]>["today"]>["details"]>[number]
|
||||
|
||||
function AppointmentRow({ appt }: { appt: AppointmentDetail }) {
|
||||
return (
|
||||
@ -53,7 +53,7 @@ function EmptyState() {
|
||||
}
|
||||
|
||||
export function UpcomingAppointmentsCard({ data }: Props) {
|
||||
const upcoming = data.upcoming_appointments as any
|
||||
const upcoming = data.upcoming_appointments
|
||||
|
||||
const tabs = [
|
||||
{ key: "today", label: "Today", data: upcoming?.today },
|
||||
|
||||
@ -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 & Record<string, any>
|
||||
export type DashboardData = HomeDashboardResponse
|
||||
|
||||
export function useDashboardData() {
|
||||
const api = useAuthApi()
|
||||
|
||||
@ -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: any) => ({
|
||||
const bodyData = bodyTypes.map((bt) => ({
|
||||
name: bt.body_type ?? "Unknown",
|
||||
vehicles_count: bt.vehicles_count ?? 0,
|
||||
}))
|
||||
|
||||
const makeData = makes.map((m: any) => ({
|
||||
const makeData = makes.map((m) => ({
|
||||
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: any, index: number) => (
|
||||
{bodyData.map((_entry, index) => (
|
||||
<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: any, index: number) => (
|
||||
{makeData.map((_entry, index) => (
|
||||
<Cell key={index} fill={COLORS[index % COLORS.length]} />
|
||||
))}
|
||||
</Bar>
|
||||
|
||||
@ -36,7 +36,7 @@ export function WorkOrdersStatusCard({ data }: Props) {
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{cards.map((card: any) => {
|
||||
{cards.map((card) => {
|
||||
const percentage = totalOrders > 0 ? ((card.count ?? 0) / totalOrders) * 100 : 0
|
||||
return (
|
||||
<div key={card.status} className="space-y-1.5">
|
||||
|
||||
@ -27,8 +27,8 @@ export function InspectionCategoryInlineForm({ onSuccess }: InlineCreateFormProp
|
||||
const handleSubmit = async (values: FormValues) => {
|
||||
try {
|
||||
const result = await api.inspections.createCategory({
|
||||
title: values.inspection_name,
|
||||
} as any)
|
||||
inspection_name: values.inspection_name,
|
||||
})
|
||||
toast.success("Inspection category created")
|
||||
form.reset()
|
||||
const item = (result as any)?.data ?? result
|
||||
|
||||
@ -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)) as any
|
||||
: api.inspections.create(payload)
|
||||
toast.promise(promise, {
|
||||
loading: isEditing ? "Updating inspection..." : "Creating inspection...",
|
||||
success: isEditing ? "Inspection updated successfully" : "Inspection created successfully",
|
||||
|
||||
@ -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?.()
|
||||
|
||||
@ -28,7 +28,7 @@ const STATUS_ICONS: Record<JobCardStatus, React.ComponentType<{ className?: stri
|
||||
check_in: LogIn,
|
||||
in_progress: Loader,
|
||||
on_hold: Pause,
|
||||
ready_to_delivery: PackageCheck,
|
||||
ready_to_deliver: PackageCheck,
|
||||
delivered: CheckCircle2,
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ export function JobCardStatusStepper({ jobCardId }: JobCardStatusStepperProps) {
|
||||
return promise
|
||||
},
|
||||
onSuccess: (_data, status) => {
|
||||
(jobCard as any)?.setStatus(status)
|
||||
jobCard?.setStatus(status)
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ export function DocumentTypeInlineForm({ onSuccess }: InlineCreateFormProps) {
|
||||
|
||||
const handleSubmit = async (values: FormValues) => {
|
||||
try {
|
||||
const result = await api.vehicleDocuments.createDocumentType({ title: values.title } as any)
|
||||
const result = await api.vehicleDocuments.createDocumentType({ title: values.title })
|
||||
toast.success("Document type created")
|
||||
form.reset()
|
||||
const item = (result as any)?.data ?? result
|
||||
|
||||
@ -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 as any)
|
||||
: api.vehicles.create(payload as any)
|
||||
? api.vehicles.update(resourceId, payload)
|
||||
: api.vehicles.create(payload)
|
||||
toast.promise(promise, {
|
||||
loading: isEditing ? "Updating vehicle..." : "Creating vehicle...",
|
||||
success: isEditing ? "Vehicle updated successfully" : "Vehicle created successfully",
|
||||
|
||||
@ -27,13 +27,11 @@ 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
|
||||
@ -48,16 +46,13 @@ 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={mergedHeaderProps}
|
||||
headerProps={resolvedHeaderProps}
|
||||
fullscreen
|
||||
>
|
||||
<Card className="rounded-none">
|
||||
|
||||
@ -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,7 +51,6 @@ 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...",
|
||||
|
||||
@ -21,6 +21,6 @@ export class AuthClient extends ApiClient {
|
||||
}
|
||||
|
||||
async logout() {
|
||||
return this.post(AUTH_ROUTES.LOGOUT, {} as never)
|
||||
return this.post(AUTH_ROUTES.LOGOUT, undefined)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,9 @@ export const CUSTOMER_ROUTES = {
|
||||
EXPORT: "/api/customers/export",
|
||||
IMPORT: "/api/customers/import",
|
||||
CUSTOMER_TYPES: "/api/customer-types",
|
||||
NOTES: "/api/customers/{id}/notes/{note_id}",
|
||||
ADD_NOTE: "/api/customers/{id}/add-note",
|
||||
DELETE_NOTE: "/api/customers/{id}/delete-note",
|
||||
UPDATE_PERMISSIONS: "/api/customers/{id}/update-permissions",
|
||||
} as const satisfies Record<string, ApiPath>
|
||||
|
||||
export class CustomersClient extends CrudClient<typeof CUSTOMER_ROUTES.INDEX, typeof CUSTOMER_ROUTES.BY_ID> {
|
||||
@ -35,10 +37,14 @@ export class CustomersClient extends CrudClient<typeof CUSTOMER_ROUTES.INDEX, ty
|
||||
}
|
||||
|
||||
async addNote(id: string, payload: { note: string }) {
|
||||
return this.post(CUSTOMER_ROUTES.NOTES, payload as never, { params: { id, note_id: "0" } } as never)
|
||||
return this.post(CUSTOMER_ROUTES.ADD_NOTE, payload as never, { params: { id } } as never)
|
||||
}
|
||||
|
||||
async deleteNote(customerId: string, noteId: number) {
|
||||
return this.delete(CUSTOMER_ROUTES.NOTES, { params: { id: customerId, note_id: String(noteId) } } as never)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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">) {
|
||||
|
||||
@ -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 "../contracts/types"
|
||||
import { ApiBaseResponse } from "src/contracts/types"
|
||||
|
||||
export const JOB_CARD_ROUTES = {
|
||||
INDEX: "/api/job-cards",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user