Merge branch 'dev' into feature/billing

This commit is contained in:
behrinml
2026-08-06 20:32:59 +02:00
56 changed files with 12670 additions and 363 deletions
+110
View File
@@ -908,12 +908,21 @@ export interface ProjectStats {
bom_cost: number;
}
// A sub-project as listed on its parent's page. The figures cover the child's
// own subtree, so the listed rows add up to the parent's roll-up minus the
// parent's own prints (#1264).
export interface ProjectChildPreview {
id: number;
name: string;
color: string | null;
status: string;
progress_percent: number | null;
descendant_count: number;
total_archives: number;
completed_prints: number;
total_print_time_hours: number;
total_filament_grams: number;
total_cost: number; // Filament + energy + BOM, matching the parent's cost card
}
export interface Project {
@@ -936,9 +945,13 @@ export interface Project {
parent_id: number | null;
parent_name: string | null;
children: ProjectChildPreview[];
descendant_count: number; // Sub-projects at any depth beneath this one (#1264)
created_at: string;
updated_at: string;
stats?: ProjectStats;
// This project's numbers combined with every sub-project's. Null when there
// are none, since it would only repeat `stats` (#1264).
rollup_stats?: ProjectStats | null;
url: string | null; // External link rendered next to project name on the card (#1155)
cover_image_filename: string | null; // Filename within project attachments dir (#1155)
}
@@ -985,6 +998,8 @@ export interface ProjectListItem {
failed_count: number; // Sum of quantities for failed prints
queue_count: number;
progress_percent: number | null; // Plates progress
parent_id: number | null; // #1264 — set when this is a sub-project
child_count: number; // #1264 — direct sub-projects only
archives: ArchivePreview[];
url: string | null; // #1155
cover_image_filename: string | null; // #1155
@@ -2908,12 +2923,94 @@ export interface GitHubBackupStatus {
configured: boolean;
enabled: boolean;
is_running: boolean;
restore_running: boolean;
progress: string | null;
last_backup_at: string | null;
last_backup_status: string | null;
next_scheduled_run: string | null;
}
// Restore from a Git backup (#2656). Cloud profiles are absent deliberately —
// the backup collector never writes them, so there is nothing to restore.
export type RestoreCategory = 'kprofiles' | 'settings' | 'spools' | 'archives';
export interface GitHubCommitInfo {
sha: string;
message: string;
author: string;
date: string;
}
export interface GitHubCommitListResponse {
success: boolean;
message: string;
branch: string;
commits: GitHubCommitInfo[];
}
/**
* Values the server interpolates into a translated note or preview detail.
* Kept to strings and numbers on purpose — anything richer would have to be
* formatted server-side and could not be translated.
*/
export type GitHubRestoreParams = Record<string, string | number>;
export interface GitHubRestorePreviewCategory {
category: RestoreCategory;
available: boolean;
item_count: number;
/** English rendering. Used as i18next's defaultValue, never shown on its own. */
detail: string | null;
/** Key under backup.restoreFromGit.details, or null when there is no caveat. */
detail_code: string | null;
detail_params: GitHubRestoreParams;
}
export interface GitHubRestorePreview {
success: boolean;
message: string;
ref: string;
commit: GitHubCommitInfo | null;
metadata_version: string | null;
categories: GitHubRestorePreviewCategory[];
}
export interface GitHubRestoreRequest {
ref?: string;
categories: RestoreCategory[];
overwrite_existing?: boolean;
}
/**
* One tally note, as a translation code plus its parameters (#2656).
*
* Same contract as {@link LocalBackupPathCheck} one card down: the server picks
* the code and supplies typed params, and the client renders
* ``t(`backup.restoreFromGit.notes.${code}`, { ...params, defaultValue: message })``.
* A code the client does not know yet falls back to the English `message`
* rather than showing the raw key.
*/
export interface GitHubRestoreNote {
code: string;
params: GitHubRestoreParams;
message: string;
}
export interface GitHubRestoreCategoryResult {
restored: number;
skipped: number;
failed: number;
notes: GitHubRestoreNote[];
}
export interface GitHubRestoreResponse {
success: boolean;
message: string;
log_id: number | null;
ref: string | null;
results: Record<string, GitHubRestoreCategoryResult>;
}
export interface LocalBackupStatus {
enabled: boolean;
schedule: string;
@@ -6970,6 +7067,19 @@ export const api = {
clearGitHubBackupLogs: (keepLast: number = 10) =>
request<{ deleted: number; message: string }>(`/github-backup/logs?keep_last=${keepLast}`, { method: 'DELETE' }),
// Restore from a Git backup (#2656)
getGitHubBackupCommits: (limit: number = 20) =>
request<GitHubCommitListResponse>(`/github-backup/commits?limit=${limit}`),
getGitHubRestorePreview: (ref: string = 'HEAD') =>
request<GitHubRestorePreview>(`/github-backup/restore/preview?ref=${encodeURIComponent(ref)}`),
restoreFromGitHub: (payload: GitHubRestoreRequest) =>
request<GitHubRestoreResponse>('/github-backup/restore', {
method: 'POST',
body: JSON.stringify(payload),
}),
// Scheduled local backups
getLocalBackupStatus: () =>
request<LocalBackupStatus>('/local-backup/status'),