@easyteam/auto-scheduler-modal-ui 0.1.1 → 0.1.3
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.
- package/dist/index.cjs +167 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +167 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -118,6 +118,8 @@ type OpenShift = {
|
|
|
118
118
|
};
|
|
119
119
|
requiredPosition: string;
|
|
120
120
|
requiredCount: number;
|
|
121
|
+
isDraft?: boolean;
|
|
122
|
+
isPublished?: boolean;
|
|
121
123
|
employeePreferences: Array<{
|
|
122
124
|
employeeId: string;
|
|
123
125
|
level: "PREFERRED" | "AVAILABLE" | "UNAVAILABLE" | string;
|
|
@@ -281,6 +283,7 @@ type AutoSchedulerModalProps = {
|
|
|
281
283
|
getStoredConfigForLocations?: (locationIds: string[]) => AutoSchedulerModalConfig | null;
|
|
282
284
|
/** When provided, persists config after successful schedule creation */
|
|
283
285
|
persistConfigForLocations?: (locationIds: string[], config: AutoSchedulerModalConfig) => void;
|
|
286
|
+
errorToast?: (msg: string) => void;
|
|
284
287
|
};
|
|
285
288
|
type Screen = "configure" | "creating" | "failed";
|
|
286
289
|
type StepStatus = "pending" | "active" | "done";
|
|
@@ -374,6 +377,7 @@ type ViolatedConstraintsPanelProps = {
|
|
|
374
377
|
};
|
|
375
378
|
styles?: Record<string, Record<string, unknown>>;
|
|
376
379
|
styleOverrides?: ViolatedConstraintsPanelStyleOverrides;
|
|
380
|
+
errorToast?: (msg: string) => void;
|
|
377
381
|
};
|
|
378
382
|
type SolveApiResponse = {
|
|
379
383
|
problemId: string;
|
|
@@ -401,7 +405,7 @@ type ScheduleRecommendation = {
|
|
|
401
405
|
why_this_resolves_it: string;
|
|
402
406
|
};
|
|
403
407
|
|
|
404
|
-
declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisdictions, openShifts, employees, timeOffs, locationPlaceholder, onClose, cancelLabel, primaryActionLabel, onPrimaryAction: _onPrimaryAction, onFixManually, initialConfig, theme, cssVarsRoot, generateRecommendationsURLAndHeaders, getTokenURLAndHeaders, onSolution, getStoredConfigForLocations, persistConfigForLocations, }: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
408
|
+
declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisdictions, openShifts, employees, timeOffs, locationPlaceholder, onClose, cancelLabel, primaryActionLabel, onPrimaryAction: _onPrimaryAction, onFixManually, initialConfig, theme, cssVarsRoot, generateRecommendationsURLAndHeaders, getTokenURLAndHeaders, onSolution, getStoredConfigForLocations, persistConfigForLocations, errorToast, }: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
405
409
|
|
|
406
410
|
/**
|
|
407
411
|
* Self-contained AutoSchedulerModal that wraps itself in ChakraProvider.
|
|
@@ -410,7 +414,7 @@ declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisd
|
|
|
410
414
|
*/
|
|
411
415
|
declare function AutoSchedulerModalWithProvider(props: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
412
416
|
|
|
413
|
-
declare function ViolatedConstraintsPanel({ violatedConstraints, title, subtitle, showSecondaryButton, secondaryButtonTitle, onSecondaryButtonClick, generateRecommendationsURLAndHeaders, styles: stylesProp, styleOverrides, }: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
417
|
+
declare function ViolatedConstraintsPanel({ violatedConstraints, title, subtitle, showSecondaryButton, secondaryButtonTitle, onSecondaryButtonClick, generateRecommendationsURLAndHeaders, styles: stylesProp, styleOverrides, errorToast, }: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
414
418
|
|
|
415
419
|
declare function ViolatedConstraintsPanelWithProvider(props: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
416
420
|
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,8 @@ type OpenShift = {
|
|
|
118
118
|
};
|
|
119
119
|
requiredPosition: string;
|
|
120
120
|
requiredCount: number;
|
|
121
|
+
isDraft?: boolean;
|
|
122
|
+
isPublished?: boolean;
|
|
121
123
|
employeePreferences: Array<{
|
|
122
124
|
employeeId: string;
|
|
123
125
|
level: "PREFERRED" | "AVAILABLE" | "UNAVAILABLE" | string;
|
|
@@ -281,6 +283,7 @@ type AutoSchedulerModalProps = {
|
|
|
281
283
|
getStoredConfigForLocations?: (locationIds: string[]) => AutoSchedulerModalConfig | null;
|
|
282
284
|
/** When provided, persists config after successful schedule creation */
|
|
283
285
|
persistConfigForLocations?: (locationIds: string[], config: AutoSchedulerModalConfig) => void;
|
|
286
|
+
errorToast?: (msg: string) => void;
|
|
284
287
|
};
|
|
285
288
|
type Screen = "configure" | "creating" | "failed";
|
|
286
289
|
type StepStatus = "pending" | "active" | "done";
|
|
@@ -374,6 +377,7 @@ type ViolatedConstraintsPanelProps = {
|
|
|
374
377
|
};
|
|
375
378
|
styles?: Record<string, Record<string, unknown>>;
|
|
376
379
|
styleOverrides?: ViolatedConstraintsPanelStyleOverrides;
|
|
380
|
+
errorToast?: (msg: string) => void;
|
|
377
381
|
};
|
|
378
382
|
type SolveApiResponse = {
|
|
379
383
|
problemId: string;
|
|
@@ -401,7 +405,7 @@ type ScheduleRecommendation = {
|
|
|
401
405
|
why_this_resolves_it: string;
|
|
402
406
|
};
|
|
403
407
|
|
|
404
|
-
declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisdictions, openShifts, employees, timeOffs, locationPlaceholder, onClose, cancelLabel, primaryActionLabel, onPrimaryAction: _onPrimaryAction, onFixManually, initialConfig, theme, cssVarsRoot, generateRecommendationsURLAndHeaders, getTokenURLAndHeaders, onSolution, getStoredConfigForLocations, persistConfigForLocations, }: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
408
|
+
declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisdictions, openShifts, employees, timeOffs, locationPlaceholder, onClose, cancelLabel, primaryActionLabel, onPrimaryAction: _onPrimaryAction, onFixManually, initialConfig, theme, cssVarsRoot, generateRecommendationsURLAndHeaders, getTokenURLAndHeaders, onSolution, getStoredConfigForLocations, persistConfigForLocations, errorToast, }: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
405
409
|
|
|
406
410
|
/**
|
|
407
411
|
* Self-contained AutoSchedulerModal that wraps itself in ChakraProvider.
|
|
@@ -410,7 +414,7 @@ declare function AutoSchedulerModal({ baseURL, isOpen, title, bannerText, jurisd
|
|
|
410
414
|
*/
|
|
411
415
|
declare function AutoSchedulerModalWithProvider(props: AutoSchedulerModalProps): react_jsx_runtime.JSX.Element;
|
|
412
416
|
|
|
413
|
-
declare function ViolatedConstraintsPanel({ violatedConstraints, title, subtitle, showSecondaryButton, secondaryButtonTitle, onSecondaryButtonClick, generateRecommendationsURLAndHeaders, styles: stylesProp, styleOverrides, }: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
417
|
+
declare function ViolatedConstraintsPanel({ violatedConstraints, title, subtitle, showSecondaryButton, secondaryButtonTitle, onSecondaryButtonClick, generateRecommendationsURLAndHeaders, styles: stylesProp, styleOverrides, errorToast, }: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
414
418
|
|
|
415
419
|
declare function ViolatedConstraintsPanelWithProvider(props: ViolatedConstraintsPanelProps): react_jsx_runtime.JSX.Element;
|
|
416
420
|
|
package/dist/index.js
CHANGED
|
@@ -889,6 +889,7 @@ var SCHEDULE_STEPS = [
|
|
|
889
889
|
{ title: "Finalizing schedule" }
|
|
890
890
|
];
|
|
891
891
|
var STEP_INTERVAL_MS = 1200;
|
|
892
|
+
var POLL_INTERVAL_MS = 2500;
|
|
892
893
|
var OPTIMIZING_STEP_INDEX = 2;
|
|
893
894
|
var NETWORK_ERROR_FAILURE = {
|
|
894
895
|
violatedConstraints: [
|
|
@@ -1344,7 +1345,7 @@ function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOpti
|
|
|
1344
1345
|
return { ruleIds: allSelectedRuleIds, ruleOverrides };
|
|
1345
1346
|
}
|
|
1346
1347
|
function toApiShift(shift, includeEmployeePreferences) {
|
|
1347
|
-
var _a
|
|
1348
|
+
var _a;
|
|
1348
1349
|
const startDateTime = "startDateTime" in shift ? shift.startDateTime : shift.startTime;
|
|
1349
1350
|
const endDateTime = "endDateTime" in shift ? shift.endDateTime : shift.endTime;
|
|
1350
1351
|
return {
|
|
@@ -1352,8 +1353,8 @@ function toApiShift(shift, includeEmployeePreferences) {
|
|
|
1352
1353
|
startDateTime,
|
|
1353
1354
|
endDateTime,
|
|
1354
1355
|
...shift.employeeId ? { employeeId: String(shift.employeeId) } : {},
|
|
1355
|
-
requiredPosition:
|
|
1356
|
-
requiredCount: (
|
|
1356
|
+
requiredPosition: shift.requiredPosition || void 0,
|
|
1357
|
+
requiredCount: (_a = shift.requiredCount) != null ? _a : 1,
|
|
1357
1358
|
location: shift.location ? {
|
|
1358
1359
|
id: String(shift.location.id),
|
|
1359
1360
|
timezone: "timezone" in shift.location ? shift.location.timezone : void 0,
|
|
@@ -1433,19 +1434,55 @@ async function getToken(fullUrl, headers) {
|
|
|
1433
1434
|
});
|
|
1434
1435
|
return (_a = tokenRes.data) == null ? void 0 : _a.token;
|
|
1435
1436
|
}
|
|
1436
|
-
async function
|
|
1437
|
+
async function submitSolveJob(baseURL, payload, options) {
|
|
1437
1438
|
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1438
1439
|
const headers = {};
|
|
1439
1440
|
if (options == null ? void 0 : options.authorization) {
|
|
1440
1441
|
headers.Authorization = options.authorization;
|
|
1441
1442
|
}
|
|
1442
1443
|
const response = await axiosInstance.post(
|
|
1443
|
-
`${cleanBaseUrl}/api/schedule/solve`,
|
|
1444
|
+
`${cleanBaseUrl}/api/schedule/solve-async`,
|
|
1444
1445
|
payload,
|
|
1445
1446
|
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1446
1447
|
);
|
|
1447
1448
|
return response.data;
|
|
1448
1449
|
}
|
|
1450
|
+
async function getJobStatus(baseURL, jobId, options) {
|
|
1451
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1452
|
+
const headers = {};
|
|
1453
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1454
|
+
headers.Authorization = options.authorization;
|
|
1455
|
+
}
|
|
1456
|
+
const response = await axiosInstance.get(
|
|
1457
|
+
`${cleanBaseUrl}/api/schedule/status/${jobId}`,
|
|
1458
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1459
|
+
);
|
|
1460
|
+
return response.data;
|
|
1461
|
+
}
|
|
1462
|
+
async function getJobResult(baseURL, jobId, options) {
|
|
1463
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1464
|
+
const headers = {};
|
|
1465
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1466
|
+
headers.Authorization = options.authorization;
|
|
1467
|
+
}
|
|
1468
|
+
const response = await axiosInstance.get(
|
|
1469
|
+
`${cleanBaseUrl}/api/schedule/result/${jobId}`,
|
|
1470
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1471
|
+
);
|
|
1472
|
+
return response.data;
|
|
1473
|
+
}
|
|
1474
|
+
async function getJobSolution(baseURL, jobId, options) {
|
|
1475
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1476
|
+
const headers = {};
|
|
1477
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1478
|
+
headers.Authorization = options.authorization;
|
|
1479
|
+
}
|
|
1480
|
+
const response = await axiosInstance.get(
|
|
1481
|
+
`${cleanBaseUrl}/api/schedule/solution/${jobId}`,
|
|
1482
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1483
|
+
);
|
|
1484
|
+
return response.data;
|
|
1485
|
+
}
|
|
1449
1486
|
|
|
1450
1487
|
// src/utils/violationMessages.ts
|
|
1451
1488
|
var SYSTEM_CONSTRAINT_MESSAGES = {
|
|
@@ -1655,12 +1692,15 @@ function ViolatedConstraintsPanel({
|
|
|
1655
1692
|
onSecondaryButtonClick,
|
|
1656
1693
|
generateRecommendationsURLAndHeaders,
|
|
1657
1694
|
styles: stylesProp,
|
|
1658
|
-
styleOverrides
|
|
1695
|
+
styleOverrides,
|
|
1696
|
+
errorToast
|
|
1659
1697
|
}) {
|
|
1660
1698
|
const [showRecommendations, setShowRecommendations] = useState(false);
|
|
1661
1699
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
1662
1700
|
const [recommendations, setRecommendations] = useState(null);
|
|
1663
1701
|
const [isLoadingRecommendations, setIsLoadingRecommendations] = useState(false);
|
|
1702
|
+
const [isDisabledRecommendations, setIsDisabledRecommendations] = useState(false);
|
|
1703
|
+
const [recommendationsButtonText, setRecommendationsButtonText] = useState("Show recommendations");
|
|
1664
1704
|
const styles = useMemo(() => {
|
|
1665
1705
|
if (stylesProp) return stylesProp;
|
|
1666
1706
|
return buildStyles(defaultStyles, styleOverrides);
|
|
@@ -1671,12 +1711,10 @@ function ViolatedConstraintsPanel({
|
|
|
1671
1711
|
const displayRecommendations = recommendations !== null ? recommendations : violatedConstraints.recommendedFixes;
|
|
1672
1712
|
const hasRecommendations = displayRecommendations.length > 0;
|
|
1673
1713
|
const handleToggleRecommendations = useCallback(async () => {
|
|
1674
|
-
var _a, _b;
|
|
1675
1714
|
if (showRecommendations) {
|
|
1676
1715
|
setShowRecommendations(false);
|
|
1677
1716
|
return;
|
|
1678
1717
|
}
|
|
1679
|
-
setShowRecommendations(true);
|
|
1680
1718
|
if (recommendations !== null) {
|
|
1681
1719
|
return;
|
|
1682
1720
|
}
|
|
@@ -1696,9 +1734,21 @@ function ViolatedConstraintsPanel({
|
|
|
1696
1734
|
}
|
|
1697
1735
|
}
|
|
1698
1736
|
);
|
|
1699
|
-
const
|
|
1700
|
-
|
|
1701
|
-
|
|
1737
|
+
const { success, data, msg } = recResponse.data;
|
|
1738
|
+
const { isExpired, recommendations: recommendations2 } = data != null ? data : {};
|
|
1739
|
+
if (!success) {
|
|
1740
|
+
const errorMsg = msg || "Failed to fetch recommendations";
|
|
1741
|
+
errorToast == null ? void 0 : errorToast(errorMsg);
|
|
1742
|
+
if (isExpired) {
|
|
1743
|
+
setIsDisabledRecommendations(true);
|
|
1744
|
+
setRecommendationsButtonText("Daily limit reached");
|
|
1745
|
+
}
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
setShowRecommendations(true);
|
|
1749
|
+
const fetchedRecommendations = recommendations2;
|
|
1750
|
+
if (fetchedRecommendations) {
|
|
1751
|
+
setRecommendations(fetchedRecommendations.map((r) => r.recommendation));
|
|
1702
1752
|
} else {
|
|
1703
1753
|
setRecommendations([]);
|
|
1704
1754
|
}
|
|
@@ -1713,7 +1763,8 @@ function ViolatedConstraintsPanel({
|
|
|
1713
1763
|
recommendations,
|
|
1714
1764
|
canFetchRecommendations,
|
|
1715
1765
|
violatedConstraints.recommendationPayload,
|
|
1716
|
-
generateRecommendationsURLAndHeaders
|
|
1766
|
+
generateRecommendationsURLAndHeaders,
|
|
1767
|
+
errorToast
|
|
1717
1768
|
]);
|
|
1718
1769
|
return /* @__PURE__ */ jsxs2(Box, { sx: styles.violationPanel, children: [
|
|
1719
1770
|
/* @__PURE__ */ jsxs2(HStack, { sx: styles.violationHeaderRow, children: [
|
|
@@ -1765,9 +1816,9 @@ function ViolatedConstraintsPanel({
|
|
|
1765
1816
|
variant: "outline",
|
|
1766
1817
|
size: "xs",
|
|
1767
1818
|
onClick: handleToggleRecommendations,
|
|
1768
|
-
isDisabled: isLoadingRecommendations,
|
|
1819
|
+
isDisabled: isLoadingRecommendations || isDisabledRecommendations,
|
|
1769
1820
|
sx: { ...styles.recommendationsToggle, minWidth: "140px" },
|
|
1770
|
-
children: isLoadingRecommendations ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" :
|
|
1821
|
+
children: isLoadingRecommendations ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" : recommendationsButtonText
|
|
1771
1822
|
}
|
|
1772
1823
|
) : null,
|
|
1773
1824
|
showSecondaryButton && secondaryButtonTitle && onSecondaryButtonClick ? /* @__PURE__ */ jsx2(
|
|
@@ -1809,7 +1860,8 @@ function AutoSchedulerModal({
|
|
|
1809
1860
|
getTokenURLAndHeaders,
|
|
1810
1861
|
onSolution,
|
|
1811
1862
|
getStoredConfigForLocations,
|
|
1812
|
-
persistConfigForLocations
|
|
1863
|
+
persistConfigForLocations,
|
|
1864
|
+
errorToast
|
|
1813
1865
|
}) {
|
|
1814
1866
|
const baseTheme = useTheme();
|
|
1815
1867
|
const { chakraOverride, styleOverrides } = useMemo2(
|
|
@@ -1831,6 +1883,7 @@ function AutoSchedulerModal({
|
|
|
1831
1883
|
const [lastFailure, setLastFailure] = useState2(null);
|
|
1832
1884
|
const [selectedLocationIds, setSelectedLocationIds] = useState2([]);
|
|
1833
1885
|
const timersRef = useRef([]);
|
|
1886
|
+
const pollIntervalRef = useRef(null);
|
|
1834
1887
|
const runIdRef = useRef(0);
|
|
1835
1888
|
const selectedByJurisdiction = useMemo2(
|
|
1836
1889
|
() => jurisdictions.map((jurisdiction) => ({
|
|
@@ -1845,6 +1898,11 @@ function AutoSchedulerModal({
|
|
|
1845
1898
|
(total, jurisdiction) => total + jurisdiction.selectedLocations.length,
|
|
1846
1899
|
0
|
|
1847
1900
|
);
|
|
1901
|
+
const locationIdsWithOpenShifts = useMemo2(() => {
|
|
1902
|
+
return new Set(
|
|
1903
|
+
openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id)
|
|
1904
|
+
);
|
|
1905
|
+
}, [openShifts]);
|
|
1848
1906
|
const selectedConstraints = useMemo2(
|
|
1849
1907
|
() => HARD_CONSTRAINT_OPTIONS.filter((option) => selectedConstraintIds.includes(option.id)),
|
|
1850
1908
|
[selectedConstraintIds]
|
|
@@ -1896,6 +1954,10 @@ function AutoSchedulerModal({
|
|
|
1896
1954
|
const clearTimers = () => {
|
|
1897
1955
|
timersRef.current.forEach((timerId) => clearTimeout(timerId));
|
|
1898
1956
|
timersRef.current = [];
|
|
1957
|
+
if (pollIntervalRef.current) {
|
|
1958
|
+
clearInterval(pollIntervalRef.current);
|
|
1959
|
+
pollIntervalRef.current = null;
|
|
1960
|
+
}
|
|
1899
1961
|
};
|
|
1900
1962
|
const resetProgress = () => {
|
|
1901
1963
|
setActiveStepIndex(0);
|
|
@@ -1945,6 +2007,12 @@ function AutoSchedulerModal({
|
|
|
1945
2007
|
}
|
|
1946
2008
|
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
1947
2009
|
useEffect(() => () => clearTimers(), []);
|
|
2010
|
+
useEffect(() => {
|
|
2011
|
+
setSelectedLocationIds((previous) => {
|
|
2012
|
+
const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
|
|
2013
|
+
return filtered.length === previous.length ? previous : filtered;
|
|
2014
|
+
});
|
|
2015
|
+
}, [locationIdsWithOpenShifts]);
|
|
1948
2016
|
const stepStatusForIndex = (index) => {
|
|
1949
2017
|
if (index <= completedStepIndex) {
|
|
1950
2018
|
return "done";
|
|
@@ -2029,7 +2097,6 @@ function AutoSchedulerModal({
|
|
|
2029
2097
|
});
|
|
2030
2098
|
const runId = resetBeforeSubmit();
|
|
2031
2099
|
schedule(STEP_INTERVAL_MS, async () => {
|
|
2032
|
-
var _a, _b;
|
|
2033
2100
|
setCompletedStepIndex(0);
|
|
2034
2101
|
setActiveStepIndex(1);
|
|
2035
2102
|
try {
|
|
@@ -2037,51 +2104,47 @@ function AutoSchedulerModal({
|
|
|
2037
2104
|
if (!token) {
|
|
2038
2105
|
throw new Error("Failed to get auto-scheduler token");
|
|
2039
2106
|
}
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2042
|
-
payload,
|
|
2043
|
-
{
|
|
2044
|
-
authorization: `Bearer ${token}`
|
|
2045
|
-
}
|
|
2046
|
-
);
|
|
2107
|
+
const auth = { authorization: `Bearer ${token}` };
|
|
2108
|
+
const { jobId } = await submitSolveJob(baseURL, payload, auth);
|
|
2047
2109
|
if (runIdRef.current !== runId) {
|
|
2048
2110
|
return;
|
|
2049
2111
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
(
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2112
|
+
setCompletedStepIndex(1);
|
|
2113
|
+
setActiveStepIndex(OPTIMIZING_STEP_INDEX);
|
|
2114
|
+
const pollForResult = async () => {
|
|
2115
|
+
if (runIdRef.current !== runId) return;
|
|
2116
|
+
try {
|
|
2117
|
+
const statusRes = await getJobStatus(baseURL, jobId, auth);
|
|
2118
|
+
if (runIdRef.current !== runId) return;
|
|
2119
|
+
if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
|
|
2120
|
+
if (pollIntervalRef.current) {
|
|
2121
|
+
clearInterval(pollIntervalRef.current);
|
|
2122
|
+
pollIntervalRef.current = null;
|
|
2123
|
+
}
|
|
2124
|
+
const response = await getJobResult(baseURL, jobId, auth);
|
|
2125
|
+
if (runIdRef.current !== runId) return;
|
|
2126
|
+
let responseForHandling = response;
|
|
2127
|
+
if (response.explanation.isFeasible) {
|
|
2128
|
+
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2129
|
+
if (runIdRef.current !== runId) return;
|
|
2130
|
+
responseForHandling = { ...response, solution };
|
|
2131
|
+
}
|
|
2132
|
+
handleSolveResult(runId, responseForHandling);
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
} catch (err) {
|
|
2136
|
+
if (runIdRef.current !== runId) return;
|
|
2137
|
+
if (pollIntervalRef.current) {
|
|
2138
|
+
clearInterval(pollIntervalRef.current);
|
|
2139
|
+
pollIntervalRef.current = null;
|
|
2140
|
+
}
|
|
2141
|
+
console.error(err);
|
|
2142
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2143
|
+
setScreen("failed");
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
pollForResult();
|
|
2147
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2085
2148
|
} catch (err) {
|
|
2086
2149
|
if (runIdRef.current !== runId) {
|
|
2087
2150
|
return;
|
|
@@ -2092,11 +2155,50 @@ function AutoSchedulerModal({
|
|
|
2092
2155
|
}
|
|
2093
2156
|
}, runId);
|
|
2094
2157
|
};
|
|
2158
|
+
const handleSolveResult = (runId, response) => {
|
|
2159
|
+
var _a, _b;
|
|
2160
|
+
if (!response.explanation.isFeasible) {
|
|
2161
|
+
const result = mapViolationsToResult(
|
|
2162
|
+
(_a = response.explanation.violations) != null ? _a : [],
|
|
2163
|
+
response.explanation.summary
|
|
2164
|
+
);
|
|
2165
|
+
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
|
|
2166
|
+
(l) => selectedLocationIds.includes(l.id)
|
|
2167
|
+
);
|
|
2168
|
+
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2169
|
+
context: {
|
|
2170
|
+
selectedLocationIds,
|
|
2171
|
+
selectedConstraintIds,
|
|
2172
|
+
constraintValues,
|
|
2173
|
+
selectedOptimizationIds,
|
|
2174
|
+
optimizationValues,
|
|
2175
|
+
timezone: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
|
|
2176
|
+
},
|
|
2177
|
+
locations: selectedLocations,
|
|
2178
|
+
employees,
|
|
2179
|
+
openShifts: openShifts.filter((s) => !s.employeeId),
|
|
2180
|
+
assignedShifts: openShifts.filter((s) => Boolean(s.employeeId)),
|
|
2181
|
+
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2182
|
+
explanation: response.explanation
|
|
2183
|
+
} : void 0;
|
|
2184
|
+
setLastFailure({
|
|
2185
|
+
...result,
|
|
2186
|
+
recommendedFixes: [],
|
|
2187
|
+
recommendationPayload
|
|
2188
|
+
});
|
|
2189
|
+
setScreen("failed");
|
|
2190
|
+
} else {
|
|
2191
|
+
afterSuccessSteps(runId, response);
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2095
2194
|
const handleAdjustConstraints = () => {
|
|
2096
2195
|
clearTimers();
|
|
2097
2196
|
setScreen("configure");
|
|
2098
2197
|
};
|
|
2099
2198
|
const handleToggleLocation = (locationId) => {
|
|
2199
|
+
if (!locationIdsWithOpenShifts.has(locationId)) {
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2100
2202
|
if (selectedLocationIds.includes(locationId)) {
|
|
2101
2203
|
setSelectedLocationIds(selectedLocationIds.filter((id) => id !== locationId));
|
|
2102
2204
|
return;
|
|
@@ -2160,6 +2262,7 @@ function AutoSchedulerModal({
|
|
|
2160
2262
|
violatedConstraints: lastFailure,
|
|
2161
2263
|
title: violationTitle,
|
|
2162
2264
|
subtitle: "",
|
|
2265
|
+
errorToast,
|
|
2163
2266
|
generateRecommendationsURLAndHeaders,
|
|
2164
2267
|
styles
|
|
2165
2268
|
}
|
|
@@ -2219,13 +2322,18 @@ function AutoSchedulerModal({
|
|
|
2219
2322
|
HStack2,
|
|
2220
2323
|
{
|
|
2221
2324
|
as: "label",
|
|
2222
|
-
sx: {
|
|
2325
|
+
sx: {
|
|
2326
|
+
...styles.optionRow,
|
|
2327
|
+
cursor: locationIdsWithOpenShifts.has(location.id) ? "pointer" : "not-allowed",
|
|
2328
|
+
opacity: locationIdsWithOpenShifts.has(location.id) ? 1 : 0.55
|
|
2329
|
+
},
|
|
2223
2330
|
children: [
|
|
2224
2331
|
/* @__PURE__ */ jsx3(
|
|
2225
2332
|
Checkbox,
|
|
2226
2333
|
{
|
|
2227
2334
|
id: `location-${location.id}`,
|
|
2228
2335
|
isChecked: selectedLocationIds.includes(location.id),
|
|
2336
|
+
isDisabled: !locationIdsWithOpenShifts.has(location.id),
|
|
2229
2337
|
onChange: () => handleToggleLocation(location.id)
|
|
2230
2338
|
}
|
|
2231
2339
|
),
|