diff --git a/apps/dashboard/app/(authenticated)/sales/job-cards/page.tsx b/apps/dashboard/app/(authenticated)/sales/job-cards/page.tsx index f2cd890..ce3b4d8 100644 --- a/apps/dashboard/app/(authenticated)/sales/job-cards/page.tsx +++ b/apps/dashboard/app/(authenticated)/sales/job-cards/page.tsx @@ -58,11 +58,11 @@ export default function JobCardsPage() { title: "Job Cards", actions: ( - {(resourceId) => ( + {(resourceId, {close}) => ( { invalidateQuery(); close();}} /> )} diff --git a/apps/dashboard/modules/auth/login-form.tsx b/apps/dashboard/modules/auth/login-form.tsx index f13fb73..cc71dbe 100644 --- a/apps/dashboard/modules/auth/login-form.tsx +++ b/apps/dashboard/modules/auth/login-form.tsx @@ -55,9 +55,9 @@ 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[1]) + onSuccess: async (data:any) => { + if (data.token && data.user) { + await login(data.token, data.user as Parameters[1]) router.push("/") } }, diff --git a/apps/dashboard/modules/job-cards/job-card-check-in-dialog.tsx b/apps/dashboard/modules/job-cards/job-card-check-in-dialog.tsx new file mode 100644 index 0000000..76386d6 --- /dev/null +++ b/apps/dashboard/modules/job-cards/job-card-check-in-dialog.tsx @@ -0,0 +1,159 @@ +"use client" + +import { z } from "zod" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { toast } from "sonner" +import { useAuthApi } from "@/shared/useApi" +import { + Rhform, + RhfTextField, + RhfSelectField, + RhfDateField, + RhfTimeField, + RhfAsyncSelectField, +} from "@/shared/components/form" +import { FieldGroup } from "@/shared/components/ui/field" +import { Button } from "@/shared/components/ui/button" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/shared/components/ui/dialog" +import { DEPARTMENT_ROUTES } from "@garage/api" +import { FUEL_LEVEL_OPTIONS } from "./job-card.schema" +import { toId } from "@/shared/lib/utils" + +// ── Schema ── + +const checkInSchema = z.object({ + check_in_date: z.string().optional(), + check_in_time: z.string().optional(), + km_in: z.string().optional(), + fuel_level: z.string().optional(), + start_date: z.string().optional(), + start_time: z.string().optional(), + department: z.object({ value: z.any(), label: z.string() }).nullable().optional(), + delivery_date: z.string().optional(), + delivery_time: z.string().optional(), +}) + +type CheckInFormValues = z.infer + +const mapLookupOption = (item: any) => ({ + value: String(item.id), + label: item.name, +}) + +const STORE_OBJECT = { storeObject: true } as const + +// ── Props ── + +type JobCardCheckInDialogProps = { + jobCardId: string + open: boolean + onOpenChange: (open: boolean) => void + onSuccess?: () => void +} + +// ── Component ── + +export function JobCardCheckInDialog({ + jobCardId, + open, + onOpenChange, + onSuccess, +}: JobCardCheckInDialogProps) { + const api = useAuthApi() + + const now = new Date() + const todayStr = now.toISOString().split("T")[0] + const currentTime = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}` + + const form = useForm({ + resolver: zodResolver(checkInSchema), + defaultValues: { + check_in_date: todayStr, + check_in_time: currentTime, + km_in: "", + fuel_level: "", + start_date: "", + start_time: "", + department: null, + delivery_date: "", + delivery_time: "", + }, + }) + + const handleSubmit = async (values: CheckInFormValues) => { + try { + await api.jobCards.checkIn(jobCardId, { + check_in_date: values.check_in_date || undefined, + check_in_time: values.check_in_time || undefined, + km_in: values.km_in ? Number(values.km_in) : undefined, + fuel_level: values.fuel_level || undefined, + start_date: values.start_date || undefined, + start_time: values.start_time || undefined, + department_id: values.department ? Number(toId(values.department)) : undefined, + delivery_date: values.delivery_date || undefined, + delivery_time: values.delivery_time || undefined, + }) + toast.success("Job card checked in successfully") + form.reset() + onSuccess?.() + } catch { + toast.error("Failed to check in job card") + } + } + + return ( + + + + Check In + + + + + + + + + + + + + + + + api.departments.list()} + mapOption={(op:any)=> ({value: op.id, label: op.name})} + {...STORE_OBJECT} + getOptionLabel={op=>op.label} + getOptionValue={op=>op} + + /> + + + + + + onOpenChange(false)}> + Cancel + + + {form.formState.isSubmitting ? "Checking in..." : "Check In"} + + + + + + + ) +} diff --git a/apps/dashboard/modules/job-cards/job-card-delivery-dialog.tsx b/apps/dashboard/modules/job-cards/job-card-delivery-dialog.tsx new file mode 100644 index 0000000..ec71b62 --- /dev/null +++ b/apps/dashboard/modules/job-cards/job-card-delivery-dialog.tsx @@ -0,0 +1,139 @@ +"use client" + +import { z } from "zod" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { toast } from "sonner" +import { useAuthApi } from "@/shared/useApi" +import { + Rhform, + RhfTextField, + RhfSelectField, + RhfDateField, + RhfTimeField, + RhfAsyncSelectField, +} from "@/shared/components/form" +import { FieldGroup } from "@/shared/components/ui/field" +import { Button } from "@/shared/components/ui/button" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/shared/components/ui/dialog" +import { DEPARTMENT_ROUTES } from "@garage/api" +import { FUEL_LEVEL_OPTIONS } from "./job-card.schema" +import { toId } from "@/shared/lib/utils" + +// ── Schema ── + +const deliverySchema = z.object({ + check_out_date: z.string().optional(), + check_out_time: z.string().optional(), + km_out: z.string().optional(), + fuel_level: z.string().optional(), + department: z.object({ value: z.number(), label: z.string() }).nullable().optional(), +}) + +type DeliveryFormValues = z.infer + +const mapLookupOption = (item: any) => ({ + value: String(item.id), + label: item.name, +}) + +const STORE_OBJECT = { storeObject: true } as const + +// ── Props ── + +type JobCardDeliveryDialogProps = { + jobCardId: string + open: boolean + onOpenChange: (open: boolean) => void + onSuccess?: () => void +} + +// ── Component ── + +export function JobCardDeliveryDialog({ + jobCardId, + open, + onOpenChange, + onSuccess, +}: JobCardDeliveryDialogProps) { + const api = useAuthApi() + + const now = new Date() + const todayStr = now.toISOString().split("T")[0] + const currentTime = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}` + + const form = useForm({ + resolver: zodResolver(deliverySchema), + defaultValues: { + check_out_date: todayStr, + check_out_time: currentTime, + km_out: "", + fuel_level: "", + department: null, + }, + }) + + const handleSubmit = async (values: DeliveryFormValues) => { + try { + await api.jobCards.delivery(jobCardId, { + check_out_date: values.check_out_date || undefined, + check_out_time: values.check_out_time || undefined, + km_out: values.km_out ? Number(values.km_out) : undefined, + fuel_level: values.fuel_level || undefined, + department_id: values.department ? Number(toId(values.department as any)) : undefined, + }) + toast.success("Job card marked as delivered successfully") + form.reset() + onSuccess?.() + } catch { + toast.error("Failed to mark job card as delivered") + } + } + + return ( + + + + Delivery + + + + + + + + + + + + api.departments.list()} + mapOption={(op: any) => ({ value: op.id, label: op.name })} + {...STORE_OBJECT} + getOptionLabel={op => op.label} + getOptionValue={op => op} + + /> + + onOpenChange(false)}> + Cancel + + + {form.formState.isSubmitting ? "Delivering..." : "Mark as Delivered"} + + + + + + + ) +} diff --git a/apps/dashboard/modules/job-cards/job-card-general-info.tsx b/apps/dashboard/modules/job-cards/job-card-general-info.tsx index dbaa260..3c95f3d 100644 --- a/apps/dashboard/modules/job-cards/job-card-general-info.tsx +++ b/apps/dashboard/modules/job-cards/job-card-general-info.tsx @@ -28,6 +28,7 @@ import { CrudShowResponse, JobCardsClient, PAYMENT_RECEIVED_ROUTES, PaymentRecei import { ResourcePage } from "@/shared/data-view/resource-page" import PaymentReceivedPage from "@/app/(authenticated)/sales/payment-received/page" import JobCardPaymentsReceived from "./job-card-payments-received" +import { formatDate } from "@/shared/utils/formatters" @@ -116,7 +117,7 @@ export function JobCardGeneralInfo({ jobCard }: { jobCard: JobCard }) { s.value === currentStatus) + const [checkInOpen, setCheckInOpen] = useState(false) + const [deliveryOpen, setDeliveryOpen] = useState(false) + const { mutate, isPending, variables } = useMutation({ mutationFn: async (status: JobCardStatus) => { const promise = api.jobCards.changeStatus(jobCardId, { status }) @@ -60,9 +66,17 @@ export function JobCardStatusStepper({ jobCardId }: JobCardStatusStepperProps) { }, }) - const handleClick = (status: JobCardStatus, index: number) => { + const handleClick = (status: JobCardStatus) => { if (isPending) return if (status === currentStatus) return + if (status === "check_in") { + setCheckInOpen(true) + return + } + if (status === "delivered") { + setDeliveryOpen(true) + return + } mutate(status) } @@ -82,7 +96,7 @@ export function JobCardStatusStepper({ jobCardId }: JobCardStatusStepperProps) { handleClick(step.value, index)} + onClick={() => handleClick(step.value)} disabled={!isClickable} className={cn( "relative flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all", @@ -120,6 +134,26 @@ export function JobCardStatusStepper({ jobCardId }: JobCardStatusStepperProps) { ) })} + + { + setCheckInOpen(false) + ;(jobCard as any)?.setStatus("check_in") + }} + /> + + { + setDeliveryOpen(false) + ;(jobCard as any)?.setStatus("delivered") + }} + /> ) } diff --git a/apps/dashboard/shared/components/form-dialog.tsx b/apps/dashboard/shared/components/form-dialog.tsx index 6c2b603..c796f9b 100644 --- a/apps/dashboard/shared/components/form-dialog.tsx +++ b/apps/dashboard/shared/components/form-dialog.tsx @@ -59,7 +59,9 @@ export function useFormDialog(paramKey?: string) { } export default function FormDialog(props: { - children: (resourceId: string | null) => React.ReactNode + children: (resourceId: string | null, + ctx: { open: (resourceId?: string) => void, close: () => void,isOpen:boolean } + ) => React.ReactNode title: string paramKey?: string classNames?: { @@ -85,7 +87,7 @@ export default function FormDialog(props: { - {props.children(resourceId)} + {props.children(resourceId, { open, close , isOpen})} diff --git a/packages/api/open-api/schema.json b/packages/api/open-api/schema.json index bdaa8fb..529acf6 100644 --- a/packages/api/open-api/schema.json +++ b/packages/api/open-api/schema.json @@ -24549,6 +24549,255 @@ } } }, + "/api/job-cards/{id}/check-in": { + "post": { + "tags": [ + "Job Cards" + ], + "summary": "POST /api/job-cards/{id}/check-in", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "check_in_date": { + "type": "string" + }, + "check_in_time": { + "type": "string" + }, + "km_in": { + "type": "integer" + }, + "fuel_level": { + "type": "string" + }, + "start_date": { + "type": "string" + }, + "start_time": { + "type": "string" + }, + "department_id": { + "type": "integer" + }, + "delivery_date": { + "type": "string" + }, + "delivery_time": { + "type": "string" + } + } + }, + "example": { + "check_in_date": "2026-04-07", + "check_in_time": "09:30", + "km_in": 50321, + "fuel_level": "half", + "start_date": "2026-04-07", + "start_time": "10:00", + "department_id": 1, + "delivery_date": "2026-04-08", + "delivery_time": "17:15" + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "check_in_date": { + "type": "string", + "format": "date-time" + }, + "check_in_time": { + "type": "string" + }, + "km_in": { + "type": "integer" + }, + "fuel_level": { + "type": "string" + }, + "start_date": { + "type": "string", + "format": "date-time" + }, + "start_time": { + "type": "string" + }, + "department_id": { + "type": "integer" + }, + "delivery_date": { + "type": "string", + "format": "date-time" + }, + "delivery_time": { + "type": "string" + } + } + } + } + }, + "example": { + "message": "Job card checked in successfully.", + "data": { + "id": 1, + "status": "check_in", + "check_in_date": "2026-04-07T00:00:00.000000Z", + "check_in_time": "09:30:00", + "km_in": 50321, + "fuel_level": "half", + "start_date": "2026-04-07T00:00:00.000000Z", + "start_time": "10:00:00", + "department_id": 1, + "delivery_date": "2026-04-08T00:00:00.000000Z", + "delivery_time": "17:15:00" + } + } + } + } + } + } + } + }, + "/api/job-cards/{id}/delivery": { + "post": { + "tags": [ + "Job Cards" + ], + "summary": "POST /api/job-cards/{id}/delivery", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "check_out_date": { + "type": "string" + }, + "check_out_time": { + "type": "string" + }, + "km_out": { + "type": "integer" + }, + "fuel_level": { + "type": "string" + }, + "department_id": { + "type": "integer" + } + } + }, + "example": { + "check_out_date": "2026-04-08", + "check_out_time": "17:15", + "km_out": 50480, + "fuel_level": "quarter", + "department_id": 1 + } + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "check_out_date": { + "type": "string" + }, + "check_out_time": { + "type": "string" + }, + "km_out": { + "type": "integer" + }, + "fuel_level": { + "type": "string" + }, + "department_id": { + "type": "integer" + } + } + } + } + }, + "example": { + "message": "Job card marked as delivered successfully.", + "data": { + "id": 1, + "status": "delivered", + "check_out_date": "2026-04-08", + "check_out_time": "17:15:00", + "km_out": 50480, + "fuel_level": "quarter", + "department_id": 1 + } + } + } + } + } + } + } + }, "/api/job-cards/{id}/add-customer-remark": { "post": { "tags": [ diff --git a/packages/api/postman/collection.json b/packages/api/postman/collection.json index 8c40e94..53a9fbc 100644 --- a/packages/api/postman/collection.json +++ b/packages/api/postman/collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "4fac95bb-3550-48ab-9943-212b6e3a11d8", + "_postman_id": "bf46649e-48e0-49d5-aa87-1790dcbc3c24", "name": "Reparee Collection", "description": "Auto-generated from OpenAPI spec. Import storage/app/openapi-default.json for the full schema.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", @@ -8025,7 +8025,7 @@ "{{table}}" ] }, - "description": "Set {{table}} to job_cards or estimates. Returns next sequence like ORD-0001 or EST-0001." + "description": "Set {{table}} to any supported table with a number column. Explicit prefixes: job_cards=ORD-, estimates=EST-, invoices=INV-, purchase_orders=PO-, bills=BILL-, expenses=EXP-, tasks=TASK-, appointments=APPT-, services=SRV-, parts=PART-, expense_items=EXP-ITEM-, inspections=INS-. Returns next sequence like ORD-0001." }, "response": [ { @@ -16771,6 +16771,222 @@ } ] }, + { + "name": "POST /api/job-cards/{id}/check-in", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{auth_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"check_in_date\": \"2026-04-07\",\n \"check_in_time\": \"09:30\",\n \"km_in\": 50321,\n \"fuel_level\": \"half\",\n \"start_date\": \"2026-04-07\",\n \"start_time\": \"10:00\",\n \"department_id\": 1,\n \"delivery_date\": \"2026-04-08\",\n \"delivery_time\": \"17:15\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/job-cards/{{id}}/check-in", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "job-cards", + "{{id}}", + "check-in" + ] + }, + "description": "Marks job card as `check_in` and updates check-in context. Optional fields: `check_in_date`, `check_in_time` (HH:MM or HH:MM:SS), `km_in`, `fuel_level`, `start_date`, `start_time`, `department_id`, `delivery_date`, `delivery_time` (HH:MM or HH:MM:SS). If check-in date/time is omitted, server current date/time is used." + }, + "response": [ + { + "name": "200 OK", + "originalRequest": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{auth_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"check_in_date\": \"2026-04-07\",\n \"check_in_time\": \"09:30\",\n \"km_in\": 50321,\n \"fuel_level\": \"half\",\n \"start_date\": \"2026-04-07\",\n \"start_time\": \"10:00\",\n \"department_id\": 1,\n \"delivery_date\": \"2026-04-08\",\n \"delivery_time\": \"17:15\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/job-cards/{{id}}/check-in", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "job-cards", + "{{id}}", + "check-in" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Job card checked in successfully.\",\n \"data\": {\n \"id\": 1,\n \"status\": \"check_in\",\n \"check_in_date\": \"2026-04-07T00:00:00.000000Z\",\n \"check_in_time\": \"09:30:00\",\n \"km_in\": 50321,\n \"fuel_level\": \"half\",\n \"start_date\": \"2026-04-07T00:00:00.000000Z\",\n \"start_time\": \"10:00:00\",\n \"department_id\": 1,\n \"delivery_date\": \"2026-04-08T00:00:00.000000Z\",\n \"delivery_time\": \"17:15:00\"\n }\n}" + } + ] + }, + { + "name": "POST /api/job-cards/{id}/delivery", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{auth_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"check_out_date\": \"2026-04-08\",\n \"check_out_time\": \"17:15\",\n \"km_out\": 50480,\n \"fuel_level\": \"quarter\",\n \"department_id\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/job-cards/{{id}}/delivery", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "job-cards", + "{{id}}", + "delivery" + ] + }, + "description": "Marks job card as `delivered` and updates delivery context. Optional fields: `check_out_date`, `check_out_time` (HH:MM or HH:MM:SS), `km_out`, `fuel_level`, `department_id`. If check-out date/time is omitted, server current date/time is used." + }, + "response": [ + { + "name": "200 OK", + "originalRequest": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{auth_token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"check_out_date\": \"2026-04-08\",\n \"check_out_time\": \"17:15\",\n \"km_out\": 50480,\n \"fuel_level\": \"quarter\",\n \"department_id\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/job-cards/{{id}}/delivery", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "job-cards", + "{{id}}", + "delivery" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Job card marked as delivered successfully.\",\n \"data\": {\n \"id\": 1,\n \"status\": \"delivered\",\n \"check_out_date\": \"2026-04-08\",\n \"check_out_time\": \"17:15:00\",\n \"km_out\": 50480,\n \"fuel_level\": \"quarter\",\n \"department_id\": 1\n }\n}" + } + ] + }, { "name": "POST /api/job-cards/{id}/add-customer-remark", "request": { diff --git a/packages/api/src/clients/job-cards.ts b/packages/api/src/clients/job-cards.ts index cb5d6ca..2d79a52 100644 --- a/packages/api/src/clients/job-cards.ts +++ b/packages/api/src/clients/job-cards.ts @@ -1,13 +1,15 @@ 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", BY_ID: "/api/job-cards/{id}", CHANGE_DATE: "/api/job-cards/{id}/change-date", CHANGE_STATUS: "/api/job-cards/{id}/change-status", + CHECK_IN: "/api/job-cards/{id}/check-in", + DELIVERY: "/api/job-cards/{id}/delivery", ADD_CUSTOMER_REMARK: "/api/job-cards/{id}/add-customer-remark", EDIT_CUSTOMER_REMARK: "/api/job-cards/{id}/edit-customer-remark", DELETE_CUSTOMER_REMARK: "/api/job-cards/{id}/delete-customer-remark", @@ -53,6 +55,14 @@ export class JobCardsClient extends CrudClient< return this.post(JOB_CARD_ROUTES.CHANGE_STATUS, payload, { params: { id } }) } + async checkIn(id: string, payload: ApiRequestBody) { + return this.post(JOB_CARD_ROUTES.CHECK_IN, payload, { params: { id } }) + } + + async delivery(id: string, payload: ApiRequestBody) { + return this.post(JOB_CARD_ROUTES.DELIVERY, payload, { params: { id } }) + } + async addCustomerRemark(id: string, payload: ApiRequestBody) { return this.post(JOB_CARD_ROUTES.ADD_CUSTOMER_REMARK, payload, { params: { id } }) } diff --git a/packages/api/types/index.ts b/packages/api/types/index.ts index 89f6cb8..f8e9ee1 100644 --- a/packages/api/types/index.ts +++ b/packages/api/types/index.ts @@ -16105,6 +16105,189 @@ export interface paths { patch?: never; trace?: never; }; + "/api/job-cards/{id}/check-in": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** POST /api/job-cards/{id}/check-in */ + post: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "check_in_date": "2026-04-07", + * "check_in_time": "09:30", + * "km_in": 50321, + * "fuel_level": "half", + * "start_date": "2026-04-07", + * "start_time": "10:00", + * "department_id": 1, + * "delivery_date": "2026-04-08", + * "delivery_time": "17:15" + * } + */ + "application/json": { + check_in_date?: string; + check_in_time?: string; + km_in?: number; + fuel_level?: string; + start_date?: string; + start_time?: string; + department_id?: number; + delivery_date?: string; + delivery_time?: string; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "message": "Job card checked in successfully.", + * "data": { + * "id": 1, + * "status": "check_in", + * "check_in_date": "2026-04-07T00:00:00.000000Z", + * "check_in_time": "09:30:00", + * "km_in": 50321, + * "fuel_level": "half", + * "start_date": "2026-04-07T00:00:00.000000Z", + * "start_time": "10:00:00", + * "department_id": 1, + * "delivery_date": "2026-04-08T00:00:00.000000Z", + * "delivery_time": "17:15:00" + * } + * } + */ + "application/json": { + message?: string; + data?: { + id?: number; + status?: string; + /** Format: date-time */ + check_in_date?: string; + check_in_time?: string; + km_in?: number; + fuel_level?: string; + /** Format: date-time */ + start_date?: string; + start_time?: string; + department_id?: number; + /** Format: date-time */ + delivery_date?: string; + delivery_time?: string; + }; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/job-cards/{id}/delivery": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** POST /api/job-cards/{id}/delivery */ + post: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "check_out_date": "2026-04-08", + * "check_out_time": "17:15", + * "km_out": 50480, + * "fuel_level": "quarter", + * "department_id": 1 + * } + */ + "application/json": { + check_out_date?: string; + check_out_time?: string; + km_out?: number; + fuel_level?: string; + department_id?: number; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "message": "Job card marked as delivered successfully.", + * "data": { + * "id": 1, + * "status": "delivered", + * "check_out_date": "2026-04-08", + * "check_out_time": "17:15:00", + * "km_out": 50480, + * "fuel_level": "quarter", + * "department_id": 1 + * } + * } + */ + "application/json": { + message?: string; + data?: { + id?: number; + status?: string; + check_out_date?: string; + check_out_time?: string; + km_out?: number; + fuel_level?: string; + department_id?: number; + }; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/job-cards/{id}/add-customer-remark": { parameters: { query?: never;