feat(FN-3764): add manage view with edit modal for CLI printing press plugi
FN-3764 adds a manage view and edit draft modal to the CLI printing press plugin, wiring in draft storage, wizard routes, and plugin view registry integration so users can view and edit their printing press drafts directly from the dashboard. Includes corresponding tests, documentation updates, and Fusion-Task-Id: FN-3764
This commit is contained in:
@@ -1,20 +1,46 @@
|
||||
/* Interim storage — replaced by FN-3766's canonical schema. Do not extend without updating that ticket. */
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { ServiceDraft } from "../wizard/types.js";
|
||||
|
||||
export class NotFoundError extends Error {
|
||||
constructor(id: string) {
|
||||
super(`Draft not found: ${id}`);
|
||||
this.name = "NotFoundError";
|
||||
}
|
||||
}
|
||||
|
||||
function mergeDraft(existing: ServiceDraft, patch: Partial<ServiceDraft>): ServiceDraft {
|
||||
const mergedCredential = patch.credential && typeof patch.credential === "object"
|
||||
? { ...existing.credential, ...patch.credential }
|
||||
: existing.credential;
|
||||
|
||||
return {
|
||||
...existing,
|
||||
...patch,
|
||||
credential: mergedCredential,
|
||||
endpoints: patch.endpoints ?? existing.endpoints,
|
||||
};
|
||||
}
|
||||
|
||||
export function createDraftStore({ rootDir }: { rootDir: string }) {
|
||||
const draftsDir = join(rootDir, ".fusion", "plugins", "cli-printing-press", "drafts");
|
||||
|
||||
async function ensureDir() { await mkdir(draftsDir, { recursive: true }); }
|
||||
|
||||
async function writeAtomic(path: string, draft: ServiceDraft): Promise<void> {
|
||||
const tempPath = `${path}.tmp-${randomUUID()}`;
|
||||
await writeFile(tempPath, JSON.stringify(draft, null, 2), "utf8");
|
||||
await rename(tempPath, path);
|
||||
}
|
||||
|
||||
return {
|
||||
async create(input: ServiceDraft) {
|
||||
await ensureDir();
|
||||
const now = new Date().toISOString();
|
||||
const draft: ServiceDraft = { ...input, id: input.id || randomUUID(), createdAt: input.createdAt || now, updatedAt: now };
|
||||
await writeFile(join(draftsDir, `${draft.id}.json`), JSON.stringify(draft, null, 2), "utf8");
|
||||
await writeAtomic(join(draftsDir, `${draft.id}.json`), draft);
|
||||
return draft;
|
||||
},
|
||||
async list() {
|
||||
@@ -26,6 +52,19 @@ export function createDraftStore({ rootDir }: { rootDir: string }) {
|
||||
async get(id: string) {
|
||||
try { return JSON.parse(await readFile(join(draftsDir, `${id}.json`), "utf8")) as ServiceDraft; } catch { return null; }
|
||||
},
|
||||
async update(id: string, patch: Partial<ServiceDraft>) {
|
||||
await ensureDir();
|
||||
const current = await this.get(id);
|
||||
if (!current) throw new NotFoundError(id);
|
||||
const updated: ServiceDraft = {
|
||||
...mergeDraft(current, patch),
|
||||
id: current.id,
|
||||
createdAt: current.createdAt,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
await writeAtomic(join(draftsDir, `${id}.json`), updated);
|
||||
return updated;
|
||||
},
|
||||
async delete(id: string) {
|
||||
await rm(join(draftsDir, `${id}.json`), { force: true });
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user