@happyvertical/smrt-manufacturing 0.53.5 → 0.54.0

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 (73) hide show
  1. package/AGENTS.md +18 -7
  2. package/README.md +70 -5
  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 -2
  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 -9
  40. package/dist/svelte/components/AssemblyForm.svelte +423 -52
  41. package/dist/svelte/components/AssemblyForm.svelte.d.ts +17 -32
  42. package/dist/svelte/components/AssemblyForm.svelte.d.ts.map +1 -1
  43. package/dist/svelte/components/AssemblyList.svelte +18 -12
  44. package/dist/svelte/components/AssemblyList.svelte.d.ts +2 -13
  45. package/dist/svelte/components/AssemblyList.svelte.d.ts.map +1 -1
  46. package/dist/svelte/components/ProductionRunList.svelte +213 -0
  47. package/dist/svelte/components/ProductionRunList.svelte.d.ts +18 -0
  48. package/dist/svelte/components/ProductionRunList.svelte.d.ts.map +1 -0
  49. package/dist/svelte/components/RequirementsTree.svelte +125 -0
  50. package/dist/svelte/components/RequirementsTree.svelte.d.ts +13 -0
  51. package/dist/svelte/components/RequirementsTree.svelte.d.ts.map +1 -0
  52. package/dist/svelte/components/RequirementsTreeLevel.svelte +120 -0
  53. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts +12 -0
  54. package/dist/svelte/components/RequirementsTreeLevel.svelte.d.ts.map +1 -0
  55. package/dist/svelte/i18n.d.ts +55 -0
  56. package/dist/svelte/i18n.d.ts.map +1 -1
  57. package/dist/svelte/i18n.js +55 -0
  58. package/dist/svelte/index.d.ts +11 -6
  59. package/dist/svelte/index.d.ts.map +1 -1
  60. package/dist/svelte/index.js +11 -6
  61. package/dist/svelte/types.d.ts +182 -0
  62. package/dist/svelte/types.d.ts.map +1 -1
  63. package/dist/svelte/types.js +250 -0
  64. package/dist/tenant-scope.d.ts +25 -0
  65. package/dist/tenant-scope.d.ts.map +1 -0
  66. package/dist/types.d.ts +207 -3
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js +13 -1
  69. package/dist/types.js.map +1 -1
  70. package/dist/ui.d.ts.map +1 -1
  71. package/dist/ui.js +23 -1
  72. package/dist/ui.js.map +1 -1
  73. package/package.json +10 -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"}
@@ -31,6 +31,29 @@ export declare const M: {
31
31
  readonly 'manufacturing.operation_form.cancel': "manufacturing.operation_form.cancel";
32
32
  readonly 'manufacturing.operation_form.error_code': "manufacturing.operation_form.error_code";
33
33
  readonly 'manufacturing.operation_form.error_name': "manufacturing.operation_form.error_name";
34
+ readonly 'manufacturing.assembly_form.name': "manufacturing.assembly_form.name";
35
+ readonly 'manufacturing.assembly_form.description': "manufacturing.assembly_form.description";
36
+ readonly 'manufacturing.assembly_form.category': "manufacturing.assembly_form.category";
37
+ readonly 'manufacturing.assembly_form.part_reference': "manufacturing.assembly_form.part_reference";
38
+ readonly 'manufacturing.assembly_form.part_reference_help': "manufacturing.assembly_form.part_reference_help";
39
+ readonly 'manufacturing.assembly_form.price': "manufacturing.assembly_form.price";
40
+ readonly 'manufacturing.assembly_form.price_help': "manufacturing.assembly_form.price_help";
41
+ readonly 'manufacturing.assembly_form.labour': "manufacturing.assembly_form.labour";
42
+ readonly 'manufacturing.assembly_form.labour_help': "manufacturing.assembly_form.labour_help";
43
+ readonly 'manufacturing.assembly_form.operation': "manufacturing.assembly_form.operation";
44
+ readonly 'manufacturing.assembly_form.operation_help': "manufacturing.assembly_form.operation_help";
45
+ readonly 'manufacturing.assembly_form.operation_none': "manufacturing.assembly_form.operation_none";
46
+ readonly 'manufacturing.assembly_form.operation_retired': "manufacturing.assembly_form.operation_retired";
47
+ readonly 'manufacturing.assembly_form.operation_unknown': "manufacturing.assembly_form.operation_unknown";
48
+ readonly 'manufacturing.assembly_form.tags': "manufacturing.assembly_form.tags";
49
+ readonly 'manufacturing.assembly_form.tags_help': "manufacturing.assembly_form.tags_help";
50
+ readonly 'manufacturing.assembly_form.submit_add': "manufacturing.assembly_form.submit_add";
51
+ readonly 'manufacturing.assembly_form.submit_edit': "manufacturing.assembly_form.submit_edit";
52
+ readonly 'manufacturing.assembly_form.saving': "manufacturing.assembly_form.saving";
53
+ readonly 'manufacturing.assembly_form.cancel': "manufacturing.assembly_form.cancel";
54
+ readonly 'manufacturing.assembly_form.error_name': "manufacturing.assembly_form.error_name";
55
+ readonly 'manufacturing.assembly_form.error_price': "manufacturing.assembly_form.error_price";
56
+ readonly 'manufacturing.assembly_form.error_labour': "manufacturing.assembly_form.error_labour";
34
57
  readonly 'manufacturing.assembly_list.caption': "manufacturing.assembly_list.caption";
35
58
  readonly 'manufacturing.assembly_list.empty': "manufacturing.assembly_list.empty";
36
59
  readonly 'manufacturing.assembly_list.name': "manufacturing.assembly_list.name";
@@ -83,5 +106,37 @@ export declare const M: {
83
106
  readonly 'manufacturing.bom_editor.error_qty': "manufacturing.bom_editor.error_qty";
84
107
  readonly 'manufacturing.bom_editor.error_uom': "manufacturing.bom_editor.error_uom";
85
108
  readonly 'manufacturing.bom_editor.error_waste': "manufacturing.bom_editor.error_waste";
109
+ readonly 'manufacturing.production_run_list.caption': "manufacturing.production_run_list.caption";
110
+ readonly 'manufacturing.production_run_list.empty': "manufacturing.production_run_list.empty";
111
+ readonly 'manufacturing.production_run_list.build': "manufacturing.production_run_list.build";
112
+ readonly 'manufacturing.production_run_list.progress': "manufacturing.production_run_list.progress";
113
+ readonly 'manufacturing.production_run_list.status': "manufacturing.production_run_list.status";
114
+ readonly 'manufacturing.production_run_list.report': "manufacturing.production_run_list.report";
115
+ readonly 'manufacturing.production_run_list.progress_value': "manufacturing.production_run_list.progress_value";
116
+ readonly 'manufacturing.production_run_list.qty_aria': "manufacturing.production_run_list.qty_aria";
117
+ readonly 'manufacturing.production_run_list.submit': "manufacturing.production_run_list.submit";
118
+ readonly 'manufacturing.production_run_list.submit_aria': "manufacturing.production_run_list.submit_aria";
119
+ readonly 'manufacturing.production_run_list.error_qty': "manufacturing.production_run_list.error_qty";
120
+ readonly 'manufacturing.production_run_list.error_too_many': "manufacturing.production_run_list.error_too_many";
121
+ readonly 'manufacturing.production_run_list.error_failed': "manufacturing.production_run_list.error_failed";
122
+ readonly 'manufacturing.production_run_status.planned': "manufacturing.production_run_status.planned";
123
+ readonly 'manufacturing.production_run_status.in_progress': "manufacturing.production_run_status.in_progress";
124
+ readonly 'manufacturing.production_run_status.done': "manufacturing.production_run_status.done";
125
+ readonly 'manufacturing.production_run_status.cancelled': "manufacturing.production_run_status.cancelled";
126
+ readonly 'manufacturing.requirements.label': "manufacturing.requirements.label";
127
+ readonly 'manufacturing.requirements.sub_label': "manufacturing.requirements.sub_label";
128
+ readonly 'manufacturing.requirements.required': "manufacturing.requirements.required";
129
+ readonly 'manufacturing.requirements.available': "manufacturing.requirements.available";
130
+ readonly 'manufacturing.requirements.short': "manufacturing.requirements.short";
131
+ readonly 'manufacturing.requirements.covered': "manufacturing.requirements.covered";
132
+ readonly 'manufacturing.requirements.buildable': "manufacturing.requirements.buildable";
133
+ readonly 'manufacturing.requirements.no_bill': "manufacturing.requirements.no_bill";
134
+ readonly 'manufacturing.requirements.totals_caption': "manufacturing.requirements.totals_caption";
135
+ readonly 'manufacturing.requirements.component': "manufacturing.requirements.component";
136
+ readonly 'manufacturing.requirements.total_required': "manufacturing.requirements.total_required";
137
+ readonly 'manufacturing.requirements.uom': "manufacturing.requirements.uom";
138
+ readonly 'manufacturing.requirements.total_short': "manufacturing.requirements.total_short";
139
+ readonly 'manufacturing.requirements.empty': "manufacturing.requirements.empty";
140
+ readonly 'manufacturing.requirements.unnamed': "manufacturing.requirements.unnamed";
86
141
  };
87
142
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4JZ,CAAC"}
@@ -32,6 +32,29 @@ export const M = defineMessages({
32
32
  'manufacturing.operation_form.cancel': 'Cancel',
33
33
  'manufacturing.operation_form.error_code': 'Enter a code.',
34
34
  'manufacturing.operation_form.error_name': 'Enter a name.',
35
+ 'manufacturing.assembly_form.name': 'Name',
36
+ 'manufacturing.assembly_form.description': 'Description',
37
+ 'manufacturing.assembly_form.category': 'Category',
38
+ 'manufacturing.assembly_form.part_reference': 'Part reference',
39
+ 'manufacturing.assembly_form.part_reference_help': 'Optional. Your own drawing or part number, alongside the SKU code.',
40
+ 'manufacturing.assembly_form.price': 'Price',
41
+ 'manufacturing.assembly_form.price_help': 'Amount in {currency}.',
42
+ 'manufacturing.assembly_form.labour': 'Estimated labour (minutes)',
43
+ 'manufacturing.assembly_form.labour_help': 'Whole minutes to build one unit. 0 means not estimated.',
44
+ 'manufacturing.assembly_form.operation': 'Default operation',
45
+ 'manufacturing.assembly_form.operation_help': 'Optional. The operation work on this assembly is booked to by default.',
46
+ 'manufacturing.assembly_form.operation_none': 'None',
47
+ 'manufacturing.assembly_form.operation_retired': '{label} (retired)',
48
+ 'manufacturing.assembly_form.operation_unknown': 'Unknown operation ({id})',
49
+ 'manufacturing.assembly_form.tags': 'Tags',
50
+ 'manufacturing.assembly_form.tags_help': 'Optional. Separate tags with commas.',
51
+ 'manufacturing.assembly_form.submit_add': 'Add assembly',
52
+ 'manufacturing.assembly_form.submit_edit': 'Save changes',
53
+ 'manufacturing.assembly_form.saving': 'Saving...',
54
+ 'manufacturing.assembly_form.cancel': 'Cancel',
55
+ 'manufacturing.assembly_form.error_name': 'Enter a name.',
56
+ 'manufacturing.assembly_form.error_price': 'Enter a price of zero or more, with no more decimals than the currency has.',
57
+ 'manufacturing.assembly_form.error_labour': 'Enter a whole number of minutes, zero or more.',
35
58
  'manufacturing.assembly_list.caption': 'Assemblies',
36
59
  'manufacturing.assembly_list.empty': 'No assemblies',
37
60
  'manufacturing.assembly_list.name': 'Name',
@@ -84,4 +107,36 @@ export const M = defineMessages({
84
107
  'manufacturing.bom_editor.error_qty': 'Enter a quantity greater than zero.',
85
108
  'manufacturing.bom_editor.error_uom': 'Enter a unit.',
86
109
  'manufacturing.bom_editor.error_waste': 'Enter a waste percent of zero or more.',
110
+ 'manufacturing.production_run_list.caption': 'Production runs',
111
+ 'manufacturing.production_run_list.empty': 'No production runs',
112
+ 'manufacturing.production_run_list.build': 'Build',
113
+ 'manufacturing.production_run_list.progress': 'Progress',
114
+ 'manufacturing.production_run_list.status': 'Status',
115
+ 'manufacturing.production_run_list.report': 'Report completion',
116
+ 'manufacturing.production_run_list.progress_value': '{completed} of {target} done',
117
+ 'manufacturing.production_run_list.qty_aria': 'Quantity completed for {label}',
118
+ 'manufacturing.production_run_list.submit': 'Report',
119
+ 'manufacturing.production_run_list.submit_aria': 'Report a completion for {label}',
120
+ 'manufacturing.production_run_list.error_qty': 'Enter a quantity greater than zero.',
121
+ 'manufacturing.production_run_list.error_too_many': 'Only {remaining} left to build.',
122
+ 'manufacturing.production_run_list.error_failed': 'The completion could not be recorded.',
123
+ 'manufacturing.production_run_status.planned': 'Planned',
124
+ 'manufacturing.production_run_status.in_progress': 'In progress',
125
+ 'manufacturing.production_run_status.done': 'Done',
126
+ 'manufacturing.production_run_status.cancelled': 'Cancelled',
127
+ 'manufacturing.requirements.label': 'Requirements',
128
+ 'manufacturing.requirements.sub_label': 'Building {component} takes',
129
+ 'manufacturing.requirements.required': 'Required: {qty} {uom}',
130
+ 'manufacturing.requirements.available': 'Available: {qty}',
131
+ 'manufacturing.requirements.short': 'Short: {qty}',
132
+ 'manufacturing.requirements.covered': 'Covered',
133
+ 'manufacturing.requirements.buildable': 'Can be built from its bill',
134
+ 'manufacturing.requirements.no_bill': 'No active bill',
135
+ 'manufacturing.requirements.totals_caption': 'Totals',
136
+ 'manufacturing.requirements.component': 'Component',
137
+ 'manufacturing.requirements.total_required': 'Required',
138
+ 'manufacturing.requirements.uom': 'Unit',
139
+ 'manufacturing.requirements.total_short': 'Short',
140
+ 'manufacturing.requirements.empty': 'Nothing is required',
141
+ 'manufacturing.requirements.unnamed': 'Unknown component',
87
142
  });