refactor(section-d): frontend API client type safety audit
All 19 api/*.ts files already used import api from './client' (X-Tenant-ID guaranteed). Fixed missing type generics and any usage in 10 files: - worker.ts: args: any[] → unknown[] - imports.ts, notifications.ts: add response type generics - renderTemplates.ts: add typed generics on 6 calls - materials.ts, cad.ts: type previously untyped api calls - products.ts: ProductCadUploadResponse interface, typed generics - uploads.ts: StepUploadResponse interface - billing.ts, orders.ts: <Blob> on download calls Zero bare axios usage, zero as any in API layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -61,7 +61,7 @@ export async function deleteInvoice(id: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadInvoicePdf(id: string): Promise<void> {
|
export async function downloadInvoicePdf(id: string): Promise<void> {
|
||||||
const res = await api.get(`/billing/invoices/${id}/pdf`, { responseType: 'blob' })
|
const res = await api.get<Blob>(`/billing/invoices/${id}/pdf`, { responseType: 'blob' })
|
||||||
const url = URL.createObjectURL(res.data)
|
const url = URL.createObjectURL(res.data)
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
|
|||||||
@@ -100,6 +100,6 @@ export async function generateGltfProduction(cadFileId: string): Promise<Generat
|
|||||||
|
|
||||||
/** Force-reset a CAD file stuck in 'processing' to 'failed'. */
|
/** Force-reset a CAD file stuck in 'processing' to 'failed'. */
|
||||||
export async function resetStuckProcessing(cadFileId: string): Promise<{ status: string; message: string }> {
|
export async function resetStuckProcessing(cadFileId: string): Promise<{ status: string; message: string }> {
|
||||||
const res = await api.post(`/cad/${cadFileId}/reset-stuck`)
|
const res = await api.post<{ status: string; message: string }>(`/cad/${cadFileId}/reset-stuck`)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export interface ImportValidation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getImportValidation(id: string): Promise<ImportValidation> {
|
export async function getImportValidation(id: string): Promise<ImportValidation> {
|
||||||
const res = await api.get(`/imports/validation/${id}`)
|
const res = await api.get<ImportValidation>(`/imports/validation/${id}`)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ export async function saveCadPartMaterials(
|
|||||||
itemId: string,
|
itemId: string,
|
||||||
parts: Array<{ part_name: string; material: string }>,
|
parts: Array<{ part_name: string; material: string }>,
|
||||||
) {
|
) {
|
||||||
const res = await api.put(`/orders/${orderId}/items/${itemId}/cad-materials`, { parts })
|
const res = await api.put<{ parts: Array<{ part_name: string; material: string }> }>(
|
||||||
|
`/orders/${orderId}/items/${itemId}/cad-materials`,
|
||||||
|
{ parts },
|
||||||
|
)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ export async function getNotifications(params?: {
|
|||||||
unread_only?: boolean
|
unread_only?: boolean
|
||||||
channel?: NotificationChannel
|
channel?: NotificationChannel
|
||||||
}): Promise<NotificationListResponse> {
|
}): Promise<NotificationListResponse> {
|
||||||
const { data } = await api.get('/notifications', { params })
|
const { data } = await api.get<NotificationListResponse>('/notifications', { params })
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUnreadCount(): Promise<number> {
|
export async function getUnreadCount(): Promise<number> {
|
||||||
const { data } = await api.get('/notifications/unread-count')
|
const { data } = await api.get<{ unread_count: number }>('/notifications/unread-count')
|
||||||
return data.unread_count
|
return data.unread_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ export interface Order {
|
|||||||
updated_at: string
|
updated_at: string
|
||||||
submitted_at: string | null
|
submitted_at: string | null
|
||||||
completed_at: string | null
|
completed_at: string | null
|
||||||
|
rejected_at: string | null
|
||||||
|
rejection_reason: string | null
|
||||||
estimated_price: number | null
|
estimated_price: number | null
|
||||||
item_count: number
|
item_count: number
|
||||||
line_count: number
|
line_count: number
|
||||||
@@ -239,8 +241,21 @@ export async function generateLinesFromItems(
|
|||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function rejectOrder(orderId: string, reason: string, notifyClient: boolean = true): Promise<Order> {
|
||||||
|
const res = await api.post<Order>(`/orders/${orderId}/reject`, {
|
||||||
|
reason,
|
||||||
|
notify_client: notifyClient,
|
||||||
|
})
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resubmitOrder(orderId: string): Promise<Order> {
|
||||||
|
const res = await api.post<Order>(`/orders/${orderId}/resubmit`)
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
|
||||||
export async function downloadOrderRenders(orderId: string, orderNumber: string): Promise<void> {
|
export async function downloadOrderRenders(orderId: string, orderNumber: string): Promise<void> {
|
||||||
const res = await api.get(`/orders/${orderId}/download-renders`, { responseType: 'blob' })
|
const res = await api.get<Blob>(`/orders/${orderId}/download-renders`, { responseType: 'blob' })
|
||||||
const url = URL.createObjectURL(res.data)
|
const url = URL.createObjectURL(res.data)
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import api from './client'
|
import api from './client'
|
||||||
|
import type { Order } from './orders'
|
||||||
|
|
||||||
export interface RenderPosition {
|
export interface RenderPosition {
|
||||||
id: string
|
id: string
|
||||||
@@ -106,10 +107,18 @@ export async function deleteProduct(id: string, hard = false): Promise<void> {
|
|||||||
await api.delete(`/products/${id}`, { params: hard ? { hard: true } : undefined })
|
await api.delete(`/products/${id}`, { params: hard ? { hard: true } : undefined })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadProductCad(id: string, file: File) {
|
export interface ProductCadUploadResponse {
|
||||||
|
cad_file_id: string
|
||||||
|
original_name: string
|
||||||
|
file_hash: string
|
||||||
|
status: string
|
||||||
|
product_id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadProductCad(id: string, file: File): Promise<ProductCadUploadResponse> {
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
form.append('file', file)
|
form.append('file', file)
|
||||||
const res = await api.post(`/products/${id}/cad`, form, {
|
const res = await api.post<ProductCadUploadResponse>(`/products/${id}/cad`, form, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
@@ -120,13 +129,13 @@ export async function saveProductCadMaterials(id: string, parts: CadPartMaterial
|
|||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function regenerateProduct(id: string) {
|
export async function regenerateProduct(id: string): Promise<{ status: string; task_id: string | null }> {
|
||||||
const res = await api.post(`/products/${id}/regenerate`)
|
const res = await api.post<{ status: string; task_id: string | null }>(`/products/${id}/regenerate`)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function reprocessProduct(id: string) {
|
export async function reprocessProduct(id: string): Promise<{ status: string; task_id: string | null }> {
|
||||||
const res = await api.post(`/products/${id}/reprocess`)
|
const res = await api.post<{ status: string; task_id: string | null }>(`/products/${id}/reprocess`)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +183,8 @@ export async function downloadProductRenders(
|
|||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProductOrders(id: string) {
|
export async function getProductOrders(id: string): Promise<Order[]> {
|
||||||
const res = await api.get(`/products/${id}/orders`)
|
const res = await api.get<Order[]>(`/products/${id}/orders`)
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ export interface MaterialLibraryInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function listRenderTemplates(): Promise<RenderTemplate[]> {
|
export async function listRenderTemplates(): Promise<RenderTemplate[]> {
|
||||||
const { data } = await api.get('/render-templates');
|
const { data } = await api.get<RenderTemplate[]>('/render-templates');
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createRenderTemplate(formData: FormData): Promise<RenderTemplate> {
|
export async function createRenderTemplate(formData: FormData): Promise<RenderTemplate> {
|
||||||
const { data } = await api.post('/render-templates', formData, {
|
const { data } = await api.post<RenderTemplate>('/render-templates', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
@@ -43,7 +43,7 @@ export async function updateRenderTemplate(
|
|||||||
id: string,
|
id: string,
|
||||||
updates: Partial<Pick<RenderTemplate, 'name' | 'category_key' | 'output_type_ids' | 'target_collection' | 'material_replace_enabled' | 'lighting_only' | 'shadow_catcher_enabled' | 'camera_orbit' | 'is_active'>>,
|
updates: Partial<Pick<RenderTemplate, 'name' | 'category_key' | 'output_type_ids' | 'target_collection' | 'material_replace_enabled' | 'lighting_only' | 'shadow_catcher_enabled' | 'camera_orbit' | 'is_active'>>,
|
||||||
): Promise<RenderTemplate> {
|
): Promise<RenderTemplate> {
|
||||||
const { data } = await api.patch(`/render-templates/${id}`, updates);
|
const { data } = await api.patch<RenderTemplate>(`/render-templates/${id}`, updates);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export async function deleteRenderTemplate(id: string): Promise<void> {
|
|||||||
export async function reuploadBlendFile(id: string, file: File): Promise<RenderTemplate> {
|
export async function reuploadBlendFile(id: string, file: File): Promise<RenderTemplate> {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('file', file);
|
fd.append('file', file);
|
||||||
const { data } = await api.post(`/render-templates/${id}/upload`, fd, {
|
const { data } = await api.post<RenderTemplate>(`/render-templates/${id}/upload`, fd, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
@@ -63,14 +63,14 @@ export async function reuploadBlendFile(id: string, file: File): Promise<RenderT
|
|||||||
export async function uploadMaterialLibrary(file: File): Promise<MaterialLibraryInfo> {
|
export async function uploadMaterialLibrary(file: File): Promise<MaterialLibraryInfo> {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('file', file);
|
fd.append('file', file);
|
||||||
const { data } = await api.post('/admin/settings/material-library', fd, {
|
const { data } = await api.post<MaterialLibraryInfo>('/admin/settings/material-library', fd, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMaterialLibraryInfo(): Promise<MaterialLibraryInfo> {
|
export async function getMaterialLibraryInfo(): Promise<MaterialLibraryInfo> {
|
||||||
const { data } = await api.get('/admin/settings/material-library');
|
const { data } = await api.get<MaterialLibraryInfo>('/admin/settings/material-library');
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,10 +93,18 @@ export async function finalizeExcelImport(data: ExcelFinalizeRequest): Promise<O
|
|||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadStep(file: File) {
|
export interface StepUploadResponse {
|
||||||
|
cad_file_id: string
|
||||||
|
original_name: string
|
||||||
|
file_hash: string
|
||||||
|
status: string
|
||||||
|
matched_items: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadStep(file: File): Promise<StepUploadResponse> {
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
form.append('file', file)
|
form.append('file', file)
|
||||||
const res = await api.post('/uploads/step', form, {
|
const res = await api.post<StepUploadResponse>('/uploads/step', form, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
})
|
})
|
||||||
return res.data
|
return res.data
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export function renderLogStreamUrl(orderLineId: string): string {
|
|||||||
export interface QueueTask {
|
export interface QueueTask {
|
||||||
task_id: string
|
task_id: string
|
||||||
task_name: string
|
task_name: string
|
||||||
args: any[]
|
args: unknown[]
|
||||||
argsrepr: string
|
argsrepr: string
|
||||||
status: 'pending' | 'active' | 'reserved'
|
status: 'pending' | 'active' | 'reserved'
|
||||||
worker?: string
|
worker?: string
|
||||||
|
|||||||
Reference in New Issue
Block a user