Files
numolus/src/data/seed.ts
T
wkhandClaude Opus 5.5 5a79ddee79 Start with an empty document; offer Datei › Neu and Datei › Muster
A first start (nothing in localStorage) now opens a blank document -- one
sheet "Blatt 1" with an empty "Tabelle 1" -- instead of the pre-filled
sample. The file menu gets "Neu" (blank document) and "Muster ·
Umsatzanalyse" (the former seed), both behind a confirm and both starting
a fresh undo history. The toolbar's "Zurücksetzen" button is gone; its job
is now Datei › Muster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:11:51 +02:00

168 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Document templates: `createBlankDocument()` (Datei › Neu, and what a first
* start opens) and `createSeedDocument()` (Datei › Muster).
*
* The pre-filled document ("roter Faden" of the study), in the v2 canvas model:
* one sheet ("Analyse") carrying three table objects and a chart object, plus a
* second empty sheet to try things out.
*
* Note on the brief: the acceptance list mentions "Übersicht!B2 = 6070" as the
* yearly Nord revenue, but the given Nord row is 1200 + 1450 + 1600 + 1720 =
* 5970. The explicit table wins -- the exact number is deterministic and the
* propagation behaviour it demonstrates is unchanged.
*/
import { parseA1 } from '../engine';
import type { ChartObject, DocumentState, TableObject } from '../store/types';
import { defaultTableView } from '../store/types';
let uid = 0;
const nextId = (p: string) => `${p}-seed-${(uid++).toString(36)}`;
function makeTable(
name: string,
x: number,
y: number,
rows: number,
cols: number,
entries: Array<[string, string | number]>,
colWidths: Record<number, number> = {},
viewOverride: Partial<TableObject['view']> = {},
): TableObject {
const cells: Record<string, string> = {};
for (const [a1, value] of entries) {
const { col, row } = parseA1(a1);
cells[`${col},${row}`] = String(value);
}
return {
kind: 'table',
id: nextId('tbl'),
name,
x,
y,
rows,
cols,
colWidths,
cells,
view: { ...defaultTableView(), ...viewOverride },
};
}
export function createSeedDocument(): DocumentState {
const umsatz = makeTable('Umsatz', 40, 40, 20, 8, [
['A1', 'Region'], ['B1', 'Q1'], ['C1', 'Q2'], ['D1', 'Q3'], ['E1', 'Q4'],
['A2', 'Nord'], ['B2', 1200], ['C2', 1450], ['D2', 1600], ['E2', 1720],
['A3', 'Süd'], ['B3', 980], ['C3', 1100], ['D3', 1250], ['E3', 1310],
['A4', 'Ost'], ['B4', 1550], ['C4', 1480], ['D4', 1620], ['E4', 1700],
['A5', 'West'], ['B5', 1340], ['C5', 1390], ['D5', 1500], ['E5', 1580],
['A6', 'Zentral'], ['B6', 1105], ['C6', 1205], ['D6', 1298], ['E6', 1402],
['A8', 'Summe'],
['B8', '=SUM(B2:B6)'], ['C8', '=SUM(C2:C6)'], ['D8', '=SUM(D2:D6)'], ['E8', '=SUM(E2:E6)'],
['A9', 'Mittelwert'],
['B9', '=AVG(B2:B6)'], ['C9', '=AVG(C2:C6)'], ['D9', '=AVG(D2:D6)'], ['E9', '=AVG(E2:E6)'],
['A10', 'Minimum'],
['B10', '=MIN(B2:B6)'], ['C10', '=MIN(C2:C6)'], ['D10', '=MIN(D2:D6)'], ['E10', '=MIN(E2:E6)'],
['A11', 'Maximum'],
['B11', '=MAX(B2:B6)'], ['C11', '=MAX(C2:C6)'], ['D11', '=MAX(D2:D6)'], ['E11', '=MAX(E2:E6)'],
['A12', 'Anzahl'],
['B12', '=COUNT(B2:B6)'], ['C12', '=COUNT(C2:C6)'], ['D12', '=COUNT(D2:D6)'], ['E12', '=COUNT(E2:E6)'],
['G2', 'Jahr Nord'], ['H2', '=SUM(B2:E2)'],
['G3', 'Schnitt/Q'], ['H3', '=SUM(B2:E2)/4'],
['G4', 'Delta Q4-Q1'], ['H4', '=E2-B2'],
], { 0: 92, 6: 96 }, { mode: 'fixed', width: 545, height: 360, frozenRows: 1, frozenCols: 1 });
const kosten = makeTable('Kosten', 40, 700, 12, 8, [
['A1', 'Region'], ['B1', 'Q1'], ['C1', 'Q2'], ['D1', 'Q3'], ['E1', 'Q4'],
['A2', 'Nord'], ['B2', 800], ['C2', 820], ['D2', 900], ['E2', 880],
['A3', 'Süd'], ['B3', 700], ['C3', 690], ['D3', 710], ['E3', 720],
['A4', 'Ost'], ['B4', 900], ['C4', 910], ['D4', 950], ['E4', 980],
['A5', 'West'], ['B5', 760], ['C5', 770], ['D5', 780], ['E5', 790],
['A6', 'Zentral'], ['B6', 640], ['C6', 650], ['D6', 660], ['E6', 670],
], { 0: 92 });
const regions = ['Nord', 'Süd', 'Ost', 'West', 'Zentral'];
const overview: Array<[string, string | number]> = [
['A1', 'Region'], ['B1', 'Umsatz Jahr'], ['C1', 'Kosten Jahr'],
['D1', 'Deckungsbeitrag'], ['E1', 'Marge %'],
];
regions.forEach((region, i) => {
const r = i + 2;
overview.push(
[`A${r}`, region],
[`B${r}`, `=SUM(Umsatz!B${r}:E${r})`],
[`C${r}`, `=SUM(Kosten!B${r}:E${r})`],
[`D${r}`, `=B${r}-C${r}`],
[`E${r}`, `=D${r}/B${r}*100`],
);
});
overview.push(
['A8', 'Gesamt'],
['B8', '=SUM(B2:B6)'], ['C8', '=SUM(C2:C6)'],
['D8', '=SUM(D2:D6)'], ['E8', '=AVG(E2:E6)'],
);
const uebersicht = makeTable('Übersicht', 1000, 40, 12, 6, overview, {
0: 84, 3: 118,
});
const chart: ChartObject = {
kind: 'chart',
id: nextId('cht'),
name: 'Umsatz je Region',
x: 1000,
y: 460,
width: 560,
height: 340,
view: 'bar',
sourceTableId: umsatz.id,
};
const analyse = {
id: nextId('sheet'),
name: 'Analyse',
objects: [umsatz, kosten, uebersicht, chart],
};
// second sheet demos the per-sheet backdrop colour + hidden grid
const blatt2 = {
id: nextId('sheet'),
name: 'Blatt 2',
objects: [],
background: '#f4f1ff',
showGrid: false,
};
return {
version: 2,
sheets: [analyse, blatt2],
activeSheetId: analyse.id,
activeObjectId: umsatz.id,
selection: { tableId: umsatz.id, anchor: { col: 0, row: 0 }, focus: { col: 0, row: 0 } },
locale: 'de-DE',
csvDelimiter: ',',
customFunctions: [],
namedRanges: [
// demos "🏷 Namen": =Umsatz_Q1 anywhere resolves to Umsatz!B2:B6, even
// from a different sheet, without a table qualifier.
{ id: nextId('nr'), name: 'Umsatz_Q1', tableId: umsatz.id, start: { col: 1, row: 1 }, end: { col: 1, row: 5 } },
],
macros: [],
};
}
/** An empty document: one sheet with one empty table, ready to type into. */
export function createBlankDocument(): DocumentState {
const table = makeTable('Tabelle 1', 40, 40, 12, 6, []);
const sheet = { id: nextId('sheet'), name: 'Blatt 1', objects: [table] };
return {
version: 2,
sheets: [sheet],
activeSheetId: sheet.id,
activeObjectId: table.id,
selection: { tableId: table.id, anchor: { col: 0, row: 0 }, focus: { col: 0, row: 0 } },
locale: 'de-DE',
csvDelimiter: ',',
customFunctions: [],
namedRanges: [],
macros: [],
};
}