@easyteam/auto-scheduler-modal-ui 0.1.0 → 0.1.2
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 +166 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +166 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -905,6 +905,7 @@ var SCHEDULE_STEPS = [
|
|
|
905
905
|
{ title: "Finalizing schedule" }
|
|
906
906
|
];
|
|
907
907
|
var STEP_INTERVAL_MS = 1200;
|
|
908
|
+
var POLL_INTERVAL_MS = 2500;
|
|
908
909
|
var OPTIMIZING_STEP_INDEX = 2;
|
|
909
910
|
var NETWORK_ERROR_FAILURE = {
|
|
910
911
|
violatedConstraints: [
|
|
@@ -1437,14 +1438,65 @@ function buildSchedulePayload(input) {
|
|
|
1437
1438
|
};
|
|
1438
1439
|
}
|
|
1439
1440
|
|
|
1440
|
-
// src/api/
|
|
1441
|
+
// src/api/api.ts
|
|
1441
1442
|
var import_axios = __toESM(require("axios"), 1);
|
|
1442
1443
|
var axiosInstance = import_axios.default.create({
|
|
1443
1444
|
headers: { "Content-Type": "application/json" }
|
|
1444
1445
|
});
|
|
1445
|
-
async function
|
|
1446
|
+
async function getToken(fullUrl, headers) {
|
|
1447
|
+
var _a;
|
|
1448
|
+
const tokenRes = await axiosInstance.get(fullUrl, {
|
|
1449
|
+
...headers ? { headers } : {}
|
|
1450
|
+
});
|
|
1451
|
+
return (_a = tokenRes.data) == null ? void 0 : _a.token;
|
|
1452
|
+
}
|
|
1453
|
+
async function submitSolveJob(baseURL, payload, options) {
|
|
1454
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1455
|
+
const headers = {};
|
|
1456
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1457
|
+
headers.Authorization = options.authorization;
|
|
1458
|
+
}
|
|
1459
|
+
const response = await axiosInstance.post(
|
|
1460
|
+
`${cleanBaseUrl}/api/schedule/solve-async`,
|
|
1461
|
+
payload,
|
|
1462
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1463
|
+
);
|
|
1464
|
+
return response.data;
|
|
1465
|
+
}
|
|
1466
|
+
async function getJobStatus(baseURL, jobId, options) {
|
|
1446
1467
|
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1447
|
-
const
|
|
1468
|
+
const headers = {};
|
|
1469
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1470
|
+
headers.Authorization = options.authorization;
|
|
1471
|
+
}
|
|
1472
|
+
const response = await axiosInstance.get(
|
|
1473
|
+
`${cleanBaseUrl}/api/schedule/status/${jobId}`,
|
|
1474
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1475
|
+
);
|
|
1476
|
+
return response.data;
|
|
1477
|
+
}
|
|
1478
|
+
async function getJobResult(baseURL, jobId, options) {
|
|
1479
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1480
|
+
const headers = {};
|
|
1481
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1482
|
+
headers.Authorization = options.authorization;
|
|
1483
|
+
}
|
|
1484
|
+
const response = await axiosInstance.get(
|
|
1485
|
+
`${cleanBaseUrl}/api/schedule/result/${jobId}`,
|
|
1486
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1487
|
+
);
|
|
1488
|
+
return response.data;
|
|
1489
|
+
}
|
|
1490
|
+
async function getJobSolution(baseURL, jobId, options) {
|
|
1491
|
+
const cleanBaseUrl = baseURL.replace(/\/$/, "");
|
|
1492
|
+
const headers = {};
|
|
1493
|
+
if (options == null ? void 0 : options.authorization) {
|
|
1494
|
+
headers.Authorization = options.authorization;
|
|
1495
|
+
}
|
|
1496
|
+
const response = await axiosInstance.get(
|
|
1497
|
+
`${cleanBaseUrl}/api/schedule/solution/${jobId}`,
|
|
1498
|
+
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1499
|
+
);
|
|
1448
1500
|
return response.data;
|
|
1449
1501
|
}
|
|
1450
1502
|
|
|
@@ -1799,7 +1851,10 @@ function AutoSchedulerModal({
|
|
|
1799
1851
|
theme,
|
|
1800
1852
|
cssVarsRoot,
|
|
1801
1853
|
generateRecommendationsURLAndHeaders,
|
|
1802
|
-
|
|
1854
|
+
getTokenURLAndHeaders,
|
|
1855
|
+
onSolution,
|
|
1856
|
+
getStoredConfigForLocations,
|
|
1857
|
+
persistConfigForLocations
|
|
1803
1858
|
}) {
|
|
1804
1859
|
const baseTheme = (0, import_react4.useTheme)();
|
|
1805
1860
|
const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(
|
|
@@ -1821,6 +1876,7 @@ function AutoSchedulerModal({
|
|
|
1821
1876
|
const [lastFailure, setLastFailure] = (0, import_react3.useState)(null);
|
|
1822
1877
|
const [selectedLocationIds, setSelectedLocationIds] = (0, import_react3.useState)([]);
|
|
1823
1878
|
const timersRef = (0, import_react3.useRef)([]);
|
|
1879
|
+
const pollIntervalRef = (0, import_react3.useRef)(null);
|
|
1824
1880
|
const runIdRef = (0, import_react3.useRef)(0);
|
|
1825
1881
|
const selectedByJurisdiction = (0, import_react3.useMemo)(
|
|
1826
1882
|
() => jurisdictions.map((jurisdiction) => ({
|
|
@@ -1886,6 +1942,10 @@ function AutoSchedulerModal({
|
|
|
1886
1942
|
const clearTimers = () => {
|
|
1887
1943
|
timersRef.current.forEach((timerId) => clearTimeout(timerId));
|
|
1888
1944
|
timersRef.current = [];
|
|
1945
|
+
if (pollIntervalRef.current) {
|
|
1946
|
+
clearInterval(pollIntervalRef.current);
|
|
1947
|
+
pollIntervalRef.current = null;
|
|
1948
|
+
}
|
|
1889
1949
|
};
|
|
1890
1950
|
const resetProgress = () => {
|
|
1891
1951
|
setActiveStepIndex(0);
|
|
@@ -1917,6 +1977,23 @@ function AutoSchedulerModal({
|
|
|
1917
1977
|
setScreen("configure");
|
|
1918
1978
|
}
|
|
1919
1979
|
}, [isOpen, initialConfig]);
|
|
1980
|
+
(0, import_react3.useEffect)(() => {
|
|
1981
|
+
if (selectedLocationIds.length === 0 || initialConfig || !getStoredConfigForLocations) {
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
const stored = getStoredConfigForLocations(selectedLocationIds);
|
|
1985
|
+
if (stored) {
|
|
1986
|
+
setSelectedConstraintIds(stored.selectedConstraintIds);
|
|
1987
|
+
setConstraintValues(stored.constraintValues);
|
|
1988
|
+
setSelectedOptimizationIds(stored.selectedOptimizationIds);
|
|
1989
|
+
setOptimizationValues(stored.optimizationValues);
|
|
1990
|
+
} else {
|
|
1991
|
+
setSelectedConstraintIds([]);
|
|
1992
|
+
setConstraintValues({});
|
|
1993
|
+
setSelectedOptimizationIds([]);
|
|
1994
|
+
setOptimizationValues({});
|
|
1995
|
+
}
|
|
1996
|
+
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
1920
1997
|
(0, import_react3.useEffect)(() => () => clearTimers(), []);
|
|
1921
1998
|
const stepStatusForIndex = (index) => {
|
|
1922
1999
|
if (index <= completedStepIndex) {
|
|
@@ -1993,52 +2070,63 @@ function AutoSchedulerModal({
|
|
|
1993
2070
|
selectedOptimizationIds,
|
|
1994
2071
|
optimizationValues
|
|
1995
2072
|
});
|
|
1996
|
-
|
|
2073
|
+
persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
|
|
2074
|
+
selectedLocationIds,
|
|
2075
|
+
selectedConstraintIds,
|
|
2076
|
+
constraintValues,
|
|
2077
|
+
selectedOptimizationIds,
|
|
2078
|
+
optimizationValues
|
|
2079
|
+
});
|
|
1997
2080
|
const runId = resetBeforeSubmit();
|
|
1998
2081
|
schedule(STEP_INTERVAL_MS, async () => {
|
|
1999
|
-
var _a, _b;
|
|
2000
2082
|
setCompletedStepIndex(0);
|
|
2001
2083
|
setActiveStepIndex(1);
|
|
2002
2084
|
try {
|
|
2003
|
-
const
|
|
2085
|
+
const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
|
|
2086
|
+
if (!token) {
|
|
2087
|
+
throw new Error("Failed to get auto-scheduler token");
|
|
2088
|
+
}
|
|
2089
|
+
const auth = { authorization: `Bearer ${token}` };
|
|
2090
|
+
const { jobId } = await submitSolveJob(baseURL, payload, auth);
|
|
2004
2091
|
if (runIdRef.current !== runId) {
|
|
2005
2092
|
return;
|
|
2006
2093
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
(
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2094
|
+
setCompletedStepIndex(1);
|
|
2095
|
+
setActiveStepIndex(OPTIMIZING_STEP_INDEX);
|
|
2096
|
+
const pollForResult = async () => {
|
|
2097
|
+
if (runIdRef.current !== runId) return;
|
|
2098
|
+
try {
|
|
2099
|
+
const statusRes = await getJobStatus(baseURL, jobId, auth);
|
|
2100
|
+
if (runIdRef.current !== runId) return;
|
|
2101
|
+
if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
|
|
2102
|
+
if (pollIntervalRef.current) {
|
|
2103
|
+
clearInterval(pollIntervalRef.current);
|
|
2104
|
+
pollIntervalRef.current = null;
|
|
2105
|
+
}
|
|
2106
|
+
const response = await getJobResult(baseURL, jobId, auth);
|
|
2107
|
+
if (runIdRef.current !== runId) return;
|
|
2108
|
+
let responseForHandling = response;
|
|
2109
|
+
if (response.explanation.isFeasible) {
|
|
2110
|
+
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2111
|
+
if (runIdRef.current !== runId) return;
|
|
2112
|
+
responseForHandling = { ...response, solution };
|
|
2113
|
+
}
|
|
2114
|
+
handleSolveResult(runId, responseForHandling);
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
} catch (err) {
|
|
2118
|
+
if (runIdRef.current !== runId) return;
|
|
2119
|
+
if (pollIntervalRef.current) {
|
|
2120
|
+
clearInterval(pollIntervalRef.current);
|
|
2121
|
+
pollIntervalRef.current = null;
|
|
2122
|
+
}
|
|
2123
|
+
console.error(err);
|
|
2124
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2125
|
+
setScreen("failed");
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
pollForResult();
|
|
2129
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2042
2130
|
} catch (err) {
|
|
2043
2131
|
if (runIdRef.current !== runId) {
|
|
2044
2132
|
return;
|
|
@@ -2049,6 +2137,42 @@ function AutoSchedulerModal({
|
|
|
2049
2137
|
}
|
|
2050
2138
|
}, runId);
|
|
2051
2139
|
};
|
|
2140
|
+
const handleSolveResult = (runId, response) => {
|
|
2141
|
+
var _a, _b;
|
|
2142
|
+
if (!response.explanation.isFeasible) {
|
|
2143
|
+
const result = mapViolationsToResult(
|
|
2144
|
+
(_a = response.explanation.violations) != null ? _a : [],
|
|
2145
|
+
response.explanation.summary
|
|
2146
|
+
);
|
|
2147
|
+
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
|
|
2148
|
+
(l) => selectedLocationIds.includes(l.id)
|
|
2149
|
+
);
|
|
2150
|
+
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2151
|
+
context: {
|
|
2152
|
+
selectedLocationIds,
|
|
2153
|
+
selectedConstraintIds,
|
|
2154
|
+
constraintValues,
|
|
2155
|
+
selectedOptimizationIds,
|
|
2156
|
+
optimizationValues,
|
|
2157
|
+
timezone: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
|
|
2158
|
+
},
|
|
2159
|
+
locations: selectedLocations,
|
|
2160
|
+
employees,
|
|
2161
|
+
openShifts: openShifts.filter((s) => !s.employeeId),
|
|
2162
|
+
assignedShifts: openShifts.filter((s) => Boolean(s.employeeId)),
|
|
2163
|
+
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2164
|
+
explanation: response.explanation
|
|
2165
|
+
} : void 0;
|
|
2166
|
+
setLastFailure({
|
|
2167
|
+
...result,
|
|
2168
|
+
recommendedFixes: [],
|
|
2169
|
+
recommendationPayload
|
|
2170
|
+
});
|
|
2171
|
+
setScreen("failed");
|
|
2172
|
+
} else {
|
|
2173
|
+
afterSuccessSteps(runId, response);
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2052
2176
|
const handleAdjustConstraints = () => {
|
|
2053
2177
|
clearTimers();
|
|
2054
2178
|
setScreen("configure");
|