proposal-system/web/src/lib/api/admin.ts
Adam Moussa 6de4bafec4 Add revision dropdown, editable WO#, and fix Revised proposal approval
- Fix: ApproveAsync now accepts both InReview and Revised proposals
- Revision dropdown in header: navigate between revisions, download PDF per rev
- Work Order Number editable in admin workspace (same pattern as PO#)
- Added WorkOrderNumber to UpdateProposalRequest DTO and service
- Info bar reordered: Customer, Site, WO#, PO#, Category, Priority
- Uniform font sizing across info bar (0.75rem labels, 0.875rem values)
- Typed getHistory API to return ProposalDetail[]
- Download PDF button in action bar for Sent/Revised proposals
2026-05-27 18:17:48 -04:00

86 lines
2.4 KiB
TypeScript

import apiClient from './client';
import { type ProposalDetail } from './proposals';
export interface DashboardStats {
pendingCount: number;
approvedThisWeek: number;
avgTurnaroundHours: number;
totalProposals: number;
}
export interface UpdateProposalRequest {
refinedScope?: string;
notes?: string;
poNumber?: string;
workOrderNumber?: string;
assignedAdminId?: string;
}
export interface AuditEntry {
id: string;
proposalId: string | null;
userId: string;
userName: string;
action: string;
details: string | null;
timestamp: string;
ipAddress: string | null;
}
export const adminApi = {
getDashboard: async (): Promise<DashboardStats> => {
const res = await apiClient.get('/admin/dashboard');
return res.data;
},
updateProposal: async (id: string, data: UpdateProposalRequest): Promise<void> => {
await apiClient.put(`/proposals/${id}`, data);
},
approveProposal: async (id: string): Promise<void> => {
await apiClient.post(`/proposals/${id}/approve`);
},
returnToReview: async (id: string): Promise<void> => {
await apiClient.post(`/proposals/${id}/return-to-review`);
},
sendProposal: async (id: string): Promise<void> => {
await apiClient.post(`/proposals/${id}/send`);
},
reviseProposal: async (id: string): Promise<void> => {
await apiClient.post(`/proposals/${id}/revise`);
},
getHistory: async (id: string): Promise<ProposalDetail[]> => {
const res = await apiClient.get(`/proposals/${id}/history`);
return res.data;
},
getAudit: async (id: string): Promise<AuditEntry[]> => {
const res = await apiClient.get(`/proposals/${id}/audit`);
return res.data;
},
generateSuggestions: async (id: string): Promise<void> => {
await apiClient.post(`/proposals/${id}/generate-suggestions`);
},
getSimilar: async (id: string): Promise<unknown[]> => {
const res = await apiClient.get(`/proposals/${id}/similar`);
return res.data;
},
getPdf: async (id: string, regenerate = false): Promise<{ downloadUrl: string } | null> => {
const params = regenerate ? '?regenerate=true' : '';
const res = await apiClient.get(`/proposals/${id}/pdf${params}`, {
responseType: 'blob',
validateStatus: (status) => status < 500,
});
if (res.status === 202) return null;
const blob = res.data as Blob;
const downloadUrl = URL.createObjectURL(blob);
return { downloadUrl };
},
};