@happyvertical/smrt-manufacturing 0.53.4 → 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 (106) hide show
  1. package/AGENTS.md +58 -7
  2. package/README.md +228 -4
  3. package/agents/multilevel-and-runs.md +28 -0
  4. package/dist/collections/AssemblyCollection.d.ts +15 -0
  5. package/dist/collections/AssemblyCollection.d.ts.map +1 -0
  6. package/dist/collections/OperationCollection.d.ts +20 -0
  7. package/dist/collections/OperationCollection.d.ts.map +1 -0
  8. package/dist/collections/ProductionRunCollection.d.ts +15 -0
  9. package/dist/collections/ProductionRunCollection.d.ts.map +1 -0
  10. package/dist/collections/ProductionRunCompletionCollection.d.ts +14 -0
  11. package/dist/collections/ProductionRunCompletionCollection.d.ts.map +1 -0
  12. package/dist/collections/RoutingStepCollection.d.ts +15 -0
  13. package/dist/collections/RoutingStepCollection.d.ts.map +1 -0
  14. package/dist/collections/index.d.ts +5 -0
  15. package/dist/collections/index.d.ts.map +1 -1
  16. package/dist/index.d.ts +23 -5
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1660 -42
  19. package/dist/index.js.map +1 -1
  20. package/dist/manifest.json +1910 -161
  21. package/dist/models/Assembly.d.ts +62 -0
  22. package/dist/models/Assembly.d.ts.map +1 -0
  23. package/dist/models/BillOfMaterials.d.ts +12 -4
  24. package/dist/models/BillOfMaterials.d.ts.map +1 -1
  25. package/dist/models/BomLine.d.ts +11 -5
  26. package/dist/models/BomLine.d.ts.map +1 -1
  27. package/dist/models/Operation.d.ts +59 -0
  28. package/dist/models/Operation.d.ts.map +1 -0
  29. package/dist/models/ProductionRun.d.ts +67 -0
  30. package/dist/models/ProductionRun.d.ts.map +1 -0
  31. package/dist/models/ProductionRunCompletion.d.ts +36 -0
  32. package/dist/models/ProductionRunCompletion.d.ts.map +1 -0
  33. package/dist/models/RoutingStep.d.ts +46 -0
  34. package/dist/models/RoutingStep.d.ts.map +1 -0
  35. package/dist/models/index.d.ts +5 -0
  36. package/dist/models/index.d.ts.map +1 -1
  37. package/dist/operation-types.d.ts +108 -0
  38. package/dist/operation-types.d.ts.map +1 -0
  39. package/dist/production-run-types.d.ts +100 -0
  40. package/dist/production-run-types.d.ts.map +1 -0
  41. package/dist/quantity.d.ts +36 -0
  42. package/dist/quantity.d.ts.map +1 -0
  43. package/dist/services/AssemblyService.d.ts +222 -0
  44. package/dist/services/AssemblyService.d.ts.map +1 -0
  45. package/dist/services/BomService.d.ts +108 -5
  46. package/dist/services/BomService.d.ts.map +1 -1
  47. package/dist/services/OperationService.d.ts +48 -0
  48. package/dist/services/OperationService.d.ts.map +1 -0
  49. package/dist/services/ProductionRunService.d.ts +123 -0
  50. package/dist/services/ProductionRunService.d.ts.map +1 -0
  51. package/dist/services/ProductionService.d.ts +11 -0
  52. package/dist/services/ProductionService.d.ts.map +1 -1
  53. package/dist/services/RoutingService.d.ts +50 -0
  54. package/dist/services/RoutingService.d.ts.map +1 -0
  55. package/dist/services/index.d.ts +5 -0
  56. package/dist/services/index.d.ts.map +1 -1
  57. package/dist/services/structure-walk.d.ts +77 -0
  58. package/dist/services/structure-walk.d.ts.map +1 -0
  59. package/dist/smrt-knowledge.json +1771 -207
  60. package/dist/svelte/components/AssemblyForm.svelte +69 -0
  61. package/dist/svelte/components/AssemblyForm.svelte.d.ts +38 -0
  62. package/dist/svelte/components/AssemblyForm.svelte.d.ts.map +1 -0
  63. package/dist/svelte/components/AssemblyList.svelte +229 -0
  64. package/dist/svelte/components/AssemblyList.svelte.d.ts +28 -0
  65. package/dist/svelte/components/AssemblyList.svelte.d.ts.map +1 -0
  66. package/dist/svelte/components/BomEditor.svelte +575 -0
  67. package/dist/svelte/components/BomEditor.svelte.d.ts +29 -0
  68. package/dist/svelte/components/BomEditor.svelte.d.ts.map +1 -0
  69. package/dist/svelte/components/BomStructureTree.svelte +164 -0
  70. package/dist/svelte/components/BomStructureTree.svelte.d.ts +16 -0
  71. package/dist/svelte/components/BomStructureTree.svelte.d.ts.map +1 -0
  72. package/dist/svelte/components/OperationForm.svelte +258 -0
  73. package/dist/svelte/components/OperationForm.svelte.d.ts +17 -0
  74. package/dist/svelte/components/OperationForm.svelte.d.ts.map +1 -0
  75. package/dist/svelte/components/OperationList.svelte +170 -0
  76. package/dist/svelte/components/OperationList.svelte.d.ts +17 -0
  77. package/dist/svelte/components/OperationList.svelte.d.ts.map +1 -0
  78. package/dist/svelte/components/ProductionRunList.svelte +213 -0
  79. package/dist/svelte/components/ProductionRunList.svelte.d.ts +18 -0
  80. package/dist/svelte/components/ProductionRunList.svelte.d.ts.map +1 -0
  81. package/dist/svelte/components/RequirementsTree.svelte +125 -0
  82. package/dist/svelte/components/RequirementsTree.svelte.d.ts +13 -0
  83. package/dist/svelte/components/RequirementsTree.svelte.d.ts.map +1 -0
  84. package/dist/svelte/components/RequirementsTreeLevel.svelte +120 -0
  85. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts +12 -0
  86. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts.map +1 -0
  87. package/dist/svelte/i18n.d.ts +119 -0
  88. package/dist/svelte/i18n.d.ts.map +1 -0
  89. package/dist/svelte/i18n.js +119 -0
  90. package/dist/svelte/index.d.ts +33 -0
  91. package/dist/svelte/index.d.ts.map +1 -0
  92. package/dist/svelte/index.js +34 -0
  93. package/dist/svelte/types.d.ts +257 -0
  94. package/dist/svelte/types.d.ts.map +1 -0
  95. package/dist/svelte/types.js +249 -0
  96. package/dist/tenant-scope.d.ts +25 -0
  97. package/dist/tenant-scope.d.ts.map +1 -0
  98. package/dist/types.d.ts +207 -3
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js +13 -1
  101. package/dist/types.js.map +1 -1
  102. package/dist/ui.d.ts +17 -0
  103. package/dist/ui.d.ts.map +1 -0
  104. package/dist/ui.js +94 -0
  105. package/dist/ui.js.map +1 -0
  106. package/package.json +41 -11
@@ -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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Message catalog for the manufacturing components. Keys are
3
+ * `manufacturing.<component>.<key>`.
4
+ */
5
+ export declare const M: {
6
+ readonly 'manufacturing.operation_list.caption': "manufacturing.operation_list.caption";
7
+ readonly 'manufacturing.operation_list.empty': "manufacturing.operation_list.empty";
8
+ readonly 'manufacturing.operation_list.code': "manufacturing.operation_list.code";
9
+ readonly 'manufacturing.operation_list.name': "manufacturing.operation_list.name";
10
+ readonly 'manufacturing.operation_list.category': "manufacturing.operation_list.category";
11
+ readonly 'manufacturing.operation_list.status': "manufacturing.operation_list.status";
12
+ readonly 'manufacturing.operation_list.actions': "manufacturing.operation_list.actions";
13
+ readonly 'manufacturing.operation_list.active': "manufacturing.operation_list.active";
14
+ readonly 'manufacturing.operation_list.retired': "manufacturing.operation_list.retired";
15
+ readonly 'manufacturing.operation_list.none': "manufacturing.operation_list.none";
16
+ readonly 'manufacturing.operation_list.select_aria': "manufacturing.operation_list.select_aria";
17
+ readonly 'manufacturing.operation_list.retire': "manufacturing.operation_list.retire";
18
+ readonly 'manufacturing.operation_list.retire_aria': "manufacturing.operation_list.retire_aria";
19
+ readonly 'manufacturing.operation_list.reinstate': "manufacturing.operation_list.reinstate";
20
+ readonly 'manufacturing.operation_list.reinstate_aria': "manufacturing.operation_list.reinstate_aria";
21
+ readonly 'manufacturing.operation_form.code': "manufacturing.operation_form.code";
22
+ readonly 'manufacturing.operation_form.code_edit_help': "manufacturing.operation_form.code_edit_help";
23
+ readonly 'manufacturing.operation_form.name': "manufacturing.operation_form.name";
24
+ readonly 'manufacturing.operation_form.category': "manufacturing.operation_form.category";
25
+ readonly 'manufacturing.operation_form.category_help': "manufacturing.operation_form.category_help";
26
+ readonly 'manufacturing.operation_form.qualification': "manufacturing.operation_form.qualification";
27
+ readonly 'manufacturing.operation_form.qualification_help': "manufacturing.operation_form.qualification_help";
28
+ readonly 'manufacturing.operation_form.submit_add': "manufacturing.operation_form.submit_add";
29
+ readonly 'manufacturing.operation_form.submit_edit': "manufacturing.operation_form.submit_edit";
30
+ readonly 'manufacturing.operation_form.saving': "manufacturing.operation_form.saving";
31
+ readonly 'manufacturing.operation_form.cancel': "manufacturing.operation_form.cancel";
32
+ readonly 'manufacturing.operation_form.error_code': "manufacturing.operation_form.error_code";
33
+ readonly 'manufacturing.operation_form.error_name': "manufacturing.operation_form.error_name";
34
+ readonly 'manufacturing.assembly_list.caption': "manufacturing.assembly_list.caption";
35
+ readonly 'manufacturing.assembly_list.empty': "manufacturing.assembly_list.empty";
36
+ readonly 'manufacturing.assembly_list.name': "manufacturing.assembly_list.name";
37
+ readonly 'manufacturing.assembly_list.assembly': "manufacturing.assembly_list.assembly";
38
+ readonly 'manufacturing.assembly_list.unnamed': "manufacturing.assembly_list.unnamed";
39
+ readonly 'manufacturing.assembly_list.part_reference': "manufacturing.assembly_list.part_reference";
40
+ readonly 'manufacturing.assembly_list.sku': "manufacturing.assembly_list.sku";
41
+ readonly 'manufacturing.assembly_list.price': "manufacturing.assembly_list.price";
42
+ readonly 'manufacturing.assembly_list.labour': "manufacturing.assembly_list.labour";
43
+ readonly 'manufacturing.assembly_list.bill': "manufacturing.assembly_list.bill";
44
+ readonly 'manufacturing.assembly_list.select_aria': "manufacturing.assembly_list.select_aria";
45
+ readonly 'manufacturing.assembly_list.none': "manufacturing.assembly_list.none";
46
+ readonly 'manufacturing.assembly_list.bill_active': "manufacturing.assembly_list.bill_active";
47
+ readonly 'manufacturing.assembly_list.bill_missing': "manufacturing.assembly_list.bill_missing";
48
+ readonly 'manufacturing.labour.minutes': "manufacturing.labour.minutes";
49
+ readonly 'manufacturing.labour.hours_minutes': "manufacturing.labour.hours_minutes";
50
+ readonly 'manufacturing.labour.not_estimated': "manufacturing.labour.not_estimated";
51
+ readonly 'manufacturing.kind.assembly': "manufacturing.kind.assembly";
52
+ readonly 'manufacturing.kind.material': "manufacturing.kind.material";
53
+ readonly 'manufacturing.kind.bought': "manufacturing.kind.bought";
54
+ readonly 'manufacturing.kind.missing': "manufacturing.kind.missing";
55
+ readonly 'manufacturing.bom_editor.caption': "manufacturing.bom_editor.caption";
56
+ readonly 'manufacturing.bom_editor.empty': "manufacturing.bom_editor.empty";
57
+ readonly 'manufacturing.bom_editor.component': "manufacturing.bom_editor.component";
58
+ readonly 'manufacturing.bom_editor.kind': "manufacturing.bom_editor.kind";
59
+ readonly 'manufacturing.bom_editor.qty_per_unit': "manufacturing.bom_editor.qty_per_unit";
60
+ readonly 'manufacturing.bom_editor.uom': "manufacturing.bom_editor.uom";
61
+ readonly 'manufacturing.bom_editor.waste_percent': "manufacturing.bom_editor.waste_percent";
62
+ readonly 'manufacturing.bom_editor.actions': "manufacturing.bom_editor.actions";
63
+ readonly 'manufacturing.bom_editor.qty_aria': "manufacturing.bom_editor.qty_aria";
64
+ readonly 'manufacturing.bom_editor.uom_aria': "manufacturing.bom_editor.uom_aria";
65
+ readonly 'manufacturing.bom_editor.waste_aria': "manufacturing.bom_editor.waste_aria";
66
+ readonly 'manufacturing.bom_editor.save': "manufacturing.bom_editor.save";
67
+ readonly 'manufacturing.bom_editor.save_aria': "manufacturing.bom_editor.save_aria";
68
+ readonly 'manufacturing.bom_editor.remove': "manufacturing.bom_editor.remove";
69
+ readonly 'manufacturing.bom_editor.remove_aria': "manufacturing.bom_editor.remove_aria";
70
+ readonly 'manufacturing.bom_editor.has_bill': "manufacturing.bom_editor.has_bill";
71
+ readonly 'manufacturing.bom_editor.no_bill': "manufacturing.bom_editor.no_bill";
72
+ readonly 'manufacturing.bom_editor.show_bill': "manufacturing.bom_editor.show_bill";
73
+ readonly 'manufacturing.bom_editor.hide_bill': "manufacturing.bom_editor.hide_bill";
74
+ readonly 'manufacturing.bom_editor.show_bill_aria': "manufacturing.bom_editor.show_bill_aria";
75
+ readonly 'manufacturing.bom_editor.hide_bill_aria': "manufacturing.bom_editor.hide_bill_aria";
76
+ readonly 'manufacturing.bom_editor.sub_bill_label': "manufacturing.bom_editor.sub_bill_label";
77
+ readonly 'manufacturing.bom_editor.loading': "manufacturing.bom_editor.loading";
78
+ readonly 'manufacturing.bom_editor.load_failed': "manufacturing.bom_editor.load_failed";
79
+ readonly 'manufacturing.bom_editor.add_heading': "manufacturing.bom_editor.add_heading";
80
+ readonly 'manufacturing.bom_editor.choose_component': "manufacturing.bom_editor.choose_component";
81
+ readonly 'manufacturing.bom_editor.add': "manufacturing.bom_editor.add";
82
+ readonly 'manufacturing.bom_editor.error_component': "manufacturing.bom_editor.error_component";
83
+ readonly 'manufacturing.bom_editor.error_qty': "manufacturing.bom_editor.error_qty";
84
+ readonly 'manufacturing.bom_editor.error_uom': "manufacturing.bom_editor.error_uom";
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";
118
+ };
119
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8HZ,CAAC"}