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
+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">) {