Wire TM Registry and Reference Library to real backend APIs

Both pages were showing hardcoded mock data (PDFs, TMX, DOCX files).
Now they:
- Fetch real data from /files/tm and /files/reference endpoints
- Accept .json/.jsonl uploads (not PDF/TMX)
- Support delete with confirmation
- Auto-select Amazon as the default client
- Show proper upload dialogs with locale/channel/file-type selectors
- Fixed api.ts functions to pass client_id, channel, file_type as
  query params (matching backend expectations)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DJP 2026-04-14 10:05:03 -04:00
parent 17bf57e865
commit 8d4dc65993
3 changed files with 485 additions and 176 deletions

View file

@ -1,11 +1,19 @@
"use client";
import React, { useState } from "react";
import React, { useState, useEffect, useCallback } from "react";
import { AppShell } from "@/components/layout/AppShell";
import { FileUploader } from "@/components/admin/FileUploader";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Table,
TableBody,
@ -21,21 +29,130 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { formatDate } from "@/lib/utils";
import { Upload, Trash2, FileText, Download } from "lucide-react";
import type { ReferenceFile } from "@/lib/types";
import { Upload, Trash2, FileText, Download, Loader2 } from "lucide-react";
import {
getReferenceFiles,
uploadReferenceFile,
deleteReferenceFile,
getClients,
} from "@/lib/api";
import type { Client } from "@/lib/types";
const mockRefFiles: ReferenceFile[] = [
{ id: "ref-001", name: "Brand_Guidelines_v3.pdf", file_type: "PDF", locale_code: undefined, client_id: undefined, file_path: "/files/ref/Brand_Guidelines_v3.pdf", uploaded_by: "Sarah Chen", uploaded_at: "2026-02-10T10:00:00Z" },
{ id: "ref-002", name: "Glossary_Master.xlsx", file_type: "XLSX", locale_code: undefined, client_id: undefined, file_path: "/files/ref/Glossary_Master.xlsx", uploaded_by: "Sarah Chen", uploaded_at: "2026-02-15T14:30:00Z" },
{ id: "ref-003", name: "StyleGuide_de-DE.pdf", file_type: "PDF", locale_code: "de-DE", client_id: undefined, file_path: "/files/ref/StyleGuide_de-DE.pdf", uploaded_by: "Emily Brown", uploaded_at: "2026-03-01T09:00:00Z" },
{ id: "ref-004", name: "StyleGuide_fr-FR.pdf", file_type: "PDF", locale_code: "fr-FR", client_id: undefined, file_path: "/files/ref/StyleGuide_fr-FR.pdf", uploaded_by: "Emily Brown", uploaded_at: "2026-03-01T09:05:00Z" },
{ id: "ref-005", name: "OMG_Voice_Profile.docx", file_type: "DOCX", locale_code: undefined, client_id: "client-001", file_path: "/files/ref/OMG_Voice_Profile.docx", uploaded_by: "James Miller", uploaded_at: "2026-03-10T11:00:00Z" },
{ id: "ref-006", name: "Prime_Terminology.xlsx", file_type: "XLSX", locale_code: undefined, client_id: undefined, file_path: "/files/ref/Prime_Terminology.xlsx", uploaded_by: "Sarah Chen", uploaded_at: "2026-03-20T15:45:00Z" },
const LOCALES = [
"de-DE", "fr-FR", "it-IT", "es-ES", "nl-NL",
"sv-SE", "pl-PL", "pt-PT", "de-AT", "fr-BE", "nl-BE", "ca-ES",
];
const FILE_TYPES = [
{ value: "glossary", label: "Glossary" },
{ value: "blacklist", label: "Blacklist" },
{ value: "tov_global", label: "TOV Global" },
{ value: "tov_supplement", label: "TOV Supplement" },
{ value: "locale_considerations", label: "Locale Considerations" },
{ value: "date_pct_formats", label: "Date/Percent Formats" },
];
const FILE_TYPE_LABELS: Record<string, string> = {
glossary: "Glossary",
blacklist: "Blacklist",
tov_global: "TOV Global",
tov_supplement: "TOV Supplement",
locale_considerations: "Locale",
date_pct_formats: "Date/Pct",
};
interface RefFileRow {
id: string;
filename: string;
file_type: string;
locale_scope: string;
uploaded_at: string;
}
export default function ReferenceLibraryPage() {
const [files] = useState(mockRefFiles);
const [files, setFiles] = useState<RefFileRow[]>([]);
const [loading, setLoading] = useState(true);
const [clients, setClients] = useState<Client[]>([]);
const [clientId, setClientId] = useState<string>("");
const [uploadOpen, setUploadOpen] = useState(false);
const [deleting, setDeleting] = useState<string | null>(null);
// Upload form state
const [uploadFile, setUploadFile] = useState<File | null>(null);
const [uploadLocale, setUploadLocale] = useState("global");
const [uploadFileType, setUploadFileType] = useState("");
const [uploading, setUploading] = useState(false);
useEffect(() => {
getClients().then((c) => {
setClients(c);
const amazon = c.find((cl) => cl.name.toLowerCase() === "amazon");
if (amazon) setClientId(amazon.id);
else if (c.length > 0) setClientId(c[0].id);
});
}, []);
const fetchFiles = useCallback(async () => {
if (!clientId) return;
setLoading(true);
try {
const data = await getReferenceFiles(clientId);
setFiles(data as unknown as RefFileRow[]);
} catch {
setFiles([]);
} finally {
setLoading(false);
}
}, [clientId]);
useEffect(() => {
fetchFiles();
}, [fetchFiles]);
const handleDelete = async (fileId: string) => {
if (!confirm("Delete this reference file?")) return;
setDeleting(fileId);
try {
await deleteReferenceFile(fileId);
setFiles((prev) => prev.filter((f) => f.id !== fileId));
} catch {
// handled by interceptor
} finally {
setDeleting(null);
}
};
const handleDownload = (fileId: string, filename: string) => {
// Open download URL in new tab
const baseUrl = process.env.NEXT_PUBLIC_API_URL || "";
const token = localStorage.getItem("access_token");
window.open(
`${baseUrl}/api/v1/files/reference/${fileId}/download?token=${token}`,
"_blank"
);
};
const handleUpload = async () => {
if (!uploadFile || !uploadFileType || !clientId) return;
setUploading(true);
try {
await uploadReferenceFile(
uploadFile,
clientId,
uploadFileType,
uploadLocale || "global"
);
setUploadOpen(false);
setUploadFile(null);
setUploadLocale("global");
setUploadFileType("");
fetchFiles();
} catch {
// handled by interceptor
} finally {
setUploading(false);
}
};
return (
<AppShell>
@ -51,74 +168,75 @@ export default function ReferenceLibraryPage() {
</div>
<Card>
<Table>
<TableHeader>
<TableRow>
<TableHead>File Name</TableHead>
<TableHead>Type</TableHead>
<TableHead>Locale</TableHead>
<TableHead>Client</TableHead>
<TableHead>Uploaded By</TableHead>
<TableHead>Date</TableHead>
<TableHead className="w-[100px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{files.map((file) => (
<TableRow key={file.id}>
<TableCell>
<div className="flex items-center gap-2">
<FileText className="h-4 w-4 text-amazon-teal" />
<span className="text-sm font-medium">
{file.name}
</span>
</div>
</TableCell>
<TableCell>
<Badge variant="gray">{file.file_type}</Badge>
</TableCell>
<TableCell>
{file.locale_code ? (
<Badge variant="outline">{file.locale_code}</Badge>
) : (
<span className="text-xs text-gray-400">Global</span>
)}
</TableCell>
<TableCell>
{file.client_id ? (
<Badge variant="outline">OMG</Badge>
) : (
<span className="text-xs text-gray-400">All</span>
)}
</TableCell>
<TableCell className="text-sm text-gray-500">
{file.uploaded_by}
</TableCell>
<TableCell className="text-sm text-gray-500">
{formatDate(file.uploaded_at)}
</TableCell>
<TableCell>
<div className="flex gap-1">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-400"
>
<Download className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-400 hover:text-amazon-error"
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</TableCell>
{loading ? (
<div className="py-12 text-center">
<Loader2 className="h-6 w-6 animate-spin mx-auto text-gray-400" />
<p className="text-sm text-gray-400 mt-2">Loading reference files...</p>
</div>
) : files.length === 0 ? (
<div className="py-12 text-center">
<FileText className="h-8 w-8 text-gray-300 mx-auto mb-2" />
<p className="text-sm text-gray-400">No reference files uploaded yet</p>
</div>
) : (
<Table>
<TableHeader>
<TableRow>
<TableHead>File Name</TableHead>
<TableHead>Type</TableHead>
<TableHead>Locale</TableHead>
<TableHead>Date</TableHead>
<TableHead className="w-[100px]"></TableHead>
</TableRow>
))}
</TableBody>
</Table>
</TableHeader>
<TableBody>
{files.map((file) => (
<TableRow key={file.id}>
<TableCell>
<div className="flex items-center gap-2">
<FileText className="h-4 w-4 text-amazon-teal" />
<span className="text-sm font-medium">
{file.filename}
</span>
</div>
</TableCell>
<TableCell>
<Badge variant="gray">
{FILE_TYPE_LABELS[file.file_type] || file.file_type}
</Badge>
</TableCell>
<TableCell>
{file.locale_scope === "global" ? (
<span className="text-xs text-gray-400">Global</span>
) : (
<Badge variant="outline">{file.locale_scope}</Badge>
)}
</TableCell>
<TableCell className="text-sm text-gray-500">
{formatDate(file.uploaded_at)}
</TableCell>
<TableCell>
<div className="flex gap-1">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-400 hover:text-amazon-error"
onClick={() => handleDelete(file.id)}
disabled={deleting === file.id}
>
{deleting === file.id ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Trash2 className="h-4 w-4" />
)}
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</Card>
<Dialog open={uploadOpen} onOpenChange={setUploadOpen}>
@ -126,11 +244,57 @@ export default function ReferenceLibraryPage() {
<DialogHeader>
<DialogTitle>Upload Reference File</DialogTitle>
</DialogHeader>
<FileUploader
accept=".pdf,.docx,.xlsx,.csv,.txt"
label="Drop your reference file here"
onUpload={() => setUploadOpen(false)}
/>
<div className="space-y-4 py-2">
<div className="space-y-2">
<Label>File (.json)</Label>
<Input
type="file"
accept=".json,.jsonl"
onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
/>
</div>
<div className="space-y-2">
<Label>File Type</Label>
<Select value={uploadFileType} onValueChange={setUploadFileType}>
<SelectTrigger>
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
{FILE_TYPES.map((ft) => (
<SelectItem key={ft.value} value={ft.value}>
{ft.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>Locale</Label>
<Select value={uploadLocale} onValueChange={setUploadLocale}>
<SelectTrigger>
<SelectValue placeholder="Global (all locales)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="global">Global (all locales)</SelectItem>
{LOCALES.map((l) => (
<SelectItem key={l} value={l}>{l}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Button
onClick={handleUpload}
disabled={!uploadFile || !uploadFileType || uploading}
className="w-full gap-2"
>
{uploading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Upload className="h-4 w-4" />
)}
Upload
</Button>
</div>
</DialogContent>
</Dialog>
</div>

View file

@ -1,11 +1,19 @@
"use client";
import React, { useState } from "react";
import React, { useState, useEffect, useCallback } from "react";
import { AppShell } from "@/components/layout/AppShell";
import { FileUploader } from "@/components/admin/FileUploader";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Table,
TableBody,
@ -21,23 +29,99 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { formatDate } from "@/lib/utils";
import { Upload, Trash2, Database } from "lucide-react";
import type { TMFile } from "@/lib/types";
import { Upload, Trash2, Database, Loader2 } from "lucide-react";
import { getTMFiles, uploadTMFile, deleteTMFile, getClients } from "@/lib/api";
import type { Client } from "@/lib/types";
const mockTMFiles: TMFile[] = [
{ id: "tm-001", name: "TM_de-DE_Master.tmx", locale_code: "de-DE", client_id: undefined, file_path: "/files/tm/TM_de-DE_Master.tmx", entry_count: 15420, uploaded_by: "Sarah Chen", uploaded_at: "2026-03-15T10:30:00Z" },
{ id: "tm-002", name: "TM_fr-FR_Master.tmx", locale_code: "fr-FR", client_id: undefined, file_path: "/files/tm/TM_fr-FR_Master.tmx", entry_count: 12890, uploaded_by: "Sarah Chen", uploaded_at: "2026-03-15T10:35:00Z" },
{ id: "tm-003", name: "TM_it-IT_Master.tmx", locale_code: "it-IT", client_id: undefined, file_path: "/files/tm/TM_it-IT_Master.tmx", entry_count: 11230, uploaded_by: "Emily Brown", uploaded_at: "2026-03-20T14:00:00Z" },
{ id: "tm-004", name: "TM_es-ES_Master.tmx", locale_code: "es-ES", client_id: undefined, file_path: "/files/tm/TM_es-ES_Master.tmx", entry_count: 13670, uploaded_by: "Emily Brown", uploaded_at: "2026-03-20T14:05:00Z" },
{ id: "tm-005", name: "TM_de-DE_OMG.tmx", locale_code: "de-DE", client_id: "client-001", file_path: "/files/tm/TM_de-DE_OMG.tmx", entry_count: 3240, uploaded_by: "James Miller", uploaded_at: "2026-04-01T09:00:00Z" },
{ id: "tm-006", name: "TM_nl-NL_Master.tmx", locale_code: "nl-NL", client_id: undefined, file_path: "/files/tm/TM_nl-NL_Master.tmx", entry_count: 8450, uploaded_by: "Sarah Chen", uploaded_at: "2026-04-05T11:20:00Z" },
{ id: "tm-007", name: "TM_sv-SE_Master.tmx", locale_code: "sv-SE", client_id: undefined, file_path: "/files/tm/TM_sv-SE_Master.tmx", entry_count: 7120, uploaded_by: "Sarah Chen", uploaded_at: "2026-04-05T11:25:00Z" },
{ id: "tm-008", name: "TM_pl-PL_Master.tmx", locale_code: "pl-PL", client_id: undefined, file_path: "/files/tm/TM_pl-PL_Master.tmx", entry_count: 6890, uploaded_by: "Emily Brown", uploaded_at: "2026-04-06T16:00:00Z" },
const LOCALES = [
"de-DE", "fr-FR", "it-IT", "es-ES", "nl-NL",
"sv-SE", "pl-PL", "pt-PT", "de-AT", "fr-BE", "nl-BE", "ca-ES",
];
const CHANNELS = [
"MASS", "VALUE", "ONSITE", "OUTBOUND", "UEFA",
"PrimeDualBenefit", "PrimeGourmetGuard", "PrimeMidfunnel",
"PrimeSpeed", "TheKiss", "DoubleDonut", "EUSelection", "BDA",
];
interface TMFileRow {
id: string;
filename: string;
locale_code: string;
channel: string;
segment_count: number;
uploaded_at: string;
}
export default function TMRegistryPage() {
const [files] = useState(mockTMFiles);
const [files, setFiles] = useState<TMFileRow[]>([]);
const [loading, setLoading] = useState(true);
const [clients, setClients] = useState<Client[]>([]);
const [clientId, setClientId] = useState<string>("");
const [uploadOpen, setUploadOpen] = useState(false);
const [deleting, setDeleting] = useState<string | null>(null);
// Upload form state
const [uploadFile, setUploadFile] = useState<File | null>(null);
const [uploadLocale, setUploadLocale] = useState("");
const [uploadChannel, setUploadChannel] = useState("");
const [uploading, setUploading] = useState(false);
useEffect(() => {
getClients().then((c) => {
setClients(c);
const amazon = c.find((cl) => cl.name.toLowerCase() === "amazon");
if (amazon) setClientId(amazon.id);
else if (c.length > 0) setClientId(c[0].id);
});
}, []);
const fetchFiles = useCallback(async () => {
if (!clientId) return;
setLoading(true);
try {
const data = await getTMFiles(clientId);
setFiles(data as unknown as TMFileRow[]);
} catch {
setFiles([]);
} finally {
setLoading(false);
}
}, [clientId]);
useEffect(() => {
fetchFiles();
}, [fetchFiles]);
const handleDelete = async (fileId: string) => {
if (!confirm("Delete this TM file?")) return;
setDeleting(fileId);
try {
await deleteTMFile(fileId);
setFiles((prev) => prev.filter((f) => f.id !== fileId));
} catch {
// handled by interceptor
} finally {
setDeleting(null);
}
};
const handleUpload = async () => {
if (!uploadFile || !uploadLocale || !uploadChannel || !clientId) return;
setUploading(true);
try {
await uploadTMFile(uploadFile, clientId, uploadLocale, uploadChannel);
setUploadOpen(false);
setUploadFile(null);
setUploadLocale("");
setUploadChannel("");
fetchFiles();
} catch {
// handled by interceptor
} finally {
setUploading(false);
}
};
return (
<AppShell>
@ -53,61 +137,71 @@ export default function TMRegistryPage() {
</div>
<Card>
<Table>
<TableHeader>
<TableRow>
<TableHead>File Name</TableHead>
<TableHead>Locale</TableHead>
<TableHead>Client</TableHead>
<TableHead>Entries</TableHead>
<TableHead>Uploaded By</TableHead>
<TableHead>Date</TableHead>
<TableHead className="w-[60px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{files.map((file) => (
<TableRow key={file.id}>
<TableCell>
<div className="flex items-center gap-2">
<Database className="h-4 w-4 text-amazon-teal" />
<span className="text-sm font-medium font-mono">
{file.name}
</span>
</div>
</TableCell>
<TableCell>
<Badge variant="gray">{file.locale_code}</Badge>
</TableCell>
<TableCell>
{file.client_id ? (
<Badge variant="outline">OMG</Badge>
) : (
<span className="text-xs text-gray-400">Global</span>
)}
</TableCell>
<TableCell className="text-sm">
{file.entry_count.toLocaleString()}
</TableCell>
<TableCell className="text-sm text-gray-500">
{file.uploaded_by}
</TableCell>
<TableCell className="text-sm text-gray-500">
{formatDate(file.uploaded_at)}
</TableCell>
<TableCell>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-400 hover:text-amazon-error"
>
<Trash2 className="h-4 w-4" />
</Button>
</TableCell>
{loading ? (
<div className="py-12 text-center">
<Loader2 className="h-6 w-6 animate-spin mx-auto text-gray-400" />
<p className="text-sm text-gray-400 mt-2">Loading TM files...</p>
</div>
) : files.length === 0 ? (
<div className="py-12 text-center">
<Database className="h-8 w-8 text-gray-300 mx-auto mb-2" />
<p className="text-sm text-gray-400">No TM files uploaded yet</p>
</div>
) : (
<Table>
<TableHeader>
<TableRow>
<TableHead>File Name</TableHead>
<TableHead>Locale</TableHead>
<TableHead>Channel</TableHead>
<TableHead>Entries</TableHead>
<TableHead>Date</TableHead>
<TableHead className="w-[60px]"></TableHead>
</TableRow>
))}
</TableBody>
</Table>
</TableHeader>
<TableBody>
{files.map((file) => (
<TableRow key={file.id}>
<TableCell>
<div className="flex items-center gap-2">
<Database className="h-4 w-4 text-amazon-teal" />
<span className="text-sm font-medium font-mono">
{file.filename}
</span>
</div>
</TableCell>
<TableCell>
<Badge variant="gray">{file.locale_code}</Badge>
</TableCell>
<TableCell>
<span className="text-sm text-gray-600">{file.channel}</span>
</TableCell>
<TableCell className="text-sm">
{file.segment_count?.toLocaleString() ?? "—"}
</TableCell>
<TableCell className="text-sm text-gray-500">
{formatDate(file.uploaded_at)}
</TableCell>
<TableCell>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-gray-400 hover:text-amazon-error"
onClick={() => handleDelete(file.id)}
disabled={deleting === file.id}
>
{deleting === file.id ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Trash2 className="h-4 w-4" />
)}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</Card>
<Dialog open={uploadOpen} onOpenChange={setUploadOpen}>
@ -115,11 +209,54 @@ export default function TMRegistryPage() {
<DialogHeader>
<DialogTitle>Upload Translation Memory</DialogTitle>
</DialogHeader>
<FileUploader
accept=".tmx,.xliff,.sdlxliff"
label="Drop your TM file here (.tmx, .xliff)"
onUpload={() => setUploadOpen(false)}
/>
<div className="space-y-4 py-2">
<div className="space-y-2">
<Label>File (.json / .jsonl)</Label>
<Input
type="file"
accept=".json,.jsonl"
onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
/>
</div>
<div className="space-y-2">
<Label>Locale</Label>
<Select value={uploadLocale} onValueChange={setUploadLocale}>
<SelectTrigger>
<SelectValue placeholder="Select locale" />
</SelectTrigger>
<SelectContent>
{LOCALES.map((l) => (
<SelectItem key={l} value={l}>{l}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>Channel</Label>
<Select value={uploadChannel} onValueChange={setUploadChannel}>
<SelectTrigger>
<SelectValue placeholder="Select channel" />
</SelectTrigger>
<SelectContent>
{CHANNELS.map((ch) => (
<SelectItem key={ch} value={ch}>{ch}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Button
onClick={handleUpload}
disabled={!uploadFile || !uploadLocale || !uploadChannel || uploading}
className="w-full gap-2"
>
{uploading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Upload className="h-4 w-4" />
)}
Upload
</Button>
</div>
</DialogContent>
</Dialog>
</div>

View file

@ -477,21 +477,23 @@ export async function updateClient(
// ─── TM Files ────────────────────────────────────────────────────────
export async function getTMFiles(): Promise<TMFile[]> {
const response = await api.get<TMFile[]>("/files/tm");
export async function getTMFiles(clientId: string): Promise<TMFile[]> {
const response = await api.get<TMFile[]>("/files/tm", {
params: { client_id: clientId },
});
return response.data;
}
export async function uploadTMFile(
file: File,
clientId: string,
localeCode: string,
clientId?: string
): Promise<TMFile> {
channel: string
): Promise<unknown> {
const formData = new FormData();
formData.append("file", file);
formData.append("locale_code", localeCode);
if (clientId) formData.append("client_id", clientId);
const response = await api.post<TMFile>("/files/tm", formData, {
const response = await api.post("/files/tm", formData, {
params: { client_id: clientId, locale_code: localeCode, channel },
headers: { "Content-Type": "multipart/form-data" },
});
return response.data;
@ -503,25 +505,31 @@ export async function deleteTMFile(fileId: string): Promise<void> {
// ─── Reference Files ─────────────────────────────────────────────────
export async function getReferenceFiles(): Promise<ReferenceFile[]> {
const response = await api.get<ReferenceFile[]>("/files/reference");
export async function getReferenceFiles(
clientId: string
): Promise<ReferenceFile[]> {
const response = await api.get<ReferenceFile[]>("/files/reference", {
params: { client_id: clientId },
});
return response.data;
}
export async function uploadReferenceFile(
file: File,
localeCode?: string,
clientId?: string
): Promise<ReferenceFile> {
clientId: string,
fileType: string,
localeScope: string
): Promise<unknown> {
const formData = new FormData();
formData.append("file", file);
if (localeCode) formData.append("locale_code", localeCode);
if (clientId) formData.append("client_id", clientId);
const response = await api.post<ReferenceFile>(
"/files/reference",
formData,
{ headers: { "Content-Type": "multipart/form-data" } }
);
const response = await api.post("/files/reference", formData, {
params: {
client_id: clientId,
file_type: fileType,
locale_scope: localeScope,
},
headers: { "Content-Type": "multipart/form-data" },
});
return response.data;
}