mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 19:45:23 +02:00
feat: add Business Central CPNP sync and expand history access
- Add bc-proxy and bc-export serverless API handlers - Add businessCentral service with updateCpnpNoInBC and downloadBusinessCentralItemsExcel - Sync cpnpNo to BC on save (CosmeticItemsView and handleSaveAll) - Parallelize handleSaveAll with Promise.all - Fix array index key, a11y click/keyboard handlers, autoFocus - Grant Change History and Pending Validation access to jingying.shi@craze-group.com Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
co-authored by
claude-flow
parent
2961d3f86a
commit
9b5932920e
@@ -0,0 +1,107 @@
|
||||
const BC_PROXY_URL = '/api/bc-proxy';
|
||||
const BC_EXPORT_URL = '/api/bc-export';
|
||||
|
||||
export interface BCUpdateResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface BCDownloadResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
filename?: string;
|
||||
}
|
||||
|
||||
async function readResponsePayload(res: Response): Promise<{ data: any; rawText: string }> {
|
||||
const rawText = await res.text();
|
||||
if (!rawText.trim()) {
|
||||
return { data: null, rawText };
|
||||
}
|
||||
|
||||
try {
|
||||
return { data: JSON.parse(rawText), rawText };
|
||||
} catch {
|
||||
return { data: rawText, rawText };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateCpnpNoInBC(articleNo: string, cpnpNo: string): Promise<BCUpdateResult> {
|
||||
try {
|
||||
const res = await fetch(BC_PROXY_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ articleNo, cpnpNo }),
|
||||
});
|
||||
|
||||
const { data, rawText } = await readResponsePayload(res);
|
||||
if (!res.ok || !data?.success) {
|
||||
const error =
|
||||
data?.error ||
|
||||
(typeof data === 'string' && data.trim()) ||
|
||||
rawText ||
|
||||
`HTTP ${res.status}`;
|
||||
return { success: false, error };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
function getFilenameFromDisposition(contentDisposition: string | null): string | null {
|
||||
if (!contentDisposition) return null;
|
||||
|
||||
const utf8Match = contentDisposition.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
|
||||
if (utf8Match?.[1]) {
|
||||
try {
|
||||
return decodeURIComponent(utf8Match[1].trim().replace(/^"|"$/g, ''));
|
||||
} catch {
|
||||
return utf8Match[1].trim().replace(/^"|"$/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
const filenameMatch = contentDisposition.match(/filename\s*=\s*([^;]+)/i);
|
||||
if (filenameMatch?.[1]) {
|
||||
return filenameMatch[1].trim().replace(/^"|"$/g, '');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function downloadBusinessCentralItemsExcel(): Promise<BCDownloadResult> {
|
||||
try {
|
||||
const res = await fetch(BC_EXPORT_URL, {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let error = `HTTP ${res.status}`;
|
||||
try {
|
||||
const { data, rawText } = await readResponsePayload(res);
|
||||
error = data?.error || (typeof data === 'string' && data.trim()) || rawText || error;
|
||||
} catch {
|
||||
error = `HTTP ${res.status}`;
|
||||
}
|
||||
return { success: false, error };
|
||||
}
|
||||
|
||||
const blob = await res.blob();
|
||||
const filename =
|
||||
getFilenameFromDisposition(res.headers.get('content-disposition')) ||
|
||||
`BusinessCentral_Items_${new Date().toISOString().split('T')[0]}.xlsx`;
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.rel = 'noopener';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
return { success: true, filename };
|
||||
} catch (err: any) {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user