@seedtactics/insight-client 17.0.1-beta.6 → 17.0.1-beta.7

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.
@@ -69,14 +69,48 @@ function basketAction(mat, basketId) {
69
69
  }
70
70
  return undefined;
71
71
  }
72
- function confirmableWork(material, basketId) {
72
+ function stationWork(material, basket) {
73
73
  const classifiedActions = LazySeq.of(material)
74
- .collect((mat) => basketAction(mat, basketId))
74
+ .collect((mat) => basketAction(mat, basket.basketId))
75
75
  .toRArray();
76
76
  if (classifiedActions.some((action) => action.type === "invalid")) {
77
77
  return { type: "inconsistent" };
78
78
  }
79
79
  const validActions = classifiedActions.filter((action) => action.type === "valid");
80
+ const descriptor = basket.loadStationWork;
81
+ if (descriptor !== undefined) {
82
+ if (!descriptor ||
83
+ !isNonBlank(descriptor.workId) ||
84
+ typeof descriptor.readyToConfirm !== "boolean")
85
+ return { type: "inconsistent" };
86
+ const awaiting = descriptor.awaitingMaterialSlots ?? [];
87
+ if (!Array.isArray(awaiting) ||
88
+ !awaiting.every(isPositiveInteger) ||
89
+ new Set(awaiting).size !== awaiting.length ||
90
+ (descriptor.readyToConfirm && awaiting.length > 0) ||
91
+ validActions.some((action) => action.workId !== descriptor.workId))
92
+ return { type: "inconsistent" };
93
+ const empty = descriptor.type === api.BasketLoadStationWorkType.ConfirmEmptyBasket;
94
+ if (empty) {
95
+ if (validActions.length > 0 ||
96
+ awaiting.length > 0 ||
97
+ material.some((mat) => mat.location.type === api.LocType.InBasket && mat.location.basketId === basket.basketId))
98
+ return { type: "inconsistent" };
99
+ }
100
+ else if (descriptor.type !== api.BasketLoadStationWorkType.Material ||
101
+ (descriptor.readyToConfirm && validActions.length === 0)) {
102
+ return { type: "inconsistent" };
103
+ }
104
+ return {
105
+ type: "active",
106
+ work: {
107
+ workId: descriptor.workId,
108
+ ready: descriptor.readyToConfirm,
109
+ confirmEmpty: empty,
110
+ awaitingSlots: awaiting,
111
+ },
112
+ };
113
+ }
80
114
  if (validActions.length === 0)
81
115
  return { type: "none" };
82
116
  const tagged = validActions.filter((action) => isNonBlank(action.workId));
@@ -85,8 +119,8 @@ function confirmableWork(material, basketId) {
85
119
  return { type: "inconsistent" };
86
120
  }
87
121
  return {
88
- type: "confirmable",
89
- work: { workId: tagged[0].workId },
122
+ type: "active",
123
+ work: { workId: tagged[0].workId, ready: true, confirmEmpty: false, awaitingSlots: [] },
90
124
  };
91
125
  }
92
126
  function loadingInstruction(mat) {
@@ -105,21 +139,28 @@ function SlotMaterial({ material, fsize, }) {
105
139
  }
106
140
  export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsize, submitCommand, }) {
107
141
  const [submission, setSubmission] = useState();
108
- const workState = useMemo(() => confirmableWork(material, basket.basketId), [basket.basketId, material]);
109
- const work = workState.type === "confirmable" ? workState.work : undefined;
110
- const submissionState = submission !== undefined && submission.workId === work?.workId ? submission.state : "idle";
142
+ const workState = useMemo(() => stationWork(material, basket), [basket, material]);
143
+ const work = workState.type === "active" ? workState.work : undefined;
144
+ const submissionState = submission !== undefined &&
145
+ submission.workId === work?.workId &&
146
+ // A conflict rejects the submitted status snapshot, not the preserved occurrence.
147
+ // Compare the captured snapshot even when refresh precedes the response.
148
+ (submission.state !== "conflict" ||
149
+ (submission.basket === basket && submission.material === material))
150
+ ? submission.state
151
+ : "idle";
111
152
  const materialBySlot = useMemo(() => LazySeq.of(material)
112
153
  .filter((mat) => mat.location.type === api.LocType.InBasket &&
113
154
  mat.location.basketId === basket.basketId &&
114
155
  mat.location.basketSlot !== undefined)
115
156
  .groupBy((mat) => mat.location.basketSlot ?? 0)
116
- .toHashMap((entry) => entry), [basket.basketId, material]);
157
+ .toHashMap((entry) => entry), [basket, material]);
117
158
  const loadsBySlot = useMemo(() => LazySeq.of(material)
118
159
  .filter((mat) => mat.action.type === api.ActionType.LoadingToBasket &&
119
160
  mat.action.loadToBasketId === basket.basketId &&
120
161
  mat.action.loadToBasketSlot !== undefined)
121
162
  .groupBy((mat) => mat.action.loadToBasketSlot ?? 0)
122
- .toHashMap((entry) => entry), [basket.basketId, material]);
163
+ .toHashMap((entry) => entry), [basket, material]);
123
164
  const slots = useMemo(() => LazySeq.of(basket.emptySlots ?? [])
124
165
  .concat(basket.unknownSlots ?? [])
125
166
  .concat(materialBySlot.keysToLazySeq())
@@ -127,19 +168,21 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
127
168
  .distinctBy((slot) => slot)
128
169
  .toSortedArray((slot) => slot), [basket.emptySlots, basket.unknownSlots, materialBySlot, loadsBySlot]);
129
170
  async function submit() {
130
- if (work === undefined || submitCommand === undefined)
171
+ if (work === undefined || !work.ready || submitCommand === undefined)
131
172
  return;
132
173
  const submittedWorkId = work.workId;
133
- setSubmission({ workId: submittedWorkId, state: "submitting" });
174
+ const pending = { workId: submittedWorkId, basket, material, state: "submitting" };
175
+ setSubmission(pending);
134
176
  try {
135
177
  const state = await submitCommand(stationNumber, { workId: submittedWorkId });
136
- setSubmission((current) => current?.workId === submittedWorkId ? { workId: submittedWorkId, state } : current);
178
+ setSubmission((current) => (current === pending ? { ...pending, state } : current));
137
179
  }
138
180
  catch {
139
- setSubmission((current) => current?.workId === submittedWorkId ? { workId: submittedWorkId, state: "error" } : current);
181
+ setSubmission((current) => (current === pending ? { ...pending, state: "error" } : current));
140
182
  }
141
183
  }
142
- const submissionDisabled = submissionState === "submitting" ||
184
+ const submissionDisabled = !work?.ready ||
185
+ submissionState === "submitting" ||
143
186
  submissionState === "accepted" ||
144
187
  submissionState === "conflict";
145
188
  return (_jsxs(Stack, { spacing: 2, sx: { mt: 2 }, children: [_jsx(Typography, { variant: "h6", children: "Basket slots" }), _jsx(Box, { sx: {
@@ -155,5 +198,7 @@ export function BasketLoadStationWorkflow({ stationNumber, basket, material, fsi
155
198
  basketId: basket.basketId,
156
199
  slot,
157
200
  }, children: _jsxs(Box, { component: "section", "data-testid": `basket-load-station-slot-${slot}`, sx: { border: "1px solid", borderColor: "text.primary", p: 2 }, children: [_jsxs(Typography, { variant: "h6", children: ["Slot ", slot] }), slotMaterial.length > 0 ? (_jsx(SlotMaterial, { material: slotMaterial, fsize: fsize })) : (_jsx(Typography, { color: isUnknown ? "warning.main" : "text.secondary", children: isUnknown ? "Unknown" : "Empty" })), loads.map((mat, index) => (_jsxs(Box, { sx: { mt: 1 }, children: [_jsx(Typography, { children: loadingInstruction(mat) }), _jsxs(Typography, { variant: "body2", color: "text.secondary", children: [mat.partName, " \u00B7 Process ", mat.action.processAfterLoad ?? mat.process] })] }, `${mat.jobUnique}-${mat.process}-${index}`)))] }) }, slot));
158
- }) }), work && submitCommand ? (_jsx(Box, { sx: { display: "flex", justifyContent: "flex-end" }, children: _jsx(Button, { variant: "contained", disabled: submissionDisabled, onClick: () => void submit(), children: "Confirm" }) })) : work ? (_jsx(Alert, { severity: "warning", children: "Basket work confirmation is not configured." })) : workState.type === "inconsistent" ? (_jsx(Alert, { severity: "warning", children: "Basket work is inconsistent. Wait for refreshed material actions before confirming." })) : null, submissionState === "accepted" ? (_jsx(Alert, { severity: "success", children: "Confirmation accepted. Waiting for refreshed work." })) : submissionState === "conflict" ? (_jsx(Alert, { severity: "warning", children: "Basket work changed before confirmation. Review the refreshed slots and try again." })) : submissionState === "error" ? (_jsx(Alert, { severity: "error", children: "Unable to confirm basket work. No work was assumed complete." })) : null] }));
201
+ }) }), work?.confirmEmpty && _jsxs(Typography, { children: ["Confirm basket ", basket.basketId, " is empty."] }), work && !work.ready && (_jsx(Alert, { severity: "info", children: work.awaitingSlots.length > 0
202
+ ? `Waiting for material for slots ${work.awaitingSlots.join(", ")}.`
203
+ : "Basket work is not ready for confirmation." })), work && submitCommand ? (_jsx(Box, { sx: { display: "flex", justifyContent: "flex-end" }, children: _jsx(Button, { variant: "contained", disabled: submissionDisabled, onClick: () => void submit(), children: "Confirm" }) })) : work ? (_jsx(Alert, { severity: "warning", children: "Basket work confirmation is not configured." })) : workState.type === "inconsistent" ? (_jsx(Alert, { severity: "warning", children: "Basket work is inconsistent. Wait for refreshed material actions before confirming." })) : null, submissionState === "accepted" ? (_jsx(Alert, { severity: "success", children: "Confirmation accepted. Waiting for refreshed work." })) : submissionState === "conflict" ? (_jsx(Alert, { severity: "warning", children: "Basket work changed before confirmation. Review the refreshed slots and try again." })) : submissionState === "error" ? (_jsx(Alert, { severity: "error", children: "Unable to confirm basket work. No work was assumed complete." })) : null] }));
159
204
  }
package/dist/index.html CHANGED
@@ -43,7 +43,7 @@
43
43
  }
44
44
  }
45
45
  </style>
46
- <script type="module" crossorigin src="/assets/index-C6EBFHuW.js"></script>
46
+ <script type="module" crossorigin src="/assets/index-CNJT3DX_.js"></script>
47
47
  <link rel="stylesheet" crossorigin href="/assets/index-DXfAwuj-.css">
48
48
  </head>
49
49
  <body>
@@ -1060,6 +1060,7 @@ export declare enum WorkorderSerialCloseout {
1060
1060
  }
1061
1061
  export declare class BasketStatus implements IBasketStatus {
1062
1062
  basketId: number;
1063
+ loadStationWork?: BasketLoadStationWork | undefined;
1063
1064
  position?: BasketPosition | undefined;
1064
1065
  emptySlots?: number[];
1065
1066
  unknownSlots?: number[];
@@ -1070,10 +1071,31 @@ export declare class BasketStatus implements IBasketStatus {
1070
1071
  }
1071
1072
  export interface IBasketStatus {
1072
1073
  basketId: number;
1074
+ loadStationWork?: BasketLoadStationWork | undefined;
1073
1075
  position?: BasketPosition | undefined;
1074
1076
  emptySlots?: number[];
1075
1077
  unknownSlots?: number[];
1076
1078
  }
1079
+ export declare class BasketLoadStationWork implements IBasketLoadStationWork {
1080
+ workId: string;
1081
+ type: BasketLoadStationWorkType;
1082
+ readyToConfirm: boolean;
1083
+ awaitingMaterialSlots?: number[];
1084
+ constructor(data?: IBasketLoadStationWork);
1085
+ init(_data?: any): void;
1086
+ static fromJS(data: any): BasketLoadStationWork;
1087
+ toJSON(data?: any): any;
1088
+ }
1089
+ export interface IBasketLoadStationWork {
1090
+ workId: string;
1091
+ type: BasketLoadStationWorkType;
1092
+ readyToConfirm: boolean;
1093
+ awaitingMaterialSlots?: number[];
1094
+ }
1095
+ export declare enum BasketLoadStationWorkType {
1096
+ Material = "Material",
1097
+ ConfirmEmptyBasket = "ConfirmEmptyBasket"
1098
+ }
1077
1099
  export declare class BasketPosition implements IBasketPosition {
1078
1100
  location: BasketLocationEnum;
1079
1101
  locationNum: number;
@@ -4412,6 +4412,9 @@ export class BasketStatus {
4412
4412
  init(_data) {
4413
4413
  if (_data) {
4414
4414
  this.basketId = _data["BasketId"];
4415
+ this.loadStationWork = _data["LoadStationWork"]
4416
+ ? BasketLoadStationWork.fromJS(_data["LoadStationWork"])
4417
+ : undefined;
4415
4418
  this.position = _data["Position"]
4416
4419
  ? BasketPosition.fromJS(_data["Position"])
4417
4420
  : undefined;
@@ -4436,6 +4439,9 @@ export class BasketStatus {
4436
4439
  toJSON(data) {
4437
4440
  data = typeof data === "object" ? data : {};
4438
4441
  data["BasketId"] = this.basketId;
4442
+ data["LoadStationWork"] = this.loadStationWork
4443
+ ? this.loadStationWork.toJSON()
4444
+ : undefined;
4439
4445
  data["Position"] = this.position ? this.position.toJSON() : undefined;
4440
4446
  if (Array.isArray(this.emptySlots)) {
4441
4447
  data["EmptySlots"] = [];
@@ -4450,6 +4456,51 @@ export class BasketStatus {
4450
4456
  return data;
4451
4457
  }
4452
4458
  }
4459
+ export class BasketLoadStationWork {
4460
+ constructor(data) {
4461
+ if (data) {
4462
+ for (var property in data) {
4463
+ if (data.hasOwnProperty(property))
4464
+ this[property] = data[property];
4465
+ }
4466
+ }
4467
+ }
4468
+ init(_data) {
4469
+ if (_data) {
4470
+ this.workId = _data["WorkId"];
4471
+ this.type = _data["Type"];
4472
+ this.readyToConfirm = _data["ReadyToConfirm"];
4473
+ if (Array.isArray(_data["AwaitingMaterialSlots"])) {
4474
+ this.awaitingMaterialSlots = [];
4475
+ for (let item of _data["AwaitingMaterialSlots"])
4476
+ this.awaitingMaterialSlots.push(item);
4477
+ }
4478
+ }
4479
+ }
4480
+ static fromJS(data) {
4481
+ data = typeof data === "object" ? data : {};
4482
+ let result = new BasketLoadStationWork();
4483
+ result.init(data);
4484
+ return result;
4485
+ }
4486
+ toJSON(data) {
4487
+ data = typeof data === "object" ? data : {};
4488
+ data["WorkId"] = this.workId;
4489
+ data["Type"] = this.type;
4490
+ data["ReadyToConfirm"] = this.readyToConfirm;
4491
+ if (Array.isArray(this.awaitingMaterialSlots)) {
4492
+ data["AwaitingMaterialSlots"] = [];
4493
+ for (let item of this.awaitingMaterialSlots)
4494
+ data["AwaitingMaterialSlots"].push(item);
4495
+ }
4496
+ return data;
4497
+ }
4498
+ }
4499
+ export var BasketLoadStationWorkType;
4500
+ (function (BasketLoadStationWorkType) {
4501
+ BasketLoadStationWorkType["Material"] = "Material";
4502
+ BasketLoadStationWorkType["ConfirmEmptyBasket"] = "ConfirmEmptyBasket";
4503
+ })(BasketLoadStationWorkType || (BasketLoadStationWorkType = {}));
4453
4504
  export class BasketPosition {
4454
4505
  constructor(data) {
4455
4506
  if (data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedtactics/insight-client",
3
- "version": "17.0.1-beta.6",
3
+ "version": "17.0.1-beta.7",
4
4
  "license": "BSD-3-Clause",
5
5
  "repository": {
6
6
  "url": "https://github.com/SeedTactics/fms-insight"