mirror of
https://github.com/temetro/temetro.git
synced 2026-08-22 16:06:39 +00:00
settings: Records import & export for temetro data
Backend: GET /api/settings/records/export downloads the clinic's full patient archive as JSON; POST /api/settings/records/import creates new patients (skips existing file numbers, drops cross-clinic provider links). Both admin-gated. Frontend: Records settings now has a working Export button and an Import flow (choose file -> preview count -> apply) wired to the new endpoints. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1496,7 +1496,25 @@
|
||||
"importDesc": "Connect an existing patient database and browse it in the same card UI.",
|
||||
"retentionTitle": "Retention",
|
||||
"retentionDescription": "How long records are kept",
|
||||
"retentionBody": "Records are kept for as long as your clinic exists. Deleting a patient permanently removes their chart from the clinic database, and the activity log keeps an audit trail of every change — who made it and when."
|
||||
"retentionBody": "Records are kept for as long as your clinic exists. Deleting a patient permanently removes their chart from the clinic database, and the activity log keeps an audit trail of every change — who made it and when.",
|
||||
"exporting": "Exporting…",
|
||||
"exportDoneTitle": "Records exported",
|
||||
"exportDoneBody": "Downloaded {{count}} patient record(s).",
|
||||
"exportFailedTitle": "Export failed",
|
||||
"exportFailedBody": "Couldn't export records. Please try again.",
|
||||
"importTemetro": "Import a temetro archive",
|
||||
"importTemetroDesc": "Load patients from a temetro export file. Existing file numbers are skipped.",
|
||||
"importChoose": "Choose file",
|
||||
"importPreview": "{{count}} patient record(s) ready to import.",
|
||||
"importCancel": "Cancel",
|
||||
"importApply": "Import",
|
||||
"importing": "Importing…",
|
||||
"importInvalidTitle": "Invalid file",
|
||||
"importInvalidBody": "That doesn't look like a temetro export.",
|
||||
"importDoneTitle": "Import complete",
|
||||
"importDoneBody": "Created {{created}}, skipped {{skipped}}.",
|
||||
"importFailedTitle": "Import failed",
|
||||
"importFailedBody": "Couldn't import records. Please try again."
|
||||
},
|
||||
"developers": {
|
||||
"description": "Access tokens for the temetro API",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { apiFetch } from "@/lib/api-client";
|
||||
import type { Patient } from "@/lib/patients";
|
||||
|
||||
// Shape of a temetro records archive (GET /api/settings/records/export).
|
||||
export type RecordsExport = {
|
||||
temetroExport: true;
|
||||
version: number;
|
||||
exportedAt: string;
|
||||
organizationId: string;
|
||||
patientCount: number;
|
||||
patients: Patient[];
|
||||
};
|
||||
|
||||
// Summary returned by POST /api/settings/records/import.
|
||||
export type ImportResult = {
|
||||
created: number;
|
||||
skipped: number;
|
||||
total: number;
|
||||
errors: string[];
|
||||
};
|
||||
|
||||
export function exportRecords(): Promise<RecordsExport> {
|
||||
return apiFetch<RecordsExport>("/api/settings/records/export");
|
||||
}
|
||||
|
||||
export function importRecords(patients: unknown[]): Promise<ImportResult> {
|
||||
return apiFetch<ImportResult>("/api/settings/records/import", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ patients }),
|
||||
});
|
||||
}
|
||||
|
||||
// Trigger a browser download of a JSON archive.
|
||||
export function downloadJson(data: unknown, filename: string): void {
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
||||
type: "application/json",
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
Reference in New Issue
Block a user