@happyvertical/smrt-manufacturing 0.53.5 → 0.53.6

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.
Files changed (67) hide show
  1. package/AGENTS.md +18 -6
  2. package/README.md +69 -3
  3. package/agents/multilevel-and-runs.md +28 -0
  4. package/dist/collections/ProductionRunCollection.d.ts +15 -0
  5. package/dist/collections/ProductionRunCollection.d.ts.map +1 -0
  6. package/dist/collections/ProductionRunCompletionCollection.d.ts +14 -0
  7. package/dist/collections/ProductionRunCompletionCollection.d.ts.map +1 -0
  8. package/dist/collections/index.d.ts +2 -0
  9. package/dist/collections/index.d.ts.map +1 -1
  10. package/dist/index.d.ts +13 -5
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +972 -81
  13. package/dist/index.js.map +1 -1
  14. package/dist/manifest.json +630 -1
  15. package/dist/models/ProductionRun.d.ts +67 -0
  16. package/dist/models/ProductionRun.d.ts.map +1 -0
  17. package/dist/models/ProductionRunCompletion.d.ts +36 -0
  18. package/dist/models/ProductionRunCompletion.d.ts.map +1 -0
  19. package/dist/models/index.d.ts +2 -0
  20. package/dist/models/index.d.ts.map +1 -1
  21. package/dist/operation-types.d.ts +9 -0
  22. package/dist/operation-types.d.ts.map +1 -1
  23. package/dist/production-run-types.d.ts +100 -0
  24. package/dist/production-run-types.d.ts.map +1 -0
  25. package/dist/quantity.d.ts +36 -0
  26. package/dist/quantity.d.ts.map +1 -0
  27. package/dist/services/AssemblyService.d.ts +7 -2
  28. package/dist/services/AssemblyService.d.ts.map +1 -1
  29. package/dist/services/BomService.d.ts +84 -5
  30. package/dist/services/BomService.d.ts.map +1 -1
  31. package/dist/services/ProductionRunService.d.ts +123 -0
  32. package/dist/services/ProductionRunService.d.ts.map +1 -0
  33. package/dist/services/ProductionService.d.ts +11 -0
  34. package/dist/services/ProductionService.d.ts.map +1 -1
  35. package/dist/services/index.d.ts +2 -0
  36. package/dist/services/index.d.ts.map +1 -1
  37. package/dist/services/structure-walk.d.ts +77 -0
  38. package/dist/services/structure-walk.d.ts.map +1 -0
  39. package/dist/smrt-knowledge.json +514 -7
  40. package/dist/svelte/components/ProductionRunList.svelte +213 -0
  41. package/dist/svelte/components/ProductionRunList.svelte.d.ts +18 -0
  42. package/dist/svelte/components/ProductionRunList.svelte.d.ts.map +1 -0
  43. package/dist/svelte/components/RequirementsTree.svelte +125 -0
  44. package/dist/svelte/components/RequirementsTree.svelte.d.ts +13 -0
  45. package/dist/svelte/components/RequirementsTree.svelte.d.ts.map +1 -0
  46. package/dist/svelte/components/RequirementsTreeLevel.svelte +120 -0
  47. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts +12 -0
  48. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts.map +1 -0
  49. package/dist/svelte/i18n.d.ts +32 -0
  50. package/dist/svelte/i18n.d.ts.map +1 -1
  51. package/dist/svelte/i18n.js +32 -0
  52. package/dist/svelte/index.d.ts +9 -4
  53. package/dist/svelte/index.d.ts.map +1 -1
  54. package/dist/svelte/index.js +9 -4
  55. package/dist/svelte/types.d.ts +87 -0
  56. package/dist/svelte/types.d.ts.map +1 -1
  57. package/dist/svelte/types.js +123 -0
  58. package/dist/tenant-scope.d.ts +25 -0
  59. package/dist/tenant-scope.d.ts.map +1 -0
  60. package/dist/types.d.ts +207 -3
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/types.js +13 -1
  63. package/dist/types.js.map +1 -1
  64. package/dist/ui.d.ts.map +1 -1
  65. package/dist/ui.js +23 -1
  66. package/dist/ui.js.map +1 -1
  67. package/package.json +11 -10
@@ -0,0 +1,213 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ProductionRunList — production runs with their progress ("12 of 25 done")
4
+ * and status, and, when the host passes `oncomplete`, a field on each open run
5
+ * to report finished units. Presentational: the host loads runs through
6
+ * `ProductionRunService`, adapts them with `toProductionRunView`, and records
7
+ * the completion it is handed (the service checks the quantity again under
8
+ * its lock).
9
+ */
10
+
11
+ import { Button, ProgressBar, StatusBadge } from '@happyvertical/smrt-ui';
12
+ import { Form, Input } from '@happyvertical/smrt-ui/forms';
13
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
14
+ import { M } from '../i18n.js';
15
+ import {
16
+ isProductionRunOpen,
17
+ type ProductionRunView,
18
+ productionRunStatusLabelKey,
19
+ productionRunStatusTone,
20
+ validateCompletionQty,
21
+ } from '../types.js';
22
+
23
+ const { t } = useI18n();
24
+
25
+ /** What a host handler may return: `false` (or a throw) keeps the field as typed. */
26
+ type HandlerResult = boolean | void | Promise<boolean | void>;
27
+
28
+ export interface ProductionRunListProps {
29
+ /** Runs to show, in the host's order. */
30
+ runs: ProductionRunView[];
31
+ /**
32
+ * Records `qty` finished units on the run; omit to hide reporting. Return
33
+ * `false` or throw to report a failure.
34
+ */
35
+ oncomplete?: (runId: string, qty: number) => HandlerResult;
36
+ /** Message shown when there are no runs. */
37
+ emptyMessage?: string;
38
+ }
39
+
40
+ const { runs, oncomplete, emptyMessage }: ProductionRunListProps = $props();
41
+
42
+ const uid = $props.id();
43
+
44
+ let drafts = $state<Record<string, string>>({});
45
+ let errors = $state<Record<string, string>>({});
46
+ let busy = $state<Record<string, boolean>>({});
47
+
48
+ /** What is left, at the six-decimal quantity precision the service uses. */
49
+ function remainingOf(run: ProductionRunView): number {
50
+ return Math.max(0, Number((run.targetQty - run.completedQty).toFixed(6)));
51
+ }
52
+
53
+ async function report(run: ProductionRunView) {
54
+ if (!oncomplete) return;
55
+ const result = validateCompletionQty(drafts[run.id] ?? '', run);
56
+ if (!result.ok) {
57
+ errors[run.id] =
58
+ result.reason === 'too_many'
59
+ ? t(M['manufacturing.production_run_list.error_too_many'], {
60
+ remaining: remainingOf(run),
61
+ })
62
+ : t(M['manufacturing.production_run_list.error_qty']);
63
+ return;
64
+ }
65
+ errors[run.id] = '';
66
+ busy[run.id] = true;
67
+ try {
68
+ if ((await oncomplete(run.id, result.qty)) === false) {
69
+ errors[run.id] = t(M['manufacturing.production_run_list.error_failed']);
70
+ } else {
71
+ drafts[run.id] = '';
72
+ }
73
+ } catch {
74
+ errors[run.id] = t(M['manufacturing.production_run_list.error_failed']);
75
+ } finally {
76
+ busy[run.id] = false;
77
+ }
78
+ }
79
+ </script>
80
+
81
+ {#if runs.length === 0}
82
+ <p class="run-list-empty">
83
+ {emptyMessage ?? t(M['manufacturing.production_run_list.empty'])}
84
+ </p>
85
+ {:else}
86
+ <div class="run-list-scroll">
87
+ <table class="run-list">
88
+ <caption class="run-list-caption">{t(M['manufacturing.production_run_list.caption'])}</caption>
89
+ <thead>
90
+ <tr>
91
+ <th scope="col">{t(M['manufacturing.production_run_list.build'])}</th>
92
+ <th scope="col">{t(M['manufacturing.production_run_list.progress'])}</th>
93
+ <th scope="col">{t(M['manufacturing.production_run_list.status'])}</th>
94
+ {#if oncomplete}
95
+ <th scope="col">{t(M['manufacturing.production_run_list.report'])}</th>
96
+ {/if}
97
+ </tr>
98
+ </thead>
99
+ <tbody>
100
+ {#each runs as run (run.id)}
101
+ {@const progressText = t(M['manufacturing.production_run_list.progress_value'], {
102
+ completed: run.completedQty,
103
+ target: run.targetQty,
104
+ })}
105
+ {@const errorId = `${uid}-${run.id}-error`}
106
+ <tr>
107
+ <th scope="row">{run.label}</th>
108
+ <td class="run-list-progress">
109
+ <ProgressBar
110
+ value={run.completedQty}
111
+ max={run.targetQty > 0 ? run.targetQty : 1}
112
+ status="healthy"
113
+ size="sm"
114
+ showLabel
115
+ label={progressText}
116
+ />
117
+ </td>
118
+ <td>
119
+ <StatusBadge
120
+ status={run.status}
121
+ tone={productionRunStatusTone(run.status)}
122
+ label={t(productionRunStatusLabelKey(run.status))}
123
+ size="sm"
124
+ />
125
+ </td>
126
+ {#if oncomplete}
127
+ <td>
128
+ {#if isProductionRunOpen(run.status)}
129
+ <Form class="run-list-report" onsubmit={() => report(run)}>
130
+ <Input
131
+ type="number"
132
+ inputmode="decimal"
133
+ step="any"
134
+ min="0"
135
+ value={drafts[run.id] ?? ''}
136
+ oninput={(event) => {
137
+ drafts[run.id] = (event.currentTarget as HTMLInputElement).value;
138
+ }}
139
+ disabled={busy[run.id]}
140
+ aria-label={t(M['manufacturing.production_run_list.qty_aria'], { label: run.label })}
141
+ aria-invalid={errors[run.id] ? 'true' : undefined}
142
+ aria-describedby={errors[run.id] ? errorId : undefined}
143
+ />
144
+ <Button
145
+ type="submit"
146
+ size="sm"
147
+ disabled={busy[run.id]}
148
+ aria-label={t(M['manufacturing.production_run_list.submit_aria'], { label: run.label })}
149
+ >
150
+ {t(M['manufacturing.production_run_list.submit'])}
151
+ </Button>
152
+ </Form>
153
+ {#if errors[run.id]}
154
+ <p class="run-list-error" id={errorId} role="alert">{errors[run.id]}</p>
155
+ {/if}
156
+ {/if}
157
+ </td>
158
+ {/if}
159
+ </tr>
160
+ {/each}
161
+ </tbody>
162
+ </table>
163
+ </div>
164
+ {/if}
165
+
166
+ <style>
167
+ .run-list-scroll {
168
+ overflow-x: auto;
169
+ }
170
+
171
+ .run-list {
172
+ width: 100%;
173
+ border-collapse: collapse;
174
+ }
175
+
176
+ .run-list-caption {
177
+ text-align: left;
178
+ font-weight: var(--smrt-typography-title-medium-weight, 500);
179
+ padding-bottom: 0.5rem;
180
+ }
181
+
182
+ .run-list th,
183
+ .run-list td {
184
+ padding: 0.5rem;
185
+ text-align: left;
186
+ vertical-align: top;
187
+ border-bottom: 1px solid var(--smrt-color-outline-variant, transparent);
188
+ }
189
+
190
+ .run-list-progress {
191
+ min-width: 10rem;
192
+ }
193
+
194
+ .run-list :global(.run-list-report) {
195
+ display: flex;
196
+ gap: 0.5rem;
197
+ align-items: center;
198
+ }
199
+
200
+ .run-list :global(.run-list-report input) {
201
+ width: 6rem;
202
+ }
203
+
204
+ .run-list-error {
205
+ margin: 0.25rem 0 0;
206
+ color: var(--smrt-color-error, inherit);
207
+ }
208
+
209
+ .run-list-empty {
210
+ margin: 0;
211
+ color: var(--smrt-color-on-surface-variant, inherit);
212
+ }
213
+ </style>
@@ -0,0 +1,18 @@
1
+ import { type ProductionRunView } from '../types.js';
2
+ /** What a host handler may return: `false` (or a throw) keeps the field as typed. */
3
+ type HandlerResult = boolean | void | Promise<boolean | void>;
4
+ export interface ProductionRunListProps {
5
+ /** Runs to show, in the host's order. */
6
+ runs: ProductionRunView[];
7
+ /**
8
+ * Records `qty` finished units on the run; omit to hide reporting. Return
9
+ * `false` or throw to report a failure.
10
+ */
11
+ oncomplete?: (runId: string, qty: number) => HandlerResult;
12
+ /** Message shown when there are no runs. */
13
+ emptyMessage?: string;
14
+ }
15
+ declare const ProductionRunList: import("svelte").Component<ProductionRunListProps, {}, "">;
16
+ type ProductionRunList = ReturnType<typeof ProductionRunList>;
17
+ export default ProductionRunList;
18
+ //# sourceMappingURL=ProductionRunList.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductionRunList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ProductionRunList.svelte.ts"],"names":[],"mappings":"AAeA,OAAO,EAEL,KAAK,iBAAiB,EAIvB,MAAM,aAAa,CAAC;AAGrB,qFAAqF;AACrF,KAAK,aAAa,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAE9D,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;IAC3D,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAoHD,QAAA,MAAM,iBAAiB,4DAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RequirementsTree — a read-only view of an exploded bill: each line with its
4
+ * level, component, kind and required quantity, nested under the
5
+ * sub-assembly whose bill it belongs to, and the totals. From a plan
6
+ * (`BomService.planRequirements`) each line also shows the available stock
7
+ * and how much is short; a short sub-assembly that was opened lists what
8
+ * building the shortfall takes.
9
+ *
10
+ * The host adapts the result with `toRequirementTree` and
11
+ * `toRequirementTotals`. This shows facts only: whether to build or buy is
12
+ * the host's decision.
13
+ */
14
+
15
+ import { Badge } from '@happyvertical/smrt-ui';
16
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
17
+ import { M } from '../i18n.js';
18
+ import type { RequirementLineView, RequirementTotalView } from '../types.js';
19
+ import RequirementsTreeLevel from './RequirementsTreeLevel.svelte';
20
+
21
+ const { t } = useI18n();
22
+
23
+ export interface RequirementsTreeProps {
24
+ /** The top bill's lines, each with the lines walked below it. */
25
+ lines: RequirementLineView[];
26
+ /** Totals per component; omit to hide the table. */
27
+ totals?: RequirementTotalView[];
28
+ /** Accessible name for the tree; defaults to "Requirements". */
29
+ label?: string;
30
+ }
31
+
32
+ const { lines, totals, label }: RequirementsTreeProps = $props();
33
+
34
+ const showShort = $derived(
35
+ (totals ?? []).some((total) => total.short !== null),
36
+ );
37
+
38
+ function quantity(value: number): string {
39
+ return String(Number(value.toFixed(6)));
40
+ }
41
+
42
+ function componentText(name: string, skuCode: string): string {
43
+ const shown = name || t(M['manufacturing.requirements.unnamed']);
44
+ return skuCode && skuCode !== name ? `${shown} (${skuCode})` : shown;
45
+ }
46
+ </script>
47
+
48
+ {#if lines.length === 0}
49
+ <p class="requirements-empty">{t(M['manufacturing.requirements.empty'])}</p>
50
+ {:else}
51
+ <RequirementsTreeLevel
52
+ {lines}
53
+ label={label ?? t(M['manufacturing.requirements.label'])}
54
+ />
55
+ {/if}
56
+
57
+ {#if totals && totals.length > 0}
58
+ <div class="requirements-scroll">
59
+ <table class="requirements-totals">
60
+ <caption>{t(M['manufacturing.requirements.totals_caption'])}</caption>
61
+ <thead>
62
+ <tr>
63
+ <th scope="col">{t(M['manufacturing.requirements.component'])}</th>
64
+ <th scope="col">{t(M['manufacturing.requirements.total_required'])}</th>
65
+ <th scope="col">{t(M['manufacturing.requirements.uom'])}</th>
66
+ {#if showShort}
67
+ <th scope="col">{t(M['manufacturing.requirements.total_short'])}</th>
68
+ {/if}
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ {#each totals as total (total.componentSkuId)}
73
+ <tr>
74
+ <th scope="row">{componentText(total.name, total.skuCode)}</th>
75
+ <td class="requirements-number">{quantity(total.required)}</td>
76
+ <td>{total.uom}</td>
77
+ {#if showShort}
78
+ <td class="requirements-number">
79
+ {#if total.short !== null && total.short > 0}
80
+ <Badge size="sm" variant="error">{quantity(total.short)}</Badge>
81
+ {:else}
82
+ {quantity(total.short ?? 0)}
83
+ {/if}
84
+ </td>
85
+ {/if}
86
+ </tr>
87
+ {/each}
88
+ </tbody>
89
+ </table>
90
+ </div>
91
+ {/if}
92
+
93
+ <style>
94
+ .requirements-empty {
95
+ margin: 0;
96
+ color: var(--smrt-color-on-surface-variant, inherit);
97
+ }
98
+
99
+ .requirements-scroll {
100
+ overflow-x: auto;
101
+ margin-top: 1rem;
102
+ }
103
+
104
+ .requirements-totals {
105
+ width: 100%;
106
+ border-collapse: collapse;
107
+ }
108
+
109
+ .requirements-totals caption {
110
+ text-align: left;
111
+ font-weight: var(--smrt-typography-title-medium-weight, 500);
112
+ padding-bottom: 0.5rem;
113
+ }
114
+
115
+ .requirements-totals th,
116
+ .requirements-totals td {
117
+ padding: 0.375rem 0.5rem;
118
+ text-align: left;
119
+ border-bottom: 1px solid var(--smrt-color-outline-variant, transparent);
120
+ }
121
+
122
+ .requirements-number {
123
+ font-variant-numeric: tabular-nums;
124
+ }
125
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { RequirementLineView, RequirementTotalView } from '../types.js';
2
+ export interface RequirementsTreeProps {
3
+ /** The top bill's lines, each with the lines walked below it. */
4
+ lines: RequirementLineView[];
5
+ /** Totals per component; omit to hide the table. */
6
+ totals?: RequirementTotalView[];
7
+ /** Accessible name for the tree; defaults to "Requirements". */
8
+ label?: string;
9
+ }
10
+ declare const RequirementsTree: import("svelte").Component<RequirementsTreeProps, {}, "">;
11
+ type RequirementsTree = ReturnType<typeof RequirementsTree>;
12
+ export default RequirementsTree;
13
+ //# sourceMappingURL=RequirementsTree.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequirementsTree.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RequirementsTree.svelte.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7E,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,oDAAoD;IACpD,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA2ED,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,120 @@
1
+ <script lang="ts">
2
+ /**
3
+ * One level of `RequirementsTree`: a list of lines, each followed by the
4
+ * lines of its sub-assembly's bill when the walk opened it. Internal to
5
+ * `RequirementsTree`.
6
+ */
7
+
8
+ import { Badge } from '@happyvertical/smrt-ui';
9
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
10
+ import { M } from '../i18n.js';
11
+ import { componentKindLabelKey, type RequirementLineView } from '../types.js';
12
+ import RequirementsTreeLevel from './RequirementsTreeLevel.svelte';
13
+
14
+ const { t } = useI18n();
15
+
16
+ export interface RequirementsTreeLevelProps {
17
+ /** The lines of this level. */
18
+ lines: RequirementLineView[];
19
+ /** Accessible name for the list. */
20
+ label: string;
21
+ }
22
+
23
+ const { lines, label }: RequirementsTreeLevelProps = $props();
24
+
25
+ function quantity(value: number): string {
26
+ return String(Number(value.toFixed(6)));
27
+ }
28
+
29
+ function componentText(line: RequirementLineView): string {
30
+ const shown = line.name || t(M['manufacturing.requirements.unnamed']);
31
+ return line.skuCode && line.skuCode !== line.name
32
+ ? `${shown} (${line.skuCode})`
33
+ : shown;
34
+ }
35
+ </script>
36
+
37
+ <ul class="requirements-tree" aria-label={label}>
38
+ {#each lines as line (line.key)}
39
+ <li class="requirements-line" data-level={line.level}>
40
+ <div class="requirements-row">
41
+ <span class="requirements-component">{componentText(line)}</span>
42
+ <Badge size="sm" variant={line.kind === 'missing' ? 'error' : 'default'}>
43
+ {t(componentKindLabelKey(line.kind))}
44
+ </Badge>
45
+ <span class="requirements-qty">
46
+ {t(M['manufacturing.requirements.required'], {
47
+ qty: quantity(line.required),
48
+ uom: line.uom,
49
+ })}
50
+ </span>
51
+ {#if line.available !== null}
52
+ <span class="requirements-qty">
53
+ {t(M['manufacturing.requirements.available'], { qty: quantity(line.available) })}
54
+ </span>
55
+ {/if}
56
+ {#if line.short !== null}
57
+ {#if line.short > 0}
58
+ <Badge size="sm" variant="error">
59
+ {t(M['manufacturing.requirements.short'], { qty: quantity(line.short) })}
60
+ </Badge>
61
+ {:else}
62
+ <span class="requirements-note">{t(M['manufacturing.requirements.covered'])}</span>
63
+ {/if}
64
+ {/if}
65
+ {#if line.kind === 'assembly' && !line.buildable}
66
+ <span class="requirements-note">{t(M['manufacturing.requirements.no_bill'])}</span>
67
+ {:else if line.buildable && line.children.length === 0 && (line.short ?? 0) > 0}
68
+ <span class="requirements-note">{t(M['manufacturing.requirements.buildable'])}</span>
69
+ {/if}
70
+ </div>
71
+ {#if line.children.length > 0}
72
+ <div class="requirements-children">
73
+ <RequirementsTreeLevel
74
+ lines={line.children}
75
+ label={t(M['manufacturing.requirements.sub_label'], {
76
+ component: line.name || t(M['manufacturing.requirements.unnamed']),
77
+ })}
78
+ />
79
+ </div>
80
+ {/if}
81
+ </li>
82
+ {/each}
83
+ </ul>
84
+
85
+ <style>
86
+ .requirements-tree {
87
+ list-style: none;
88
+ margin: 0;
89
+ padding: 0;
90
+ }
91
+
92
+ .requirements-line {
93
+ padding: 0.25rem 0;
94
+ }
95
+
96
+ .requirements-row {
97
+ display: flex;
98
+ flex-wrap: wrap;
99
+ align-items: center;
100
+ gap: 0.5rem;
101
+ }
102
+
103
+ .requirements-component {
104
+ font-weight: var(--smrt-typography-title-small-weight, 500);
105
+ }
106
+
107
+ .requirements-qty {
108
+ font-variant-numeric: tabular-nums;
109
+ }
110
+
111
+ .requirements-note {
112
+ color: var(--smrt-color-on-surface-variant, inherit);
113
+ }
114
+
115
+ .requirements-children {
116
+ margin-left: 1rem;
117
+ padding-left: 0.75rem;
118
+ border-left: 2px solid var(--smrt-color-outline-variant, transparent);
119
+ }
120
+ </style>
@@ -0,0 +1,12 @@
1
+ import { type RequirementLineView } from '../types.js';
2
+ import RequirementsTreeLevel from './RequirementsTreeLevel.svelte';
3
+ export interface RequirementsTreeLevelProps {
4
+ /** The lines of this level. */
5
+ lines: RequirementLineView[];
6
+ /** Accessible name for the list. */
7
+ label: string;
8
+ }
9
+ declare const RequirementsTreeLevel: import("svelte").Component<RequirementsTreeLevelProps, {}, "">;
10
+ type RequirementsTreeLevel = ReturnType<typeof RequirementsTreeLevel>;
11
+ export default RequirementsTreeLevel;
12
+ //# sourceMappingURL=RequirementsTreeLevel.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequirementsTreeLevel.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RequirementsTreeLevel.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,qBAAqB,MAAM,gCAAgC,CAAC;AAGnE,MAAM,WAAW,0BAA0B;IACzC,+BAA+B;IAC/B,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AA4ED,QAAA,MAAM,qBAAqB,gEAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
@@ -83,5 +83,37 @@ export declare const M: {
83
83
  readonly 'manufacturing.bom_editor.error_qty': "manufacturing.bom_editor.error_qty";
84
84
  readonly 'manufacturing.bom_editor.error_uom': "manufacturing.bom_editor.error_uom";
85
85
  readonly 'manufacturing.bom_editor.error_waste': "manufacturing.bom_editor.error_waste";
86
+ readonly 'manufacturing.production_run_list.caption': "manufacturing.production_run_list.caption";
87
+ readonly 'manufacturing.production_run_list.empty': "manufacturing.production_run_list.empty";
88
+ readonly 'manufacturing.production_run_list.build': "manufacturing.production_run_list.build";
89
+ readonly 'manufacturing.production_run_list.progress': "manufacturing.production_run_list.progress";
90
+ readonly 'manufacturing.production_run_list.status': "manufacturing.production_run_list.status";
91
+ readonly 'manufacturing.production_run_list.report': "manufacturing.production_run_list.report";
92
+ readonly 'manufacturing.production_run_list.progress_value': "manufacturing.production_run_list.progress_value";
93
+ readonly 'manufacturing.production_run_list.qty_aria': "manufacturing.production_run_list.qty_aria";
94
+ readonly 'manufacturing.production_run_list.submit': "manufacturing.production_run_list.submit";
95
+ readonly 'manufacturing.production_run_list.submit_aria': "manufacturing.production_run_list.submit_aria";
96
+ readonly 'manufacturing.production_run_list.error_qty': "manufacturing.production_run_list.error_qty";
97
+ readonly 'manufacturing.production_run_list.error_too_many': "manufacturing.production_run_list.error_too_many";
98
+ readonly 'manufacturing.production_run_list.error_failed': "manufacturing.production_run_list.error_failed";
99
+ readonly 'manufacturing.production_run_status.planned': "manufacturing.production_run_status.planned";
100
+ readonly 'manufacturing.production_run_status.in_progress': "manufacturing.production_run_status.in_progress";
101
+ readonly 'manufacturing.production_run_status.done': "manufacturing.production_run_status.done";
102
+ readonly 'manufacturing.production_run_status.cancelled': "manufacturing.production_run_status.cancelled";
103
+ readonly 'manufacturing.requirements.label': "manufacturing.requirements.label";
104
+ readonly 'manufacturing.requirements.sub_label': "manufacturing.requirements.sub_label";
105
+ readonly 'manufacturing.requirements.required': "manufacturing.requirements.required";
106
+ readonly 'manufacturing.requirements.available': "manufacturing.requirements.available";
107
+ readonly 'manufacturing.requirements.short': "manufacturing.requirements.short";
108
+ readonly 'manufacturing.requirements.covered': "manufacturing.requirements.covered";
109
+ readonly 'manufacturing.requirements.buildable': "manufacturing.requirements.buildable";
110
+ readonly 'manufacturing.requirements.no_bill': "manufacturing.requirements.no_bill";
111
+ readonly 'manufacturing.requirements.totals_caption': "manufacturing.requirements.totals_caption";
112
+ readonly 'manufacturing.requirements.component': "manufacturing.requirements.component";
113
+ readonly 'manufacturing.requirements.total_required': "manufacturing.requirements.total_required";
114
+ readonly 'manufacturing.requirements.uom': "manufacturing.requirements.uom";
115
+ readonly 'manufacturing.requirements.total_short': "manufacturing.requirements.total_short";
116
+ readonly 'manufacturing.requirements.empty': "manufacturing.requirements.empty";
117
+ readonly 'manufacturing.requirements.unnamed': "manufacturing.requirements.unnamed";
86
118
  };
87
119
  //# sourceMappingURL=i18n.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsFZ,CAAC"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8HZ,CAAC"}
@@ -84,4 +84,36 @@ export const M = defineMessages({
84
84
  'manufacturing.bom_editor.error_qty': 'Enter a quantity greater than zero.',
85
85
  'manufacturing.bom_editor.error_uom': 'Enter a unit.',
86
86
  'manufacturing.bom_editor.error_waste': 'Enter a waste percent of zero or more.',
87
+ 'manufacturing.production_run_list.caption': 'Production runs',
88
+ 'manufacturing.production_run_list.empty': 'No production runs',
89
+ 'manufacturing.production_run_list.build': 'Build',
90
+ 'manufacturing.production_run_list.progress': 'Progress',
91
+ 'manufacturing.production_run_list.status': 'Status',
92
+ 'manufacturing.production_run_list.report': 'Report completion',
93
+ 'manufacturing.production_run_list.progress_value': '{completed} of {target} done',
94
+ 'manufacturing.production_run_list.qty_aria': 'Quantity completed for {label}',
95
+ 'manufacturing.production_run_list.submit': 'Report',
96
+ 'manufacturing.production_run_list.submit_aria': 'Report a completion for {label}',
97
+ 'manufacturing.production_run_list.error_qty': 'Enter a quantity greater than zero.',
98
+ 'manufacturing.production_run_list.error_too_many': 'Only {remaining} left to build.',
99
+ 'manufacturing.production_run_list.error_failed': 'The completion could not be recorded.',
100
+ 'manufacturing.production_run_status.planned': 'Planned',
101
+ 'manufacturing.production_run_status.in_progress': 'In progress',
102
+ 'manufacturing.production_run_status.done': 'Done',
103
+ 'manufacturing.production_run_status.cancelled': 'Cancelled',
104
+ 'manufacturing.requirements.label': 'Requirements',
105
+ 'manufacturing.requirements.sub_label': 'Building {component} takes',
106
+ 'manufacturing.requirements.required': 'Required: {qty} {uom}',
107
+ 'manufacturing.requirements.available': 'Available: {qty}',
108
+ 'manufacturing.requirements.short': 'Short: {qty}',
109
+ 'manufacturing.requirements.covered': 'Covered',
110
+ 'manufacturing.requirements.buildable': 'Can be built from its bill',
111
+ 'manufacturing.requirements.no_bill': 'No active bill',
112
+ 'manufacturing.requirements.totals_caption': 'Totals',
113
+ 'manufacturing.requirements.component': 'Component',
114
+ 'manufacturing.requirements.total_required': 'Required',
115
+ 'manufacturing.requirements.uom': 'Unit',
116
+ 'manufacturing.requirements.total_short': 'Short',
117
+ 'manufacturing.requirements.empty': 'Nothing is required',
118
+ 'manufacturing.requirements.unnamed': 'Unknown component',
87
119
  });
@@ -4,8 +4,9 @@
4
4
  * Props-driven Svelte 5 surfaces: the managed list of operations (a list with
5
5
  * retire and reinstate, and a form), and assemblies (a list, a policy-driven
6
6
  * form, and a bill-of-materials editor whose sub-assemblies expand to their own
7
- * bills). Hosts load data through the package services and collections and
8
- * pass plain view objects.
7
+ * bills), production runs with their progress and completion reporting, and
8
+ * the read-only exploded requirements of a bill. Hosts load data through the
9
+ * package services and collections and pass plain view objects.
9
10
  * Auto-registers components with ModuleUIRegistry on import.
10
11
  *
11
12
  * @packageDocumentation
@@ -17,12 +18,16 @@ import BomEditor from './components/BomEditor.svelte';
17
18
  import BomStructureTree from './components/BomStructureTree.svelte';
18
19
  import OperationForm from './components/OperationForm.svelte';
19
20
  import OperationList from './components/OperationList.svelte';
20
- export { AssemblyForm, AssemblyList, BomEditor, BomStructureTree, OperationForm, OperationList, };
21
+ import ProductionRunList from './components/ProductionRunList.svelte';
22
+ import RequirementsTree from './components/RequirementsTree.svelte';
23
+ export { AssemblyForm, AssemblyList, BomEditor, BomStructureTree, OperationForm, OperationList, ProductionRunList, RequirementsTree, };
21
24
  export type AssemblyFormProps = ComponentProps<typeof AssemblyForm>;
22
25
  export type AssemblyListProps = ComponentProps<typeof AssemblyList>;
23
26
  export type BomEditorProps = ComponentProps<typeof BomEditor>;
24
27
  export type BomStructureTreeProps = ComponentProps<typeof BomStructureTree>;
25
28
  export type OperationFormProps = ComponentProps<typeof OperationForm>;
26
29
  export type OperationListProps = ComponentProps<typeof OperationList>;
27
- export { type AssemblyView, type BomComponentOption, type BomEditorLine, type BomLineDraft, type BomLineDraftField, type BomLineInput, type BomLineValidation, type ComponentKind, componentKindLabelKey, type OperationFormDraft, type OperationFormField, type OperationFormInitial, type OperationFormValidation, type OperationFormValues, type OperationView, operationStatusBadgeKey, splitLabourMinutes, toAssemblyView, toBomEditorLine, toBomEditorLines, validateBomLineInput, validateOperationForm, } from './types.js';
30
+ export type ProductionRunListProps = ComponentProps<typeof ProductionRunList>;
31
+ export type RequirementsTreeProps = ComponentProps<typeof RequirementsTree>;
32
+ export { type AssemblyView, type BomComponentOption, type BomEditorLine, type BomLineDraft, type BomLineDraftField, type BomLineInput, type BomLineValidation, type CompletionQtyValidation, type ComponentKind, componentKindLabelKey, isProductionRunOpen, type OperationFormDraft, type OperationFormField, type OperationFormInitial, type OperationFormValidation, type OperationFormValues, type OperationView, operationStatusBadgeKey, type ProductionRunStatus, type ProductionRunView, productionRunStatusLabelKey, productionRunStatusTone, type RequirementLineView, type RequirementTotalView, splitLabourMinutes, toAssemblyView, toBomEditorLine, toBomEditorLines, toProductionRunView, toRequirementTotals, toRequirementTree, validateBomLineInput, validateCompletionQty, validateOperationForm, } from './types.js';
28
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,SAAS,MAAM,+BAA+B,CAAC;AACtD,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AACpE,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAE9D,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE5E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AAEtE,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,YAAY,MAAM,kCAAkC,CAAC;AAC5D,OAAO,SAAS,MAAM,+BAA+B,CAAC;AACtD,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AACpE,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,iBAAiB,MAAM,uCAAuC,CAAC;AACtE,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AAEpE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC;AACpE,MAAM,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9D,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE5E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9E,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE5E,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}