This commit is contained in:
2026-04-01 15:34:50 +03:00
parent 13b56d4960
commit 5f3d208158
120 changed files with 36338 additions and 21265 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+8
View File
@@ -27,6 +27,10 @@ import { TasksClient } from "./clients/tasks"
import { AppointmentsClient } from "./clients/appointments"
import { ShopTimingsClient } from "./clients/shop-timings"
import { ShopCalendarsClient } from "./clients/shop-calendars"
import { HolidayYearsClient } from "./clients/holiday-years"
import { TaxesClient } from "./clients/taxes"
import { InvoicesClient } from "./clients/invoices"
import { HomeClient } from "./clients/home"
export function createApi(options?: ApiClientOptions) {
return {
@@ -58,6 +62,10 @@ export function createApi(options?: ApiClientOptions) {
appointments: new AppointmentsClient(undefined, options),
shopTimings: new ShopTimingsClient(undefined, options),
shopCalendars: new ShopCalendarsClient(undefined, options),
holidayYears: new HolidayYearsClient(undefined, options),
taxes: new TaxesClient(undefined, options),
invoices: new InvoicesClient(undefined, options),
home: new HomeClient(undefined, options),
}
}
+19
View File
@@ -3,6 +3,8 @@ import type { ApiPath, ApiRequestBody } from "../infra/types"
import type { ApiListQueryParams } from "../contracts/types"
export const EXPENSE_ROUTES = {
INDEX: "/api/expenses",
BY_ID: "/api/expenses/{id}",
ITEMS: "/api/expense-items",
ITEM_BY_ID: "/api/expense-items/{id}",
TOGGLE_ITEM_STATUS: "/api/toggle-expense-item-status",
@@ -71,4 +73,21 @@ export class ExpensesClient extends ApiClient {
async destroyExpense(id: string) {
return this.delete(EXPENSE_ROUTES.EXPENSE_BY_ID, { params: { id } })
}
// ── Standard CRUD aliases (for ResourcePage compatibility) ──
async list(query?: ApiListQueryParams) {
return this.listExpenses(query)
}
async create(payload: ApiRequestBody<typeof EXPENSE_ROUTES.EXPENSES, "post">) {
return this.createExpense(payload)
}
async update(id: string, payload: ApiRequestBody<typeof EXPENSE_ROUTES.EXPENSE_BY_ID, "put">) {
return this.updateExpense(id, payload)
}
async destroy(id: string) {
return this.destroyExpense(id)
}
}
+21
View File
@@ -0,0 +1,21 @@
import { ApiClient, type ApiClientOptions } from "../infra/client"
import type { ApiPath, ApiRequestBody } from "../infra/types"
import type { ApiListQueryParams } from "../contracts/types"
export const HOLIDAY_YEAR_ROUTES = {
INDEX: "/api/holiday-years",
} as const satisfies Record<string, ApiPath>
export class HolidayYearsClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
}
async list(query?: ApiListQueryParams) {
return this.get(HOLIDAY_YEAR_ROUTES.INDEX, query ? { query } as never : undefined)
}
async create(payload: ApiRequestBody<typeof HOLIDAY_YEAR_ROUTES.INDEX, "post">) {
return this.post(HOLIDAY_YEAR_ROUTES.INDEX, payload)
}
}
+18
View File
@@ -0,0 +1,18 @@
import { ApiClient, type ApiClientOptions } from "../infra/client"
import type { ApiPath, ApiResponse } from "../infra/types"
export const HOME_ROUTES = {
DASHBOARD: "/api/home",
} as const satisfies Record<string, ApiPath>
export type HomeDashboardResponse = ApiResponse<typeof HOME_ROUTES.DASHBOARD, "get">
export class HomeClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
}
async dashboard() {
return this.get(HOME_ROUTES.DASHBOARD)
}
}
+4
View File
@@ -26,3 +26,7 @@ export { TasksClient, TASK_ROUTES } from "./tasks"
export { AppointmentsClient, APPOINTMENT_ROUTES } from "./appointments"
export { ShopTimingsClient, SHOP_TIMING_ROUTES } from "./shop-timings"
export { ShopCalendarsClient, SHOP_CALENDAR_ROUTES } from "./shop-calendars"
export { HolidayYearsClient, HOLIDAY_YEAR_ROUTES } from "./holiday-years"
export { TaxesClient, TAX_ROUTES } from "./taxes"
export { InvoicesClient, INVOICE_ROUTES } from "./invoices"
export { HomeClient, HOME_ROUTES, type HomeDashboardResponse } from "./home"
+109
View File
@@ -0,0 +1,109 @@
import { CrudClient } from "../infra/crud-client"
import { ApiClient, type ApiClientOptions } from "../infra/client"
import type { ApiPath, ApiRequestBody } from "../infra/types"
import type { ApiListQueryParams } from "../contracts/types"
export const INVOICE_ROUTES = {
INDEX: "/api/invoices",
BY_ID: "/api/invoices/{id}",
ADD_ATTACHMENT: "/api/invoices/{id}/add-attachment",
DELETE_ATTACHMENT: "/api/invoices/{id}/delete-attachment",
DOCUMENTS: "/api/invoice-documents",
DOCUMENT_BY_ID: "/api/invoice-documents/{id}",
NOTES: "/api/invoice-notes",
NOTE_BY_ID: "/api/invoice-notes/{id}",
LABELS: "/api/invoice-labels",
LABEL_BY_ID: "/api/invoice-labels/{id}",
} as const satisfies Record<string, ApiPath>
export class InvoicesClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
}
// ── Invoices ──
async list(query?: ApiListQueryParams) {
return this.get(INVOICE_ROUTES.INDEX, query ? { query } as never : undefined)
}
async show(id: string) {
return this.get(INVOICE_ROUTES.BY_ID, { params: { id } } as never)
}
async create(payload: ApiRequestBody<typeof INVOICE_ROUTES.INDEX, "post">) {
return this.post(INVOICE_ROUTES.INDEX, payload)
}
async update(id: string, payload: ApiRequestBody<typeof INVOICE_ROUTES.BY_ID, "put">) {
return this.put(INVOICE_ROUTES.BY_ID, payload, { params: { id } })
}
async destroy(id: string) {
return this.delete(INVOICE_ROUTES.BY_ID, { params: { id } })
}
// ── Attachments ──
async addAttachment(id: string, file: File) {
const fd = new FormData()
fd.append("attachments[]", file)
return this.postFormData(`/api/invoices/${id}/add-attachment`, fd)
}
async deleteAttachment(invoiceId: string, attachmentId: number) {
return this.delete(INVOICE_ROUTES.DELETE_ATTACHMENT, {
params: { id: invoiceId },
body: { attachment_id: attachmentId },
} as never)
}
// ── Documents ──
async listDocuments(query?: ApiListQueryParams & { invoice_id?: string | number }) {
return this.get(INVOICE_ROUTES.DOCUMENTS, query ? { query } as never : undefined)
}
async createDocument(payload: ApiRequestBody<typeof INVOICE_ROUTES.DOCUMENTS, "post">) {
return this.post(INVOICE_ROUTES.DOCUMENTS, payload)
}
async updateDocument(id: string, payload: ApiRequestBody<typeof INVOICE_ROUTES.DOCUMENT_BY_ID, "put">) {
return this.put(INVOICE_ROUTES.DOCUMENT_BY_ID, payload, { params: { id } })
}
async destroyDocument(id: string) {
return this.delete(INVOICE_ROUTES.DOCUMENT_BY_ID, { params: { id } })
}
// ── Notes ──
async listNotes(query?: ApiListQueryParams & { invoice_id?: string | number }) {
return this.get(INVOICE_ROUTES.NOTES, query ? { query } as never : undefined)
}
async createNote(payload: ApiRequestBody<typeof INVOICE_ROUTES.NOTES, "post">) {
return this.post(INVOICE_ROUTES.NOTES, payload)
}
async updateNote(id: string, payload: ApiRequestBody<typeof INVOICE_ROUTES.NOTE_BY_ID, "put">) {
return this.put(INVOICE_ROUTES.NOTE_BY_ID, payload, { params: { id } })
}
async destroyNote(id: string) {
return this.delete(INVOICE_ROUTES.NOTE_BY_ID, { params: { id } })
}
// ── Labels ──
async listLabels(query?: ApiListQueryParams) {
return this.get(INVOICE_ROUTES.LABELS, query ? { query } as never : undefined)
}
async createLabel(payload: ApiRequestBody<typeof INVOICE_ROUTES.LABELS, "post">) {
return this.post(INVOICE_ROUTES.LABELS, payload)
}
async updateLabel(id: string, payload: ApiRequestBody<typeof INVOICE_ROUTES.LABEL_BY_ID, "put">) {
return this.put(INVOICE_ROUTES.LABEL_BY_ID, payload, { params: { id } })
}
async destroyLabel(id: string) {
return this.delete(INVOICE_ROUTES.LABEL_BY_ID, { params: { id } })
}
}
+13 -4
View File
@@ -28,6 +28,11 @@ export class JobCardsClient extends ApiClient {
return this.get(JOB_CARD_ROUTES.INDEX, query ? { query } as never : undefined)
}
async show(id: string) {
const res = await this.get(JOB_CARD_ROUTES.INDEX, { params: { id }, query: { id } } as never)
return { ...res, data: res.data?.[0] ?? null, }
}
async create(payload: ApiRequestBody<typeof JOB_CARD_ROUTES.INDEX, "post">) {
return this.post(JOB_CARD_ROUTES.INDEX, payload)
}
@@ -72,12 +77,16 @@ export class JobCardsClient extends ApiClient {
return this.delete(JOB_CARD_ROUTES.DELETE_SHOP_RECOMMENDATION, { params: { id } })
}
async addAttachment(id: string, payload: ApiRequestBody<typeof JOB_CARD_ROUTES.ADD_ATTACHMENT, "post">) {
return this.post(JOB_CARD_ROUTES.ADD_ATTACHMENT, payload, { params: { id } })
async addAttachment(id: string, files: File[]) {
const fd = new FormData()
for (const file of files) {
fd.append("attachments[]", file)
}
return this.postFormData(`/api/job-cards/${id}/add-attachment`, fd)
}
async deleteAttachment(id: string, payload: ApiRequestBody<typeof JOB_CARD_ROUTES.DELETE_ATTACHMENT, "post">) {
return this.post(JOB_CARD_ROUTES.DELETE_ATTACHMENT, payload, { params: { id } })
async deleteAttachment(id: string, attachmentId: number) {
return this.post(JOB_CARD_ROUTES.DELETE_ATTACHMENT, { attachment_id: attachmentId } as never, { params: { id } })
}
async changeServiceWriter(id: string, payload: ApiRequestBody<typeof JOB_CARD_ROUTES.CHANGE_SERVICE_WRITER, "post">) {
+40
View File
@@ -0,0 +1,40 @@
import { ApiClient, type ApiClientOptions } from "../infra/client"
import type { ApiPath, ApiRequestBody } from "../infra/types"
import type { ApiListQueryParams } from "../contracts/types"
export const TAX_ROUTES = {
INDEX: "/api/taxes",
BY_ID: "/api/taxes/{id}",
SET_DEFAULT: "/api/set-default-tax",
REMOVE_DEFAULT: "/api/remove-default-tax",
} as const satisfies Record<string, ApiPath>
export class TaxesClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
}
async list(query?: ApiListQueryParams) {
return this.get(TAX_ROUTES.INDEX, query ? { query } as never : undefined)
}
async create(payload: ApiRequestBody<typeof TAX_ROUTES.INDEX, "post">) {
return this.post(TAX_ROUTES.INDEX, payload)
}
async update(id: string, payload: ApiRequestBody<typeof TAX_ROUTES.BY_ID, "put">) {
return this.put(TAX_ROUTES.BY_ID, payload, { params: { id } })
}
async destroy(id: string) {
return this.delete(TAX_ROUTES.BY_ID, { params: { id } })
}
async setDefault(payload: ApiRequestBody<typeof TAX_ROUTES.SET_DEFAULT, "post">) {
return this.post(TAX_ROUTES.SET_DEFAULT, payload)
}
async removeDefault(payload: ApiRequestBody<typeof TAX_ROUTES.REMOVE_DEFAULT, "post">) {
return this.post(TAX_ROUTES.REMOVE_DEFAULT, payload)
}
}
+26 -6
View File
@@ -34,16 +34,36 @@ export class VehicleDocumentsClient extends ApiClient {
}
// ── Vehicle Documents ──
async listDocuments(query?: ApiListQueryParams) {
async listDocuments(query?: ApiListQueryParams & { vehicle_id?: string | number }) {
return this.get(VEHICLE_DOCUMENT_ROUTES.DOCUMENTS, query ? { query } as never : undefined)
}
async createDocument(payload: ApiRequestBody<typeof VEHICLE_DOCUMENT_ROUTES.DOCUMENTS, "post">) {
return this.post(VEHICLE_DOCUMENT_ROUTES.DOCUMENTS, payload)
async createDocument(payload: Record<string, any>) {
const fd = new FormData()
for (const [key, value] of Object.entries(payload)) {
if (value == null) continue
if (value instanceof File) {
fd.append(key, value)
} else {
fd.append(key, String(value))
}
}
return this.postFormData(VEHICLE_DOCUMENT_ROUTES.DOCUMENTS, fd)
}
async updateDocument(id: string, payload: ApiRequestBody<typeof VEHICLE_DOCUMENT_ROUTES.DOCUMENT_BY_ID, "put">) {
return this.put(VEHICLE_DOCUMENT_ROUTES.DOCUMENT_BY_ID, payload, { params: { id } })
async updateDocument(id: string, payload: Record<string, any>) {
const fd = new FormData()
for (const [key, value] of Object.entries(payload)) {
if (value == null) continue
if (value instanceof File) {
fd.append(key, value)
} else {
fd.append(key, String(value))
}
}
fd.append("_method", "PUT")
const url = VEHICLE_DOCUMENT_ROUTES.DOCUMENT_BY_ID.replace("{id}", id)
return this.postFormData(url, fd)
}
async destroyDocument(id: string) {
@@ -51,7 +71,7 @@ export class VehicleDocumentsClient extends ApiClient {
}
// ── Mileage ──
async listMileage(query?: ApiListQueryParams) {
async listMileage(query?: ApiListQueryParams & { vehicle_id?: string | number }) {
return this.get(VEHICLE_DOCUMENT_ROUTES.MILEAGE, query ? { query } as never : undefined)
}
+29 -5
View File
@@ -12,6 +12,19 @@ export const VEHICLE_ROUTES = {
UNLINK_CUSTOMER: "/api/unlink-customer-from-vehicle",
} as const satisfies Record<string, ApiPath>
function buildVehicleFormData(payload: Record<string, any>): FormData {
const fd = new FormData()
for (const [key, value] of Object.entries(payload)) {
if (value == null) continue
if (value instanceof File) {
fd.append(key, value)
} else {
fd.append(key, String(value))
}
}
return fd
}
export class VehiclesClient extends ApiClient {
constructor(baseUrl?: string, defaultOptions?: ApiClientOptions) {
super(baseUrl, defaultOptions)
@@ -21,12 +34,23 @@ export class VehiclesClient extends ApiClient {
return this.get(VEHICLE_ROUTES.INDEX, query ? { query } as never : undefined)
}
async create(payload: ApiRequestBody<typeof VEHICLE_ROUTES.INDEX, "post">) {
return this.post(VEHICLE_ROUTES.INDEX, payload)
async getById(id: string) {
return this.get(VEHICLE_ROUTES.BY_ID, { params: { id } })
}
async create(payload: ApiRequestBody<typeof VEHICLE_ROUTES.INDEX, "post">) {
const fd = buildVehicleFormData(payload)
return this.postFormData(VEHICLE_ROUTES.INDEX, fd)
}
async update(id: string, payload: ApiRequestBody<typeof VEHICLE_ROUTES.BY_ID, "put">) {
return this.put(VEHICLE_ROUTES.BY_ID, payload, { params: { id } })
const fd = buildVehicleFormData(payload)
fd.append("_method", "PUT")
const url = VEHICLE_ROUTES.BY_ID.replace("{id}", id)
return this.postFormData(url, fd)
}
async destroy(id: string) {
@@ -41,8 +65,8 @@ export class VehiclesClient extends ApiClient {
return this.post(VEHICLE_ROUTES.IMPORT, payload)
}
async getOwners() {
return this.get(VEHICLE_ROUTES.GET_OWNERS)
async getOwners(vehicleId: string | number, query?: ApiListQueryParams) {
return this.get(VEHICLE_ROUTES.GET_OWNERS, { query: { vehicle_id: vehicleId, ...query } } as never)
}
async linkCustomer(payload: ApiRequestBody<typeof VEHICLE_ROUTES.LINK_CUSTOMER, "post">) {
+2375 -168
View File
File diff suppressed because it is too large Load Diff