@eintrek/erp-shell 0.1.74 → 0.1.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,9 +10,11 @@
10
10
  * The main entry re-exports the same modules, so a tour defined against
11
11
  * either entry is the same data, and the bus and the "?" entry are shared.
12
12
  */
13
- export type { GuideAbortAction, GuideAnchorKey, GuideApiFailure, GuideApiMatch, GuideAppId, GuideCaptureEvent, GuideConfig, GuideContext, GuideFacts, GuideGate, GuideMission, GuideNothingToSave, GuideOnMissing, GuidePage, GuideStep, GuideStepKind, GuideSuite, GuideText, GuideTrigger, } from "./types";
13
+ export type { GuideAbortAction, GuideAnchorKey, GuideApiFailure, GuideApiMatch, GuideAppId, GuideCaptureEvent, GuideConfig, GuideContext, GuideFacts, GuideFlow, GuideGate, GuideMission, GuideNothingToSave, GuideOnMissing, GuidePage, GuideSection, GuideStep, GuideStepKind, GuideSuite, GuideText, GuideTrigger, } from "./types";
14
14
  export { API_EVENT as GUIDE_API_EVENT, GUIDE_EVENT_PREFIX, GUIDE_LAYER_ATTRIBUTE, apiCallDetail, emitGuideEvent, guideAnchor, reportApiCall, reportApiEvent, type ApiCallReport, type ApiEventDetail, type GuideAnchorProps, } from "./events";
15
- export { guideMenuItems, guideMenuShown, openGuidePanel, openPageGuide, openPagesPanel, useGuideEntry, usePageGuideEntry, usePageGuides, type GuideEntry, type GuideMenuItem, type PageGuidesEntry, } from "./guide-store";
15
+ export { guideMenuItems, guideMenuShown, openGuidePanel, openPageGuide, openPagesPanel, openSectionGuide, openFlow, openFlowsPanel, setSectionGuideValues, useGuideEntry, usePageGuideEntry, usePageGuides, useSectionGuideValues, type GuideEntry, type GuideMenuItem, type PageGuidesEntry, } from "./guide-store";
16
+ export { SectionGuideButton, type SectionGuideButtonProps } from "./section-button";
17
+ export { THAI_MONTHS_SHORT, formatBaht, thaiMonth, thaiMonthYear } from "./format";
16
18
  export { GuideRuntime, type GuideMenu, type GuideRouting, type GuideRuntimeProps, } from "./guide-runtime";
17
19
  export { GuideProgressConflict, loadGuides, storeGuides, type GuideProgressAdapter, type GuideProgressRequest, type GuideProgressResponse, } from "./progress-sync";
18
20
  export type { GuideAudience, GuideMode, PagesMode } from "./progress";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Small formatters for section guides' copy (shell 0.1.75): the app passes
3
+ * its own computed numbers, and the copy says them the way the page does —
4
+ * "ม.ค. รับ 500,000 − จ่าย 320,000 = คงเหลือ 180,000".
5
+ *
6
+ * Pure: the tests run it in Node.
7
+ */
8
+ export declare const THAI_MONTHS_SHORT: readonly ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
9
+ /**
10
+ * A baht amount with thousands separators, no currency sign: 500000 →
11
+ * "500,000"; 1234.5 → "1,234.50"; -320000 → "-320,000". Whole amounts
12
+ * have no decimals unless `decimals` says so; others have two. Not a
13
+ * finite number → "" (a step whose value is missing is skipped anyway).
14
+ */
15
+ export declare function formatBaht(n: unknown, opts?: {
16
+ decimals?: number;
17
+ }): string;
18
+ /** "2026-01" (or a Date, or 1) → "ม.ค."; "" when it is not a month. */
19
+ export declare function thaiMonth(m: unknown): string;
20
+ /**
21
+ * "2026-01" → "ม.ค. 69" (the Buddhist year's last two digits), or with
22
+ * `{ year: "full" }` "ม.ค. 2569"; just the month when no year is known.
23
+ */
24
+ export declare function thaiMonthYear(m: unknown, opts?: {
25
+ year?: "short" | "full";
26
+ }): string;
@@ -21,11 +21,30 @@ export interface PageGuidesEntry {
21
21
  } | null;
22
22
  /** Page guides with an `href` this person has (the panel's list). */
23
23
  listed: number;
24
+ /**
25
+ * Section guides on screen here that this person has ("อธิบายส่วนนี้ ·
26
+ * <title>"), in the suite's order. Shell ≥ 0.1.75.
27
+ */
28
+ sections?: ReadonlyArray<{
29
+ key: string;
30
+ title: string;
31
+ }>;
32
+ /** Flows that start on this page ("พาทำ · <title>"). Shell ≥ 0.1.76. */
33
+ flows?: ReadonlyArray<{
34
+ key: string;
35
+ title: string;
36
+ }>;
37
+ /** Flows this person has (the panel's "พาทำงาน" list). Shell ≥ 0.1.76. */
38
+ flowsListed?: number;
24
39
  }
25
- /** Called by the host: this page's guide and the listed count, and how to open them. */
26
- export declare function publishPageGuides(next: PageGuidesEntry | null, openPage: (() => void) | null, openPages: (() => void) | null): void;
27
- /** The "?" menu's items, in order (pure: the menu and its tests share it). */
28
- export type GuideMenuItem = "page" | "missions" | "pages" | "help";
40
+ /** Called by the host: this page's guide, the listed count, the sections on screen, and how to open them. */
41
+ export declare function publishPageGuides(next: PageGuidesEntry | null, openPage: (() => void) | null, openPages: (() => void) | null, openSection?: ((key: string) => void) | null, openFlow?: ((key: string) => void) | null, openFlows?: (() => void) | null): void;
42
+ /**
43
+ * The "?" menu's items, in order (pure: the menu and its tests share it).
44
+ * "sections": one entry per section guide; "flowsHere": one "พาทำ · <title>"
45
+ * per flow starting here; "flows": "พาทำงาน" (the panel's flows).
46
+ */
47
+ export type GuideMenuItem = "page" | "sections" | "flowsHere" | "missions" | "pages" | "flows" | "help";
29
48
  export declare function guideMenuItems(e: GuideEntry | null, p?: PageGuidesEntry): GuideMenuItem[];
30
49
  /**
31
50
  * Whether the "?" press opens a menu rather than the help dialog: a tour
@@ -55,3 +74,32 @@ export declare function usePageGuideEntry(): {
55
74
  title: string;
56
75
  open: () => void;
57
76
  } | null;
77
+ /**
78
+ * Opens a section guide ("อธิบายส่วนนี้"), when it is on screen here for this
79
+ * person. The values it shows are the ones the app last rendered
80
+ * (`useSectionGuideValues`).
81
+ */
82
+ export declare function openSectionGuide(key: string): void;
83
+ /** Starts a flow that starts on this page ("พาทำ · <title>"). */
84
+ export declare function openFlow(key: string): void;
85
+ /** Opens the panel on the flows alone ("พาทำงาน"). */
86
+ export declare function openFlowsPanel(): void;
87
+ type Values = Readonly<Record<string, unknown>>;
88
+ /**
89
+ * Sets (or with undefined, clears) a section guide's live values. The
90
+ * engine reads the latest whenever it draws a step, so a page that
91
+ * re-renders mid-guide never leaves an old number on screen.
92
+ */
93
+ export declare function setSectionGuideValues(key: string, values: Values | undefined): void;
94
+ /** A section guide's live values now (empty when the app has given none). */
95
+ export declare function getSectionGuideValues(key: string | null | undefined): Values;
96
+ /** Re-renders when any section guide's values change (the engine's own use). */
97
+ export declare function useSectionValuesVersion(): number;
98
+ /**
99
+ * Hands a section guide the numbers its copy uses, from the component that
100
+ * computes them: `useSectionGuideValues("cash-flow", { month: "ม.ค.", cashIn,
101
+ * … })`. Call it on every render (the latest wins); they go when the
102
+ * component unmounts. undefined values: nothing.
103
+ */
104
+ export declare function useSectionGuideValues(key: string, values: Values | undefined): void;
105
+ export {};
@@ -191,7 +191,8 @@ export declare function PageOfferCard(p: {
191
191
  title: string;
192
192
  steps: number;
193
193
  onStart: () => void;
194
- onLater: () => void;
194
+ onLater: () => void; /** "คู่มือหน้านี้" by default; a flow's: "พาทำงาน". */
195
+ eyebrow?: string;
195
196
  }): import("react/jsx-runtime").JSX.Element;
196
197
  /** One page guide in the panel's "คู่มือแต่ละหน้า" list. */
197
198
  export interface PageItem {
@@ -200,6 +201,14 @@ export interface PageItem {
200
201
  /** This person has ended this version (ดูแล้ว); otherwise ใหม่. */
201
202
  seen: boolean;
202
203
  }
204
+ /** One flow in the panel's "พาทำงาน" list. Shell ≥ 0.1.76. */
205
+ export interface FlowItem {
206
+ key: string;
207
+ title: string;
208
+ minutes?: number;
209
+ /** This person has finished this version (ทำแล้ว). */
210
+ done: boolean;
211
+ }
203
212
  export declare function QuestChip({ done, total, pulse, onOpen }: {
204
213
  done: number;
205
214
  total: number;
@@ -236,9 +245,14 @@ export declare function QuestPanel(p: {
236
245
  * (shell ≥ 0.1.74). Omitted or empty: no section.
237
246
  */
238
247
  pages?: PageItem[];
239
- /** false: the page guides alone (no missions for this person, or "คู่มือทุกหน้า"). */
248
+ /** false: no missions (none for this person, "คู่มือทุกหน้า" or "พาทำงาน"). */
240
249
  showMissions?: boolean;
241
250
  onOpenPage?: (key: string) => void;
251
+ /** The "พาทำงาน" section: the flows this person has (shell ≥ 0.1.76). */
252
+ flows?: FlowItem[];
253
+ onOpenFlow?: (key: string) => void;
254
+ /** Without missions: one list alone ("คู่มือทุกหน้า" → pages, "พาทำงาน" → flows). */
255
+ only?: "pages" | "flows";
242
256
  }): import("react/jsx-runtime").JSX.Element;
243
257
  export declare function GuideToast({ text }: {
244
258
  text: string;
@@ -58,11 +58,16 @@ export type GuideScreen = {
58
58
  replayNext: boolean;
59
59
  /** A page guide's step: the page key (the mission is `page:<key>`). Shell ≥ 0.1.74. */
60
60
  page?: string;
61
+ /** A section guide's step: the section key (the mission is `section:<key>`). Shell ≥ 0.1.75. */
62
+ section?: string;
63
+ /** A flow's step: the flow key (the mission is `flow:<key>`). Shell ≥ 0.1.76. */
64
+ flow?: string;
61
65
  }
62
66
  /** The first-visit offer of this page's guide: a small card, not modal. */
63
67
  | {
64
68
  name: "pageOffer";
65
- page: string;
69
+ page: string; /** The offer is a flow's (`page` is the flow key). Shell ≥ 0.1.76. */
70
+ flow?: boolean;
66
71
  } | {
67
72
  name: "celebrate";
68
73
  mission: string;
@@ -98,24 +103,43 @@ export interface GuideState {
98
103
  pagesMode: PagesMode;
99
104
  /** Page rows by page key, as the server holds them (or as just written). */
100
105
  pages: Record<string, PageRow>;
106
+ /** Section rows by section key (`<app>.section.<key>`). Shell ≥ 0.1.75. */
107
+ sections: Record<string, PageRow>;
108
+ /** Flow rows by flow key (`<app>.flow.<key>`). Shell ≥ 0.1.76. */
109
+ flows: Record<string, PageRow>;
101
110
  /**
102
- * The page guide being played: `write` when its row is still to be
103
- * written (the offer was taken, or this person has never ended this
104
- * version); a replay from "?" of a recorded page writes nothing.
111
+ * The page or section guide being played: `write` when its row is still
112
+ * to be written (the offer was taken, or this person has never ended this
113
+ * version); a replay of a recorded one writes nothing.
105
114
  */
106
115
  pageRun: {
107
116
  key: string;
108
117
  write: boolean;
118
+ kind?: ReadKind;
119
+ /** A flow's: the tour's captured ids before it started, put back at its end. */
120
+ context?: Record<string, string>;
121
+ } | null;
122
+ /** What the panel shows: everything, the page guides alone ("คู่มือทุกหน้า"), or the flows alone ("พาทำงาน"). */
123
+ panelSection: "all" | "pages" | "flows";
124
+ /**
125
+ * Steps of the mission being played that passed by themselves (a click
126
+ * whose outcome already held): not counted in "ขั้นที่ x/y". Shell ≥ 0.1.75.
127
+ */
128
+ autoPassed: {
129
+ mission: string;
130
+ steps: number[];
109
131
  } | null;
110
- /** What the panel shows: everything, or the page guides alone ("คู่มือทุกหน้า"). */
111
- panelSection: "all" | "pages";
112
132
  }
113
133
  /** The page-guide part of the state, from the progress answer. */
114
134
  export interface GuidePagesInit {
115
135
  pageAudience: GuideAudience;
116
136
  pagesMode: PagesMode;
117
137
  pages: Record<string, PageRow>;
138
+ sections?: Record<string, PageRow>;
139
+ flows?: Record<string, PageRow>;
118
140
  }
141
+ /** A guide with its own row: a page guide, a section guide or a flow. */
142
+ export type ReadKind = "page" | "section" | "flow";
119
143
  export interface ReplayPage {
120
144
  mission: string;
121
145
  /** The mission's abort anchors that were on the page at the start. */
@@ -138,6 +162,8 @@ export interface GuideEnv {
138
162
  targetValue: () => string;
139
163
  /** An app dialog (not the tour's) is open. */
140
164
  dialogOpen?: () => boolean;
165
+ /** The running (or opening) section guide's live values (GuideContext.values). */
166
+ values?: Readonly<Record<string, unknown>>;
141
167
  }
142
168
  export type GuideSignal = {
143
169
  kind: "api";
@@ -221,7 +247,7 @@ export type GuideAction = {
221
247
  type: "dismissComplete";
222
248
  } | {
223
249
  type: "openPanel";
224
- section?: "all" | "pages";
250
+ section?: "all" | "pages" | "flows";
225
251
  } | {
226
252
  type: "closePanel";
227
253
  } | {
@@ -242,10 +268,11 @@ export type GuideAction = {
242
268
  | {
243
269
  type: "idleCelebrate";
244
270
  }
245
- /** The host found this page's guide due (pageOfferDue): show the offer card. */
271
+ /** The host found this page's guide (or, with `flow`, a flow) due: show the offer card. */
246
272
  | {
247
273
  type: "pageOffer";
248
274
  page: string;
275
+ flow?: boolean;
249
276
  }
250
277
  /** The offer's [เริ่ม]. */
251
278
  | {
@@ -269,6 +296,25 @@ export type GuideAction = {
269
296
  type: "pageRow";
270
297
  key: string;
271
298
  row: PageRow;
299
+ kind?: ReadKind;
300
+ }
301
+ /** "อธิบายส่วนนี้": a section guide on screen here (its button, or "?"). */
302
+ | {
303
+ type: "startSection";
304
+ section: string;
305
+ }
306
+ /** "พาทำ · <title>", or the panel's "พาทำงาน" (once on its start page). */
307
+ | {
308
+ type: "startFlow";
309
+ flow: string;
310
+ }
311
+ /** After a refresh, in the same session: back into a flow at its checkpoint. */
312
+ | {
313
+ type: "resumeFlow";
314
+ flow: string;
315
+ step: string;
316
+ context: Record<string, string>;
317
+ write: boolean;
272
318
  };
273
319
  export type GuideEffect = {
274
320
  type: "save";
@@ -291,12 +337,13 @@ export type GuideEffect = {
291
337
  type: "abort";
292
338
  actions: GuideAbortAction[];
293
339
  }
294
- /** Write a page guide's row, once: `<app>.page.<key>`. */
340
+ /** Write a page (or section) guide's row, once: `<app>.page.<key>` / `<app>.section.<key>`. */
295
341
  | {
296
342
  type: "savePage";
297
343
  key: string;
298
344
  version: number;
299
345
  status: "completed" | "skipped";
346
+ kind?: ReadKind;
300
347
  };
301
348
  export interface GuideResult {
302
349
  state: GuideState;
@@ -312,6 +359,8 @@ export declare const COPY: {
312
359
  readonly hiddenToast: "ซ่อนแล้ว";
313
360
  readonly aborted: "ไกด์ไปต่อไม่ได้ ลองอีกครั้งภายหลัง";
314
361
  readonly pageDone: "จบคู่มือหน้านี้";
362
+ readonly sectionDone: "จบคำอธิบายส่วนนี้";
363
+ readonly flowDone: "พาทำงานเสร็จแล้ว";
315
364
  readonly bridgeTitle: "กดปุ่มเมนู";
316
365
  readonly bridgeWhy: (app: string) => string;
317
366
  };
@@ -389,10 +438,14 @@ export declare function chipVisible(state: GuideState, suite: GuideSuite, ctx: G
389
438
  * whose `requires` they lack or whose `skipIf` holds is left out (the step on
390
439
  * screen always counts).
391
440
  */
392
- export declare function stepPosition(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>): {
441
+ export declare function stepPosition(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>, autoPassed?: readonly number[]): {
393
442
  no: number;
394
443
  total: number;
395
444
  };
445
+ /** A step entered now would pass at once: a click or select step whose route or visible outcome holds. */
446
+ export declare function wouldAutoPass(step: GuideStep, env: GuideEnv): boolean;
447
+ /** The steps of this mission that passed by themselves in this run. */
448
+ export declare function autoPassedOf(state: GuideState, mission: string): readonly number[];
396
449
  /** "กด [บันทึก]" → "กด บันทึก" (for screen readers). */
397
450
  export declare function plainTitle(t: string): string;
398
451
  /** The step's status line (GuideStep.status): trimmed text, or null. */
@@ -428,6 +481,29 @@ export declare function firstStepReady(suite: GuideSuite, pageKey: string, env:
428
481
  * route, a moment of nothing loading, the primary tab, not hidden.
429
482
  */
430
483
  export declare function pageOfferDue(state: GuideState, suite: GuideSuite, env: GuideEnv): string | null;
484
+ /** The section guide's row says this person has ended this version of it. */
485
+ export declare function sectionRecorded(state: GuideState, section: {
486
+ key: string;
487
+ version: number;
488
+ }): boolean;
489
+ /** The flow's row says this person has ended this version of it. */
490
+ export declare function flowRecorded(state: GuideState, flow: {
491
+ key: string;
492
+ version: number;
493
+ }): boolean;
494
+ /** The flows this person has that start on this page ("พาทำ · <title>"). */
495
+ export declare function flowsAvailable(suite: GuideSuite, env: GuideEnv): string[];
496
+ /**
497
+ * The flow to offer now, or null: only in "offer" mode, only a flow that
498
+ * says `offer: true`, on its start page, with no row of this version, when
499
+ * nothing else is up and this page's own guide is not due (it comes first:
500
+ * no guide here, offer: false, or already recorded), its first step's
501
+ * target on screen, no app dialog open. The host adds the welcome and the
502
+ * idle wait, as for the page guide's offer.
503
+ */
504
+ export declare function flowOfferDue(state: GuideState, suite: GuideSuite, env: GuideEnv): string | null;
505
+ /** The section guides on screen here that this person has (the "?" entries, the buttons). */
506
+ export declare function sectionsAvailable(suite: GuideSuite, env: GuideEnv): string[];
431
507
  /** The step before this one that this person sees, or -1. */
432
508
  export declare function previousShownIndex(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>): number;
433
509
  /**
@@ -6,7 +6,7 @@
6
6
  * A page guide runs through the missions' machinery as a synthetic mission
7
7
  * `page:<key>` in replay mode, so nothing it does writes tour progress.
8
8
  */
9
- import type { GuideContext, GuideMission, GuidePage, GuideSuite } from "./types";
9
+ import type { GuideContext, GuideFlow, GuideMission, GuidePage, GuideSection, GuideSuite } from "./types";
10
10
  /** The synthetic mission key of a page guide: `page:<key>`. */
11
11
  export declare const PAGE_PREFIX = "page:";
12
12
  export declare function isPageMissionKey(key: string | null | undefined): boolean;
@@ -54,3 +54,54 @@ export declare function pageShown(page: GuidePage, ctx: GuideContext): boolean;
54
54
  export declare function currentPage(suite: GuideSuite, where: PageWhere, ctx: GuideContext): GuidePage | null;
55
55
  /** The page guides this person has, for the panel's list (those with an `href`). */
56
56
  export declare function listedPages(suite: GuideSuite, ctx: GuideContext): GuidePage[];
57
+ /** The synthetic mission key of a section guide: `section:<key>`. */
58
+ export declare const SECTION_PREFIX = "section:";
59
+ export declare function isSectionMissionKey(key: string | null | undefined): boolean;
60
+ export declare function sectionKeyOf(missionKey: string | null | undefined): string | undefined;
61
+ /** Why a section guide cannot run, or null when it can. */
62
+ export declare function sectionProblem(app: string, section: GuideSection): string | null;
63
+ /** The suite's section guides that can run (as validPages: the rest dropped, with a warning in development). */
64
+ export declare function validSections(suite: GuideSuite): GuideSection[];
65
+ export declare function findSection(suite: GuideSuite, key: string | null | undefined): GuideSection | undefined;
66
+ /** A live value is missing: undefined, null, "" or a number that is not finite. */
67
+ export declare function valueMissing(v: unknown): boolean;
68
+ /**
69
+ * The section guide as a mission the machine plays: `section:<key>`, read
70
+ * steps only, checkpoints unless they say otherwise. A step is skipped while
71
+ * a value it `needs` is missing, or when its title comes out empty.
72
+ */
73
+ export declare function sectionMission(section: GuideSection): GuideMission;
74
+ /** Whether the section guide applies here: one of its routes matches and its root is on screen. */
75
+ export declare function sectionApplies(section: GuideSection, where: PageWhere): boolean;
76
+ /** Whether this person has the section guide: `visible` (only true) and `requires`. */
77
+ export declare function sectionShown(section: GuideSection, ctx: GuideContext): boolean;
78
+ /** The section guides on screen here that this person has, in the suite's order. */
79
+ export declare function sectionsHere(suite: GuideSuite, where: PageWhere, ctx: GuideContext): GuideSection[];
80
+ /** The synthetic mission key of a flow: `flow:<key>`. */
81
+ export declare const FLOW_PREFIX = "flow:";
82
+ export declare function isFlowMissionKey(key: string | null | undefined): boolean;
83
+ export declare function flowKeyOf(missionKey: string | null | undefined): string | undefined;
84
+ /** Why a flow cannot run, or null when it can. */
85
+ export declare function flowProblem(app: string, flow: GuideFlow): string | null;
86
+ /** The suite's flows that can run (as validPages: the rest dropped, with a warning in development). */
87
+ export declare function validFlows(suite: GuideSuite): GuideFlow[];
88
+ export declare function findFlow(suite: GuideSuite, key: string | null | undefined): GuideFlow | undefined;
89
+ /** The flow as a mission the machine plays (in replay mode): `flow:<key>`, its steps as authored. */
90
+ export declare function flowMission(flow: GuideFlow): GuideMission;
91
+ /**
92
+ * The flow's chain: its start routes, every step's `route` and route
93
+ * trigger, and its `transit` routes. Leaving all of them ends the flow.
94
+ */
95
+ export declare function flowChain(flow: GuideFlow): string[];
96
+ /** Whether the person is on one of the flow's pages. */
97
+ export declare function inFlowChain(flow: GuideFlow, where: Pick<PageWhere, "code" | "pathname">): boolean;
98
+ /** Whether the person is on the flow's start page. */
99
+ export declare function onFlowStart(flow: GuideFlow, where: Pick<PageWhere, "code" | "pathname" | "search">): boolean;
100
+ /** Whether this person has the flow: `visible` (only true) and `requires`. */
101
+ export declare function flowShown(flow: GuideFlow, ctx: GuideContext): boolean;
102
+ /** The flows this person has (the panel's "พาทำงาน" list), in the suite's order. */
103
+ export declare function shownFlows(suite: GuideSuite, ctx: GuideContext): GuideFlow[];
104
+ /** The flows this person has that start on this page ("พาทำ · <title>"). */
105
+ export declare function flowsHere(suite: GuideSuite, where: PageWhere, ctx: GuideContext): GuideFlow[];
106
+ /** Where the panel sends the person to start a flow, or null when it needs an id it does not have. */
107
+ export declare function flowHref(flow: GuideFlow, ctx: GuideContext): string | null;
@@ -67,10 +67,11 @@ export declare function loadGuides(adapter: GuideProgressAdapter, suite: GuideSu
67
67
  */
68
68
  export declare function storeGuides(adapter: GuideProgressAdapter, suite: GuideSuite, progress: GuideProgress): Promise<GuideProgress | null>;
69
69
  /**
70
- * Writes a page guide's row (`<app>.page.<key>`) once: the minimal body,
70
+ * Writes a page guide's row (`<app>.page.<key>`), or a section guide's
71
+ * (`<app>.section.<key>`, kind "section"), once: the minimal body,
71
72
  * with the same 409 handling as the tour's row. Another tab that already
72
73
  * recorded this version wins (its row is returned, nothing is sent again);
73
74
  * an older version's row is replaced with its rev; a newer one stops. Returns
74
75
  * the stored row, or null when it could not be saved.
75
76
  */
76
- export declare function storePageRow(adapter: GuideProgressAdapter, app: string, key: string, version: number, status: "completed" | "skipped", rev: number): Promise<PageRow | null>;
77
+ export declare function storePageRow(adapter: GuideProgressAdapter, app: string, key: string, version: number, status: "completed" | "skipped", rev: number, kind?: "page" | "section" | "flow"): Promise<PageRow | null>;
@@ -55,6 +55,10 @@ export interface GuidesAnswer {
55
55
  pageAudience: GuideAudience;
56
56
  /** Page rows by page key (the part after `<app>.page.`). */
57
57
  pages: Record<string, PageRow>;
58
+ /** Section rows by section key (the part after `<app>.section.`). Shell ≥ 0.1.75. */
59
+ sections: Record<string, PageRow>;
60
+ /** Flow rows by flow key (the part after `<app>.flow.`). Shell ≥ 0.1.76. */
61
+ flows: Record<string, PageRow>;
58
62
  /** The suite's row; a fresh one when the user has none or an older version. */
59
63
  progress: GuideProgress;
60
64
  /** The server holds a newer version than this app's tour: do not run. */
@@ -72,10 +76,10 @@ export declare function parseGuidesAnswer(payload: unknown, suite: {
72
76
  version: number;
73
77
  app: string;
74
78
  }): GuidesAnswer | null;
75
- /** The row key of a page guide: `<app>.page.<key>`. */
76
- export declare function pageRowKey(app: string, key: string): string;
77
- /** Page rows among the list's rows, by page key. Off-shape rows are left out. */
78
- export declare function parsePageRows(rows: unknown[], app: string): Record<string, PageRow>;
79
+ /** The row key of a page guide `<app>.page.<key>`, a section guide `<app>.section.<key>`, or a flow `<app>.flow.<key>`. */
80
+ export declare function pageRowKey(app: string, key: string, kind?: "page" | "section" | "flow"): string;
81
+ /** Page (or section) rows among the list's rows, by key. Off-shape rows are left out. */
82
+ export declare function parsePageRows(rows: unknown[], app: string, kind?: "page" | "section" | "flow"): Record<string, PageRow>;
79
83
  /** One page row (a list entry, a PUT answer, a 409's `current`); null when off-shape. */
80
84
  export declare function parsePageRow(raw: unknown): PageRow | null;
81
85
  /**
@@ -0,0 +1,12 @@
1
+ export interface SectionGuideButtonProps {
2
+ /** The GuideSection's key. */
3
+ guideKey: string;
4
+ /**
5
+ * The numbers the guide's copy uses (`ctx.values`), from this render.
6
+ * Optional: the section may call `useSectionGuideValues` itself instead.
7
+ */
8
+ values?: Readonly<Record<string, unknown>>;
9
+ /** Replaces the default look (a 22px round icon button in the text colour). */
10
+ className?: string;
11
+ }
12
+ export declare function SectionGuideButton({ guideKey, values, className }: SectionGuideButtonProps): import("react/jsx-runtime").JSX.Element | null;
@@ -41,6 +41,12 @@ export interface GuideContext {
41
41
  can: (policyKey: string) => boolean;
42
42
  /** Whether an anchor is on screen now. */
43
43
  visible: (anchor: GuideAnchorKey) => boolean;
44
+ /**
45
+ * A section guide's live values, as the app last rendered them
46
+ * (`useSectionGuideValues` / `SectionGuideButton values`); empty
47
+ * elsewhere. Shell ≥ 0.1.75.
48
+ */
49
+ values?: Readonly<Record<string, unknown>>;
44
50
  }
45
51
  /** Text that may depend on the context. */
46
52
  export type GuideText = string | ((ctx: GuideContext) => string);
@@ -171,6 +177,12 @@ export interface GuideStep {
171
177
  status?: (ctx: GuideContext) => string | null | undefined;
172
178
  /** Skip the step (silently) when true. Runs every time it is entered. */
173
179
  skipIf?: (ctx: GuideContext) => boolean;
180
+ /**
181
+ * Section guides: the live values this step's copy uses
182
+ * (`ctx.values`). The step is skipped while any is missing (undefined,
183
+ * null, "" or a number that is not finite). Shell ≥ 0.1.75.
184
+ */
185
+ needs?: string[];
174
186
  /** A policy key the step needs (checked with `guides.can`); skipped when
175
187
  * the user lacks it. */
176
188
  requires?: string;
@@ -378,6 +390,90 @@ export interface GuidePage {
378
390
  */
379
391
  steps: GuideStep[];
380
392
  }
393
+ /**
394
+ * A section guide ("อธิบายส่วนนี้", shell ≥ 0.1.75): explains one section of
395
+ * a page — a number-heavy card or table — row by row, with the formula and
396
+ * this record's own numbers. Read-only ("view" steps only; a step may point
397
+ * at a row or cell inside the section with `targetId`), never offered by
398
+ * itself: it opens from the section's `SectionGuideButton` or from "?" →
399
+ * "อธิบายส่วนนี้ · <title>". Same switch and audience as page guides
400
+ * (`guides.<app>.pages` not off); one row per section guide per person,
401
+ * `<app>.section.<key>`, written once. Authors:
402
+ * _claude-work/guides-r4/GUIDE-PAGES.md §10.
403
+ */
404
+ export interface GuideSection {
405
+ /** Stable, [a-z0-9-]: the row key is `${suite.app}.section.${key}` (≤ 64 characters). */
406
+ key: string;
407
+ version: number;
408
+ /** The page(s) the section is on, as GuidePage.route. */
409
+ route: string | string[];
410
+ /** The section's root: the guide is there only while it is on screen. */
411
+ anchor: GuideAnchorKey;
412
+ /** "สรุปกระแสเงินสด": the "?" entry and the bubble's heading. ≤ 40 characters. */
413
+ title: string;
414
+ /** Only `true` shows it (as GuideMission.visible). */
415
+ visible?: (ctx: GuideContext) => boolean | undefined;
416
+ /** A policy key the section needs (checked with `guides.can`). */
417
+ requires?: string;
418
+ /**
419
+ * "view" steps (the default here), about 12 at most; titles ≤ 40 and whys
420
+ * ≤ 90 characters (the app's tests check). Copy may be a function of
421
+ * `ctx.values`; list what it uses in `needs`, and a step whose value is
422
+ * missing is skipped (so is one whose title comes out empty).
423
+ */
424
+ steps: GuideStep[];
425
+ }
426
+ /**
427
+ * A workflow guide ("พาทำงาน", shell ≥ 0.1.76): walks one job across pages —
428
+ * "เปิดใบขอซื้อจากบรรทัดงบ" — and stops before anything irreversible. Not a
429
+ * mission: never in "ไกด์เริ่มต้น n/N" or the welcome. Same switch and
430
+ * audience as page guides (`guides.<app>.pages` not off; every member,
431
+ * filtered by `visible` / `requires`). Listed under "?" → "พาทำงาน" and,
432
+ * on its start page, "พาทำ · <title>". One row per flow per person,
433
+ * `<app>.flow.<key>`, written once at the end (completed or skipped).
434
+ * Authors: _claude-work/guides-r4/GUIDE-PAGES.md §11.
435
+ */
436
+ export interface GuideFlow {
437
+ /** Stable, [a-z0-9-]: the row key is `${suite.app}.flow.${key}` (≤ 64 characters). */
438
+ key: string;
439
+ /** Raise it to offer this flow again (only when `offer: true`). */
440
+ version: number;
441
+ /** "เปิดใบขอซื้อจากบรรทัดงบ": the "?" entries, the panel, the bubble. ≤ 30 characters. */
442
+ title: string;
443
+ /** Rough minutes, for "ประมาณ n นาที" in the panel. */
444
+ minutes?: number;
445
+ /** Only `true` shows it (as GuideMission.visible). */
446
+ visible?: (ctx: GuideContext) => boolean | undefined;
447
+ /** A policy key the flow needs (checked with `guides.can`). */
448
+ requires?: string;
449
+ /** Where it starts: "พาทำ · <title>" is in "?" there, and the offer (if any) only there. */
450
+ startRoute: string | string[];
451
+ /**
452
+ * Where the panel's "พาทำงาน" list sends the person to start it, when
453
+ * `startRoute` needs an id (`[code]` is filled in). Default: the first
454
+ * `startRoute` when it has no other params.
455
+ */
456
+ href?: GuideText;
457
+ /**
458
+ * Pages the flow may pass through without ending (a redirect, a picker
459
+ * page), besides `startRoute` and its steps' routes.
460
+ */
461
+ transit?: string[];
462
+ /** true: offered once on its start page in `offer` mode, after the missions and the page guide's offer. Default false. */
463
+ offer?: boolean;
464
+ /**
465
+ * true: the flow may save something harmless and reversible ("commit",
466
+ * "input" and "select" steps). Every commit step must name its `abort`.
467
+ * Default false: "view" and "click" steps only.
468
+ */
469
+ allowCommit?: boolean;
470
+ /**
471
+ * Steps as a mission's: `route`, checkpoints and resume across pages. The
472
+ * last step on an irreversible button is a "view" step (the app's
473
+ * deny-list test checks).
474
+ */
475
+ steps: GuideStep[];
476
+ }
381
477
  /** One app's whole tour: the welcome, the missions, the finish. */
382
478
  export interface GuideSuite {
383
479
  /** Progress key on the server, "<app>.<name>": "peoplx.first-run". */
@@ -423,6 +519,10 @@ export interface GuideSuite {
423
519
  missions: GuideMission[];
424
520
  /** Page guides ("คู่มือหน้านี้"), shell ≥ 0.1.74. */
425
521
  pages?: GuidePage[];
522
+ /** Section guides ("อธิบายส่วนนี้"), shell ≥ 0.1.75. */
523
+ sections?: GuideSection[];
524
+ /** Workflow guides ("พาทำงาน"), shell ≥ 0.1.76. */
525
+ flows?: GuideFlow[];
426
526
  }
427
527
  /** AppLayoutShell's `guides` prop. */
428
528
  export interface GuideConfig {