feat: add template checkpoint edit dialog and vendor management components
- Implemented TemplateCheckpointEditDialog for creating and editing inspection checkpoints. - Added VendorActions component for managing vendor actions including edit, activate/deactivate, and delete. - Created VendorContext for managing vendor state across components. - Developed VendorGeneralInfo component to display detailed vendor information. - Introduced AedSymbol and Money components for consistent currency representation. - Added PromptDialog for user input prompts throughout the application. - Implemented RelationLink component for unified related-data display in CRUD tables. - Created InspectionTemplatesClient for API interactions related to inspection templates.
This commit is contained in:
@@ -13,6 +13,7 @@ import { ShopTypesClient } from "./clients/shop-types"
|
||||
import { InventoryClient } from "./clients/inventory"
|
||||
import { VendorsClient } from "./clients/vendors"
|
||||
import { InspectionsClient } from "./clients/inspections"
|
||||
import { InspectionTemplatesClient } from "./clients/inspection-templates"
|
||||
import { LabelsClient } from "./clients/labels"
|
||||
import { InsuranceTypesClient } from "./clients/insurance-types"
|
||||
import { EstimatesClient } from "./clients/estimates"
|
||||
@@ -75,6 +76,7 @@ export function createApi(options?: ApiClientOptions) {
|
||||
inventory: new InventoryClient(undefined, options),
|
||||
vendors: new VendorsClient(undefined, options),
|
||||
inspections: new InspectionsClient(undefined, options),
|
||||
inspectionTemplates: new InspectionTemplatesClient(undefined, options),
|
||||
labels: new LabelsClient(undefined, options),
|
||||
insuranceTypes: new InsuranceTypesClient(undefined, options),
|
||||
estimates: new EstimatesClient(undefined, options),
|
||||
|
||||
@@ -12,6 +12,18 @@ export { ShopTypesClient, SHOP_TYPE_ROUTES, type ShopTypeCreatePayload, type Sho
|
||||
export { InventoryClient, INVENTORY_ROUTES } from "./inventory"
|
||||
export { VendorsClient, VENDOR_ROUTES } from "./vendors"
|
||||
export { InspectionsClient, INSPECTION_ROUTES } from "./inspections"
|
||||
export { InspectionTemplatesClient, INSPECTION_TEMPLATE_ROUTES } from "./inspection-templates"
|
||||
export type {
|
||||
InspectionTemplate,
|
||||
InspectionTemplateSection,
|
||||
InspectionTemplateCheckPoint,
|
||||
InspectionTemplateCheckPointRecordType,
|
||||
InspectionSeverity,
|
||||
InspectionTemplateCreatePayload,
|
||||
InspectionTemplateUpdatePayload,
|
||||
InspectionTemplateSectionPayload,
|
||||
InspectionTemplateCheckPointPayload,
|
||||
} from "./inspection-templates"
|
||||
export { LabelsClient, LABEL_ROUTES } from "./labels"
|
||||
export { InsuranceTypesClient, INSURANCE_TYPE_ROUTES } from "./insurance-types"
|
||||
export { EstimatesClient, ESTIMATE_ROUTES } from "./estimates"
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import { ApiClient, type ApiClientOptions } from "../infra/client"
|
||||
|
||||
export const INSPECTION_TEMPLATE_ROUTES = {
|
||||
INDEX: "/api/inspection-templates",
|
||||
BY_ID: "/api/inspection-templates/{id}",
|
||||
DUPLICATE: "/api/inspection-templates/{id}/duplicate",
|
||||
SECTIONS: "/api/inspection-templates/{template}/sections",
|
||||
SECTION_BY_ID: "/api/inspection-templates/{template}/sections/{section}",
|
||||
CHECKPOINTS: "/api/inspection-templates/{template}/sections/{section}/checkpoints",
|
||||
CHECKPOINT_BY_ID: "/api/inspection-templates/{template}/sections/{section}/checkpoints/{id}",
|
||||
} as const
|
||||
|
||||
export type InspectionTemplateCheckPointRecordType =
|
||||
| "record_conditions"
|
||||
| "record_audio"
|
||||
| "record_video"
|
||||
| "capture_photo"
|
||||
| "wire_frame"
|
||||
| "none"
|
||||
|
||||
export type InspectionSeverity = "good" | "attention" | "critical" | "na" | "not_inspected"
|
||||
|
||||
export type InspectionTemplateCheckPoint = {
|
||||
id: number
|
||||
section_id: number
|
||||
name: string
|
||||
description?: string | null
|
||||
record_type: InspectionTemplateCheckPointRecordType
|
||||
severity_default: InspectionSeverity
|
||||
recommended_service_id?: number | null
|
||||
recommended_part_id?: number | null
|
||||
sort_order: number
|
||||
}
|
||||
|
||||
export type InspectionTemplateSection = {
|
||||
id: number
|
||||
template_id: number
|
||||
name: string
|
||||
sort_order: number
|
||||
icon?: string | null
|
||||
color?: string | null
|
||||
check_points?: InspectionTemplateCheckPoint[]
|
||||
}
|
||||
|
||||
export type InspectionTemplate = {
|
||||
id: number
|
||||
name: string
|
||||
description?: string | null
|
||||
shop_type_id?: number | null
|
||||
vehicle_type?: string | null
|
||||
is_active: boolean
|
||||
sections?: InspectionTemplateSection[]
|
||||
}
|
||||
|
||||
export type InspectionTemplateCreatePayload = {
|
||||
name: string
|
||||
description?: string | null
|
||||
shop_type_id?: number | null
|
||||
vehicle_type?: string | null
|
||||
is_active?: boolean
|
||||
}
|
||||
|
||||
export type InspectionTemplateUpdatePayload = Partial<InspectionTemplateCreatePayload>
|
||||
|
||||
export type InspectionTemplateSectionPayload = {
|
||||
name: string
|
||||
sort_order?: number
|
||||
icon?: string | null
|
||||
color?: string | null
|
||||
}
|
||||
|
||||
export type InspectionTemplateCheckPointPayload = {
|
||||
name: string
|
||||
description?: string | null
|
||||
record_type?: InspectionTemplateCheckPointRecordType
|
||||
severity_default?: InspectionSeverity
|
||||
recommended_service_id?: number | null
|
||||
recommended_part_id?: number | null
|
||||
sort_order?: number
|
||||
}
|
||||
|
||||
type ListResponse<T> = {
|
||||
data: T[]
|
||||
meta?: {
|
||||
current_page: number
|
||||
last_page: number
|
||||
per_page: number
|
||||
total: number
|
||||
}
|
||||
}
|
||||
|
||||
type ItemResponse<T> = { data: T; message?: string }
|
||||
|
||||
export class InspectionTemplatesClient extends ApiClient {
|
||||
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
|
||||
super(baseUrl, defaultOptions)
|
||||
}
|
||||
|
||||
async list(query?: Record<string, string | number | boolean>): Promise<ListResponse<InspectionTemplate>> {
|
||||
return this.get(INSPECTION_TEMPLATE_ROUTES.INDEX as never, { query } as never) as never
|
||||
}
|
||||
|
||||
async show(id: number | string): Promise<ItemResponse<InspectionTemplate>> {
|
||||
return this.get(INSPECTION_TEMPLATE_ROUTES.BY_ID as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async create(payload: InspectionTemplateCreatePayload): Promise<ItemResponse<InspectionTemplate>> {
|
||||
return this.post(INSPECTION_TEMPLATE_ROUTES.INDEX as never, payload as never) as never
|
||||
}
|
||||
|
||||
async update(id: number | string, payload: InspectionTemplateUpdatePayload): Promise<ItemResponse<InspectionTemplate>> {
|
||||
return this.put(INSPECTION_TEMPLATE_ROUTES.BY_ID as never, payload as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async destroy(id: number | string): Promise<{ message: string }> {
|
||||
return this.delete(INSPECTION_TEMPLATE_ROUTES.BY_ID as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async duplicate(id: number | string): Promise<ItemResponse<InspectionTemplate>> {
|
||||
return this.post(INSPECTION_TEMPLATE_ROUTES.DUPLICATE as never, undefined as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
// ── Sections ──
|
||||
async listSections(templateId: number | string): Promise<ListResponse<InspectionTemplateSection>> {
|
||||
return this.get(INSPECTION_TEMPLATE_ROUTES.SECTIONS as never, { params: { template: String(templateId) } } as never) as never
|
||||
}
|
||||
|
||||
async createSection(templateId: number | string, payload: InspectionTemplateSectionPayload): Promise<ItemResponse<InspectionTemplateSection>> {
|
||||
return this.post(INSPECTION_TEMPLATE_ROUTES.SECTIONS as never, payload as never, { params: { template: String(templateId) } } as never) as never
|
||||
}
|
||||
|
||||
async updateSection(templateId: number | string, sectionId: number | string, payload: Partial<InspectionTemplateSectionPayload>): Promise<ItemResponse<InspectionTemplateSection>> {
|
||||
return this.put(INSPECTION_TEMPLATE_ROUTES.SECTION_BY_ID as never, payload as never, { params: { template: String(templateId), section: String(sectionId) } } as never) as never
|
||||
}
|
||||
|
||||
async destroySection(templateId: number | string, sectionId: number | string): Promise<{ message: string }> {
|
||||
return this.delete(INSPECTION_TEMPLATE_ROUTES.SECTION_BY_ID as never, { params: { template: String(templateId), section: String(sectionId) } } as never) as never
|
||||
}
|
||||
|
||||
// ── Checkpoints ──
|
||||
async listCheckpoints(templateId: number | string, sectionId: number | string): Promise<ListResponse<InspectionTemplateCheckPoint>> {
|
||||
return this.get(INSPECTION_TEMPLATE_ROUTES.CHECKPOINTS as never, { params: { template: String(templateId), section: String(sectionId) } } as never) as never
|
||||
}
|
||||
|
||||
async createCheckpoint(templateId: number | string, sectionId: number | string, payload: InspectionTemplateCheckPointPayload): Promise<ItemResponse<InspectionTemplateCheckPoint>> {
|
||||
return this.post(INSPECTION_TEMPLATE_ROUTES.CHECKPOINTS as never, payload as never, { params: { template: String(templateId), section: String(sectionId) } } as never) as never
|
||||
}
|
||||
|
||||
async updateCheckpoint(templateId: number | string, sectionId: number | string, id: number | string, payload: Partial<InspectionTemplateCheckPointPayload>): Promise<ItemResponse<InspectionTemplateCheckPoint>> {
|
||||
return this.put(INSPECTION_TEMPLATE_ROUTES.CHECKPOINT_BY_ID as never, payload as never, { params: { template: String(templateId), section: String(sectionId), id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async destroyCheckpoint(templateId: number | string, sectionId: number | string, id: number | string): Promise<{ message: string }> {
|
||||
return this.delete(INSPECTION_TEMPLATE_ROUTES.CHECKPOINT_BY_ID as never, { params: { template: String(templateId), section: String(sectionId), id: String(id) } } as never) as never
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export const INSPECTION_ROUTES = {
|
||||
CATEGORY_BY_ID: "/api/inspection-categories/{id}",
|
||||
INDEX: "/api/inspections",
|
||||
BY_ID: "/api/inspections/{id}",
|
||||
FROM_TEMPLATE: "/api/inspections/from-template",
|
||||
CHANGE_STATUS: "/api/change-inspection-status",
|
||||
CHECKPOINT_LABELS: "/api/check-point-label",
|
||||
CHECKPOINT_LABEL_BY_ID: "/api/check-point-label/{id}",
|
||||
@@ -54,6 +55,63 @@ export class InspectionsClient extends CrudClient<
|
||||
return this.post(INSPECTION_ROUTES.CHANGE_STATUS, payload)
|
||||
}
|
||||
|
||||
async showOne(id: number | string): Promise<{ data: any }> {
|
||||
return this.get(`/api/inspections/{id}` as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async sign(id: number | string, who: "technician" | "customer", dataUrl: string) {
|
||||
return this.post(`/api/inspections/{id}/sign` as never, { who, signature: dataUrl } as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload one or many photos/videos/audio files to a checkpoint via the
|
||||
* unified attachments endpoint. The single-file `addCheckpointAttachment`
|
||||
* (body-based) above stays available for legacy callers.
|
||||
*/
|
||||
async uploadCheckpointAttachments(
|
||||
checkpointId: number | string,
|
||||
files: File[],
|
||||
caption?: string,
|
||||
): Promise<{ data: Array<{ id: number; url: string; media_type: string; sort_order: number; caption?: string | null }> }> {
|
||||
const fd = new FormData()
|
||||
files.forEach((f) => fd.append("attachments[]", f))
|
||||
if (caption) fd.append("caption", caption)
|
||||
const url = `/api/inspection-check-points/${encodeURIComponent(String(checkpointId))}/attachments`
|
||||
return (this as any).postFormData(url, fd)
|
||||
}
|
||||
|
||||
async destroyCheckpointAttachment(checkpointId: number | string, attachmentId: number | string) {
|
||||
return this.delete(
|
||||
`/api/inspection-check-points/{id}/attachments/{attachmentId}` as never,
|
||||
{ params: { id: String(checkpointId), attachmentId: String(attachmentId) } } as never,
|
||||
) as never
|
||||
}
|
||||
|
||||
async share(id: number | string): Promise<{ data: { share_token: string; share_url: string; share_enabled_at: string; share_expires_at: string } }> {
|
||||
return this.post(`/api/inspections/{id}/share` as never, undefined as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async revokeShare(id: number | string): Promise<{ message: string }> {
|
||||
return this.post(`/api/inspections/{id}/revoke-share` as never, undefined as never, { params: { id: String(id) } } as never) as never
|
||||
}
|
||||
|
||||
async createFromTemplate(payload: {
|
||||
template_id: number
|
||||
title: string
|
||||
customer_id: number | string
|
||||
vehicle_id: number | string
|
||||
department_id: number | string
|
||||
employee_id: number | string
|
||||
order_number: string
|
||||
date: string
|
||||
time: string
|
||||
odometer?: number
|
||||
note?: string
|
||||
inspection_category_id?: number | string
|
||||
}) {
|
||||
return this.post(INSPECTION_ROUTES.FROM_TEMPLATE as never, payload as never) as never
|
||||
}
|
||||
|
||||
// ── Checkpoint Labels ──
|
||||
async listCheckpointLabels(query?: ApiListQueryParams) {
|
||||
return this.get(INSPECTION_ROUTES.CHECKPOINT_LABELS, query ? { query } as never : undefined)
|
||||
|
||||
@@ -18,6 +18,10 @@ export class VendorsClient extends CrudClient<
|
||||
super(baseUrl, defaultOptions, VENDOR_ROUTES.INDEX, VENDOR_ROUTES.BY_ID)
|
||||
}
|
||||
|
||||
async getById(id: string) {
|
||||
return this.show(id)
|
||||
}
|
||||
|
||||
async toggleStatus(payload: ApiRequestBody<typeof VENDOR_ROUTES.TOGGLE_STATUS, "post">) {
|
||||
return this.post(VENDOR_ROUTES.TOGGLE_STATUS, payload)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export const SellRatesTaxInclusive = ['Tax Inclusive', 'Tax Exclusive'] as const
|
||||
export type SellRatesTaxInclusive = (typeof SellRatesTaxInclusive)[number];
|
||||
// Tables
|
||||
|
||||
export const Tables= ['bills', 'payments', 'expenses', 'invoices', 'job_cards', 'credit_notes', 'vendor_credits', 'estimates','purchase-order'] as const;
|
||||
export const Tables= ['bills', 'payments', 'expenses', 'invoices', 'job_cards', 'credit_notes', 'vendor_credits', 'estimates','purchase-order', 'inventory_adjustments'] as const;
|
||||
export type Tables = (typeof Tables)[number];
|
||||
|
||||
export const GiveDiscounts = ['no', 'line_item_level', 'transaction_level'] as const;
|
||||
|
||||
Reference in New Issue
Block a user