mirror of
https://github.com/velocitatem/cvfs.git
synced 2026-05-31 16:53:38 +00:00
Redesign webapp with minimal style and full backend integration
- New monochrome design system in globals.css (no shadows, tight spacing, system-font)
- Root layout stripped to html/body; home page owns its Header/Footer
- Dashboard fully wired to FastAPI backend: upload, branch, submission, publish, download
- CVTree rebuilt as compact file-tree component driven by real version data
- DiffViewer rebuilt as minimal git-diff display using real patch data
- API client (libs/api.ts) extended with all CRUD operations
- Header redesigned to match minimal style
- Backend: added GET /documents/{id}/versions/{id}/download endpoint for DOCX export
https://claude.ai/code/session_01Xmxm2QLgFBgRJyYD6VukR6
This commit is contained in:
@@ -1,50 +1,133 @@
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:9812";
|
||||
const API = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:9812";
|
||||
|
||||
export type StructuredBlock = {
|
||||
path: string
|
||||
block_type: string
|
||||
text: string
|
||||
keywords: string[]
|
||||
}
|
||||
path: string;
|
||||
block_type: string;
|
||||
text: string;
|
||||
keywords: string[];
|
||||
};
|
||||
|
||||
export type Patch = {
|
||||
id: string
|
||||
target_path: string
|
||||
operation: string
|
||||
rationale?: string | null
|
||||
new_value?: string | null
|
||||
created_at: string
|
||||
}
|
||||
id: string;
|
||||
target_path: string;
|
||||
operation: string;
|
||||
old_value?: string | null;
|
||||
new_value?: string | null;
|
||||
metadata_json?: Record<string, unknown> | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type Version = {
|
||||
id: string
|
||||
branch_name: string
|
||||
version_label?: string | null
|
||||
parent_version_id?: string | null
|
||||
structured_blocks?: StructuredBlock[] | null
|
||||
patches?: Patch[]
|
||||
}
|
||||
id: string;
|
||||
branch_name: string;
|
||||
version_label?: string | null;
|
||||
parent_version_id?: string | null;
|
||||
structured_blocks?: StructuredBlock[] | null;
|
||||
artifact_docx_key?: string | null;
|
||||
patches: Patch[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Document = {
|
||||
id: string
|
||||
title: string
|
||||
description?: string | null
|
||||
owner_id: string
|
||||
versions: Version[]
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string | null;
|
||||
owner_id: string;
|
||||
root_version_id?: string | null;
|
||||
versions: Version[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type Submission = {
|
||||
id: string;
|
||||
version_id: string;
|
||||
company_name: string;
|
||||
role_title: string;
|
||||
job_url?: string | null;
|
||||
job_description?: string | null;
|
||||
status: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type PublicAsset = {
|
||||
id: string;
|
||||
slug: string;
|
||||
artifact_key: string;
|
||||
is_public: boolean;
|
||||
url?: string | null;
|
||||
version_id?: string | null;
|
||||
submission_id?: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
async function req<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${API}${path}`, {
|
||||
...init,
|
||||
headers: { accept: "application/json", ...init?.headers },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const detail = await res.text().catch(() => res.statusText);
|
||||
throw new Error(detail || `HTTP ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchDocuments(): Promise<Document[]> {
|
||||
const response = await fetch(`${API_BASE_URL}/api/v1/documents`, {
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error("Unable to load documents")
|
||||
}
|
||||
const payload = await response.json()
|
||||
return payload?.items ?? []
|
||||
export const fetchDocuments = (): Promise<Document[]> =>
|
||||
req<{ items: Document[] }>("/api/v1/documents", { cache: "no-store" }).then(r => r.items);
|
||||
|
||||
export const fetchDocument = (id: string): Promise<Document> =>
|
||||
req<Document>(`/api/v1/documents/${id}`, { cache: "no-store" });
|
||||
|
||||
export async function uploadDocument(title: string, description: string | null, file: File): Promise<Document> {
|
||||
const form = new FormData();
|
||||
form.append("title", title);
|
||||
if (description) form.append("description", description);
|
||||
form.append("file", file);
|
||||
return req<Document>("/api/v1/documents", { method: "POST", body: form });
|
||||
}
|
||||
|
||||
export { API_BASE_URL }
|
||||
export const downloadVersionUrl = (documentId: string, versionId: string): string =>
|
||||
`${API}/api/v1/documents/${documentId}/versions/${versionId}/download`;
|
||||
|
||||
export async function createBranch(
|
||||
parentVersionId: string,
|
||||
branchName: string,
|
||||
versionLabel?: string | null,
|
||||
patches: Record<string, unknown>[] = [],
|
||||
): Promise<Version> {
|
||||
return req<Version>("/api/v1/versions/branches", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ parent_version_id: parentVersionId, branch_name: branchName, version_label: versionLabel ?? null, patches }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function createSubmission(
|
||||
versionId: string,
|
||||
companyName: string,
|
||||
roleTitle: string,
|
||||
jobUrl?: string | null,
|
||||
jobDescription?: string | null,
|
||||
): Promise<Submission> {
|
||||
return req<Submission>("/api/v1/submissions", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ version_id: versionId, company_name: companyName, role_title: roleTitle, job_url: jobUrl ?? null, job_description: jobDescription ?? null }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function publishVersion(
|
||||
versionId?: string | null,
|
||||
submissionId?: string | null,
|
||||
slug?: string | null,
|
||||
): Promise<PublicAsset> {
|
||||
return req<PublicAsset>("/api/v1/public/publish", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ version_id: versionId ?? null, submission_id: submissionId ?? null, slug: slug ?? null }),
|
||||
});
|
||||
}
|
||||
|
||||
export { API as API_BASE_URL };
|
||||
|
||||
Reference in New Issue
Block a user