style: format frontend, docs and skills with prettier; add .prettierignore
One-time prettier --write so the new format:check CI gate starts green. .prettierignore excludes generated (api-client.ts, documentation.json), vendored (public/cibg-huisstijl), and backend (dotnet format owns it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import { assertNever } from '@shared/kernel/fp';
|
||||
import { Brief, BriefStatus, LetterBlock, LetterSection, LibraryPassage, allBlocks, canSubmit } from './brief';
|
||||
import {
|
||||
Brief,
|
||||
BriefStatus,
|
||||
LetterBlock,
|
||||
LetterSection,
|
||||
LibraryPassage,
|
||||
allBlocks,
|
||||
canSubmit,
|
||||
} from './brief';
|
||||
import { RichTextBlock, deepCopyBlock, emptyBlock } from '@shared/kernel/rich-text';
|
||||
|
||||
/**
|
||||
@@ -59,8 +67,15 @@ function nextLocalIndex(brief: Brief): number {
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
function mapSection(brief: Brief, sectionKey: string, f: (s: LetterSection) => LetterSection): Brief {
|
||||
return { ...brief, sections: brief.sections.map((s) => (s.sectionKey === sectionKey ? f(s) : s)) };
|
||||
function mapSection(
|
||||
brief: Brief,
|
||||
sectionKey: string,
|
||||
f: (s: LetterSection) => LetterSection,
|
||||
): Brief {
|
||||
return {
|
||||
...brief,
|
||||
sections: brief.sections.map((s) => (s.sectionKey === sectionKey ? f(s) : s)),
|
||||
};
|
||||
}
|
||||
|
||||
/** The section a block currently lives in, or undefined if the block is gone. */
|
||||
@@ -86,7 +101,11 @@ function withEdit(s: BriefState, f: (b: Brief) => Brief): BriefState {
|
||||
return { ...s, brief };
|
||||
}
|
||||
|
||||
function insertPassages(brief: Brief, sectionKey: string, passages: readonly LibraryPassage[]): Brief {
|
||||
function insertPassages(
|
||||
brief: Brief,
|
||||
sectionKey: string,
|
||||
passages: readonly LibraryPassage[],
|
||||
): Brief {
|
||||
let idx = nextLocalIndex(brief);
|
||||
// The freeze happens HERE: each block gets a deep VALUE copy of the library content,
|
||||
// so later library edits can never mutate this letter (frozen snapshot).
|
||||
@@ -102,7 +121,11 @@ function insertPassages(brief: Brief, sectionKey: string, passages: readonly Lib
|
||||
}
|
||||
|
||||
function addFreeText(brief: Brief, sectionKey: string): Brief {
|
||||
const block: LetterBlock = { type: 'freeText', blockId: `local-${nextLocalIndex(brief)}`, content: emptyBlock() };
|
||||
const block: LetterBlock = {
|
||||
type: 'freeText',
|
||||
blockId: `local-${nextLocalIndex(brief)}`,
|
||||
content: emptyBlock(),
|
||||
};
|
||||
return mapSection(brief, sectionKey, (s) => ({ ...s, blocks: [...s.blocks, block] }));
|
||||
}
|
||||
|
||||
@@ -118,7 +141,11 @@ function editBlockContent(brief: Brief, blockId: string, content: RichTextBlock)
|
||||
);
|
||||
}
|
||||
|
||||
function moveWithinSection(blocks: readonly LetterBlock[], blockId: string, toIndex: number): LetterBlock[] {
|
||||
function moveWithinSection(
|
||||
blocks: readonly LetterBlock[],
|
||||
blockId: string,
|
||||
toIndex: number,
|
||||
): LetterBlock[] {
|
||||
const from = blocks.findIndex((b) => b.blockId === blockId);
|
||||
if (from === -1) return [...blocks];
|
||||
const clamped = Math.max(0, Math.min(toIndex, blocks.length - 1));
|
||||
@@ -140,12 +167,18 @@ export function reduce(s: BriefState, m: BriefMsg): BriefState {
|
||||
// Section-level guard (defense-in-depth): locked sections never accept edits, even if a
|
||||
// Msg reaches the reducer. The UI already hides controls for locked sections.
|
||||
case 'PassagesInserted':
|
||||
return withEdit(s, (b) => (isSectionEditable(b, m.sectionKey) ? insertPassages(b, m.sectionKey, m.passages) : b));
|
||||
return withEdit(s, (b) =>
|
||||
isSectionEditable(b, m.sectionKey) ? insertPassages(b, m.sectionKey, m.passages) : b,
|
||||
);
|
||||
case 'FreeTextBlockAdded':
|
||||
return withEdit(s, (b) => (isSectionEditable(b, m.sectionKey) ? addFreeText(b, m.sectionKey) : b));
|
||||
return withEdit(s, (b) =>
|
||||
isSectionEditable(b, m.sectionKey) ? addFreeText(b, m.sectionKey) : b,
|
||||
);
|
||||
case 'BlockContentEdited':
|
||||
return withEdit(s, (b) =>
|
||||
isSectionEditable(b, sectionKeyOfBlock(b, m.blockId)) ? editBlockContent(b, m.blockId, m.content) : b,
|
||||
isSectionEditable(b, sectionKeyOfBlock(b, m.blockId))
|
||||
? editBlockContent(b, m.blockId, m.content)
|
||||
: b,
|
||||
);
|
||||
case 'BlockRemoved':
|
||||
return withEdit(s, (b) =>
|
||||
@@ -157,18 +190,34 @@ export function reduce(s: BriefState, m: BriefMsg): BriefState {
|
||||
return withEdit(s, (b) =>
|
||||
isSectionEditable(b, sectionKeyOfBlock(b, m.blockId))
|
||||
? mapBlocks(b, (blocks) =>
|
||||
blocks.some((x) => x.blockId === m.blockId) ? moveWithinSection(blocks, m.blockId, m.toIndex) : [...blocks],
|
||||
blocks.some((x) => x.blockId === m.blockId)
|
||||
? moveWithinSection(blocks, m.blockId, m.toIndex)
|
||||
: [...blocks],
|
||||
)
|
||||
: b,
|
||||
);
|
||||
|
||||
case 'Submitted':
|
||||
// Guard the transition AND the completeness invariant.
|
||||
return transition(s, 'draft', () => ({ tag: 'submitted', submittedBy: m.by, submittedAt: m.at }), canSubmit);
|
||||
return transition(
|
||||
s,
|
||||
'draft',
|
||||
() => ({ tag: 'submitted', submittedBy: m.by, submittedAt: m.at }),
|
||||
canSubmit,
|
||||
);
|
||||
case 'Approved':
|
||||
return transition(s, 'submitted', () => ({ tag: 'approved', approvedBy: m.by, approvedAt: m.at }));
|
||||
return transition(s, 'submitted', () => ({
|
||||
tag: 'approved',
|
||||
approvedBy: m.by,
|
||||
approvedAt: m.at,
|
||||
}));
|
||||
case 'Rejected':
|
||||
return transition(s, 'submitted', () => ({ tag: 'rejected', rejectedBy: m.by, rejectedAt: m.at, comments: m.comments }));
|
||||
return transition(s, 'submitted', () => ({
|
||||
tag: 'rejected',
|
||||
rejectedBy: m.by,
|
||||
rejectedAt: m.at,
|
||||
comments: m.comments,
|
||||
}));
|
||||
case 'Sent':
|
||||
return transition(s, 'approved', () => ({ tag: 'sent', sentAt: m.at }));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user