@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 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/solveSchedule.ts
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 solveSchedule(baseURL, payload) {
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 response = await axiosInstance.post(`${cleanBaseUrl}/api/schedule/solve`, payload);
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
- onSolution
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
- const solvePromise = solveSchedule(baseURL, payload);
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 response = await solvePromise;
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
- if (!response.explanation.isFeasible) {
2008
- const result = mapViolationsToResult(
2009
- (_a = response.explanation.violations) != null ? _a : [],
2010
- response.explanation.summary
2011
- );
2012
- const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
2013
- (l) => selectedLocationIds.includes(l.id)
2014
- );
2015
- const recommendationPayload = generateRecommendationsURLAndHeaders ? {
2016
- context: {
2017
- selectedLocationIds,
2018
- selectedConstraintIds,
2019
- constraintValues,
2020
- selectedOptimizationIds,
2021
- optimizationValues,
2022
- timezone: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
2023
- },
2024
- locations: selectedLocations,
2025
- employees,
2026
- openShifts: openShifts.filter((s) => !s.employeeId),
2027
- assignedShifts: openShifts.filter((s) => Boolean(s.employeeId)),
2028
- timeOffs: timeOffs != null ? timeOffs : [],
2029
- explanation: response.explanation
2030
- } : void 0;
2031
- setLastFailure({
2032
- ...result,
2033
- recommendedFixes: [],
2034
- recommendationPayload
2035
- });
2036
- setScreen("failed");
2037
- } else {
2038
- setCompletedStepIndex(1);
2039
- setActiveStepIndex(OPTIMIZING_STEP_INDEX);
2040
- afterSuccessSteps(runId, response);
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");