feat(fp): WP-26 — admin org-template editor
CI / frontend (push) Failing after 57s
CI / storybook-a11y (push) Successful in 4m27s
CI / backend (push) Successful in 1m15s
CI / codeql (csharp) (push) Failing after 1m42s
CI / codeql (javascript-typescript) (push) Failing after 1m19s
CI / api-client-drift (push) Successful in 1m40s
CI / e2e (push) Failing after 3h11m33s

Edit the letter's org identity in place on the same canvas the drafter composes
on (editableRegions='template'): letterhead/signature/footer become inline
controls, content a read-only sample. Margins (bounded), logo upload (reuses the
shared upload transport + single-upload), version history + rollback, proefbrief,
and publish-with-impact-confirmation. House form-machine idiom
(org-template.machine.ts) + root store with debounced save. Capability-gated
(orgtemplate:edit) with a deny-by-default alert; route /brief/huisstijl.

Backend + generated client were already in place (WP-23). Also fixes a
pre-existing red check:tokens (WP-24 canvas hex fallbacks) and threads the
published logo through to the drafter's canvas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-06 08:22:55 +02:00
co-authored by Claude Opus 4.8
parent 1bb9383344
commit f6c837f281
20 changed files with 5530 additions and 182 deletions
@@ -33,4 +33,11 @@ export class AccessStore {
const rd = this.capabilities();
return rd.tag === 'Success' && rd.value.includes(capability);
}
/** True once `/me` has resolved (success or failure) — lets a page-level gate tell
"still loading" apart from "denied", so an admin doesn't flash the denial alert. */
readonly ready = computed(() => {
const tag = this.capabilities().tag;
return tag === 'Success' || tag === 'Failure';
});
}
+7 -1
View File
@@ -38,6 +38,12 @@ export interface XhrUploadHandle {
/** Sentinel rejection so the caller can tell a user-cancel from a real failure. */
export const UPLOAD_ABORTED = Symbol('upload-aborted');
/** Direct URL to a stored document's bytes. Pure (no injection) so a store can build
a letterhead-logo `src` without pulling `ApiClient` into its dependency graph. */
export function uploadContentUrl(documentId: string): string {
return `${environment.apiBaseUrl}/api/v1/uploads/${documentId}/content`;
}
/**
* Infrastructure: the only place upload HTTP lives. Reads (categories, status,
* delete) go through the NSwag client; the multipart POST is hand-written XHR
@@ -94,7 +100,7 @@ export class UploadAdapter {
* browser opens it. demo-* ids (dev simulation) have no bytes and 404 here.
*/
contentUrl(documentId: string): string {
return `${environment.apiBaseUrl}/api/v1/uploads/${documentId}/content`;
return uploadContentUrl(documentId);
}
/**