@optifye/dashboard-core 6.10.1 → 6.10.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.css +6 -8
- package/dist/index.d.mts +67 -7
- package/dist/index.d.ts +67 -7
- package/dist/index.js +1316 -872
- package/dist/index.mjs +1311 -874
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import Hls3, { Events, ErrorTypes } from 'hls.js';
|
|
|
11
11
|
import useSWR from 'swr';
|
|
12
12
|
import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
|
|
13
13
|
import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
|
|
14
|
-
import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Filter, Search, Edit2, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Shield, UserCircle } from 'lucide-react';
|
|
14
|
+
import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Filter, Search, Edit2, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, BarChart3, UserCog, Shield, UserCircle } from 'lucide-react';
|
|
15
15
|
import { toast } from 'sonner';
|
|
16
16
|
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
|
|
17
17
|
import { Slot } from '@radix-ui/react-slot';
|
|
@@ -1818,22 +1818,112 @@ var qualityService = {
|
|
|
1818
1818
|
}
|
|
1819
1819
|
};
|
|
1820
1820
|
|
|
1821
|
-
// src/lib/services/
|
|
1821
|
+
// src/lib/services/backendClient.ts
|
|
1822
|
+
var ACCESS_TOKEN_REFRESH_BUFFER_MS = 6e4;
|
|
1823
|
+
var cachedAccessToken = null;
|
|
1824
|
+
var cachedAccessTokenExpiresAtMs = null;
|
|
1825
|
+
var cachedUserId = null;
|
|
1826
|
+
var inFlightRequests = /* @__PURE__ */ new Map();
|
|
1827
|
+
var authListenerSubscription = null;
|
|
1828
|
+
var authListenerSupabase = null;
|
|
1829
|
+
var clearBackendClientCaches = () => {
|
|
1830
|
+
cachedAccessToken = null;
|
|
1831
|
+
cachedAccessTokenExpiresAtMs = null;
|
|
1832
|
+
cachedUserId = null;
|
|
1833
|
+
inFlightRequests.clear();
|
|
1834
|
+
};
|
|
1835
|
+
var initBackendClientAuthListener = (supabase) => {
|
|
1836
|
+
if (authListenerSupabase === supabase && authListenerSubscription) return;
|
|
1837
|
+
if (authListenerSubscription) {
|
|
1838
|
+
authListenerSubscription.unsubscribe();
|
|
1839
|
+
authListenerSubscription = null;
|
|
1840
|
+
}
|
|
1841
|
+
authListenerSupabase = supabase;
|
|
1842
|
+
const { data } = supabase.auth.onAuthStateChange(() => {
|
|
1843
|
+
clearBackendClientCaches();
|
|
1844
|
+
});
|
|
1845
|
+
authListenerSubscription = data.subscription;
|
|
1846
|
+
};
|
|
1822
1847
|
var getBackendUrl = () => {
|
|
1823
1848
|
const url = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
1824
1849
|
if (!url) {
|
|
1825
1850
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
1826
1851
|
}
|
|
1827
|
-
return url;
|
|
1852
|
+
return url.replace(/\/$/, "");
|
|
1828
1853
|
};
|
|
1829
|
-
var getAuthToken = async () => {
|
|
1830
|
-
const
|
|
1831
|
-
if (
|
|
1832
|
-
|
|
1854
|
+
var getAuthToken = async (supabase) => {
|
|
1855
|
+
const now2 = Date.now();
|
|
1856
|
+
if (cachedAccessToken && cachedAccessTokenExpiresAtMs && now2 < cachedAccessTokenExpiresAtMs - ACCESS_TOKEN_REFRESH_BUFFER_MS) {
|
|
1857
|
+
return cachedAccessToken;
|
|
1858
|
+
}
|
|
1859
|
+
const {
|
|
1860
|
+
data: { session }
|
|
1861
|
+
} = await supabase.auth.getSession();
|
|
1833
1862
|
if (!session?.access_token) {
|
|
1863
|
+
clearBackendClientCaches();
|
|
1834
1864
|
throw new Error("No authentication token available. Please log in.");
|
|
1835
1865
|
}
|
|
1836
|
-
|
|
1866
|
+
cachedAccessToken = session.access_token;
|
|
1867
|
+
cachedUserId = session.user?.id || null;
|
|
1868
|
+
const expiresAtSeconds = session?.expires_at;
|
|
1869
|
+
cachedAccessTokenExpiresAtMs = typeof expiresAtSeconds === "number" ? expiresAtSeconds * 1e3 : now2 + 5 * 60 * 1e3;
|
|
1870
|
+
return cachedAccessToken;
|
|
1871
|
+
};
|
|
1872
|
+
var defaultDedupeKey = (method, url, body) => {
|
|
1873
|
+
const bodyKey = body === void 0 ? "" : typeof body === "string" ? body : JSON.stringify(body);
|
|
1874
|
+
return `${cachedUserId || "anon"}::${method.toUpperCase()}::${url}::${bodyKey}`;
|
|
1875
|
+
};
|
|
1876
|
+
var fetchBackendJson = async (supabase, endpoint, options = {}) => {
|
|
1877
|
+
const baseUrl = getBackendUrl();
|
|
1878
|
+
const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
|
|
1879
|
+
const method = (options.method || "GET").toString();
|
|
1880
|
+
const bodyForKey = options.body;
|
|
1881
|
+
const dedupeKey = options.dedupeKey || defaultDedupeKey(method, url, bodyForKey);
|
|
1882
|
+
const existing = inFlightRequests.get(dedupeKey);
|
|
1883
|
+
if (existing) {
|
|
1884
|
+
return existing;
|
|
1885
|
+
}
|
|
1886
|
+
const requestPromise = (async () => {
|
|
1887
|
+
const headers = new Headers(options.headers || {});
|
|
1888
|
+
if (!options.skipAuth) {
|
|
1889
|
+
const token = await getAuthToken(supabase);
|
|
1890
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1891
|
+
}
|
|
1892
|
+
if (!headers.has("Content-Type") && options.body !== void 0) {
|
|
1893
|
+
headers.set("Content-Type", "application/json");
|
|
1894
|
+
}
|
|
1895
|
+
const response = await fetch(url, {
|
|
1896
|
+
...options,
|
|
1897
|
+
headers
|
|
1898
|
+
});
|
|
1899
|
+
if (!response.ok) {
|
|
1900
|
+
const errorText = await response.text();
|
|
1901
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
1902
|
+
}
|
|
1903
|
+
if (response.status === 204) return void 0;
|
|
1904
|
+
const text = await response.text();
|
|
1905
|
+
return text ? JSON.parse(text) : void 0;
|
|
1906
|
+
})();
|
|
1907
|
+
inFlightRequests.set(dedupeKey, requestPromise);
|
|
1908
|
+
try {
|
|
1909
|
+
return await requestPromise;
|
|
1910
|
+
} finally {
|
|
1911
|
+
inFlightRequests.delete(dedupeKey);
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
// src/lib/services/workspaceService.ts
|
|
1916
|
+
var getBackendUrl2 = () => {
|
|
1917
|
+
const url = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
1918
|
+
if (!url) {
|
|
1919
|
+
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
1920
|
+
}
|
|
1921
|
+
return url;
|
|
1922
|
+
};
|
|
1923
|
+
var getAuthToken2 = async () => {
|
|
1924
|
+
const supabase = _getSupabaseInstance();
|
|
1925
|
+
if (!supabase) throw new Error("Supabase client not initialized");
|
|
1926
|
+
return getAuthToken(supabase);
|
|
1837
1927
|
};
|
|
1838
1928
|
var workspaceService = {
|
|
1839
1929
|
// Cache for workspace display names to avoid repeated API calls
|
|
@@ -1841,26 +1931,56 @@ var workspaceService = {
|
|
|
1841
1931
|
_cacheTimestamp: 0,
|
|
1842
1932
|
_cacheExpiryMs: 5 * 60 * 1e3,
|
|
1843
1933
|
// 5 minutes cache
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1934
|
+
// Cache for workspace lists to avoid repeated API calls (line configuration changes infrequently)
|
|
1935
|
+
_workspacesCache: /* @__PURE__ */ new Map(),
|
|
1936
|
+
_workspacesInFlight: /* @__PURE__ */ new Map(),
|
|
1937
|
+
_workspacesCacheExpiryMs: 10 * 60 * 1e3,
|
|
1938
|
+
// 10 minutes cache
|
|
1939
|
+
async getWorkspaces(lineId, options) {
|
|
1940
|
+
const enabledOnly = options?.enabledOnly ?? false;
|
|
1941
|
+
const force = options?.force ?? false;
|
|
1942
|
+
const cacheKey = `${lineId}::enabledOnly=${enabledOnly}`;
|
|
1943
|
+
const now2 = Date.now();
|
|
1944
|
+
const cached = this._workspacesCache.get(cacheKey);
|
|
1945
|
+
if (!force && cached && now2 - cached.timestamp < this._workspacesCacheExpiryMs) {
|
|
1946
|
+
return cached.workspaces;
|
|
1947
|
+
}
|
|
1948
|
+
const inFlight = this._workspacesInFlight.get(cacheKey);
|
|
1949
|
+
if (!force && inFlight) {
|
|
1950
|
+
return inFlight;
|
|
1951
|
+
}
|
|
1952
|
+
const fetchPromise = (async () => {
|
|
1953
|
+
try {
|
|
1954
|
+
const token = await getAuthToken2();
|
|
1955
|
+
const apiUrl = getBackendUrl2();
|
|
1956
|
+
const params = new URLSearchParams({ line_id: lineId });
|
|
1957
|
+
if (enabledOnly) params.set("enabled_only", "true");
|
|
1958
|
+
const response = await fetch(`${apiUrl}/api/workspaces?${params.toString()}`, {
|
|
1959
|
+
headers: {
|
|
1960
|
+
"Authorization": `Bearer ${token}`,
|
|
1961
|
+
"Content-Type": "application/json"
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
if (!response.ok) {
|
|
1965
|
+
const errorText = await response.text();
|
|
1966
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
1852
1967
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1968
|
+
const data = await response.json();
|
|
1969
|
+
const workspaces = data.workspaces || [];
|
|
1970
|
+
this._workspacesCache.set(cacheKey, { workspaces, timestamp: Date.now() });
|
|
1971
|
+
return workspaces;
|
|
1972
|
+
} catch (error) {
|
|
1973
|
+
console.error("Error fetching workspaces:", error);
|
|
1974
|
+
throw error;
|
|
1975
|
+
} finally {
|
|
1976
|
+
this._workspacesInFlight.delete(cacheKey);
|
|
1857
1977
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
}
|
|
1978
|
+
})();
|
|
1979
|
+
this._workspacesInFlight.set(cacheKey, fetchPromise);
|
|
1980
|
+
return fetchPromise;
|
|
1981
|
+
},
|
|
1982
|
+
async getEnabledWorkspaces(lineId, options) {
|
|
1983
|
+
return this.getWorkspaces(lineId, { enabledOnly: true, force: options?.force });
|
|
1864
1984
|
},
|
|
1865
1985
|
/**
|
|
1866
1986
|
* Fetches workspace display names from the database
|
|
@@ -1868,8 +1988,8 @@ var workspaceService = {
|
|
|
1868
1988
|
*/
|
|
1869
1989
|
async getWorkspaceDisplayNames(companyId, lineId) {
|
|
1870
1990
|
try {
|
|
1871
|
-
const token = await
|
|
1872
|
-
const apiUrl =
|
|
1991
|
+
const token = await getAuthToken2();
|
|
1992
|
+
const apiUrl = getBackendUrl2();
|
|
1873
1993
|
const params = new URLSearchParams();
|
|
1874
1994
|
if (companyId) params.append("company_id", companyId);
|
|
1875
1995
|
if (lineId) params.append("line_id", lineId);
|
|
@@ -1928,16 +2048,39 @@ var workspaceService = {
|
|
|
1928
2048
|
this._workspaceDisplayNamesCache.clear();
|
|
1929
2049
|
this._cacheTimestamp = 0;
|
|
1930
2050
|
},
|
|
2051
|
+
clearWorkspacesCache() {
|
|
2052
|
+
this._workspacesCache.clear();
|
|
2053
|
+
this._workspacesInFlight.clear();
|
|
2054
|
+
},
|
|
2055
|
+
invalidateWorkspacesCacheForLine(lineId) {
|
|
2056
|
+
const prefix = `${lineId}::`;
|
|
2057
|
+
Array.from(this._workspacesCache.keys()).forEach((key) => {
|
|
2058
|
+
if (key.startsWith(prefix)) this._workspacesCache.delete(key);
|
|
2059
|
+
});
|
|
2060
|
+
Array.from(this._workspacesInFlight.keys()).forEach((key) => {
|
|
2061
|
+
if (key.startsWith(prefix)) this._workspacesInFlight.delete(key);
|
|
2062
|
+
});
|
|
2063
|
+
},
|
|
2064
|
+
_patchCachedWorkspacesForLine(lineId, workspaceRowId, patch) {
|
|
2065
|
+
const prefix = `${lineId}::`;
|
|
2066
|
+
Array.from(this._workspacesCache.entries()).forEach(([key, entry]) => {
|
|
2067
|
+
if (!key.startsWith(prefix)) return;
|
|
2068
|
+
const nextWorkspaces = (entry.workspaces || []).map(
|
|
2069
|
+
(ws) => ws?.id === workspaceRowId ? { ...ws, ...patch } : ws
|
|
2070
|
+
);
|
|
2071
|
+
this._workspacesCache.set(key, { workspaces: nextWorkspaces, timestamp: Date.now() });
|
|
2072
|
+
});
|
|
2073
|
+
},
|
|
1931
2074
|
/**
|
|
1932
2075
|
* Updates the display name for a workspace
|
|
1933
2076
|
* @param workspaceId - The workspace UUID
|
|
1934
2077
|
* @param displayName - The new display name
|
|
1935
|
-
* @returns
|
|
2078
|
+
* @returns Updated workspace record from backend
|
|
1936
2079
|
*/
|
|
1937
2080
|
async updateWorkspaceDisplayName(workspaceId, displayName) {
|
|
1938
2081
|
try {
|
|
1939
|
-
const token = await
|
|
1940
|
-
const apiUrl =
|
|
2082
|
+
const token = await getAuthToken2();
|
|
2083
|
+
const apiUrl = getBackendUrl2();
|
|
1941
2084
|
const response = await fetch(`${apiUrl}/api/workspaces/${workspaceId}/display-name`, {
|
|
1942
2085
|
method: "PATCH",
|
|
1943
2086
|
headers: {
|
|
@@ -1950,7 +2093,15 @@ var workspaceService = {
|
|
|
1950
2093
|
const errorText = await response.text();
|
|
1951
2094
|
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
1952
2095
|
}
|
|
2096
|
+
const data = await response.json();
|
|
1953
2097
|
this.clearWorkspaceDisplayNamesCache();
|
|
2098
|
+
const updatedWorkspace = data?.workspace || null;
|
|
2099
|
+
if (updatedWorkspace?.line_id && updatedWorkspace?.id) {
|
|
2100
|
+
this._patchCachedWorkspacesForLine(updatedWorkspace.line_id, updatedWorkspace.id, {
|
|
2101
|
+
display_name: updatedWorkspace.display_name
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
return updatedWorkspace;
|
|
1954
2105
|
} catch (error) {
|
|
1955
2106
|
console.error(`Error updating workspace display name for ${workspaceId}:`, error);
|
|
1956
2107
|
throw error;
|
|
@@ -1958,8 +2109,8 @@ var workspaceService = {
|
|
|
1958
2109
|
},
|
|
1959
2110
|
async updateWorkspaceAction(updates) {
|
|
1960
2111
|
try {
|
|
1961
|
-
const token = await
|
|
1962
|
-
const apiUrl =
|
|
2112
|
+
const token = await getAuthToken2();
|
|
2113
|
+
const apiUrl = getBackendUrl2();
|
|
1963
2114
|
const response = await fetch(`${apiUrl}/api/workspaces/actions/update`, {
|
|
1964
2115
|
method: "POST",
|
|
1965
2116
|
headers: {
|
|
@@ -1972,6 +2123,7 @@ var workspaceService = {
|
|
|
1972
2123
|
const errorText = await response.text();
|
|
1973
2124
|
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
1974
2125
|
}
|
|
2126
|
+
this.clearWorkspacesCache();
|
|
1975
2127
|
} catch (error) {
|
|
1976
2128
|
console.error("Error updating workspace actions:", error);
|
|
1977
2129
|
throw error;
|
|
@@ -1979,8 +2131,8 @@ var workspaceService = {
|
|
|
1979
2131
|
},
|
|
1980
2132
|
async updateActionThresholds(thresholds) {
|
|
1981
2133
|
try {
|
|
1982
|
-
const token = await
|
|
1983
|
-
const apiUrl =
|
|
2134
|
+
const token = await getAuthToken2();
|
|
2135
|
+
const apiUrl = getBackendUrl2();
|
|
1984
2136
|
const response = await fetch(`${apiUrl}/api/workspaces/action-thresholds/update`, {
|
|
1985
2137
|
method: "POST",
|
|
1986
2138
|
headers: {
|
|
@@ -2000,8 +2152,8 @@ var workspaceService = {
|
|
|
2000
2152
|
},
|
|
2001
2153
|
async getActionThresholds(lineId, date, shiftId = 0) {
|
|
2002
2154
|
try {
|
|
2003
|
-
const token = await
|
|
2004
|
-
const apiUrl =
|
|
2155
|
+
const token = await getAuthToken2();
|
|
2156
|
+
const apiUrl = getBackendUrl2();
|
|
2005
2157
|
const response = await fetch(
|
|
2006
2158
|
`${apiUrl}/api/workspaces/action-thresholds?line_id=${lineId}&date=${date}&shift_id=${shiftId}`,
|
|
2007
2159
|
{
|
|
@@ -2056,8 +2208,8 @@ var workspaceService = {
|
|
|
2056
2208
|
const totalPPH = outputWorkspaces.reduce((sum, ws) => sum + (ws.action_pph_threshold || 0), 0);
|
|
2057
2209
|
const operationalDate = getOperationalDate(defaultTimezone || "UTC");
|
|
2058
2210
|
try {
|
|
2059
|
-
const token = await
|
|
2060
|
-
const apiUrl =
|
|
2211
|
+
const token = await getAuthToken2();
|
|
2212
|
+
const apiUrl = getBackendUrl2();
|
|
2061
2213
|
const response = await fetch(
|
|
2062
2214
|
`${apiUrl}/api/workspaces/line-thresholds?line_id=${lineId}`,
|
|
2063
2215
|
{
|
|
@@ -2088,8 +2240,8 @@ var workspaceService = {
|
|
|
2088
2240
|
// Returns ShiftConfiguration array, which is the logical representation.
|
|
2089
2241
|
async getShiftConfigurations(lineId) {
|
|
2090
2242
|
try {
|
|
2091
|
-
const token = await
|
|
2092
|
-
const apiUrl =
|
|
2243
|
+
const token = await getAuthToken2();
|
|
2244
|
+
const apiUrl = getBackendUrl2();
|
|
2093
2245
|
const response = await fetch(
|
|
2094
2246
|
`${apiUrl}/api/workspaces/shift-configurations?line_id=${lineId}`,
|
|
2095
2247
|
{
|
|
@@ -2119,8 +2271,8 @@ var workspaceService = {
|
|
|
2119
2271
|
},
|
|
2120
2272
|
async updateShiftConfigurations(shiftConfig) {
|
|
2121
2273
|
try {
|
|
2122
|
-
const token = await
|
|
2123
|
-
const apiUrl =
|
|
2274
|
+
const token = await getAuthToken2();
|
|
2275
|
+
const apiUrl = getBackendUrl2();
|
|
2124
2276
|
const response = await fetch(
|
|
2125
2277
|
`${apiUrl}/api/workspaces/shift-configurations`,
|
|
2126
2278
|
{
|
|
@@ -2157,8 +2309,8 @@ var workspaceService = {
|
|
|
2157
2309
|
*/
|
|
2158
2310
|
async fetchBulkTargets(params) {
|
|
2159
2311
|
try {
|
|
2160
|
-
const token = await
|
|
2161
|
-
const apiUrl =
|
|
2312
|
+
const token = await getAuthToken2();
|
|
2313
|
+
const apiUrl = getBackendUrl2();
|
|
2162
2314
|
const {
|
|
2163
2315
|
companyId,
|
|
2164
2316
|
lineIds,
|
|
@@ -2344,7 +2496,17 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2344
2496
|
}
|
|
2345
2497
|
}
|
|
2346
2498
|
const dataWithUptime = processedData.map((workspace) => {
|
|
2347
|
-
const
|
|
2499
|
+
const mapKey = this.getUptimeMapKey(workspace.line_id, workspace.workspace_id);
|
|
2500
|
+
const uptimeDetails = uptimeMap.get(mapKey);
|
|
2501
|
+
console.log(`[getWorkspaceHealthStatus] Lookup:`, {
|
|
2502
|
+
mapKey,
|
|
2503
|
+
workspaceLineId: workspace.line_id,
|
|
2504
|
+
workspaceId: workspace.workspace_id,
|
|
2505
|
+
workspaceName: workspace.workspace_display_name,
|
|
2506
|
+
found: !!uptimeDetails,
|
|
2507
|
+
uptime: uptimeDetails?.percentage,
|
|
2508
|
+
downtime: uptimeDetails ? Math.max(0, uptimeDetails.expectedMinutes - uptimeDetails.actualMinutes) : null
|
|
2509
|
+
});
|
|
2348
2510
|
if (uptimeDetails) {
|
|
2349
2511
|
return {
|
|
2350
2512
|
...workspace,
|
|
@@ -2705,6 +2867,13 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2705
2867
|
clearCache() {
|
|
2706
2868
|
this.cache.clear();
|
|
2707
2869
|
}
|
|
2870
|
+
/**
|
|
2871
|
+
* Generate a composite key for the uptime map that includes line_id
|
|
2872
|
+
* This prevents data collision when multiple lines have workspaces with the same workspace_id
|
|
2873
|
+
*/
|
|
2874
|
+
getUptimeMapKey(lineId, workspaceId) {
|
|
2875
|
+
return lineId ? `${lineId}::${workspaceId}` : workspaceId;
|
|
2876
|
+
}
|
|
2708
2877
|
async calculateWorkspaceUptime(companyId, passedShiftConfig, timezone, lineShiftConfigs, overrideDate, overrideShiftId) {
|
|
2709
2878
|
const supabase = _getSupabaseInstance();
|
|
2710
2879
|
if (!supabase) throw new Error("Supabase client not initialized");
|
|
@@ -2734,7 +2903,7 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2734
2903
|
const queryShiftId = overrideShiftId ?? currentShiftId;
|
|
2735
2904
|
const tableName = `performance_metrics_${companyId.replace(/-/g, "_")}`;
|
|
2736
2905
|
try {
|
|
2737
|
-
const { data: queryData, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly, output_array").eq("date", queryDate).eq("shift_id", queryShiftId);
|
|
2906
|
+
const { data: queryData, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly, output_array, line_id").eq("date", queryDate).eq("shift_id", queryShiftId);
|
|
2738
2907
|
if (error) {
|
|
2739
2908
|
console.error("Error fetching performance metrics:", error);
|
|
2740
2909
|
return /* @__PURE__ */ new Map();
|
|
@@ -2780,7 +2949,8 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2780
2949
|
}
|
|
2781
2950
|
const completedWindow = uptimeMinutes + downtimeMinutes;
|
|
2782
2951
|
const percentage = completedWindow > 0 ? Number((uptimeMinutes / completedWindow * 100).toFixed(1)) : 100;
|
|
2783
|
-
|
|
2952
|
+
const mapKey = this.getUptimeMapKey(record.line_id, record.workspace_id);
|
|
2953
|
+
uptimeMap.set(mapKey, {
|
|
2784
2954
|
expectedMinutes: completedMinutes,
|
|
2785
2955
|
actualMinutes: uptimeMinutes,
|
|
2786
2956
|
percentage,
|
|
@@ -2822,6 +2992,17 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2822
2992
|
uniqueQueries.get(key).lineConfigs.push({ lineId, shiftStartDate, completedMinutes });
|
|
2823
2993
|
});
|
|
2824
2994
|
console.log(`[calculateWorkspaceUptimeMultiLine] Querying ${uniqueQueries.size} unique date/shift combinations for ${lineShiftConfigs.size} lines`);
|
|
2995
|
+
uniqueQueries.forEach((queryConfig, key) => {
|
|
2996
|
+
console.log(`[calculateWorkspaceUptimeMultiLine] Query batch ${key}:`, {
|
|
2997
|
+
date: queryConfig.date,
|
|
2998
|
+
shiftId: queryConfig.shiftId,
|
|
2999
|
+
lineConfigs: queryConfig.lineConfigs.map((lc) => ({
|
|
3000
|
+
lineId: lc.lineId,
|
|
3001
|
+
completedMinutes: lc.completedMinutes,
|
|
3002
|
+
shiftStartDate: lc.shiftStartDate.toISOString()
|
|
3003
|
+
}))
|
|
3004
|
+
});
|
|
3005
|
+
});
|
|
2825
3006
|
const queryPromises = Array.from(uniqueQueries.entries()).map(async ([key, { date, shiftId, lineConfigs }]) => {
|
|
2826
3007
|
try {
|
|
2827
3008
|
const { data, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly, output_array, line_id").eq("date", date).eq("shift_id", shiftId);
|
|
@@ -2838,8 +3019,14 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2838
3019
|
const results = await Promise.all(queryPromises);
|
|
2839
3020
|
for (const { records, lineConfigs } of results) {
|
|
2840
3021
|
for (const record of records) {
|
|
2841
|
-
const lineConfig = lineConfigs.find((lc) =>
|
|
2842
|
-
|
|
3022
|
+
const lineConfig = lineConfigs.find((lc) => lc.lineId === record.line_id);
|
|
3023
|
+
console.log(`[calculateWorkspaceUptimeMultiLine] Record match:`, {
|
|
3024
|
+
recordLineId: record.line_id,
|
|
3025
|
+
recordWorkspaceId: record.workspace_id,
|
|
3026
|
+
recordWorkspaceDisplayName: record.workspace_display_name,
|
|
3027
|
+
availableLineIds: lineConfigs.map((lc) => lc.lineId),
|
|
3028
|
+
matchFound: !!lineConfig,
|
|
3029
|
+
matchedLineId: lineConfig?.lineId || "FALLBACK to " + lineConfigs[0]?.lineId
|
|
2843
3030
|
});
|
|
2844
3031
|
const effectiveLineConfig = lineConfig || lineConfigs[0];
|
|
2845
3032
|
if (!effectiveLineConfig) continue;
|
|
@@ -2882,12 +3069,22 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
2882
3069
|
}
|
|
2883
3070
|
const completedWindow = uptimeMinutes + downtimeMinutes;
|
|
2884
3071
|
const percentage = completedWindow > 0 ? Number((uptimeMinutes / completedWindow * 100).toFixed(1)) : 100;
|
|
2885
|
-
|
|
3072
|
+
const mapKey = this.getUptimeMapKey(record.line_id, record.workspace_id);
|
|
3073
|
+
uptimeMap.set(mapKey, {
|
|
2886
3074
|
expectedMinutes: completedMinutes,
|
|
2887
3075
|
actualMinutes: uptimeMinutes,
|
|
2888
3076
|
percentage,
|
|
2889
3077
|
lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2890
3078
|
});
|
|
3079
|
+
console.log(`[calculateWorkspaceUptimeMultiLine] Storing uptime:`, {
|
|
3080
|
+
mapKey,
|
|
3081
|
+
lineId: record.line_id,
|
|
3082
|
+
workspaceId: record.workspace_id,
|
|
3083
|
+
workspaceDisplayName: record.workspace_display_name,
|
|
3084
|
+
expectedMinutes: completedMinutes,
|
|
3085
|
+
actualMinutes: uptimeMinutes,
|
|
3086
|
+
percentage
|
|
3087
|
+
});
|
|
2891
3088
|
}
|
|
2892
3089
|
}
|
|
2893
3090
|
console.log(`[calculateWorkspaceUptimeMultiLine] Calculated uptime for ${uptimeMap.size} workspaces`);
|
|
@@ -4535,7 +4732,7 @@ var getSupabaseClient = () => {
|
|
|
4535
4732
|
}
|
|
4536
4733
|
return createClient(url, key);
|
|
4537
4734
|
};
|
|
4538
|
-
var
|
|
4735
|
+
var getAuthToken3 = async () => {
|
|
4539
4736
|
try {
|
|
4540
4737
|
const supabase = getSupabaseClient();
|
|
4541
4738
|
const { data: { session } } = await supabase.auth.getSession();
|
|
@@ -4569,7 +4766,7 @@ var S3ClipsSupabaseService = class {
|
|
|
4569
4766
|
* Fetch with authentication and error handling
|
|
4570
4767
|
*/
|
|
4571
4768
|
async fetchWithAuth(endpoint, body) {
|
|
4572
|
-
const token = await
|
|
4769
|
+
const token = await getAuthToken3();
|
|
4573
4770
|
if (!token) {
|
|
4574
4771
|
throw new Error("Authentication required");
|
|
4575
4772
|
}
|
|
@@ -6279,7 +6476,7 @@ var IDLE_TIME_REASON_COLORS = {
|
|
|
6279
6476
|
bg: "bg-amber-50",
|
|
6280
6477
|
border: "border-amber-200"
|
|
6281
6478
|
},
|
|
6282
|
-
"Machine
|
|
6479
|
+
"Machine Downtime": {
|
|
6283
6480
|
hex: "#3b82f6",
|
|
6284
6481
|
// blue-500 - Scheduled/Technical
|
|
6285
6482
|
text: "text-blue-600",
|
|
@@ -6715,6 +6912,7 @@ var SupabaseProvider = ({ client, children }) => {
|
|
|
6715
6912
|
_setSupabaseInstance(client);
|
|
6716
6913
|
useEffect(() => {
|
|
6717
6914
|
_setSupabaseInstance(client);
|
|
6915
|
+
initBackendClientAuthListener(client);
|
|
6718
6916
|
}, [client]);
|
|
6719
6917
|
const contextValue = useMemo(() => ({ supabase: client }), [client]);
|
|
6720
6918
|
return /* @__PURE__ */ jsx(SupabaseContext.Provider, { value: contextValue, children });
|
|
@@ -7629,33 +7827,10 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
7629
7827
|
console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
|
|
7630
7828
|
console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
|
|
7631
7829
|
console.log(`[useWorkspaceDetailedMetrics] Fetching from backend API for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
|
|
7632
|
-
const
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
}
|
|
7636
|
-
const apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
7637
|
-
const response = await fetch(
|
|
7638
|
-
`${apiUrl}/api/dashboard/workspace/${workspaceId}/metrics?date=${queryDate}&shift_id=${queryShiftId}&company_id=${companyId}`,
|
|
7639
|
-
{
|
|
7640
|
-
headers: {
|
|
7641
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
7642
|
-
"Content-Type": "application/json"
|
|
7643
|
-
}
|
|
7644
|
-
}
|
|
7830
|
+
const backendData = await fetchBackendJson(
|
|
7831
|
+
supabase,
|
|
7832
|
+
`/api/dashboard/workspace/${workspaceId}/metrics?date=${queryDate}&shift_id=${queryShiftId}&company_id=${companyId}`
|
|
7645
7833
|
);
|
|
7646
|
-
if (!response.ok) {
|
|
7647
|
-
const errorText = await response.text();
|
|
7648
|
-
console.error("[useWorkspaceDetailedMetrics] Backend API error response:", errorText);
|
|
7649
|
-
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
7650
|
-
}
|
|
7651
|
-
const responseText = await response.text();
|
|
7652
|
-
let backendData;
|
|
7653
|
-
try {
|
|
7654
|
-
backendData = JSON.parse(responseText);
|
|
7655
|
-
} catch (parseError) {
|
|
7656
|
-
console.error("[useWorkspaceDetailedMetrics] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
|
|
7657
|
-
throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
|
|
7658
|
-
}
|
|
7659
7834
|
const data = backendData.metrics;
|
|
7660
7835
|
if (data && options?.shiftConfig) {
|
|
7661
7836
|
const dynamicShiftName = getShiftNameById(
|
|
@@ -7669,20 +7844,10 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
7669
7844
|
}
|
|
7670
7845
|
if (!data && !date && shiftId === void 0) {
|
|
7671
7846
|
console.log("[useWorkspaceDetailedMetrics] No data found for current date/shift, attempting to find most recent data...");
|
|
7672
|
-
const
|
|
7673
|
-
|
|
7674
|
-
{
|
|
7675
|
-
headers: {
|
|
7676
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
7677
|
-
"Content-Type": "application/json"
|
|
7678
|
-
}
|
|
7679
|
-
}
|
|
7847
|
+
const fallbackData = await fetchBackendJson(
|
|
7848
|
+
supabase,
|
|
7849
|
+
`/api/dashboard/workspace/${workspaceId}/metrics?company_id=${companyId}&latest=true`
|
|
7680
7850
|
);
|
|
7681
|
-
if (!fallbackResponse.ok) {
|
|
7682
|
-
const errorText = await fallbackResponse.text();
|
|
7683
|
-
throw new Error(`Backend API error (${fallbackResponse.status}): ${errorText}`);
|
|
7684
|
-
}
|
|
7685
|
-
const fallbackData = await fallbackResponse.json();
|
|
7686
7851
|
const recentData = fallbackData.metrics;
|
|
7687
7852
|
if (recentData) {
|
|
7688
7853
|
console.log(`[useWorkspaceDetailedMetrics] Found fallback data from date: ${recentData.date}, shift: ${recentData.shift_id}`);
|
|
@@ -8244,15 +8409,171 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
8244
8409
|
// Force refresh without cache
|
|
8245
8410
|
};
|
|
8246
8411
|
};
|
|
8412
|
+
|
|
8413
|
+
// src/lib/stores/shiftConfigStore.ts
|
|
8414
|
+
var shiftConfigsByLineId = /* @__PURE__ */ new Map();
|
|
8415
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
8416
|
+
var inFlightFetchesByLineId = /* @__PURE__ */ new Map();
|
|
8417
|
+
var subscriptionsByLineId = /* @__PURE__ */ new Map();
|
|
8418
|
+
var calculateBreakDuration = (startTime, endTime) => {
|
|
8419
|
+
const [sh, sm] = startTime.split(":").map(Number);
|
|
8420
|
+
const [eh, em] = endTime.split(":").map(Number);
|
|
8421
|
+
let startMinutes = sh * 60 + sm;
|
|
8422
|
+
let endMinutes = eh * 60 + em;
|
|
8423
|
+
if (endMinutes < startMinutes) {
|
|
8424
|
+
endMinutes += 24 * 60;
|
|
8425
|
+
}
|
|
8426
|
+
return endMinutes - startMinutes;
|
|
8427
|
+
};
|
|
8428
|
+
var stripSeconds = (timeStr) => {
|
|
8429
|
+
if (!timeStr) return timeStr;
|
|
8430
|
+
return timeStr.substring(0, 5);
|
|
8431
|
+
};
|
|
8432
|
+
var buildShiftConfigFromOperatingHoursRows = (rows, fallback) => {
|
|
8433
|
+
const mapped = (rows || []).map((row) => ({
|
|
8434
|
+
shiftId: row.shift_id,
|
|
8435
|
+
shiftName: row.shift_name || `Shift ${row.shift_id}`,
|
|
8436
|
+
startTime: stripSeconds(row.start_time),
|
|
8437
|
+
endTime: stripSeconds(row.end_time),
|
|
8438
|
+
breaks: (() => {
|
|
8439
|
+
const raw = Array.isArray(row.breaks) ? row.breaks : Array.isArray(row.breaks?.breaks) ? row.breaks.breaks : [];
|
|
8440
|
+
return raw.map((b) => ({
|
|
8441
|
+
startTime: stripSeconds(b.start || b.startTime || "00:00"),
|
|
8442
|
+
endTime: stripSeconds(b.end || b.endTime || "00:00"),
|
|
8443
|
+
duration: calculateBreakDuration(
|
|
8444
|
+
stripSeconds(b.start || b.startTime || "00:00"),
|
|
8445
|
+
stripSeconds(b.end || b.endTime || "00:00")
|
|
8446
|
+
),
|
|
8447
|
+
remarks: b.remarks || b.name || ""
|
|
8448
|
+
}));
|
|
8449
|
+
})(),
|
|
8450
|
+
timezone: row.timezone || void 0
|
|
8451
|
+
}));
|
|
8452
|
+
const day = mapped.find((s) => s.shiftId === 0);
|
|
8453
|
+
const night = mapped.find((s) => s.shiftId === 1);
|
|
8454
|
+
return {
|
|
8455
|
+
shifts: mapped,
|
|
8456
|
+
timezone: mapped[0]?.timezone || fallback?.timezone,
|
|
8457
|
+
dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
|
|
8458
|
+
nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
|
|
8459
|
+
transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
|
|
8460
|
+
};
|
|
8461
|
+
};
|
|
8462
|
+
var shiftConfigStore = {
|
|
8463
|
+
get(lineId) {
|
|
8464
|
+
return shiftConfigsByLineId.get(lineId);
|
|
8465
|
+
},
|
|
8466
|
+
set(lineId, config) {
|
|
8467
|
+
shiftConfigsByLineId.set(lineId, config);
|
|
8468
|
+
listeners.forEach((listener) => listener(lineId));
|
|
8469
|
+
},
|
|
8470
|
+
setFromOperatingHoursRows(lineId, rows, fallback) {
|
|
8471
|
+
const config = buildShiftConfigFromOperatingHoursRows(rows, fallback);
|
|
8472
|
+
this.set(lineId, config);
|
|
8473
|
+
return config;
|
|
8474
|
+
},
|
|
8475
|
+
getMany(lineIds) {
|
|
8476
|
+
const map = /* @__PURE__ */ new Map();
|
|
8477
|
+
lineIds.forEach((lineId) => {
|
|
8478
|
+
const config = shiftConfigsByLineId.get(lineId);
|
|
8479
|
+
if (config) map.set(lineId, config);
|
|
8480
|
+
});
|
|
8481
|
+
return map;
|
|
8482
|
+
},
|
|
8483
|
+
subscribe(listener) {
|
|
8484
|
+
listeners.add(listener);
|
|
8485
|
+
return () => listeners.delete(listener);
|
|
8486
|
+
},
|
|
8487
|
+
clear() {
|
|
8488
|
+
shiftConfigsByLineId.clear();
|
|
8489
|
+
listeners.forEach((listener) => listener("*"));
|
|
8490
|
+
}
|
|
8491
|
+
};
|
|
8492
|
+
var fetchAndStoreShiftConfig = async (supabase, lineId, fallback) => {
|
|
8493
|
+
const existing = inFlightFetchesByLineId.get(lineId);
|
|
8494
|
+
if (existing) return existing;
|
|
8495
|
+
const promise = (async () => {
|
|
8496
|
+
try {
|
|
8497
|
+
const { data, error } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").eq("line_id", lineId);
|
|
8498
|
+
if (error) {
|
|
8499
|
+
throw new Error(`Failed to fetch shift config: ${error.message}`);
|
|
8500
|
+
}
|
|
8501
|
+
if (!data || data.length === 0) {
|
|
8502
|
+
if (fallback) {
|
|
8503
|
+
shiftConfigStore.set(lineId, fallback);
|
|
8504
|
+
return fallback;
|
|
8505
|
+
}
|
|
8506
|
+
return null;
|
|
8507
|
+
}
|
|
8508
|
+
return shiftConfigStore.setFromOperatingHoursRows(lineId, data, fallback);
|
|
8509
|
+
} finally {
|
|
8510
|
+
inFlightFetchesByLineId.delete(lineId);
|
|
8511
|
+
}
|
|
8512
|
+
})();
|
|
8513
|
+
inFlightFetchesByLineId.set(lineId, promise);
|
|
8514
|
+
return promise;
|
|
8515
|
+
};
|
|
8516
|
+
var ensureShiftConfigSubscription = (supabase, lineId, fallback) => {
|
|
8517
|
+
const existing = subscriptionsByLineId.get(lineId);
|
|
8518
|
+
if (existing && existing.supabase === supabase) {
|
|
8519
|
+
existing.refCount += 1;
|
|
8520
|
+
return () => {
|
|
8521
|
+
const current = subscriptionsByLineId.get(lineId);
|
|
8522
|
+
if (!current) return;
|
|
8523
|
+
current.refCount -= 1;
|
|
8524
|
+
if (current.refCount <= 0) {
|
|
8525
|
+
current.channel.unsubscribe();
|
|
8526
|
+
subscriptionsByLineId.delete(lineId);
|
|
8527
|
+
}
|
|
8528
|
+
};
|
|
8529
|
+
}
|
|
8530
|
+
if (existing) {
|
|
8531
|
+
existing.channel.unsubscribe();
|
|
8532
|
+
subscriptionsByLineId.delete(lineId);
|
|
8533
|
+
}
|
|
8534
|
+
const channel = supabase.channel(`shift_config_${lineId}`).on(
|
|
8535
|
+
"postgres_changes",
|
|
8536
|
+
{
|
|
8537
|
+
event: "*",
|
|
8538
|
+
schema: "public",
|
|
8539
|
+
table: "line_operating_hours",
|
|
8540
|
+
filter: `line_id=eq.${lineId}`
|
|
8541
|
+
},
|
|
8542
|
+
() => {
|
|
8543
|
+
fetchAndStoreShiftConfig(supabase, lineId, fallback).catch((err) => {
|
|
8544
|
+
console.error("[shiftConfigStore] Failed to refresh shift config", { lineId, err });
|
|
8545
|
+
});
|
|
8546
|
+
}
|
|
8547
|
+
).subscribe();
|
|
8548
|
+
subscriptionsByLineId.set(lineId, { supabase, channel, refCount: 1 });
|
|
8549
|
+
return () => {
|
|
8550
|
+
const current = subscriptionsByLineId.get(lineId);
|
|
8551
|
+
if (!current) return;
|
|
8552
|
+
current.refCount -= 1;
|
|
8553
|
+
if (current.refCount <= 0) {
|
|
8554
|
+
current.channel.unsubscribe();
|
|
8555
|
+
subscriptionsByLineId.delete(lineId);
|
|
8556
|
+
}
|
|
8557
|
+
};
|
|
8558
|
+
};
|
|
8559
|
+
|
|
8560
|
+
// src/lib/hooks/useLineShiftConfig.ts
|
|
8247
8561
|
var useLineShiftConfig = (lineId, fallbackConfig) => {
|
|
8248
|
-
const [shiftConfig, setShiftConfig] = useState(
|
|
8249
|
-
|
|
8562
|
+
const [shiftConfig, setShiftConfig] = useState(() => {
|
|
8563
|
+
if (!lineId || lineId === "factory" || lineId === "all") return fallbackConfig || null;
|
|
8564
|
+
return shiftConfigStore.get(lineId) || fallbackConfig || null;
|
|
8565
|
+
});
|
|
8566
|
+
const [isLoading, setIsLoading] = useState(() => {
|
|
8567
|
+
if (!lineId || lineId === "factory" || lineId === "all") return false;
|
|
8568
|
+
return !shiftConfigStore.get(lineId);
|
|
8569
|
+
});
|
|
8250
8570
|
const [error, setError] = useState(null);
|
|
8251
8571
|
const supabase = useSupabase();
|
|
8252
8572
|
useEffect(() => {
|
|
8253
8573
|
if (!lineId || lineId === "factory" || lineId === "all") {
|
|
8254
8574
|
setShiftConfig(fallbackConfig || null);
|
|
8255
8575
|
setIsLoading(false);
|
|
8576
|
+
setError(null);
|
|
8256
8577
|
return;
|
|
8257
8578
|
}
|
|
8258
8579
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -8260,98 +8581,49 @@ var useLineShiftConfig = (lineId, fallbackConfig) => {
|
|
|
8260
8581
|
console.warn(`[useShiftConfig] Invalid line ID format: ${lineId}, using fallback`);
|
|
8261
8582
|
setShiftConfig(fallbackConfig || null);
|
|
8262
8583
|
setIsLoading(false);
|
|
8584
|
+
setError(null);
|
|
8263
8585
|
return;
|
|
8264
8586
|
}
|
|
8265
8587
|
let mounted = true;
|
|
8266
|
-
const
|
|
8267
|
-
const
|
|
8268
|
-
|
|
8269
|
-
let startMinutes = sh * 60 + sm;
|
|
8270
|
-
let endMinutes = eh * 60 + em;
|
|
8271
|
-
if (endMinutes < startMinutes) {
|
|
8272
|
-
endMinutes += 24 * 60;
|
|
8273
|
-
}
|
|
8274
|
-
return endMinutes - startMinutes;
|
|
8588
|
+
const syncFromStore = () => {
|
|
8589
|
+
const stored = shiftConfigStore.get(lineId);
|
|
8590
|
+
setShiftConfig(stored || fallbackConfig || null);
|
|
8275
8591
|
};
|
|
8276
|
-
|
|
8592
|
+
syncFromStore();
|
|
8593
|
+
const unsubscribeStore = shiftConfigStore.subscribe((changedLineId) => {
|
|
8594
|
+
if (!mounted) return;
|
|
8595
|
+
if (changedLineId === "*" || changedLineId === lineId) {
|
|
8596
|
+
syncFromStore();
|
|
8597
|
+
}
|
|
8598
|
+
});
|
|
8599
|
+
const unsubscribeRealtime = ensureShiftConfigSubscription(supabase, lineId, fallbackConfig);
|
|
8600
|
+
const ensureLoaded = async () => {
|
|
8277
8601
|
try {
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
const { data: shiftsData, error: shiftsError } = await supabase.from("line_operating_hours").select("shift_id, shift_name, start_time, end_time, breaks, timezone").eq("line_id", lineId);
|
|
8282
|
-
if (shiftsError) {
|
|
8283
|
-
console.error(`[useLineShiftConfig] \u274C Error fetching shifts:`, shiftsError);
|
|
8284
|
-
throw new Error(`Failed to fetch shift config: ${shiftsError.message}`);
|
|
8285
|
-
}
|
|
8286
|
-
if (!shiftsData || shiftsData.length === 0) {
|
|
8287
|
-
console.warn(`[useLineShiftConfig] \u26A0\uFE0F No shift config found for line ${lineId}, using fallback`);
|
|
8288
|
-
if (mounted) {
|
|
8289
|
-
setShiftConfig(fallbackConfig || null);
|
|
8290
|
-
setIsLoading(false);
|
|
8291
|
-
}
|
|
8602
|
+
const existing = shiftConfigStore.get(lineId);
|
|
8603
|
+
if (existing) {
|
|
8604
|
+
if (mounted) setIsLoading(false);
|
|
8292
8605
|
return;
|
|
8293
8606
|
}
|
|
8294
|
-
const stripSeconds = (timeStr) => {
|
|
8295
|
-
if (!timeStr) return timeStr;
|
|
8296
|
-
return timeStr.substring(0, 5);
|
|
8297
|
-
};
|
|
8298
|
-
const mapped = shiftsData.map((shift) => ({
|
|
8299
|
-
shiftId: shift.shift_id,
|
|
8300
|
-
shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
|
|
8301
|
-
startTime: stripSeconds(shift.start_time),
|
|
8302
|
-
endTime: stripSeconds(shift.end_time),
|
|
8303
|
-
breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
|
|
8304
|
-
startTime: b.start || b.startTime || "00:00",
|
|
8305
|
-
endTime: b.end || b.endTime || "00:00",
|
|
8306
|
-
duration: calculateBreakDuration2(
|
|
8307
|
-
b.start || b.startTime || "00:00",
|
|
8308
|
-
b.end || b.endTime || "00:00"
|
|
8309
|
-
),
|
|
8310
|
-
remarks: b.remarks || b.name || ""
|
|
8311
|
-
})) : [],
|
|
8312
|
-
timezone: shift.timezone
|
|
8313
|
-
}));
|
|
8314
|
-
const day = mapped.find((s) => s.shiftId === 0);
|
|
8315
|
-
const night = mapped.find((s) => s.shiftId === 1);
|
|
8316
|
-
const config = {
|
|
8317
|
-
shifts: mapped,
|
|
8318
|
-
timezone: mapped[0]?.timezone,
|
|
8319
|
-
dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallbackConfig?.dayShift,
|
|
8320
|
-
nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallbackConfig?.nightShift,
|
|
8321
|
-
transitionPeriodMinutes: fallbackConfig?.transitionPeriodMinutes || 0
|
|
8322
|
-
};
|
|
8323
|
-
console.log(`[useLineShiftConfig] \u2705 Built config from DB:`, config);
|
|
8324
8607
|
if (mounted) {
|
|
8325
|
-
|
|
8326
|
-
|
|
8608
|
+
setIsLoading(true);
|
|
8609
|
+
setError(null);
|
|
8327
8610
|
}
|
|
8611
|
+
await fetchAndStoreShiftConfig(supabase, lineId, fallbackConfig);
|
|
8328
8612
|
} catch (err) {
|
|
8329
8613
|
console.error("[useShiftConfig] Error fetching shift config:", err);
|
|
8330
8614
|
if (mounted) {
|
|
8331
8615
|
setError(err instanceof Error ? err.message : "Unknown error occurred");
|
|
8332
|
-
setShiftConfig(fallbackConfig || null);
|
|
8333
|
-
setIsLoading(false);
|
|
8616
|
+
setShiftConfig(shiftConfigStore.get(lineId) || fallbackConfig || null);
|
|
8334
8617
|
}
|
|
8618
|
+
} finally {
|
|
8619
|
+
if (mounted) setIsLoading(false);
|
|
8335
8620
|
}
|
|
8336
8621
|
};
|
|
8337
|
-
|
|
8338
|
-
const subscription = supabase.channel(`shift_config_${lineId}`).on(
|
|
8339
|
-
"postgres_changes",
|
|
8340
|
-
{
|
|
8341
|
-
event: "*",
|
|
8342
|
-
// Listen to all events (INSERT, UPDATE, DELETE)
|
|
8343
|
-
schema: "public",
|
|
8344
|
-
table: "line_operating_hours",
|
|
8345
|
-
filter: `line_id=eq.${lineId}`
|
|
8346
|
-
},
|
|
8347
|
-
(payload) => {
|
|
8348
|
-
console.log("[useShiftConfig] Real-time update received:", payload);
|
|
8349
|
-
fetchShiftConfig();
|
|
8350
|
-
}
|
|
8351
|
-
).subscribe();
|
|
8622
|
+
ensureLoaded();
|
|
8352
8623
|
return () => {
|
|
8353
8624
|
mounted = false;
|
|
8354
|
-
|
|
8625
|
+
unsubscribeStore();
|
|
8626
|
+
unsubscribeRealtime();
|
|
8355
8627
|
};
|
|
8356
8628
|
}, [lineId, supabase]);
|
|
8357
8629
|
return {
|
|
@@ -8442,8 +8714,8 @@ var useLineWorkspaceMetrics = (lineId, options) => {
|
|
|
8442
8714
|
queryShiftId,
|
|
8443
8715
|
metricsTable
|
|
8444
8716
|
});
|
|
8445
|
-
const
|
|
8446
|
-
const enabledWorkspaceIds =
|
|
8717
|
+
const enabledWorkspaces = await workspaceService.getEnabledWorkspaces(lineId);
|
|
8718
|
+
const enabledWorkspaceIds = enabledWorkspaces.map((ws) => ws.id);
|
|
8447
8719
|
if (enabledWorkspaceIds.length === 0) {
|
|
8448
8720
|
setWorkspaces([]);
|
|
8449
8721
|
setInitialized(true);
|
|
@@ -8541,14 +8813,6 @@ var useHistoricWorkspaceMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
8541
8813
|
isFetchingRef.current = true;
|
|
8542
8814
|
setIsLoading(true);
|
|
8543
8815
|
setError(null);
|
|
8544
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
8545
|
-
if (!session?.access_token) {
|
|
8546
|
-
throw new Error("No authentication token available");
|
|
8547
|
-
}
|
|
8548
|
-
const apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
8549
|
-
if (!apiUrl) {
|
|
8550
|
-
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
8551
|
-
}
|
|
8552
8816
|
const params = new URLSearchParams({
|
|
8553
8817
|
company_id: entityConfig.companyId || "",
|
|
8554
8818
|
date
|
|
@@ -8556,20 +8820,10 @@ var useHistoricWorkspaceMetrics = (workspaceId, date, shiftId, options) => {
|
|
|
8556
8820
|
if (shiftId !== void 0) {
|
|
8557
8821
|
params.append("shift_id", shiftId.toString());
|
|
8558
8822
|
}
|
|
8559
|
-
const
|
|
8560
|
-
|
|
8561
|
-
{
|
|
8562
|
-
headers: {
|
|
8563
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
8564
|
-
"Content-Type": "application/json"
|
|
8565
|
-
}
|
|
8566
|
-
}
|
|
8823
|
+
const data = await fetchBackendJson(
|
|
8824
|
+
supabase,
|
|
8825
|
+
`/api/dashboard/workspace/${workspaceId}/metrics?${params.toString()}`
|
|
8567
8826
|
);
|
|
8568
|
-
if (!response.ok) {
|
|
8569
|
-
const errorText = await response.text();
|
|
8570
|
-
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
8571
|
-
}
|
|
8572
|
-
const data = await response.json();
|
|
8573
8827
|
const fetchedMetrics = data.metrics;
|
|
8574
8828
|
if (!fetchedMetrics) {
|
|
8575
8829
|
setMetrics(null);
|
|
@@ -8789,14 +9043,6 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
|
|
|
8789
9043
|
const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
|
|
8790
9044
|
const queryDate = date || currentShift.date;
|
|
8791
9045
|
const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
|
|
8792
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
8793
|
-
if (!session?.access_token) {
|
|
8794
|
-
throw new Error("No authentication token available");
|
|
8795
|
-
}
|
|
8796
|
-
const apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
8797
|
-
if (!apiUrl) {
|
|
8798
|
-
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
8799
|
-
}
|
|
8800
9046
|
const params = new URLSearchParams({
|
|
8801
9047
|
date: queryDate,
|
|
8802
9048
|
shift_id: queryShiftId.toString(),
|
|
@@ -8804,20 +9050,10 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
|
|
|
8804
9050
|
limit: limit.toString(),
|
|
8805
9051
|
filter: filter2
|
|
8806
9052
|
});
|
|
8807
|
-
const
|
|
8808
|
-
|
|
8809
|
-
{
|
|
8810
|
-
headers: {
|
|
8811
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
8812
|
-
"Content-Type": "application/json"
|
|
8813
|
-
}
|
|
8814
|
-
}
|
|
9053
|
+
const data = await fetchBackendJson(
|
|
9054
|
+
supabase,
|
|
9055
|
+
`/api/dashboard/leaderboard?${params.toString()}`
|
|
8815
9056
|
);
|
|
8816
|
-
if (!response.ok) {
|
|
8817
|
-
const errorText = await response.text();
|
|
8818
|
-
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
8819
|
-
}
|
|
8820
|
-
const data = await response.json();
|
|
8821
9057
|
setLeaderboard(data.leaderboard || []);
|
|
8822
9058
|
} catch (err) {
|
|
8823
9059
|
console.error("[useLeaderboardMetrics] Error fetching leaderboard:", err);
|
|
@@ -8843,11 +9079,12 @@ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
|
|
|
8843
9079
|
const [isLoading, setIsLoading] = useState(true);
|
|
8844
9080
|
const [error, setError] = useState(null);
|
|
8845
9081
|
const supabase = useSupabase();
|
|
8846
|
-
const lineIdsKey = useMemo(() => lineIds.sort().join(","), [lineIds]);
|
|
9082
|
+
const lineIdsKey = useMemo(() => lineIds.slice().sort().join(","), [lineIds]);
|
|
8847
9083
|
useEffect(() => {
|
|
8848
9084
|
if (!lineIds || lineIds.length === 0) {
|
|
8849
9085
|
setShiftConfigMap(/* @__PURE__ */ new Map());
|
|
8850
9086
|
setIsLoading(false);
|
|
9087
|
+
setError(null);
|
|
8851
9088
|
return;
|
|
8852
9089
|
}
|
|
8853
9090
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -8856,56 +9093,56 @@ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
|
|
|
8856
9093
|
console.warn("[useMultiLineShiftConfigs] No valid line IDs provided");
|
|
8857
9094
|
setShiftConfigMap(/* @__PURE__ */ new Map());
|
|
8858
9095
|
setIsLoading(false);
|
|
9096
|
+
setError(null);
|
|
8859
9097
|
return;
|
|
8860
9098
|
}
|
|
8861
9099
|
let mounted = true;
|
|
8862
|
-
const
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
const buildShiftConfigFromRows = (shifts, fallback) => {
|
|
8877
|
-
const mapped = shifts.map((shift) => ({
|
|
8878
|
-
shiftId: shift.shift_id,
|
|
8879
|
-
shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
|
|
8880
|
-
startTime: stripSeconds(shift.start_time),
|
|
8881
|
-
endTime: stripSeconds(shift.end_time),
|
|
8882
|
-
breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
|
|
8883
|
-
startTime: b.start || b.startTime || "00:00",
|
|
8884
|
-
endTime: b.end || b.endTime || "00:00",
|
|
8885
|
-
duration: calculateBreakDuration2(
|
|
8886
|
-
b.start || b.startTime || "00:00",
|
|
8887
|
-
b.end || b.endTime || "00:00"
|
|
8888
|
-
),
|
|
8889
|
-
remarks: b.remarks || b.name || ""
|
|
8890
|
-
})) : [],
|
|
8891
|
-
timezone: shift.timezone
|
|
8892
|
-
}));
|
|
8893
|
-
const day = mapped.find((s) => s.shiftId === 0);
|
|
8894
|
-
const night = mapped.find((s) => s.shiftId === 1);
|
|
8895
|
-
return {
|
|
8896
|
-
shifts: mapped,
|
|
8897
|
-
timezone: mapped[0]?.timezone,
|
|
8898
|
-
dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
|
|
8899
|
-
nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
|
|
8900
|
-
transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
|
|
8901
|
-
};
|
|
9100
|
+
const syncFromStore = (changedLineId) => {
|
|
9101
|
+
setShiftConfigMap((prev) => {
|
|
9102
|
+
const next = new Map(prev);
|
|
9103
|
+
const updateLine = (lineId) => {
|
|
9104
|
+
const config = shiftConfigStore.get(lineId) || fallbackConfig;
|
|
9105
|
+
if (config) next.set(lineId, config);
|
|
9106
|
+
};
|
|
9107
|
+
if (!changedLineId || changedLineId === "*") {
|
|
9108
|
+
validLineIds.forEach(updateLine);
|
|
9109
|
+
} else if (validLineIds.includes(changedLineId)) {
|
|
9110
|
+
updateLine(changedLineId);
|
|
9111
|
+
}
|
|
9112
|
+
return next;
|
|
9113
|
+
});
|
|
8902
9114
|
};
|
|
9115
|
+
const initialMap = /* @__PURE__ */ new Map();
|
|
9116
|
+
validLineIds.forEach((lineId) => {
|
|
9117
|
+
const config = shiftConfigStore.get(lineId) || fallbackConfig;
|
|
9118
|
+
if (config) initialMap.set(lineId, config);
|
|
9119
|
+
});
|
|
9120
|
+
setShiftConfigMap(initialMap);
|
|
9121
|
+
const unsubscribeStore = shiftConfigStore.subscribe((changedLineId) => {
|
|
9122
|
+
if (!mounted) return;
|
|
9123
|
+
if (changedLineId === "*" || validLineIds.includes(changedLineId)) {
|
|
9124
|
+
syncFromStore(changedLineId);
|
|
9125
|
+
}
|
|
9126
|
+
});
|
|
9127
|
+
const unsubscribeRealtimeList = validLineIds.map(
|
|
9128
|
+
(lineId) => ensureShiftConfigSubscription(supabase, lineId, fallbackConfig)
|
|
9129
|
+
);
|
|
8903
9130
|
const fetchAllConfigs = async () => {
|
|
8904
9131
|
try {
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
9132
|
+
const missingLineIds = validLineIds.filter((lineId) => !shiftConfigStore.get(lineId));
|
|
9133
|
+
if (missingLineIds.length === 0) {
|
|
9134
|
+
if (mounted) {
|
|
9135
|
+
setIsLoading(false);
|
|
9136
|
+
setError(null);
|
|
9137
|
+
}
|
|
9138
|
+
return;
|
|
9139
|
+
}
|
|
9140
|
+
if (mounted) {
|
|
9141
|
+
setIsLoading(true);
|
|
9142
|
+
setError(null);
|
|
9143
|
+
}
|
|
9144
|
+
console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${missingLineIds.length} lines`);
|
|
9145
|
+
const { data, error: fetchError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").in("line_id", missingLineIds);
|
|
8909
9146
|
if (fetchError) {
|
|
8910
9147
|
console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
|
|
8911
9148
|
throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
|
|
@@ -8917,33 +9154,23 @@ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
|
|
|
8917
9154
|
}
|
|
8918
9155
|
lineShiftsMap.get(row.line_id).push(row);
|
|
8919
9156
|
});
|
|
8920
|
-
const configMap = /* @__PURE__ */ new Map();
|
|
8921
9157
|
lineShiftsMap.forEach((shifts, lineId) => {
|
|
8922
|
-
|
|
8923
|
-
configMap.set(lineId, config);
|
|
9158
|
+
shiftConfigStore.setFromOperatingHoursRows(lineId, shifts, fallbackConfig);
|
|
8924
9159
|
});
|
|
8925
|
-
|
|
8926
|
-
if (!
|
|
9160
|
+
missingLineIds.forEach((lineId) => {
|
|
9161
|
+
if (!lineShiftsMap.has(lineId) && fallbackConfig) {
|
|
8927
9162
|
console.warn(`[useMultiLineShiftConfigs] No config found for line ${lineId}, using fallback`);
|
|
8928
|
-
|
|
9163
|
+
shiftConfigStore.set(lineId, fallbackConfig);
|
|
8929
9164
|
}
|
|
8930
9165
|
});
|
|
8931
|
-
console.log(`[useMultiLineShiftConfigs]
|
|
9166
|
+
console.log(`[useMultiLineShiftConfigs] Stored configs for ${lineShiftsMap.size} lines`);
|
|
8932
9167
|
if (mounted) {
|
|
8933
|
-
setShiftConfigMap(configMap);
|
|
8934
9168
|
setIsLoading(false);
|
|
8935
9169
|
}
|
|
8936
9170
|
} catch (err) {
|
|
8937
9171
|
console.error("[useMultiLineShiftConfigs] Error:", err);
|
|
8938
9172
|
if (mounted) {
|
|
8939
9173
|
setError(err instanceof Error ? err.message : "Failed to fetch shift configs");
|
|
8940
|
-
if (fallbackConfig) {
|
|
8941
|
-
const fallbackMap = /* @__PURE__ */ new Map();
|
|
8942
|
-
validLineIds.forEach((lineId) => {
|
|
8943
|
-
fallbackMap.set(lineId, fallbackConfig);
|
|
8944
|
-
});
|
|
8945
|
-
setShiftConfigMap(fallbackMap);
|
|
8946
|
-
}
|
|
8947
9174
|
setIsLoading(false);
|
|
8948
9175
|
}
|
|
8949
9176
|
}
|
|
@@ -8951,6 +9178,8 @@ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
|
|
|
8951
9178
|
fetchAllConfigs();
|
|
8952
9179
|
return () => {
|
|
8953
9180
|
mounted = false;
|
|
9181
|
+
unsubscribeStore();
|
|
9182
|
+
unsubscribeRealtimeList.forEach((unsub) => unsub());
|
|
8954
9183
|
};
|
|
8955
9184
|
}, [lineIdsKey, supabase]);
|
|
8956
9185
|
return {
|
|
@@ -9293,8 +9522,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9293
9522
|
const operationalDateForSubscription = currentShiftDetails.date;
|
|
9294
9523
|
const targetLineIds = [currentLineIdToUse];
|
|
9295
9524
|
if (targetLineIds.length === 0) return;
|
|
9296
|
-
const
|
|
9297
|
-
const
|
|
9525
|
+
const baseFilterParts = `date=eq.${operationalDateForSubscription},shift_id=eq.${currentShiftDetails.shiftId}`;
|
|
9526
|
+
const lineIdFilterPart = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
9527
|
+
const wsMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
|
|
9528
|
+
const lineMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
|
|
9298
9529
|
const createSubscription = (table, filter2, channelNameBase, callback) => {
|
|
9299
9530
|
const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
9300
9531
|
const channel = supabase.channel(channelName).on(
|
|
@@ -9771,12 +10002,12 @@ var useRealtimeLineMetrics = ({
|
|
|
9771
10002
|
const companyId = entityConfig.companyId;
|
|
9772
10003
|
const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
|
|
9773
10004
|
const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
10005
|
+
const enabledWorkspaceLists = await Promise.all(
|
|
10006
|
+
targetLineIds.map((lineId2) => workspaceService.getEnabledWorkspaces(lineId2))
|
|
10007
|
+
);
|
|
10008
|
+
const enabledWorkspaceIds = Array.from(
|
|
10009
|
+
new Set(enabledWorkspaceLists.flatMap((workspaces) => workspaces.map((ws) => ws.id)))
|
|
10010
|
+
);
|
|
9780
10011
|
const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
|
|
9781
10012
|
line_id,
|
|
9782
10013
|
workspace_id,
|
|
@@ -9836,8 +10067,8 @@ var useRealtimeLineMetrics = ({
|
|
|
9836
10067
|
const companyId = entityConfig.companyId;
|
|
9837
10068
|
const metricsTablePrefix = getMetricsTablePrefix(companyId || "");
|
|
9838
10069
|
const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
|
|
9839
|
-
const
|
|
9840
|
-
const enabledWorkspaceIds =
|
|
10070
|
+
const enabledWorkspaces = await workspaceService.getEnabledWorkspaces(lineIdRef.current);
|
|
10071
|
+
const enabledWorkspaceIds = enabledWorkspaces.map((ws) => ws.id);
|
|
9841
10072
|
const { data: workspaceData, error: workspaceError } = enabledWorkspaceIds.length > 0 ? await supabase.from(metricsTable).select(`
|
|
9842
10073
|
workspace_id,
|
|
9843
10074
|
workspace_name,
|
|
@@ -9957,20 +10188,23 @@ var useRealtimeLineMetrics = ({
|
|
|
9957
10188
|
const companyId = entityConfig.companyId;
|
|
9958
10189
|
const metricsTablePrefix = getMetricsTablePrefix();
|
|
9959
10190
|
const metricsTable = `${metricsTablePrefix}_${(companyId || "").replace(/-/g, "_")}`;
|
|
10191
|
+
const currentDate = urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
10192
|
+
const baseFilterParts = `date=eq.${currentDate},shift_id=eq.${shiftId}`;
|
|
10193
|
+
const lineIdFilterPart = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
10194
|
+
const filter2 = `${baseFilterParts},${lineIdFilterPart}`;
|
|
9960
10195
|
const lineMetricsChannel = supabase.channel(`line-metrics-${timestamp}`).on(
|
|
9961
10196
|
"postgres_changes",
|
|
9962
10197
|
{
|
|
9963
10198
|
event: "*",
|
|
9964
10199
|
schema: "public",
|
|
9965
10200
|
table: "line_metrics",
|
|
9966
|
-
filter:
|
|
10201
|
+
filter: filter2
|
|
9967
10202
|
},
|
|
9968
10203
|
async (payload) => {
|
|
9969
10204
|
const payloadData = payload.new;
|
|
9970
10205
|
if (process.env.NODE_ENV === "development") {
|
|
9971
10206
|
console.log("Line metrics update received:", payloadData);
|
|
9972
10207
|
}
|
|
9973
|
-
const currentDate = urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
9974
10208
|
if (payloadData?.date === currentDate && payloadData?.shift_id === shiftId) {
|
|
9975
10209
|
queueUpdate();
|
|
9976
10210
|
}
|
|
@@ -9986,14 +10220,13 @@ var useRealtimeLineMetrics = ({
|
|
|
9986
10220
|
event: "*",
|
|
9987
10221
|
schema: "public",
|
|
9988
10222
|
table: metricsTable,
|
|
9989
|
-
filter:
|
|
10223
|
+
filter: filter2
|
|
9990
10224
|
},
|
|
9991
10225
|
async (payload) => {
|
|
9992
10226
|
const payloadData = payload.new;
|
|
9993
10227
|
if (process.env.NODE_ENV === "development") {
|
|
9994
10228
|
console.log(`${metricsTablePrefix} update received:`, payloadData);
|
|
9995
10229
|
}
|
|
9996
|
-
const currentDate = urlDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
|
|
9997
10230
|
if (payloadData?.date === currentDate && payloadData?.shift_id === shiftId) {
|
|
9998
10231
|
queueUpdate();
|
|
9999
10232
|
}
|
|
@@ -10004,7 +10237,7 @@ var useRealtimeLineMetrics = ({
|
|
|
10004
10237
|
}
|
|
10005
10238
|
});
|
|
10006
10239
|
channelsRef.current = [lineMetricsChannel, metricsChannel];
|
|
10007
|
-
}, [supabase, queueUpdate, urlDate, shiftId, entityConfig, dateTimeConfig.defaultTimezone]);
|
|
10240
|
+
}, [supabase, queueUpdate, urlDate, shiftId, entityConfig, timezone, dateTimeConfig.defaultTimezone]);
|
|
10008
10241
|
const prevShiftIdRef = useRef(void 0);
|
|
10009
10242
|
useEffect(() => {
|
|
10010
10243
|
if (!lineId) return;
|
|
@@ -10783,34 +11016,16 @@ var useFactoryOverviewMetrics = (date, shiftId) => {
|
|
|
10783
11016
|
if (lineIds.length === 0) {
|
|
10784
11017
|
throw new Error("No lines configured in entityConfig");
|
|
10785
11018
|
}
|
|
10786
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
10787
|
-
if (!session?.access_token) {
|
|
10788
|
-
throw new Error("No authentication token available");
|
|
10789
|
-
}
|
|
10790
|
-
const apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
10791
|
-
if (!apiUrl) {
|
|
10792
|
-
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
10793
|
-
}
|
|
10794
11019
|
const params = new URLSearchParams({
|
|
10795
11020
|
line_ids: lineIds.join(","),
|
|
10796
11021
|
date: queryDate,
|
|
10797
11022
|
shift_id: queryShiftId.toString(),
|
|
10798
11023
|
company_id: entityConfig.companyId || ""
|
|
10799
11024
|
});
|
|
10800
|
-
const
|
|
10801
|
-
|
|
10802
|
-
{
|
|
10803
|
-
headers: {
|
|
10804
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
10805
|
-
"Content-Type": "application/json"
|
|
10806
|
-
}
|
|
10807
|
-
}
|
|
11025
|
+
const data = await fetchBackendJson(
|
|
11026
|
+
supabase,
|
|
11027
|
+
`/api/dashboard/factory-overview?${params.toString()}`
|
|
10808
11028
|
);
|
|
10809
|
-
if (!response.ok) {
|
|
10810
|
-
const errorText = await response.text();
|
|
10811
|
-
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
10812
|
-
}
|
|
10813
|
-
const data = await response.json();
|
|
10814
11029
|
setMetrics(data);
|
|
10815
11030
|
} catch (err) {
|
|
10816
11031
|
console.error("[useFactoryOverviewMetrics] Error fetching factory overview:", err);
|
|
@@ -10839,6 +11054,46 @@ var isInitialized = false;
|
|
|
10839
11054
|
var isInitializing = false;
|
|
10840
11055
|
var initializedWithLineIds = [];
|
|
10841
11056
|
var initializationPromise = null;
|
|
11057
|
+
var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
|
|
11058
|
+
var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
|
|
11059
|
+
workspaceDisplayNamesListeners.forEach((listener) => {
|
|
11060
|
+
try {
|
|
11061
|
+
listener(changedLineId);
|
|
11062
|
+
} catch (err) {
|
|
11063
|
+
console.error("[workspaceDisplayNames] Listener error", err);
|
|
11064
|
+
}
|
|
11065
|
+
});
|
|
11066
|
+
};
|
|
11067
|
+
var subscribeWorkspaceDisplayNames = (listener) => {
|
|
11068
|
+
workspaceDisplayNamesListeners.add(listener);
|
|
11069
|
+
return () => workspaceDisplayNamesListeners.delete(listener);
|
|
11070
|
+
};
|
|
11071
|
+
var getAllWorkspaceDisplayNamesSnapshot = (lineId) => {
|
|
11072
|
+
if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
|
|
11073
|
+
return { ...runtimeWorkspaceDisplayNames[lineId] };
|
|
11074
|
+
}
|
|
11075
|
+
if (lineId) return {};
|
|
11076
|
+
const allNames = {};
|
|
11077
|
+
Object.entries(runtimeWorkspaceDisplayNames).forEach(([cachedLineId, lineNames]) => {
|
|
11078
|
+
Object.entries(lineNames).forEach(([workspaceId, displayName]) => {
|
|
11079
|
+
allNames[`${cachedLineId}_${workspaceId}`] = displayName;
|
|
11080
|
+
});
|
|
11081
|
+
});
|
|
11082
|
+
return allNames;
|
|
11083
|
+
};
|
|
11084
|
+
var upsertWorkspaceDisplayNameInCache = (params) => {
|
|
11085
|
+
const { lineId, workspaceId, displayName, enabled } = params;
|
|
11086
|
+
if (!lineId || !workspaceId) return;
|
|
11087
|
+
if (!runtimeWorkspaceDisplayNames[lineId]) {
|
|
11088
|
+
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
11089
|
+
}
|
|
11090
|
+
if (enabled === false || !displayName) {
|
|
11091
|
+
delete runtimeWorkspaceDisplayNames[lineId][workspaceId];
|
|
11092
|
+
} else {
|
|
11093
|
+
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
11094
|
+
}
|
|
11095
|
+
notifyWorkspaceDisplayNamesListeners(lineId);
|
|
11096
|
+
};
|
|
10842
11097
|
function getCurrentLineIds() {
|
|
10843
11098
|
try {
|
|
10844
11099
|
const config = _getDashboardConfigInstance();
|
|
@@ -10878,15 +11133,20 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
|
10878
11133
|
console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
|
|
10879
11134
|
runtimeWorkspaceDisplayNames = {};
|
|
10880
11135
|
if (targetLineIds.length > 0) {
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
11136
|
+
const results = await Promise.all(
|
|
11137
|
+
targetLineIds.map(async (lineId) => {
|
|
11138
|
+
console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
|
|
11139
|
+
const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
|
|
11140
|
+
return { lineId, lineDisplayNamesMap };
|
|
11141
|
+
})
|
|
11142
|
+
);
|
|
11143
|
+
results.forEach(({ lineId, lineDisplayNamesMap }) => {
|
|
10884
11144
|
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
10885
11145
|
lineDisplayNamesMap.forEach((displayName, workspaceId) => {
|
|
10886
11146
|
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
10887
11147
|
});
|
|
10888
11148
|
console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
|
|
10889
|
-
}
|
|
11149
|
+
});
|
|
10890
11150
|
} else {
|
|
10891
11151
|
console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
|
|
10892
11152
|
const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
|
|
@@ -10899,6 +11159,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
|
10899
11159
|
initializedWithLineIds = targetLineIds;
|
|
10900
11160
|
console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
|
|
10901
11161
|
console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
|
|
11162
|
+
notifyWorkspaceDisplayNamesListeners(explicitLineId);
|
|
10902
11163
|
} catch (error) {
|
|
10903
11164
|
console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
|
|
10904
11165
|
} finally {
|
|
@@ -10921,6 +11182,7 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
|
|
|
10921
11182
|
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
10922
11183
|
});
|
|
10923
11184
|
console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
|
|
11185
|
+
notifyWorkspaceDisplayNamesListeners(lineId);
|
|
10924
11186
|
} catch (error) {
|
|
10925
11187
|
console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
|
|
10926
11188
|
}
|
|
@@ -10939,6 +11201,7 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
|
|
|
10939
11201
|
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
10940
11202
|
});
|
|
10941
11203
|
console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
|
|
11204
|
+
notifyWorkspaceDisplayNamesListeners(lineId);
|
|
10942
11205
|
} catch (error) {
|
|
10943
11206
|
console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
|
|
10944
11207
|
}
|
|
@@ -10978,6 +11241,7 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
|
|
|
10978
11241
|
runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
|
|
10979
11242
|
});
|
|
10980
11243
|
console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
|
|
11244
|
+
notifyWorkspaceDisplayNamesListeners(lineId);
|
|
10981
11245
|
}).catch((error) => {
|
|
10982
11246
|
console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
|
|
10983
11247
|
});
|
|
@@ -11018,6 +11282,7 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
|
|
|
11018
11282
|
runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
|
|
11019
11283
|
});
|
|
11020
11284
|
console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
|
|
11285
|
+
notifyWorkspaceDisplayNamesListeners(lineId);
|
|
11021
11286
|
}).catch((error) => {
|
|
11022
11287
|
console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
|
|
11023
11288
|
});
|
|
@@ -11059,6 +11324,9 @@ var getAllWorkspaceDisplayNamesAsync = async (companyId, lineId) => {
|
|
|
11059
11324
|
while (isInitializing) {
|
|
11060
11325
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
11061
11326
|
}
|
|
11327
|
+
if (lineId && isInitialized && !runtimeWorkspaceDisplayNames[lineId]) {
|
|
11328
|
+
await preInitializeWorkspaceDisplayNames(lineId);
|
|
11329
|
+
}
|
|
11062
11330
|
if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
|
|
11063
11331
|
return { ...runtimeWorkspaceDisplayNames[lineId] };
|
|
11064
11332
|
}
|
|
@@ -11091,6 +11359,7 @@ var refreshWorkspaceDisplayNames = async (companyId) => {
|
|
|
11091
11359
|
runtimeWorkspaceDisplayNames = {};
|
|
11092
11360
|
isInitialized = false;
|
|
11093
11361
|
await initializeWorkspaceDisplayNames();
|
|
11362
|
+
notifyWorkspaceDisplayNamesListeners();
|
|
11094
11363
|
};
|
|
11095
11364
|
var clearWorkspaceDisplayNamesCache = () => {
|
|
11096
11365
|
workspaceService.clearWorkspaceDisplayNamesCache();
|
|
@@ -11099,6 +11368,7 @@ var clearWorkspaceDisplayNamesCache = () => {
|
|
|
11099
11368
|
isInitializing = false;
|
|
11100
11369
|
initializedWithLineIds = [];
|
|
11101
11370
|
initializationPromise = null;
|
|
11371
|
+
notifyWorkspaceDisplayNamesListeners();
|
|
11102
11372
|
};
|
|
11103
11373
|
|
|
11104
11374
|
// src/lib/hooks/useWorkspaceDisplayNames.ts
|
|
@@ -11121,6 +11391,23 @@ var useWorkspaceDisplayNames = (lineId, companyId) => {
|
|
|
11121
11391
|
useEffect(() => {
|
|
11122
11392
|
fetchDisplayNames();
|
|
11123
11393
|
}, [fetchDisplayNames]);
|
|
11394
|
+
useEffect(() => {
|
|
11395
|
+
const snapshot = getAllWorkspaceDisplayNamesSnapshot(lineId);
|
|
11396
|
+
if (Object.keys(snapshot).length > 0) {
|
|
11397
|
+
setDisplayNames(snapshot);
|
|
11398
|
+
setLoading(false);
|
|
11399
|
+
}
|
|
11400
|
+
const unsubscribe = subscribeWorkspaceDisplayNames((changedLineId) => {
|
|
11401
|
+
if (!lineId) {
|
|
11402
|
+
setDisplayNames(getAllWorkspaceDisplayNamesSnapshot());
|
|
11403
|
+
return;
|
|
11404
|
+
}
|
|
11405
|
+
if (!changedLineId || changedLineId === "*" || changedLineId === lineId) {
|
|
11406
|
+
setDisplayNames(getAllWorkspaceDisplayNamesSnapshot(lineId));
|
|
11407
|
+
}
|
|
11408
|
+
});
|
|
11409
|
+
return unsubscribe;
|
|
11410
|
+
}, [lineId]);
|
|
11124
11411
|
return {
|
|
11125
11412
|
displayNames,
|
|
11126
11413
|
loading,
|
|
@@ -11129,7 +11416,7 @@ var useWorkspaceDisplayNames = (lineId, companyId) => {
|
|
|
11129
11416
|
};
|
|
11130
11417
|
};
|
|
11131
11418
|
var useWorkspaceDisplayName = (workspaceId, lineId, companyId) => {
|
|
11132
|
-
const [displayName, setDisplayName] = useState(workspaceId);
|
|
11419
|
+
const [displayName, setDisplayName] = useState(() => getWorkspaceDisplayName(workspaceId, lineId));
|
|
11133
11420
|
const [loading, setLoading] = useState(true);
|
|
11134
11421
|
const [error, setError] = useState(null);
|
|
11135
11422
|
const fetchDisplayName = useCallback(async () => {
|
|
@@ -11148,6 +11435,18 @@ var useWorkspaceDisplayName = (workspaceId, lineId, companyId) => {
|
|
|
11148
11435
|
useEffect(() => {
|
|
11149
11436
|
fetchDisplayName();
|
|
11150
11437
|
}, [fetchDisplayName]);
|
|
11438
|
+
useEffect(() => {
|
|
11439
|
+
const unsubscribe = subscribeWorkspaceDisplayNames((changedLineId) => {
|
|
11440
|
+
if (!lineId) {
|
|
11441
|
+
setDisplayName(getWorkspaceDisplayName(workspaceId));
|
|
11442
|
+
return;
|
|
11443
|
+
}
|
|
11444
|
+
if (!changedLineId || changedLineId === "*" || changedLineId === lineId) {
|
|
11445
|
+
setDisplayName(getWorkspaceDisplayName(workspaceId, lineId));
|
|
11446
|
+
}
|
|
11447
|
+
});
|
|
11448
|
+
return unsubscribe;
|
|
11449
|
+
}, [workspaceId, lineId]);
|
|
11151
11450
|
return {
|
|
11152
11451
|
displayName,
|
|
11153
11452
|
loading,
|
|
@@ -11178,6 +11477,18 @@ var useWorkspaceDisplayNamesMap = (workspaceIds, lineId, companyId) => {
|
|
|
11178
11477
|
useEffect(() => {
|
|
11179
11478
|
fetchDisplayNames();
|
|
11180
11479
|
}, [fetchDisplayNames]);
|
|
11480
|
+
useEffect(() => {
|
|
11481
|
+
const unsubscribe = subscribeWorkspaceDisplayNames((changedLineId) => {
|
|
11482
|
+
if (lineId && changedLineId && changedLineId !== "*" && changedLineId !== lineId) return;
|
|
11483
|
+
const snapshot = getAllWorkspaceDisplayNamesSnapshot(lineId);
|
|
11484
|
+
const next = {};
|
|
11485
|
+
workspaceIds.forEach((id3) => {
|
|
11486
|
+
if (snapshot[id3]) next[id3] = snapshot[id3];
|
|
11487
|
+
});
|
|
11488
|
+
setDisplayNames(next);
|
|
11489
|
+
});
|
|
11490
|
+
return unsubscribe;
|
|
11491
|
+
}, [workspaceIds, lineId]);
|
|
11181
11492
|
return {
|
|
11182
11493
|
displayNames,
|
|
11183
11494
|
loading,
|
|
@@ -11424,13 +11735,19 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
11424
11735
|
setError(null);
|
|
11425
11736
|
try {
|
|
11426
11737
|
const lineWorkspaceMap = /* @__PURE__ */ new Map();
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11738
|
+
const allEnabledWorkspaceIdSet = /* @__PURE__ */ new Set();
|
|
11739
|
+
const perLineEnabledIds = await Promise.all(
|
|
11740
|
+
configuredLineIds.map(async (lineId) => {
|
|
11741
|
+
const enabledWorkspaces = await workspaceService.getEnabledWorkspaces(lineId);
|
|
11742
|
+
const enabledIds = enabledWorkspaces.map((ws) => ws.id);
|
|
11743
|
+
return { lineId, enabledIds };
|
|
11744
|
+
})
|
|
11745
|
+
);
|
|
11746
|
+
perLineEnabledIds.forEach(({ lineId, enabledIds }) => {
|
|
11431
11747
|
lineWorkspaceMap.set(lineId, enabledIds);
|
|
11432
|
-
|
|
11433
|
-
}
|
|
11748
|
+
enabledIds.forEach((id3) => allEnabledWorkspaceIdSet.add(id3));
|
|
11749
|
+
});
|
|
11750
|
+
const allEnabledWorkspaceIds = Array.from(allEnabledWorkspaceIdSet);
|
|
11434
11751
|
if (allEnabledWorkspaceIds.length === 0) {
|
|
11435
11752
|
setWorkspaces([]);
|
|
11436
11753
|
setInitialized(true);
|
|
@@ -11551,43 +11868,61 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
11551
11868
|
});
|
|
11552
11869
|
}
|
|
11553
11870
|
const setupSubscription = () => {
|
|
11554
|
-
|
|
11555
|
-
|
|
11871
|
+
if (!metricsTable || configuredLineIds.length === 0) return [];
|
|
11872
|
+
const groupsToSubscribe = hasSpecificDateShift || shiftGroups.length === 0 ? [
|
|
11556
11873
|
{
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
},
|
|
11561
|
-
async (payload) => {
|
|
11562
|
-
const data = payload.new || payload.old;
|
|
11563
|
-
const dataKey = `${data?.date}-${data?.shift_id}`;
|
|
11564
|
-
if (!validDateShiftCombos.has(dataKey)) {
|
|
11565
|
-
return;
|
|
11566
|
-
}
|
|
11567
|
-
if (fetchTimeoutRef.current) {
|
|
11568
|
-
clearTimeout(fetchTimeoutRef.current);
|
|
11569
|
-
}
|
|
11570
|
-
fetchTimeoutRef.current = setTimeout(async () => {
|
|
11571
|
-
if (!isFetchingRef.current) {
|
|
11572
|
-
await fetchWorkspaceMetrics();
|
|
11573
|
-
}
|
|
11574
|
-
fetchTimeoutRef.current = null;
|
|
11575
|
-
}, 300);
|
|
11874
|
+
date: fallbackQueryDate,
|
|
11875
|
+
shiftId: fallbackQueryShiftId,
|
|
11876
|
+
lineIds: configuredLineIds
|
|
11576
11877
|
}
|
|
11577
|
-
|
|
11578
|
-
|
|
11878
|
+
] : shiftGroups.map((group) => ({
|
|
11879
|
+
date: group.date,
|
|
11880
|
+
shiftId: group.shiftId,
|
|
11881
|
+
lineIds: group.lineIds
|
|
11882
|
+
}));
|
|
11883
|
+
return groupsToSubscribe.map((group, index) => {
|
|
11884
|
+
const lineIdFilterPart = `line_id=in.(${group.lineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
11885
|
+
const filter2 = `date=eq.${group.date},shift_id=eq.${group.shiftId},${lineIdFilterPart}`;
|
|
11886
|
+
const channelName = `all-workspace-metrics-g${index}-${group.date}-${group.shiftId}-${Date.now()}`.replace(
|
|
11887
|
+
/[^a-zA-Z0-9_-]/g,
|
|
11888
|
+
""
|
|
11889
|
+
);
|
|
11890
|
+
return supabase.channel(channelName).on(
|
|
11891
|
+
"postgres_changes",
|
|
11892
|
+
{
|
|
11893
|
+
event: "*",
|
|
11894
|
+
schema,
|
|
11895
|
+
table: metricsTable,
|
|
11896
|
+
filter: filter2
|
|
11897
|
+
},
|
|
11898
|
+
async (payload) => {
|
|
11899
|
+
const data = payload.new || payload.old;
|
|
11900
|
+
const dataKey = `${data?.date}-${data?.shift_id}`;
|
|
11901
|
+
if (!validDateShiftCombos.has(dataKey)) {
|
|
11902
|
+
return;
|
|
11903
|
+
}
|
|
11904
|
+
if (fetchTimeoutRef.current) {
|
|
11905
|
+
clearTimeout(fetchTimeoutRef.current);
|
|
11906
|
+
}
|
|
11907
|
+
fetchTimeoutRef.current = setTimeout(async () => {
|
|
11908
|
+
if (!isFetchingRef.current) {
|
|
11909
|
+
await fetchWorkspaceMetrics();
|
|
11910
|
+
}
|
|
11911
|
+
fetchTimeoutRef.current = null;
|
|
11912
|
+
}, 300);
|
|
11913
|
+
}
|
|
11914
|
+
).subscribe();
|
|
11915
|
+
});
|
|
11579
11916
|
};
|
|
11580
|
-
const
|
|
11917
|
+
const channels = setupSubscription();
|
|
11581
11918
|
return () => {
|
|
11582
11919
|
if (fetchTimeoutRef.current) {
|
|
11583
11920
|
clearTimeout(fetchTimeoutRef.current);
|
|
11584
11921
|
fetchTimeoutRef.current = null;
|
|
11585
11922
|
}
|
|
11586
|
-
|
|
11587
|
-
supabase.removeChannel(channel);
|
|
11588
|
-
}
|
|
11923
|
+
channels.forEach((channel) => supabase.removeChannel(channel));
|
|
11589
11924
|
};
|
|
11590
|
-
}, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
|
|
11925
|
+
}, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId, configuredLineIds]);
|
|
11591
11926
|
useEffect(() => {
|
|
11592
11927
|
setInitialized(false);
|
|
11593
11928
|
}, [fallbackQueryDate, fallbackQueryShiftId, shiftGroups]);
|
|
@@ -13025,6 +13360,85 @@ function useLineSupervisor(lineId) {
|
|
|
13025
13360
|
error
|
|
13026
13361
|
};
|
|
13027
13362
|
}
|
|
13363
|
+
var useSupervisorsByLineIds = (lineIds, options) => {
|
|
13364
|
+
const supabase = useSupabase();
|
|
13365
|
+
const enabled = options?.enabled ?? true;
|
|
13366
|
+
const lineIdsKey = useMemo(() => (lineIds || []).slice().sort().join(","), [lineIds]);
|
|
13367
|
+
const targetLineIdSet = useMemo(() => new Set(lineIds || []), [lineIdsKey]);
|
|
13368
|
+
const [supervisorsByLineId, setSupervisorsByLineId] = useState(/* @__PURE__ */ new Map());
|
|
13369
|
+
const [supervisorNamesByLineId, setSupervisorNamesByLineId] = useState(/* @__PURE__ */ new Map());
|
|
13370
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
13371
|
+
const [error, setError] = useState(null);
|
|
13372
|
+
const fetchSupervisors = useCallback(async () => {
|
|
13373
|
+
if (!enabled || !supabase) {
|
|
13374
|
+
setIsLoading(false);
|
|
13375
|
+
return;
|
|
13376
|
+
}
|
|
13377
|
+
try {
|
|
13378
|
+
setIsLoading(true);
|
|
13379
|
+
setError(null);
|
|
13380
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor");
|
|
13381
|
+
if (fetchError) {
|
|
13382
|
+
throw fetchError;
|
|
13383
|
+
}
|
|
13384
|
+
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
13385
|
+
(data || []).forEach((row) => {
|
|
13386
|
+
const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
|
|
13387
|
+
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
13388
|
+
const email = row.email || "";
|
|
13389
|
+
const displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
13390
|
+
const supervisor = {
|
|
13391
|
+
userId: row.user_id,
|
|
13392
|
+
email,
|
|
13393
|
+
displayName
|
|
13394
|
+
};
|
|
13395
|
+
assignedLineIds.forEach((lineId) => {
|
|
13396
|
+
if (typeof lineId !== "string") return;
|
|
13397
|
+
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
13398
|
+
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
13399
|
+
existing.push(supervisor);
|
|
13400
|
+
nextSupervisorsByLineId.set(lineId, existing);
|
|
13401
|
+
});
|
|
13402
|
+
});
|
|
13403
|
+
const nextSupervisorNamesByLineId = /* @__PURE__ */ new Map();
|
|
13404
|
+
nextSupervisorsByLineId.forEach((supervisors, lineId) => {
|
|
13405
|
+
nextSupervisorNamesByLineId.set(lineId, supervisors.map((s) => s.displayName).join(", "));
|
|
13406
|
+
});
|
|
13407
|
+
setSupervisorsByLineId(nextSupervisorsByLineId);
|
|
13408
|
+
setSupervisorNamesByLineId(nextSupervisorNamesByLineId);
|
|
13409
|
+
} catch (err) {
|
|
13410
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
13411
|
+
setSupervisorsByLineId(/* @__PURE__ */ new Map());
|
|
13412
|
+
setSupervisorNamesByLineId(/* @__PURE__ */ new Map());
|
|
13413
|
+
} finally {
|
|
13414
|
+
setIsLoading(false);
|
|
13415
|
+
}
|
|
13416
|
+
}, [enabled, supabase, targetLineIdSet]);
|
|
13417
|
+
useEffect(() => {
|
|
13418
|
+
fetchSupervisors();
|
|
13419
|
+
}, [fetchSupervisors, lineIdsKey]);
|
|
13420
|
+
useEffect(() => {
|
|
13421
|
+
if (!enabled || !supabase) return;
|
|
13422
|
+
let channel = null;
|
|
13423
|
+
channel = supabase.channel("supervisors-by-line").on(
|
|
13424
|
+
"postgres_changes",
|
|
13425
|
+
{ event: "*", schema: "public", table: "user_roles", filter: "role_level=eq.supervisor" },
|
|
13426
|
+
() => {
|
|
13427
|
+
fetchSupervisors();
|
|
13428
|
+
}
|
|
13429
|
+
).subscribe();
|
|
13430
|
+
return () => {
|
|
13431
|
+
if (channel) supabase.removeChannel(channel);
|
|
13432
|
+
};
|
|
13433
|
+
}, [enabled, supabase, fetchSupervisors]);
|
|
13434
|
+
return {
|
|
13435
|
+
supervisorNamesByLineId,
|
|
13436
|
+
supervisorsByLineId,
|
|
13437
|
+
isLoading,
|
|
13438
|
+
error,
|
|
13439
|
+
refetch: fetchSupervisors
|
|
13440
|
+
};
|
|
13441
|
+
};
|
|
13028
13442
|
function useIdleTimeReasons({
|
|
13029
13443
|
workspaceId,
|
|
13030
13444
|
lineId,
|
|
@@ -13430,6 +13844,102 @@ var getConfigurableShortWorkspaceDisplayName = (workspaceId, workspaceConfig, li
|
|
|
13430
13844
|
return fullName;
|
|
13431
13845
|
};
|
|
13432
13846
|
|
|
13847
|
+
// src/lib/utils/kpis.ts
|
|
13848
|
+
var toNumber = (value) => {
|
|
13849
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
13850
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
13851
|
+
const parsed = Number(value);
|
|
13852
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
13853
|
+
}
|
|
13854
|
+
return 0;
|
|
13855
|
+
};
|
|
13856
|
+
var createDefaultKPIs = () => ({
|
|
13857
|
+
underperformingWorkers: { current: 0, total: 0, change: 0 },
|
|
13858
|
+
efficiency: { value: 0, change: 0 },
|
|
13859
|
+
outputProgress: { current: 0, target: 0, idealOutput: 0, change: 0 },
|
|
13860
|
+
avgCycleTime: { value: 0, change: 0 },
|
|
13861
|
+
qualityCompliance: { value: 95, change: 0 }
|
|
13862
|
+
});
|
|
13863
|
+
var buildKPIsFromLineMetricsRow = (row) => {
|
|
13864
|
+
if (!row) return createDefaultKPIs();
|
|
13865
|
+
const avgEfficiency = toNumber(row.avg_efficiency);
|
|
13866
|
+
const avgCycleTime = toNumber(row.avg_cycle_time);
|
|
13867
|
+
const currentOutput = toNumber(row.current_output);
|
|
13868
|
+
const lineThreshold = toNumber(row.line_threshold);
|
|
13869
|
+
const idealOutput = toNumber(row.ideal_output) || lineThreshold || 0;
|
|
13870
|
+
const underperformingWorkspaces = toNumber(row.underperforming_workspaces);
|
|
13871
|
+
const totalWorkspaces = toNumber(row.total_workspaces);
|
|
13872
|
+
return {
|
|
13873
|
+
underperformingWorkers: {
|
|
13874
|
+
current: underperformingWorkspaces,
|
|
13875
|
+
total: totalWorkspaces,
|
|
13876
|
+
change: 0
|
|
13877
|
+
},
|
|
13878
|
+
efficiency: {
|
|
13879
|
+
value: avgEfficiency,
|
|
13880
|
+
change: 0
|
|
13881
|
+
},
|
|
13882
|
+
outputProgress: {
|
|
13883
|
+
current: currentOutput,
|
|
13884
|
+
target: lineThreshold,
|
|
13885
|
+
idealOutput,
|
|
13886
|
+
change: 0
|
|
13887
|
+
},
|
|
13888
|
+
avgCycleTime: {
|
|
13889
|
+
value: avgCycleTime,
|
|
13890
|
+
change: 0
|
|
13891
|
+
},
|
|
13892
|
+
qualityCompliance: {
|
|
13893
|
+
value: 95,
|
|
13894
|
+
change: 0
|
|
13895
|
+
}
|
|
13896
|
+
};
|
|
13897
|
+
};
|
|
13898
|
+
var aggregateKPIsFromLineMetricsRows = (rows) => {
|
|
13899
|
+
if (!rows || rows.length === 0) return createDefaultKPIs();
|
|
13900
|
+
const currentOutputSum = rows.reduce((sum, row) => sum + toNumber(row.current_output), 0);
|
|
13901
|
+
const lineThresholdSum = rows.reduce((sum, row) => sum + toNumber(row.line_threshold), 0);
|
|
13902
|
+
const idealOutputSum = rows.reduce(
|
|
13903
|
+
(sum, row) => sum + (toNumber(row.ideal_output) || toNumber(row.line_threshold)),
|
|
13904
|
+
0
|
|
13905
|
+
);
|
|
13906
|
+
const totalWorkspacesAll = rows.reduce((sum, row) => sum + toNumber(row.total_workspaces), 0);
|
|
13907
|
+
const weightedEfficiencySum = rows.reduce(
|
|
13908
|
+
(sum, row) => sum + toNumber(row.avg_efficiency) * toNumber(row.total_workspaces),
|
|
13909
|
+
0
|
|
13910
|
+
);
|
|
13911
|
+
const avgEfficiency = totalWorkspacesAll > 0 ? weightedEfficiencySum / totalWorkspacesAll : 0;
|
|
13912
|
+
const numLines = rows.length;
|
|
13913
|
+
const avgCycleTime = numLines > 0 ? rows.reduce((sum, row) => sum + toNumber(row.avg_cycle_time), 0) / numLines : 0;
|
|
13914
|
+
const totalUnderperforming = rows.reduce((sum, row) => sum + toNumber(row.underperforming_workspaces), 0);
|
|
13915
|
+
const totalWorkspaces = rows.reduce((sum, row) => sum + toNumber(row.total_workspaces), 0);
|
|
13916
|
+
return {
|
|
13917
|
+
underperformingWorkers: {
|
|
13918
|
+
current: totalUnderperforming,
|
|
13919
|
+
total: totalWorkspaces,
|
|
13920
|
+
change: 0
|
|
13921
|
+
},
|
|
13922
|
+
efficiency: {
|
|
13923
|
+
value: avgEfficiency,
|
|
13924
|
+
change: 0
|
|
13925
|
+
},
|
|
13926
|
+
outputProgress: {
|
|
13927
|
+
current: currentOutputSum,
|
|
13928
|
+
target: lineThresholdSum,
|
|
13929
|
+
idealOutput: idealOutputSum,
|
|
13930
|
+
change: 0
|
|
13931
|
+
},
|
|
13932
|
+
avgCycleTime: {
|
|
13933
|
+
value: avgCycleTime,
|
|
13934
|
+
change: 0
|
|
13935
|
+
},
|
|
13936
|
+
qualityCompliance: {
|
|
13937
|
+
value: 95,
|
|
13938
|
+
change: 0
|
|
13939
|
+
}
|
|
13940
|
+
};
|
|
13941
|
+
};
|
|
13942
|
+
|
|
13433
13943
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
13434
13944
|
function r(e) {
|
|
13435
13945
|
var t, f, n = "";
|
|
@@ -28466,6 +28976,7 @@ if (typeof document !== "undefined") {
|
|
|
28466
28976
|
}
|
|
28467
28977
|
}
|
|
28468
28978
|
var BASE_HLS_CONFIG = {
|
|
28979
|
+
// Keep buffer small to reduce wasted downloads on slow links
|
|
28469
28980
|
maxBufferLength: 3,
|
|
28470
28981
|
maxMaxBufferLength: 8,
|
|
28471
28982
|
maxBufferSize: 50 * 1e3 * 1e3,
|
|
@@ -28473,10 +28984,10 @@ var BASE_HLS_CONFIG = {
|
|
|
28473
28984
|
manifestLoadingTimeOut: 15e3,
|
|
28474
28985
|
manifestLoadingMaxRetry: 3,
|
|
28475
28986
|
manifestLoadingRetryDelay: 500,
|
|
28476
|
-
levelLoadingTimeOut:
|
|
28987
|
+
levelLoadingTimeOut: 2e4,
|
|
28477
28988
|
levelLoadingMaxRetry: 5,
|
|
28478
28989
|
levelLoadingRetryDelay: 500,
|
|
28479
|
-
fragLoadingTimeOut:
|
|
28990
|
+
fragLoadingTimeOut: 2e4,
|
|
28480
28991
|
fragLoadingMaxRetry: 5,
|
|
28481
28992
|
fragLoadingRetryDelay: 500,
|
|
28482
28993
|
startPosition: -1,
|
|
@@ -28489,7 +29000,10 @@ var BASE_HLS_CONFIG = {
|
|
|
28489
29000
|
abrBandWidthFactor: 0.95,
|
|
28490
29001
|
abrBandWidthUpFactor: 0.7,
|
|
28491
29002
|
abrMaxWithRealBitrate: false,
|
|
28492
|
-
|
|
29003
|
+
// Favor a conservative first rendition on constrained networks
|
|
29004
|
+
abrEwmaDefaultEstimate: 1e6,
|
|
29005
|
+
startLevel: 0,
|
|
29006
|
+
capLevelToPlayerSize: true
|
|
28493
29007
|
};
|
|
28494
29008
|
var HlsVideoPlayer = forwardRef(({
|
|
28495
29009
|
src,
|
|
@@ -29573,7 +30087,7 @@ var getSupabaseClient2 = () => {
|
|
|
29573
30087
|
}
|
|
29574
30088
|
return createClient(url, key);
|
|
29575
30089
|
};
|
|
29576
|
-
var
|
|
30090
|
+
var getAuthToken4 = async () => {
|
|
29577
30091
|
try {
|
|
29578
30092
|
const supabase = getSupabaseClient2();
|
|
29579
30093
|
const { data: { session } } = await supabase.auth.getSession();
|
|
@@ -29596,7 +30110,7 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
29596
30110
|
setIsLoading(true);
|
|
29597
30111
|
setError(null);
|
|
29598
30112
|
try {
|
|
29599
|
-
const token = await
|
|
30113
|
+
const token = await getAuthToken4();
|
|
29600
30114
|
if (!token) {
|
|
29601
30115
|
throw new Error("Authentication required");
|
|
29602
30116
|
}
|
|
@@ -30645,7 +31159,7 @@ var FileManagerFilters = ({
|
|
|
30645
31159
|
const ROOT_CAUSE_OPTIONS = [
|
|
30646
31160
|
"Operator Absent",
|
|
30647
31161
|
"Operator Idle",
|
|
30648
|
-
"Machine
|
|
31162
|
+
"Machine Downtime",
|
|
30649
31163
|
"No Material"
|
|
30650
31164
|
];
|
|
30651
31165
|
const getIdleTimeRootCause = useCallback((clipId) => {
|
|
@@ -30685,7 +31199,7 @@ var FileManagerFilters = ({
|
|
|
30685
31199
|
method: "POST",
|
|
30686
31200
|
headers: {
|
|
30687
31201
|
"Content-Type": "application/json",
|
|
30688
|
-
"Authorization": `Bearer ${await
|
|
31202
|
+
"Authorization": `Bearer ${await getAuthToken5()}`
|
|
30689
31203
|
},
|
|
30690
31204
|
body: JSON.stringify({
|
|
30691
31205
|
action: "clip-metadata",
|
|
@@ -30718,7 +31232,7 @@ var FileManagerFilters = ({
|
|
|
30718
31232
|
});
|
|
30719
31233
|
}
|
|
30720
31234
|
}, [workspaceId, date, shift]);
|
|
30721
|
-
const
|
|
31235
|
+
const getAuthToken5 = async () => {
|
|
30722
31236
|
try {
|
|
30723
31237
|
const { createClient: createClient5 } = await import('@supabase/supabase-js');
|
|
30724
31238
|
const supabase = createClient5(
|
|
@@ -30744,7 +31258,7 @@ var FileManagerFilters = ({
|
|
|
30744
31258
|
method: "POST",
|
|
30745
31259
|
headers: {
|
|
30746
31260
|
"Content-Type": "application/json",
|
|
30747
|
-
"Authorization": `Bearer ${await
|
|
31261
|
+
"Authorization": `Bearer ${await getAuthToken5()}`
|
|
30748
31262
|
},
|
|
30749
31263
|
body: JSON.stringify({
|
|
30750
31264
|
action: "percentile-clips",
|
|
@@ -32388,7 +32902,7 @@ var BottlenecksContent = ({
|
|
|
32388
32902
|
fetchClipCounts();
|
|
32389
32903
|
}
|
|
32390
32904
|
}, [workspaceId, effectiveDateString, effectiveShiftId, s3ClipsService, fetchClipCounts, isEffectiveShiftReady]);
|
|
32391
|
-
const
|
|
32905
|
+
const getAuthToken5 = useCallback(async () => {
|
|
32392
32906
|
try {
|
|
32393
32907
|
const { createClient: createClient5 } = await import('@supabase/supabase-js');
|
|
32394
32908
|
const supabase = createClient5(
|
|
@@ -32407,7 +32921,7 @@ var BottlenecksContent = ({
|
|
|
32407
32921
|
const fetchTriageClips = async () => {
|
|
32408
32922
|
setIsLoadingTriageClips(true);
|
|
32409
32923
|
try {
|
|
32410
|
-
const token = await
|
|
32924
|
+
const token = await getAuthToken5();
|
|
32411
32925
|
if (!token) {
|
|
32412
32926
|
console.error("[BottlenecksContent] No auth token available");
|
|
32413
32927
|
return;
|
|
@@ -32455,7 +32969,7 @@ var BottlenecksContent = ({
|
|
|
32455
32969
|
}
|
|
32456
32970
|
};
|
|
32457
32971
|
fetchTriageClips();
|
|
32458
|
-
}, [triageMode, workspaceId, effectiveDateString, effectiveShiftId,
|
|
32972
|
+
}, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, getAuthToken5, isEffectiveShiftReady]);
|
|
32459
32973
|
useEffect(() => {
|
|
32460
32974
|
if (!triageMode || triageClips.length === 0 || !session?.access_token) {
|
|
32461
32975
|
return;
|
|
@@ -32786,7 +33300,20 @@ var BottlenecksContent = ({
|
|
|
32786
33300
|
updateActiveFilter(categoryId);
|
|
32787
33301
|
}
|
|
32788
33302
|
try {
|
|
32789
|
-
|
|
33303
|
+
const metadataPromise = loadCategoryMetadata(categoryId, false).catch((err) => {
|
|
33304
|
+
console.warn(`[BottlenecksContent] Metadata fetch error (non-blocking):`, err);
|
|
33305
|
+
return null;
|
|
33306
|
+
});
|
|
33307
|
+
const video = await s3ClipsService.getClipById(clipId);
|
|
33308
|
+
if (video) {
|
|
33309
|
+
setPendingVideo(video);
|
|
33310
|
+
setCurrentClipId(clipId);
|
|
33311
|
+
setAllVideos([video]);
|
|
33312
|
+
setCurrentIndex(0);
|
|
33313
|
+
} else {
|
|
33314
|
+
throw new Error(`Failed to load video data for clip ${clipId}`);
|
|
33315
|
+
}
|
|
33316
|
+
await metadataPromise;
|
|
32790
33317
|
let metadataArray = categoryMetadataRef.current;
|
|
32791
33318
|
const clipExistsInMetadata = metadataArray.some((clip) => clip.clipId === clipId);
|
|
32792
33319
|
if (metadataArray.length === 0 || !clipExistsInMetadata) {
|
|
@@ -32803,16 +33330,7 @@ var BottlenecksContent = ({
|
|
|
32803
33330
|
}
|
|
32804
33331
|
setCurrentMetadataIndex(clickedClipIndex);
|
|
32805
33332
|
currentMetadataIndexRef.current = clickedClipIndex;
|
|
32806
|
-
|
|
32807
|
-
if (video) {
|
|
32808
|
-
setPendingVideo(video);
|
|
32809
|
-
setCurrentClipId(clipId);
|
|
32810
|
-
setAllVideos([video]);
|
|
32811
|
-
setCurrentIndex(0);
|
|
32812
|
-
console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
|
|
32813
|
-
} else {
|
|
32814
|
-
throw new Error(`Failed to load video data for clip ${clipId}`);
|
|
32815
|
-
}
|
|
33333
|
+
console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
|
|
32816
33334
|
} catch (error2) {
|
|
32817
33335
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
32818
33336
|
if (isMountedRef.current) {
|
|
@@ -35699,7 +36217,7 @@ var STATIC_COLORS = {
|
|
|
35699
36217
|
// red-600 - Critical/Urgent
|
|
35700
36218
|
"No Material": "#f59e0b",
|
|
35701
36219
|
// amber-500 - Warning/Supply Chain
|
|
35702
|
-
"Machine
|
|
36220
|
+
"Machine Downtime": "#3b82f6",
|
|
35703
36221
|
// blue-500 - Scheduled/Technical
|
|
35704
36222
|
"Operator Idle": "#8b5cf6"
|
|
35705
36223
|
// violet-500 - Low Priority/Behavioral
|
|
@@ -38084,7 +38602,7 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
38084
38602
|
}
|
|
38085
38603
|
);
|
|
38086
38604
|
};
|
|
38087
|
-
var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
38605
|
+
var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons }) => {
|
|
38088
38606
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
38089
38607
|
const entityConfig = useEntityConfig();
|
|
38090
38608
|
const generatePDF = async () => {
|
|
@@ -38159,8 +38677,10 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
38159
38677
|
doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
|
|
38160
38678
|
};
|
|
38161
38679
|
const perfOverviewStartY = 93;
|
|
38680
|
+
const hasIdleTimeReason = idleTimeReasons && idleTimeReasons.length > 0;
|
|
38681
|
+
const perfOverviewHeight = hasIdleTimeReason ? 80 : 70;
|
|
38162
38682
|
doc.setFillColor(245, 245, 245);
|
|
38163
|
-
doc.roundedRect(15, perfOverviewStartY, 180,
|
|
38683
|
+
doc.roundedRect(15, perfOverviewStartY, 180, perfOverviewHeight, 3, 3, "F");
|
|
38164
38684
|
doc.setFontSize(18);
|
|
38165
38685
|
doc.setFont("helvetica", "bold");
|
|
38166
38686
|
doc.setTextColor(40, 40, 40);
|
|
@@ -38184,32 +38704,68 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
38184
38704
|
doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 2);
|
|
38185
38705
|
doc.setFont("helvetica", "bold");
|
|
38186
38706
|
doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 2);
|
|
38707
|
+
createKPIBox(kpiStartY + kpiSpacing * 3);
|
|
38708
|
+
doc.setFont("helvetica", "normal");
|
|
38709
|
+
doc.text("Total Idle Time:", 25, kpiStartY + kpiSpacing * 3);
|
|
38710
|
+
doc.setFont("helvetica", "bold");
|
|
38711
|
+
const idleTimeFormatted = formatIdleTime(workspace.idle_time);
|
|
38712
|
+
doc.text(idleTimeFormatted, 120, kpiStartY + kpiSpacing * 3);
|
|
38713
|
+
if (hasIdleTimeReason) {
|
|
38714
|
+
createKPIBox(kpiStartY + kpiSpacing * 4);
|
|
38715
|
+
doc.setFont("helvetica", "normal");
|
|
38716
|
+
doc.text("Top Idle Reason:", 25, kpiStartY + kpiSpacing * 4);
|
|
38717
|
+
doc.setFont("helvetica", "bold");
|
|
38718
|
+
const topReason = idleTimeReasons[0];
|
|
38719
|
+
const reasonName = topReason.name.replace(/_/g, " ");
|
|
38720
|
+
const reasonText = `${reasonName} (${topReason.value.toFixed(1)}%)`;
|
|
38721
|
+
doc.text(reasonText, 120, kpiStartY + kpiSpacing * 4);
|
|
38722
|
+
}
|
|
38723
|
+
const separatorBeforeHourlyY = hasIdleTimeReason ? 183 : 173;
|
|
38187
38724
|
doc.setDrawColor(180, 180, 180);
|
|
38188
38725
|
doc.setLineWidth(0.8);
|
|
38189
|
-
doc.line(20,
|
|
38190
|
-
const hourlyPerfStartY =
|
|
38726
|
+
doc.line(20, separatorBeforeHourlyY, 190, separatorBeforeHourlyY);
|
|
38727
|
+
const hourlyPerfStartY = hasIdleTimeReason ? 188 : 178;
|
|
38191
38728
|
const hourlyData = workspace.hourly_action_counts || [];
|
|
38192
38729
|
const hourlyTarget = workspace.pph_threshold;
|
|
38193
|
-
const
|
|
38194
|
-
const
|
|
38730
|
+
const pageHeight = doc.internal.pageSize.height;
|
|
38731
|
+
const maxContentY = pageHeight - 15;
|
|
38732
|
+
const baseTableStartY = hasIdleTimeReason ? 219 : 209;
|
|
38733
|
+
let tableStartY = baseTableStartY;
|
|
38734
|
+
let rowHeight = 8;
|
|
38735
|
+
let headerFontSize = 11;
|
|
38736
|
+
let contentFontSize = 11;
|
|
38737
|
+
let titleFontSize = 18;
|
|
38195
38738
|
const bottomPadding = 8;
|
|
38739
|
+
const estimatedEndY = tableStartY + hourlyData.length * rowHeight + bottomPadding;
|
|
38740
|
+
if (estimatedEndY > maxContentY) {
|
|
38741
|
+
rowHeight = 6.5;
|
|
38742
|
+
headerFontSize = 9;
|
|
38743
|
+
contentFontSize = 9;
|
|
38744
|
+
titleFontSize = 16;
|
|
38745
|
+
tableStartY = hasIdleTimeReason ? 215 : 205;
|
|
38746
|
+
}
|
|
38196
38747
|
const backgroundHeight = tableStartY - hourlyPerfStartY + hourlyData.length * rowHeight + bottomPadding;
|
|
38197
38748
|
doc.setFillColor(245, 245, 245);
|
|
38198
38749
|
doc.roundedRect(15, hourlyPerfStartY, 180, backgroundHeight, 3, 3, "F");
|
|
38199
|
-
doc.setFontSize(
|
|
38750
|
+
doc.setFontSize(titleFontSize);
|
|
38200
38751
|
doc.setFont("helvetica", "bold");
|
|
38201
38752
|
doc.setTextColor(40, 40, 40);
|
|
38202
|
-
|
|
38753
|
+
const hourlyTitleY = hasIdleTimeReason ? 198 : 188;
|
|
38754
|
+
doc.text("Hourly Performance", 20, hourlyTitleY);
|
|
38203
38755
|
doc.setTextColor(0, 0, 0);
|
|
38204
|
-
doc.setFontSize(
|
|
38756
|
+
doc.setFontSize(headerFontSize);
|
|
38205
38757
|
doc.setFont("helvetica", "bold");
|
|
38206
|
-
|
|
38207
|
-
|
|
38208
|
-
doc.text("
|
|
38209
|
-
doc.text("
|
|
38758
|
+
const baseHeaderY = hasIdleTimeReason ? 208 : 198;
|
|
38759
|
+
const headerY = titleFontSize === 16 ? hasIdleTimeReason ? 206 : 196 : baseHeaderY;
|
|
38760
|
+
doc.text("Time Range", 25, headerY);
|
|
38761
|
+
doc.text("Output", 95, headerY);
|
|
38762
|
+
doc.text("Target", 135, headerY);
|
|
38763
|
+
doc.text("Status", 170, headerY);
|
|
38210
38764
|
doc.setLineWidth(0.3);
|
|
38211
38765
|
doc.setDrawColor(200, 200, 200);
|
|
38212
|
-
|
|
38766
|
+
const separatorY = headerY + 3;
|
|
38767
|
+
doc.line(20, separatorY, 190, separatorY);
|
|
38768
|
+
doc.setFontSize(contentFontSize);
|
|
38213
38769
|
doc.setFont("helvetica", "normal");
|
|
38214
38770
|
let yPos = tableStartY;
|
|
38215
38771
|
const workspaceDate = new Date(workspace.date);
|
|
@@ -45661,23 +46217,6 @@ function HomeView({
|
|
|
45661
46217
|
loading: displayNamesLoading,
|
|
45662
46218
|
error: displayNamesError
|
|
45663
46219
|
} = useWorkspaceDisplayNames(displayNameLineId, void 0);
|
|
45664
|
-
useCallback(() => {
|
|
45665
|
-
console.log("Refetching KPIs after line metrics update");
|
|
45666
|
-
}, []);
|
|
45667
|
-
const {
|
|
45668
|
-
kpis,
|
|
45669
|
-
isLoading: kpisLoading,
|
|
45670
|
-
error: kpisError,
|
|
45671
|
-
refetch: refetchKPIs
|
|
45672
|
-
} = useLineKPIs({
|
|
45673
|
-
lineId: selectedLineId
|
|
45674
|
-
});
|
|
45675
|
-
const onLineMetricsUpdate = useCallback(() => {
|
|
45676
|
-
const timer = setTimeout(() => {
|
|
45677
|
-
refetchKPIs();
|
|
45678
|
-
}, 1e3);
|
|
45679
|
-
return () => clearTimeout(timer);
|
|
45680
|
-
}, [refetchKPIs]);
|
|
45681
46220
|
const {
|
|
45682
46221
|
workspaceMetrics,
|
|
45683
46222
|
lineMetrics,
|
|
@@ -45686,10 +46225,22 @@ function HomeView({
|
|
|
45686
46225
|
refetch: refetchMetrics
|
|
45687
46226
|
} = useDashboardMetrics({
|
|
45688
46227
|
lineId: selectedLineId,
|
|
45689
|
-
onLineMetricsUpdate,
|
|
45690
46228
|
userAccessibleLineIds: allLineIds
|
|
45691
46229
|
// Pass user's accessible lines for supervisor filtering
|
|
45692
46230
|
});
|
|
46231
|
+
const kpis = useMemo(() => {
|
|
46232
|
+
const lineMetricsRows = lineMetrics || [];
|
|
46233
|
+
if (selectedLineId === factoryViewId) {
|
|
46234
|
+
if (metricsLoading && lineMetricsRows.length === 0) return null;
|
|
46235
|
+
return aggregateKPIsFromLineMetricsRows(lineMetricsRows);
|
|
46236
|
+
}
|
|
46237
|
+
const row = lineMetricsRows.find((r2) => r2?.line_id === selectedLineId);
|
|
46238
|
+
if (!row) {
|
|
46239
|
+
if (metricsLoading) return null;
|
|
46240
|
+
return buildKPIsFromLineMetricsRow(null);
|
|
46241
|
+
}
|
|
46242
|
+
return buildKPIsFromLineMetricsRow(row);
|
|
46243
|
+
}, [selectedLineId, factoryViewId, lineMetrics, metricsLoading]);
|
|
45693
46244
|
const {
|
|
45694
46245
|
activeBreaks: allActiveBreaks,
|
|
45695
46246
|
isLoading: breaksLoading,
|
|
@@ -46008,12 +46559,10 @@ function HomeView({
|
|
|
46008
46559
|
useEffect(() => {
|
|
46009
46560
|
if (metricsError) {
|
|
46010
46561
|
setErrorMessage(metricsError.message);
|
|
46011
|
-
} else if (kpisError) {
|
|
46012
|
-
setErrorMessage(kpisError.message);
|
|
46013
46562
|
} else {
|
|
46014
46563
|
setErrorMessage(null);
|
|
46015
46564
|
}
|
|
46016
|
-
}, [metricsError
|
|
46565
|
+
}, [metricsError]);
|
|
46017
46566
|
const handleLineChange = useCallback((value) => {
|
|
46018
46567
|
setIsChangingFilter(true);
|
|
46019
46568
|
setSelectedLineId(value);
|
|
@@ -46029,14 +46578,14 @@ function HomeView({
|
|
|
46029
46578
|
}
|
|
46030
46579
|
}, [LINE_FILTER_STORAGE_KEY]);
|
|
46031
46580
|
useEffect(() => {
|
|
46032
|
-
if (!metricsLoading &&
|
|
46581
|
+
if (!metricsLoading && isChangingFilter) {
|
|
46033
46582
|
if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
|
|
46034
46583
|
setIsChangingFilter(false);
|
|
46035
46584
|
}
|
|
46036
46585
|
}
|
|
46037
|
-
}, [metricsLoading,
|
|
46586
|
+
}, [metricsLoading, workspaceMetrics, isChangingFilter, selectedLineId, factoryViewId]);
|
|
46038
46587
|
useEffect(() => {
|
|
46039
|
-
if (!metricsLoading && !
|
|
46588
|
+
if (!metricsLoading && !hasInitialDataLoaded) {
|
|
46040
46589
|
setHasInitialDataLoaded(true);
|
|
46041
46590
|
trackCoreEvent("Home View Loaded", {
|
|
46042
46591
|
default_line_id: defaultLineId,
|
|
@@ -46044,7 +46593,7 @@ function HomeView({
|
|
|
46044
46593
|
is_supervisor: isSupervisor
|
|
46045
46594
|
});
|
|
46046
46595
|
}
|
|
46047
|
-
}, [metricsLoading,
|
|
46596
|
+
}, [metricsLoading, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
|
|
46048
46597
|
const lineTitle = useMemo(() => {
|
|
46049
46598
|
return factoryName;
|
|
46050
46599
|
}, [factoryName]);
|
|
@@ -46058,7 +46607,7 @@ function HomeView({
|
|
|
46058
46607
|
] });
|
|
46059
46608
|
}, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
|
|
46060
46609
|
const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized;
|
|
46061
|
-
const isDataLoading = metricsLoading
|
|
46610
|
+
const isDataLoading = metricsLoading;
|
|
46062
46611
|
if (isInitialLoading) {
|
|
46063
46612
|
return /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading Dashboard..." });
|
|
46064
46613
|
}
|
|
@@ -46202,7 +46751,7 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
|
46202
46751
|
const lineIdsKey = useMemo(() => {
|
|
46203
46752
|
if (!props.lineIds) return "";
|
|
46204
46753
|
if (Array.isArray(props.lineIds)) {
|
|
46205
|
-
return props.lineIds.sort().join(",");
|
|
46754
|
+
return props.lineIds.slice().sort().join(",");
|
|
46206
46755
|
}
|
|
46207
46756
|
const values = Object.values(props.lineIds).filter(Boolean);
|
|
46208
46757
|
return values.sort().join(",");
|
|
@@ -47325,9 +47874,15 @@ var KPIDetailView = ({
|
|
|
47325
47874
|
};
|
|
47326
47875
|
var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
|
|
47327
47876
|
var KPIDetailView_default = KPIDetailViewWithDisplayNames;
|
|
47328
|
-
var LineCard = ({
|
|
47329
|
-
|
|
47330
|
-
|
|
47877
|
+
var LineCard = ({
|
|
47878
|
+
line,
|
|
47879
|
+
kpis,
|
|
47880
|
+
isLoading,
|
|
47881
|
+
error,
|
|
47882
|
+
onClick,
|
|
47883
|
+
supervisorEnabled = false,
|
|
47884
|
+
supervisorName
|
|
47885
|
+
}) => {
|
|
47331
47886
|
const isOnTrack = React24__default.useMemo(() => {
|
|
47332
47887
|
if (!kpis) return null;
|
|
47333
47888
|
return kpis.efficiency.value > 90;
|
|
@@ -47436,6 +47991,7 @@ var KPIsOverviewView = ({
|
|
|
47436
47991
|
const [error, setError] = useState(null);
|
|
47437
47992
|
const supabase = useSupabase();
|
|
47438
47993
|
const dashboardConfig = useDashboardConfig();
|
|
47994
|
+
const entityConfig = useEntityConfig();
|
|
47439
47995
|
const navigation = useNavigation(navigate);
|
|
47440
47996
|
const dateTimeConfig = useDateTimeConfig();
|
|
47441
47997
|
const representativeLineId = lineIds?.[0] || lines[0]?.id;
|
|
@@ -47443,6 +47999,27 @@ var KPIsOverviewView = ({
|
|
|
47443
47999
|
const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
|
|
47444
48000
|
const dbTimezone = useAppTimezone();
|
|
47445
48001
|
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
48002
|
+
const factoryViewId = entityConfig.factoryViewId || "factory";
|
|
48003
|
+
const {
|
|
48004
|
+
lineMetrics,
|
|
48005
|
+
isLoading: metricsLoading,
|
|
48006
|
+
error: metricsError
|
|
48007
|
+
} = useDashboardMetrics({
|
|
48008
|
+
lineId: factoryViewId,
|
|
48009
|
+
userAccessibleLineIds: lineIds
|
|
48010
|
+
});
|
|
48011
|
+
const defaultKPIs = React24__default.useMemo(() => createDefaultKPIs(), []);
|
|
48012
|
+
const kpisByLineId = React24__default.useMemo(() => {
|
|
48013
|
+
const map = /* @__PURE__ */ new Map();
|
|
48014
|
+
lineMetrics.forEach((row) => {
|
|
48015
|
+
if (row?.line_id) map.set(row.line_id, buildKPIsFromLineMetricsRow(row));
|
|
48016
|
+
});
|
|
48017
|
+
return map;
|
|
48018
|
+
}, [lineMetrics]);
|
|
48019
|
+
const visibleLineIds = React24__default.useMemo(() => lines.map((l) => l.id), [lines]);
|
|
48020
|
+
const { supervisorNamesByLineId } = useSupervisorsByLineIds(visibleLineIds, {
|
|
48021
|
+
enabled: supervisorEnabled && visibleLineIds.length > 0
|
|
48022
|
+
});
|
|
47446
48023
|
useEffect(() => {
|
|
47447
48024
|
trackCorePageView("KPIs Overview");
|
|
47448
48025
|
}, []);
|
|
@@ -47660,8 +48237,12 @@ var KPIsOverviewView = ({
|
|
|
47660
48237
|
LineCard,
|
|
47661
48238
|
{
|
|
47662
48239
|
line,
|
|
48240
|
+
kpis: metricsError ? null : kpisByLineId.get(line.id) ?? (metricsLoading ? null : defaultKPIs),
|
|
48241
|
+
isLoading: metricsLoading,
|
|
48242
|
+
error: metricsError,
|
|
47663
48243
|
onClick: (kpis) => handleLineClick(line, kpis),
|
|
47664
|
-
supervisorEnabled
|
|
48244
|
+
supervisorEnabled,
|
|
48245
|
+
supervisorName: supervisorNamesByLineId.get(line.id) || null
|
|
47665
48246
|
},
|
|
47666
48247
|
line.id
|
|
47667
48248
|
)) }) })
|
|
@@ -48485,43 +49066,6 @@ var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
|
48485
49066
|
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
48486
49067
|
return Number(hoursDiff.toFixed(1));
|
|
48487
49068
|
};
|
|
48488
|
-
var calculateBreakDuration = (startTime, endTime) => {
|
|
48489
|
-
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
48490
|
-
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
48491
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
48492
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
48493
|
-
if (endMinutes < startMinutes) {
|
|
48494
|
-
endMinutes += 24 * 60;
|
|
48495
|
-
}
|
|
48496
|
-
return endMinutes - startMinutes;
|
|
48497
|
-
};
|
|
48498
|
-
var parseBreaksFromDB = (dbBreaks) => {
|
|
48499
|
-
if (!dbBreaks) return [];
|
|
48500
|
-
if (Array.isArray(dbBreaks)) {
|
|
48501
|
-
return dbBreaks.map((breakItem) => ({
|
|
48502
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
48503
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
48504
|
-
duration: calculateBreakDuration(
|
|
48505
|
-
breakItem.start || breakItem.startTime || "00:00",
|
|
48506
|
-
breakItem.end || breakItem.endTime || "00:00"
|
|
48507
|
-
),
|
|
48508
|
-
remarks: breakItem.remarks || breakItem.name || ""
|
|
48509
|
-
}));
|
|
48510
|
-
} else if (dbBreaks.breaks && Array.isArray(dbBreaks.breaks)) {
|
|
48511
|
-
return dbBreaks.breaks.map((breakItem) => ({
|
|
48512
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
48513
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
48514
|
-
duration: calculateBreakDuration(
|
|
48515
|
-
breakItem.start || breakItem.startTime || "00:00",
|
|
48516
|
-
breakItem.end || breakItem.endTime || "00:00"
|
|
48517
|
-
),
|
|
48518
|
-
remarks: breakItem.remarks || breakItem.name || ""
|
|
48519
|
-
}));
|
|
48520
|
-
} else {
|
|
48521
|
-
console.warn("Unexpected breaks format:", dbBreaks);
|
|
48522
|
-
return [];
|
|
48523
|
-
}
|
|
48524
|
-
};
|
|
48525
49069
|
var getStoredLineState = (lineId) => {
|
|
48526
49070
|
try {
|
|
48527
49071
|
return JSON.parse(localStorage.getItem(`line_${lineId}_open`) || "false");
|
|
@@ -48813,16 +49357,9 @@ var ShiftsView = ({
|
|
|
48813
49357
|
() => lineIds.map((id3) => ({
|
|
48814
49358
|
id: id3,
|
|
48815
49359
|
name: lineNames[id3] || `Line ${id3.substring(0, 4)}`,
|
|
48816
|
-
|
|
48817
|
-
|
|
48818
|
-
|
|
48819
|
-
breaks: []
|
|
48820
|
-
},
|
|
48821
|
-
nightShift: {
|
|
48822
|
-
startTime: "20:00",
|
|
48823
|
-
endTime: "04:00",
|
|
48824
|
-
breaks: []
|
|
48825
|
-
},
|
|
49360
|
+
timezone: "Asia/Kolkata",
|
|
49361
|
+
shifts: [],
|
|
49362
|
+
// Will be populated from DB
|
|
48826
49363
|
isOpen: true,
|
|
48827
49364
|
isSaving: false,
|
|
48828
49365
|
saveSuccess: false
|
|
@@ -48860,58 +49397,37 @@ var ShiftsView = ({
|
|
|
48860
49397
|
setLoading(false);
|
|
48861
49398
|
return;
|
|
48862
49399
|
}
|
|
48863
|
-
const { data:
|
|
48864
|
-
if (
|
|
48865
|
-
console.error("Error fetching
|
|
48866
|
-
showToast("error", "Error loading
|
|
48867
|
-
setError("Failed to load
|
|
48868
|
-
return;
|
|
48869
|
-
}
|
|
48870
|
-
const { data: nightShiftOperatingHours, error: nightShiftError } = await supabase.from("line_operating_hours").select("line_id, start_time, end_time, breaks").eq("shift_id", 1).in("line_id", enabledLineIds);
|
|
48871
|
-
if (nightShiftError) {
|
|
48872
|
-
console.error("Error fetching night shift operating hours:", nightShiftError);
|
|
48873
|
-
showToast("error", "Error loading night shift data");
|
|
48874
|
-
setError("Failed to load night shift data");
|
|
49400
|
+
const { data: allShifts, error: shiftsError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").in("line_id", enabledLineIds);
|
|
49401
|
+
if (shiftsError) {
|
|
49402
|
+
console.error("Error fetching shifts:", shiftsError);
|
|
49403
|
+
showToast("error", "Error loading shift data");
|
|
49404
|
+
setError("Failed to load shift data");
|
|
48875
49405
|
return;
|
|
48876
49406
|
}
|
|
48877
|
-
const
|
|
48878
|
-
|
|
48879
|
-
|
|
48880
|
-
|
|
48881
|
-
breaks: parseBreaksFromDB(item.breaks)
|
|
48882
|
-
};
|
|
48883
|
-
return map;
|
|
49407
|
+
const shiftsByLine = (allShifts || []).reduce((acc, row) => {
|
|
49408
|
+
if (!acc[row.line_id]) acc[row.line_id] = [];
|
|
49409
|
+
acc[row.line_id].push(row);
|
|
49410
|
+
return acc;
|
|
48884
49411
|
}, {});
|
|
48885
|
-
const
|
|
48886
|
-
|
|
48887
|
-
|
|
48888
|
-
|
|
48889
|
-
breaks: parseBreaksFromDB(item.breaks)
|
|
48890
|
-
};
|
|
48891
|
-
return map;
|
|
49412
|
+
const { data: linesData } = await supabase.from("lines").select("id, line_name").in("id", enabledLineIds);
|
|
49413
|
+
const lineNameMap = (linesData || []).reduce((acc, line) => {
|
|
49414
|
+
if (line.line_name) acc[line.id] = line.line_name;
|
|
49415
|
+
return acc;
|
|
48892
49416
|
}, {});
|
|
48893
49417
|
setLineConfigs((prev) => {
|
|
48894
49418
|
const enabledConfigs = prev.filter((config) => enabledLineIds.includes(config.id));
|
|
48895
49419
|
return enabledConfigs.map((config) => {
|
|
48896
|
-
const
|
|
48897
|
-
const
|
|
48898
|
-
const
|
|
48899
|
-
|
|
48900
|
-
|
|
48901
|
-
|
|
48902
|
-
|
|
48903
|
-
|
|
48904
|
-
|
|
48905
|
-
|
|
48906
|
-
|
|
48907
|
-
...newConfig.nightShift,
|
|
48908
|
-
...nightShiftHoursMap[lineId]
|
|
48909
|
-
};
|
|
48910
|
-
}
|
|
48911
|
-
if (newConfig.isOpen === void 0) {
|
|
48912
|
-
newConfig.isOpen = getStoredLineState(lineId);
|
|
48913
|
-
}
|
|
48914
|
-
return newConfig;
|
|
49420
|
+
const rows = shiftsByLine[config.id] || [];
|
|
49421
|
+
const builtConfig = buildShiftConfigFromOperatingHoursRows(rows);
|
|
49422
|
+
const lineName = lineNameMap[config.id] || lineNames[config.id] || `Line ${config.id.substring(0, 4)}`;
|
|
49423
|
+
const sortedShifts = [...builtConfig.shifts || []].sort((a, b) => a.shiftId - b.shiftId);
|
|
49424
|
+
return {
|
|
49425
|
+
...config,
|
|
49426
|
+
name: lineName,
|
|
49427
|
+
shifts: sortedShifts,
|
|
49428
|
+
timezone: builtConfig.timezone || config.timezone,
|
|
49429
|
+
isOpen: config.isOpen ?? getStoredLineState(config.id)
|
|
49430
|
+
};
|
|
48915
49431
|
});
|
|
48916
49432
|
});
|
|
48917
49433
|
setLoading(false);
|
|
@@ -48934,183 +49450,79 @@ var ShiftsView = ({
|
|
|
48934
49450
|
);
|
|
48935
49451
|
});
|
|
48936
49452
|
}, []);
|
|
48937
|
-
const
|
|
48938
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
48939
|
-
const typedConfig = config;
|
|
48940
|
-
if (typedConfig.id === lineId) {
|
|
48941
|
-
const updatedDayShift = { ...typedConfig.dayShift, startTime: value };
|
|
48942
|
-
return {
|
|
48943
|
-
...typedConfig,
|
|
48944
|
-
dayShift: updatedDayShift
|
|
48945
|
-
};
|
|
48946
|
-
}
|
|
48947
|
-
return typedConfig;
|
|
48948
|
-
}));
|
|
48949
|
-
}, []);
|
|
48950
|
-
const updateDayShiftEndTime = useCallback((lineId, value) => {
|
|
48951
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
48952
|
-
const typedConfig = config;
|
|
48953
|
-
if (typedConfig.id === lineId) {
|
|
48954
|
-
const updatedDayShift = { ...typedConfig.dayShift, endTime: value };
|
|
48955
|
-
return {
|
|
48956
|
-
...typedConfig,
|
|
48957
|
-
dayShift: updatedDayShift
|
|
48958
|
-
};
|
|
48959
|
-
}
|
|
48960
|
-
return typedConfig;
|
|
48961
|
-
}));
|
|
48962
|
-
}, []);
|
|
48963
|
-
const updateNightShiftStartTime = useCallback((lineId, value) => {
|
|
49453
|
+
const updateShiftTime = useCallback((lineId, shiftIndex, field, value) => {
|
|
48964
49454
|
setLineConfigs((prev) => prev.map((config) => {
|
|
48965
|
-
|
|
48966
|
-
|
|
48967
|
-
|
|
48968
|
-
|
|
48969
|
-
...typedConfig,
|
|
48970
|
-
nightShift: updatedNightShift
|
|
48971
|
-
};
|
|
48972
|
-
}
|
|
48973
|
-
return typedConfig;
|
|
48974
|
-
}));
|
|
48975
|
-
}, []);
|
|
48976
|
-
const updateNightShiftEndTime = useCallback((lineId, value) => {
|
|
48977
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
48978
|
-
const typedConfig = config;
|
|
48979
|
-
if (typedConfig.id === lineId) {
|
|
48980
|
-
const updatedNightShift = { ...typedConfig.nightShift, endTime: value };
|
|
48981
|
-
return {
|
|
48982
|
-
...typedConfig,
|
|
48983
|
-
nightShift: updatedNightShift
|
|
48984
|
-
};
|
|
48985
|
-
}
|
|
48986
|
-
return typedConfig;
|
|
48987
|
-
}));
|
|
48988
|
-
}, []);
|
|
48989
|
-
const addDayShiftBreak = useCallback((lineId) => {
|
|
48990
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
48991
|
-
const typedConfig = config;
|
|
48992
|
-
if (typedConfig.id === lineId) {
|
|
48993
|
-
const dayShift = { ...typedConfig.dayShift };
|
|
48994
|
-
const newBreak = {
|
|
48995
|
-
startTime: dayShift.startTime,
|
|
48996
|
-
endTime: dayShift.startTime,
|
|
48997
|
-
duration: 0,
|
|
48998
|
-
remarks: ""
|
|
48999
|
-
};
|
|
49000
|
-
return {
|
|
49001
|
-
...typedConfig,
|
|
49002
|
-
dayShift: {
|
|
49003
|
-
...dayShift,
|
|
49004
|
-
breaks: [...dayShift.breaks, newBreak]
|
|
49005
|
-
}
|
|
49006
|
-
};
|
|
49007
|
-
}
|
|
49008
|
-
return typedConfig;
|
|
49009
|
-
}));
|
|
49010
|
-
}, []);
|
|
49011
|
-
const addNightShiftBreak = useCallback((lineId) => {
|
|
49012
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
49013
|
-
const typedConfig = config;
|
|
49014
|
-
if (typedConfig.id === lineId) {
|
|
49015
|
-
const nightShift = { ...typedConfig.nightShift };
|
|
49016
|
-
const newBreak = {
|
|
49017
|
-
startTime: nightShift.startTime,
|
|
49018
|
-
endTime: nightShift.startTime,
|
|
49019
|
-
duration: 0
|
|
49020
|
-
};
|
|
49021
|
-
return {
|
|
49022
|
-
...typedConfig,
|
|
49023
|
-
nightShift: {
|
|
49024
|
-
...nightShift,
|
|
49025
|
-
breaks: [...nightShift.breaks, newBreak]
|
|
49026
|
-
}
|
|
49027
|
-
};
|
|
49028
|
-
}
|
|
49029
|
-
return typedConfig;
|
|
49030
|
-
}));
|
|
49031
|
-
}, []);
|
|
49032
|
-
const updateDayShiftBreak = useCallback((lineId, index, field, value) => {
|
|
49033
|
-
setLineConfigs((prev) => prev.map((config) => {
|
|
49034
|
-
const typedConfig = config;
|
|
49035
|
-
if (typedConfig.id === lineId) {
|
|
49036
|
-
const dayShift = { ...typedConfig.dayShift };
|
|
49037
|
-
const newBreaks = [...dayShift.breaks];
|
|
49038
|
-
newBreaks[index] = { ...newBreaks[index], [field]: value };
|
|
49039
|
-
if (field === "startTime" || field === "endTime") {
|
|
49040
|
-
const startParts = newBreaks[index].startTime.split(":").map(Number);
|
|
49041
|
-
const endParts = newBreaks[index].endTime.split(":").map(Number);
|
|
49042
|
-
let startMinutes = startParts[0] * 60 + startParts[1];
|
|
49043
|
-
let endMinutes = endParts[0] * 60 + endParts[1];
|
|
49044
|
-
if (endMinutes < startMinutes) {
|
|
49045
|
-
endMinutes += 24 * 60;
|
|
49046
|
-
}
|
|
49047
|
-
newBreaks[index].duration = endMinutes - startMinutes;
|
|
49455
|
+
if (config.id === lineId && config.shifts) {
|
|
49456
|
+
const newShifts = [...config.shifts];
|
|
49457
|
+
if (newShifts[shiftIndex]) {
|
|
49458
|
+
newShifts[shiftIndex] = { ...newShifts[shiftIndex], [field]: value };
|
|
49048
49459
|
}
|
|
49049
|
-
return {
|
|
49050
|
-
...typedConfig,
|
|
49051
|
-
dayShift: {
|
|
49052
|
-
...dayShift,
|
|
49053
|
-
breaks: newBreaks
|
|
49054
|
-
}
|
|
49055
|
-
};
|
|
49460
|
+
return { ...config, shifts: newShifts };
|
|
49056
49461
|
}
|
|
49057
|
-
return
|
|
49462
|
+
return config;
|
|
49058
49463
|
}));
|
|
49059
49464
|
}, []);
|
|
49060
|
-
const
|
|
49465
|
+
const addShiftBreak = useCallback((lineId, shiftIndex) => {
|
|
49061
49466
|
setLineConfigs((prev) => prev.map((config) => {
|
|
49062
|
-
|
|
49063
|
-
|
|
49064
|
-
|
|
49065
|
-
|
|
49066
|
-
|
|
49067
|
-
|
|
49068
|
-
|
|
49069
|
-
|
|
49070
|
-
|
|
49071
|
-
|
|
49072
|
-
|
|
49073
|
-
|
|
49074
|
-
|
|
49075
|
-
|
|
49467
|
+
if (config.id === lineId && config.shifts) {
|
|
49468
|
+
const newShifts = [...config.shifts];
|
|
49469
|
+
if (newShifts[shiftIndex]) {
|
|
49470
|
+
const shift = newShifts[shiftIndex];
|
|
49471
|
+
const newBreak = {
|
|
49472
|
+
startTime: shift.startTime,
|
|
49473
|
+
endTime: shift.startTime,
|
|
49474
|
+
duration: 0,
|
|
49475
|
+
remarks: ""
|
|
49476
|
+
};
|
|
49477
|
+
newShifts[shiftIndex] = {
|
|
49478
|
+
...shift,
|
|
49479
|
+
breaks: [...shift.breaks || [], newBreak]
|
|
49480
|
+
};
|
|
49076
49481
|
}
|
|
49077
|
-
return {
|
|
49078
|
-
...typedConfig,
|
|
49079
|
-
nightShift: {
|
|
49080
|
-
...nightShift,
|
|
49081
|
-
breaks: newBreaks
|
|
49082
|
-
}
|
|
49083
|
-
};
|
|
49482
|
+
return { ...config, shifts: newShifts };
|
|
49084
49483
|
}
|
|
49085
|
-
return
|
|
49484
|
+
return config;
|
|
49086
49485
|
}));
|
|
49087
49486
|
}, []);
|
|
49088
|
-
const
|
|
49487
|
+
const updateShiftBreak = useCallback((lineId, shiftIndex, breakIndex, field, value) => {
|
|
49089
49488
|
setLineConfigs((prev) => prev.map((config) => {
|
|
49090
|
-
if (config.id === lineId) {
|
|
49091
|
-
const
|
|
49092
|
-
|
|
49093
|
-
|
|
49094
|
-
|
|
49095
|
-
|
|
49096
|
-
|
|
49489
|
+
if (config.id === lineId && config.shifts) {
|
|
49490
|
+
const newShifts = [...config.shifts];
|
|
49491
|
+
if (newShifts[shiftIndex]) {
|
|
49492
|
+
const shift = newShifts[shiftIndex];
|
|
49493
|
+
const newBreaks = [...shift.breaks || []];
|
|
49494
|
+
if (newBreaks[breakIndex]) {
|
|
49495
|
+
newBreaks[breakIndex] = { ...newBreaks[breakIndex], [field]: value };
|
|
49496
|
+
if (field === "startTime" || field === "endTime") {
|
|
49497
|
+
const startParts = newBreaks[breakIndex].startTime.split(":").map(Number);
|
|
49498
|
+
const endParts = newBreaks[breakIndex].endTime.split(":").map(Number);
|
|
49499
|
+
let startMinutes = startParts[0] * 60 + startParts[1];
|
|
49500
|
+
let endMinutes = endParts[0] * 60 + endParts[1];
|
|
49501
|
+
if (endMinutes < startMinutes) {
|
|
49502
|
+
endMinutes += 24 * 60;
|
|
49503
|
+
}
|
|
49504
|
+
newBreaks[breakIndex].duration = endMinutes - startMinutes;
|
|
49505
|
+
}
|
|
49097
49506
|
}
|
|
49098
|
-
|
|
49507
|
+
newShifts[shiftIndex] = { ...shift, breaks: newBreaks };
|
|
49508
|
+
}
|
|
49509
|
+
return { ...config, shifts: newShifts };
|
|
49099
49510
|
}
|
|
49100
49511
|
return config;
|
|
49101
49512
|
}));
|
|
49102
49513
|
}, []);
|
|
49103
|
-
const
|
|
49514
|
+
const removeShiftBreak = useCallback((lineId, shiftIndex, breakIndex) => {
|
|
49104
49515
|
setLineConfigs((prev) => prev.map((config) => {
|
|
49105
|
-
if (config.id === lineId) {
|
|
49106
|
-
const
|
|
49107
|
-
|
|
49108
|
-
|
|
49109
|
-
|
|
49110
|
-
...
|
|
49111
|
-
breaks:
|
|
49112
|
-
}
|
|
49113
|
-
}
|
|
49516
|
+
if (config.id === lineId && config.shifts) {
|
|
49517
|
+
const newShifts = [...config.shifts];
|
|
49518
|
+
if (newShifts[shiftIndex]) {
|
|
49519
|
+
const shift = newShifts[shiftIndex];
|
|
49520
|
+
newShifts[shiftIndex] = {
|
|
49521
|
+
...shift,
|
|
49522
|
+
breaks: (shift.breaks || []).filter((_, i) => i !== breakIndex)
|
|
49523
|
+
};
|
|
49524
|
+
}
|
|
49525
|
+
return { ...config, shifts: newShifts };
|
|
49114
49526
|
}
|
|
49115
49527
|
return config;
|
|
49116
49528
|
}));
|
|
@@ -49124,29 +49536,26 @@ var ShiftsView = ({
|
|
|
49124
49536
|
if (!lineConfig) {
|
|
49125
49537
|
throw new Error("Line configuration not found");
|
|
49126
49538
|
}
|
|
49127
|
-
const
|
|
49128
|
-
|
|
49129
|
-
|
|
49130
|
-
|
|
49131
|
-
|
|
49132
|
-
|
|
49133
|
-
|
|
49134
|
-
|
|
49135
|
-
|
|
49136
|
-
|
|
49137
|
-
|
|
49138
|
-
|
|
49139
|
-
|
|
49140
|
-
|
|
49141
|
-
|
|
49142
|
-
|
|
49143
|
-
|
|
49144
|
-
if (dayResult.error) {
|
|
49145
|
-
throw new Error(`Failed to save day shift: ${dayResult.error.message}`);
|
|
49539
|
+
const allSavedRows = [];
|
|
49540
|
+
for (const shift of lineConfig.shifts || []) {
|
|
49541
|
+
const shiftData = {
|
|
49542
|
+
line_id: lineId,
|
|
49543
|
+
shift_id: shift.shiftId,
|
|
49544
|
+
shift_name: shift.shiftName,
|
|
49545
|
+
start_time: shift.startTime,
|
|
49546
|
+
end_time: shift.endTime,
|
|
49547
|
+
breaks: formatBreaks(shift.breaks || [])
|
|
49548
|
+
};
|
|
49549
|
+
const { data, error: error2 } = await supabase.from("line_operating_hours").upsert(shiftData).select();
|
|
49550
|
+
if (error2) {
|
|
49551
|
+
throw new Error(`Failed to save shift ${shift.shiftName}: ${error2.message}`);
|
|
49552
|
+
}
|
|
49553
|
+
if (data) {
|
|
49554
|
+
allSavedRows.push(...data);
|
|
49555
|
+
}
|
|
49146
49556
|
}
|
|
49147
|
-
|
|
49148
|
-
|
|
49149
|
-
throw new Error(`Failed to save night shift: ${nightResult.error.message}`);
|
|
49557
|
+
if (allSavedRows.length > 0) {
|
|
49558
|
+
shiftConfigStore.setFromOperatingHoursRows(lineId, allSavedRows, shiftConfigStore.get(lineId));
|
|
49150
49559
|
}
|
|
49151
49560
|
setLineConfigs((prev) => prev.map(
|
|
49152
49561
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: true } : config
|
|
@@ -49225,48 +49634,34 @@ var ShiftsView = ({
|
|
|
49225
49634
|
)
|
|
49226
49635
|
] })
|
|
49227
49636
|
] }) }),
|
|
49228
|
-
/* @__PURE__ */
|
|
49229
|
-
|
|
49230
|
-
|
|
49231
|
-
|
|
49232
|
-
|
|
49233
|
-
|
|
49234
|
-
|
|
49235
|
-
|
|
49236
|
-
|
|
49237
|
-
|
|
49238
|
-
|
|
49239
|
-
|
|
49240
|
-
|
|
49241
|
-
|
|
49242
|
-
|
|
49243
|
-
|
|
49244
|
-
|
|
49245
|
-
|
|
49246
|
-
|
|
49247
|
-
|
|
49248
|
-
|
|
49249
|
-
|
|
49250
|
-
|
|
49251
|
-
|
|
49252
|
-
|
|
49253
|
-
|
|
49254
|
-
|
|
49255
|
-
|
|
49256
|
-
breaks: config.nightShift.breaks,
|
|
49257
|
-
onStartTimeChange: (value) => updateNightShiftStartTime(config.id, value),
|
|
49258
|
-
onEndTimeChange: (value) => updateNightShiftEndTime(config.id, value),
|
|
49259
|
-
onBreakUpdate: (index, field, value) => updateNightShiftBreak(config.id, index, field, value),
|
|
49260
|
-
onBreakRemove: (index) => removeNightShiftBreak(config.id, index),
|
|
49261
|
-
onBreakAdd: () => addNightShiftBreak(config.id),
|
|
49262
|
-
shiftHours: calculateShiftHours(
|
|
49263
|
-
config.nightShift.startTime,
|
|
49264
|
-
config.nightShift.endTime,
|
|
49265
|
-
config.nightShift.breaks
|
|
49266
|
-
)
|
|
49267
|
-
}
|
|
49268
|
-
)
|
|
49269
|
-
] })
|
|
49637
|
+
/* @__PURE__ */ jsx("div", { id: `shift-panel-${config.id}`, className: "p-3 sm:p-4 md:p-6 border-t border-gray-200 w-full", children: config.shifts && config.shifts.length > 0 ? config.shifts.map((shift, shiftIndex) => /* @__PURE__ */ jsx(
|
|
49638
|
+
ShiftPanel,
|
|
49639
|
+
{
|
|
49640
|
+
title: shift.shiftName,
|
|
49641
|
+
icon: (
|
|
49642
|
+
// Icon based on shift name (case-insensitive)
|
|
49643
|
+
shift.shiftName.toLowerCase().includes("day") ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) }) : shift.shiftName.toLowerCase().includes("night") ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) }) : /* @__PURE__ */ jsx(Clock, { className: "w-5 h-5 text-gray-600" })
|
|
49644
|
+
),
|
|
49645
|
+
startTime: shift.startTime,
|
|
49646
|
+
endTime: shift.endTime,
|
|
49647
|
+
breaks: shift.breaks || [],
|
|
49648
|
+
onStartTimeChange: (value) => updateShiftTime(config.id, shiftIndex, "startTime", value),
|
|
49649
|
+
onEndTimeChange: (value) => updateShiftTime(config.id, shiftIndex, "endTime", value),
|
|
49650
|
+
onBreakUpdate: (breakIndex, field, value) => updateShiftBreak(config.id, shiftIndex, breakIndex, field, value),
|
|
49651
|
+
onBreakRemove: (breakIndex) => removeShiftBreak(config.id, shiftIndex, breakIndex),
|
|
49652
|
+
onBreakAdd: () => addShiftBreak(config.id, shiftIndex),
|
|
49653
|
+
shiftHours: calculateShiftHours(
|
|
49654
|
+
shift.startTime,
|
|
49655
|
+
shift.endTime,
|
|
49656
|
+
shift.breaks || []
|
|
49657
|
+
)
|
|
49658
|
+
},
|
|
49659
|
+
shift.shiftId
|
|
49660
|
+
)) : /* @__PURE__ */ jsxs("div", { className: "text-center text-gray-500 py-8", children: [
|
|
49661
|
+
/* @__PURE__ */ jsx(Clock, { className: "w-12 h-12 mx-auto mb-3 text-gray-400" }),
|
|
49662
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "No shifts configured for this line" }),
|
|
49663
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 mt-1", children: "Shifts will appear here once configured in the database" })
|
|
49664
|
+
] }) })
|
|
49270
49665
|
] }, config.id)) })
|
|
49271
49666
|
] })
|
|
49272
49667
|
] });
|
|
@@ -50117,6 +50512,7 @@ var TargetsViewUI = ({
|
|
|
50117
50512
|
onUpdateSelectedSKU,
|
|
50118
50513
|
skuRequired = false
|
|
50119
50514
|
}) => {
|
|
50515
|
+
const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames();
|
|
50120
50516
|
if (isLoading) {
|
|
50121
50517
|
return /* @__PURE__ */ jsx("div", { className: "flex h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading targets..." }) }) });
|
|
50122
50518
|
}
|
|
@@ -50264,7 +50660,7 @@ var TargetsViewUI = ({
|
|
|
50264
50660
|
] })
|
|
50265
50661
|
] }) }),
|
|
50266
50662
|
/* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
|
|
50267
|
-
const formattedName = formatWorkspaceName(workspace.name, lineId);
|
|
50663
|
+
const formattedName = workspaceDisplayNames[`${lineId}_${workspace.name}`] || formatWorkspaceName(workspace.name, lineId);
|
|
50268
50664
|
return /* @__PURE__ */ jsxs(
|
|
50269
50665
|
"div",
|
|
50270
50666
|
{
|
|
@@ -50988,8 +51384,17 @@ var TargetsView = ({
|
|
|
50988
51384
|
};
|
|
50989
51385
|
const handleUpdateWorkspaceDisplayName = useCallback(async (workspaceId, displayName) => {
|
|
50990
51386
|
try {
|
|
50991
|
-
await workspaceService.updateWorkspaceDisplayName(workspaceId, displayName);
|
|
50992
|
-
|
|
51387
|
+
const updated = await workspaceService.updateWorkspaceDisplayName(workspaceId, displayName);
|
|
51388
|
+
if (updated?.line_id && updated?.workspace_id) {
|
|
51389
|
+
upsertWorkspaceDisplayNameInCache({
|
|
51390
|
+
lineId: updated.line_id,
|
|
51391
|
+
workspaceId: updated.workspace_id,
|
|
51392
|
+
displayName: updated?.display_name || displayName,
|
|
51393
|
+
enabled: updated?.enable
|
|
51394
|
+
});
|
|
51395
|
+
} else {
|
|
51396
|
+
await forceRefreshWorkspaceDisplayNames();
|
|
51397
|
+
}
|
|
50993
51398
|
toast.success("Workspace name updated successfully");
|
|
50994
51399
|
} catch (error) {
|
|
50995
51400
|
console.error("Error updating workspace display name:", error);
|
|
@@ -51668,7 +52073,13 @@ var WorkspaceDetailView = ({
|
|
|
51668
52073
|
}
|
|
51669
52074
|
)
|
|
51670
52075
|
] }),
|
|
51671
|
-
activeTab === "overview" && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 lg:gap-2", children: renderHeaderActions ? renderHeaderActions(workspace) : /* @__PURE__ */ jsx(
|
|
52076
|
+
activeTab === "overview" && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 lg:gap-2", children: renderHeaderActions ? renderHeaderActions(workspace) : /* @__PURE__ */ jsx(
|
|
52077
|
+
WorkspacePdfGenerator,
|
|
52078
|
+
{
|
|
52079
|
+
workspace,
|
|
52080
|
+
idleTimeReasons: idleTimeChartData
|
|
52081
|
+
}
|
|
52082
|
+
) }),
|
|
51672
52083
|
activeTab === "monthly_history" && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 lg:gap-2", children: /* @__PURE__ */ jsx(
|
|
51673
52084
|
WorkspaceMonthlyPdfGenerator,
|
|
51674
52085
|
{
|
|
@@ -52723,6 +53134,7 @@ var WorkspaceHealthView = ({
|
|
|
52723
53134
|
const [selectedWorkspace, setSelectedWorkspace] = useState(null);
|
|
52724
53135
|
const [selectedDate, setSelectedDate] = useState(void 0);
|
|
52725
53136
|
const [selectedShiftId, setSelectedShiftId] = useState(void 0);
|
|
53137
|
+
const [showDatePicker, setShowDatePicker] = useState(false);
|
|
52726
53138
|
const effectiveTimezone = timezone || "UTC";
|
|
52727
53139
|
const currentShiftDetails = getCurrentShift(effectiveTimezone, shiftConfig);
|
|
52728
53140
|
const operationalDate = currentShiftDetails.date;
|
|
@@ -52741,8 +53153,9 @@ var WorkspaceHealthView = ({
|
|
|
52741
53153
|
loading,
|
|
52742
53154
|
error,
|
|
52743
53155
|
refetch,
|
|
52744
|
-
shiftConfig: loadedShiftConfig
|
|
53156
|
+
shiftConfig: loadedShiftConfig,
|
|
52745
53157
|
// Get shift config from the hook to pass to modal
|
|
53158
|
+
shiftConfigMap
|
|
52746
53159
|
} = useWorkspaceHealth({
|
|
52747
53160
|
lineId: effectiveLineIdForFetch,
|
|
52748
53161
|
// undefined in factory view = fetch all lines
|
|
@@ -52754,6 +53167,13 @@ var WorkspaceHealthView = ({
|
|
|
52754
53167
|
date: selectedDate,
|
|
52755
53168
|
shiftId: selectedShiftId
|
|
52756
53169
|
});
|
|
53170
|
+
const modalShiftConfig = useMemo(() => {
|
|
53171
|
+
if (!selectedWorkspace) return void 0;
|
|
53172
|
+
if (isFactoryView) {
|
|
53173
|
+
return shiftConfigMap.get(selectedWorkspace.line_id);
|
|
53174
|
+
}
|
|
53175
|
+
return loadedShiftConfig || void 0;
|
|
53176
|
+
}, [isFactoryView, loadedShiftConfig, selectedWorkspace, shiftConfigMap]);
|
|
52757
53177
|
const handleWorkspaceClick = useCallback(
|
|
52758
53178
|
(workspace) => {
|
|
52759
53179
|
const url = `/workspace/${workspace.workspace_id}`;
|
|
@@ -52771,28 +53191,6 @@ var WorkspaceHealthView = ({
|
|
|
52771
53191
|
const handleCloseDetails = useCallback(() => {
|
|
52772
53192
|
setSelectedWorkspace(null);
|
|
52773
53193
|
}, []);
|
|
52774
|
-
const handleExport = useCallback(() => {
|
|
52775
|
-
const csv = [
|
|
52776
|
-
["Workspace", "Line", "Company", "Status", "Last Heartbeat", "Consecutive Misses"],
|
|
52777
|
-
...workspaces.map((w) => [
|
|
52778
|
-
w.workspace_display_name || "",
|
|
52779
|
-
w.line_name || "",
|
|
52780
|
-
w.company_name || "",
|
|
52781
|
-
w.status,
|
|
52782
|
-
w.last_heartbeat,
|
|
52783
|
-
w.consecutive_misses?.toString() || "0"
|
|
52784
|
-
])
|
|
52785
|
-
].map((row) => row.join(",")).join("\n");
|
|
52786
|
-
const blob = new Blob([csv], { type: "text/csv" });
|
|
52787
|
-
const url = window.URL.createObjectURL(blob);
|
|
52788
|
-
const a = document.createElement("a");
|
|
52789
|
-
a.href = url;
|
|
52790
|
-
a.download = `workspace-health-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`;
|
|
52791
|
-
document.body.appendChild(a);
|
|
52792
|
-
a.click();
|
|
52793
|
-
document.body.removeChild(a);
|
|
52794
|
-
window.URL.revokeObjectURL(url);
|
|
52795
|
-
}, [workspaces]);
|
|
52796
53194
|
const getStatusIcon = (status) => {
|
|
52797
53195
|
switch (status) {
|
|
52798
53196
|
case "healthy":
|
|
@@ -52830,104 +53228,42 @@ var WorkspaceHealthView = ({
|
|
|
52830
53228
|
}
|
|
52831
53229
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
52832
53230
|
/* @__PURE__ */ jsxs("div", { className: clsx("min-h-screen bg-slate-50", className), children: [
|
|
52833
|
-
/* @__PURE__ */
|
|
52834
|
-
/* @__PURE__ */
|
|
52835
|
-
|
|
52836
|
-
|
|
52837
|
-
|
|
52838
|
-
|
|
52839
|
-
|
|
52840
|
-
|
|
52841
|
-
|
|
52842
|
-
|
|
52843
|
-
|
|
52844
|
-
|
|
52845
|
-
|
|
52846
|
-
/* @__PURE__ */ jsx(
|
|
52847
|
-
"button",
|
|
52848
|
-
{
|
|
52849
|
-
onClick: () => {
|
|
52850
|
-
refetch();
|
|
52851
|
-
},
|
|
52852
|
-
className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
52853
|
-
"aria-label": "Refresh",
|
|
52854
|
-
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" })
|
|
52855
|
-
}
|
|
52856
|
-
),
|
|
52857
|
-
/* @__PURE__ */ jsx(
|
|
52858
|
-
"button",
|
|
52859
|
-
{
|
|
52860
|
-
onClick: handleExport,
|
|
52861
|
-
className: "p-1.5 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
52862
|
-
"aria-label": "Export CSV",
|
|
52863
|
-
children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" })
|
|
52864
|
-
}
|
|
52865
|
-
)
|
|
52866
|
-
] })
|
|
52867
|
-
] }),
|
|
52868
|
-
/* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
52869
|
-
/* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900", children: "System Health" }),
|
|
52870
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
|
|
52871
|
-
/* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" }),
|
|
52872
|
-
/* @__PURE__ */ jsx("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-emerald-500" })
|
|
52873
|
-
] })
|
|
52874
|
-
] }) })
|
|
53231
|
+
/* @__PURE__ */ jsx("header", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
53232
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx(
|
|
53233
|
+
BackButtonMinimal,
|
|
53234
|
+
{
|
|
53235
|
+
onClick: () => router.push("/"),
|
|
53236
|
+
text: "Back",
|
|
53237
|
+
size: "default",
|
|
53238
|
+
"aria-label": "Navigate back to dashboard"
|
|
53239
|
+
}
|
|
53240
|
+
) }),
|
|
53241
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
53242
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "System Health" }),
|
|
53243
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1 text-center", children: "Monitor workspace connectivity and operational status" })
|
|
52875
53244
|
] }),
|
|
52876
|
-
/* @__PURE__ */
|
|
52877
|
-
/* @__PURE__ */ jsx(
|
|
52878
|
-
|
|
53245
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
53246
|
+
/* @__PURE__ */ jsx(
|
|
53247
|
+
"button",
|
|
52879
53248
|
{
|
|
52880
|
-
onClick: () =>
|
|
52881
|
-
text: "
|
|
52882
|
-
|
|
52883
|
-
|
|
53249
|
+
onClick: () => setShowDatePicker(!showDatePicker),
|
|
53250
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
53251
|
+
"aria-label": "Select date",
|
|
53252
|
+
children: /* @__PURE__ */ jsx(Calendar, { className: "h-5 w-5" })
|
|
52884
53253
|
}
|
|
52885
|
-
)
|
|
52886
|
-
/* @__PURE__ */ jsx(
|
|
52887
|
-
|
|
52888
|
-
|
|
52889
|
-
|
|
52890
|
-
|
|
52891
|
-
|
|
52892
|
-
|
|
52893
|
-
|
|
52894
|
-
|
|
52895
|
-
|
|
52896
|
-
|
|
52897
|
-
|
|
52898
|
-
refetch();
|
|
52899
|
-
},
|
|
52900
|
-
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
52901
|
-
"aria-label": "Refresh",
|
|
52902
|
-
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
|
|
52903
|
-
}
|
|
52904
|
-
),
|
|
52905
|
-
/* @__PURE__ */ jsx(
|
|
52906
|
-
"button",
|
|
52907
|
-
{
|
|
52908
|
-
onClick: handleExport,
|
|
52909
|
-
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
52910
|
-
"aria-label": "Export CSV",
|
|
52911
|
-
children: /* @__PURE__ */ jsx(Download, { className: "h-5 w-5" })
|
|
52912
|
-
}
|
|
52913
|
-
)
|
|
52914
|
-
] }),
|
|
52915
|
-
/* @__PURE__ */ jsx("div", { className: "w-full h-8" })
|
|
52916
|
-
] }) }),
|
|
52917
|
-
/* @__PURE__ */ jsx("div", { className: "mt-1 sm:mt-2", children: /* @__PURE__ */ jsx(
|
|
52918
|
-
HealthDateShiftSelector,
|
|
52919
|
-
{
|
|
52920
|
-
selectedDate: selectedDate || operationalDate,
|
|
52921
|
-
selectedShiftId: selectedShiftId ?? currentShiftDetails.shiftId,
|
|
52922
|
-
shiftConfig,
|
|
52923
|
-
timezone: effectiveTimezone,
|
|
52924
|
-
onDateChange: setSelectedDate,
|
|
52925
|
-
onShiftChange: setSelectedShiftId,
|
|
52926
|
-
isCurrentShift: isViewingCurrentShift,
|
|
52927
|
-
onReturnToLive: handleReturnToLive
|
|
52928
|
-
}
|
|
52929
|
-
) })
|
|
52930
|
-
] }),
|
|
53254
|
+
),
|
|
53255
|
+
/* @__PURE__ */ jsx(
|
|
53256
|
+
"button",
|
|
53257
|
+
{
|
|
53258
|
+
onClick: () => refetch(),
|
|
53259
|
+
disabled: loading,
|
|
53260
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
53261
|
+
"aria-label": "Refresh",
|
|
53262
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { className: `h-5 w-5 ${loading ? "animate-spin" : ""}` })
|
|
53263
|
+
}
|
|
53264
|
+
)
|
|
53265
|
+
] })
|
|
53266
|
+
] }) }) }),
|
|
52931
53267
|
/* @__PURE__ */ jsxs("div", { className: "max-w-7xl mx-auto p-4 space-y-6", children: [
|
|
52932
53268
|
summary && /* @__PURE__ */ jsxs(
|
|
52933
53269
|
motion.div,
|
|
@@ -53003,13 +53339,79 @@ var WorkspaceHealthView = ({
|
|
|
53003
53339
|
)
|
|
53004
53340
|
] })
|
|
53005
53341
|
] }),
|
|
53342
|
+
showDatePicker && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
53343
|
+
/* @__PURE__ */ jsx(
|
|
53344
|
+
"div",
|
|
53345
|
+
{
|
|
53346
|
+
className: "fixed inset-0 bg-black/20 z-40",
|
|
53347
|
+
onClick: () => setShowDatePicker(false)
|
|
53348
|
+
}
|
|
53349
|
+
),
|
|
53350
|
+
/* @__PURE__ */ jsx("div", { className: "fixed top-20 right-4 md:right-8 z-50 bg-white rounded-lg shadow-xl border border-gray-200 p-4 w-80", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
53351
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
53352
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-gray-500 mb-2", children: "Select Date" }),
|
|
53353
|
+
/* @__PURE__ */ jsx(
|
|
53354
|
+
"input",
|
|
53355
|
+
{
|
|
53356
|
+
type: "date",
|
|
53357
|
+
value: selectedDate || operationalDate,
|
|
53358
|
+
max: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
53359
|
+
onChange: (e) => {
|
|
53360
|
+
setSelectedDate(e.target.value);
|
|
53361
|
+
},
|
|
53362
|
+
className: "w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
53363
|
+
}
|
|
53364
|
+
)
|
|
53365
|
+
] }),
|
|
53366
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
53367
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-gray-500 mb-2", children: "Select Shift" }),
|
|
53368
|
+
/* @__PURE__ */ jsx(
|
|
53369
|
+
"select",
|
|
53370
|
+
{
|
|
53371
|
+
value: selectedShiftId ?? currentShiftDetails.shiftId,
|
|
53372
|
+
onChange: (e) => setSelectedShiftId(Number(e.target.value)),
|
|
53373
|
+
className: "w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white",
|
|
53374
|
+
children: (shiftConfig?.shifts || []).map((shift) => /* @__PURE__ */ jsxs("option", { value: shift.shiftId, children: [
|
|
53375
|
+
shift.shiftName,
|
|
53376
|
+
" (",
|
|
53377
|
+
shift.startTime,
|
|
53378
|
+
" - ",
|
|
53379
|
+
shift.endTime,
|
|
53380
|
+
")"
|
|
53381
|
+
] }, shift.shiftId))
|
|
53382
|
+
}
|
|
53383
|
+
)
|
|
53384
|
+
] }),
|
|
53385
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 pt-2", children: [
|
|
53386
|
+
!isViewingCurrentShift && /* @__PURE__ */ jsx(
|
|
53387
|
+
"button",
|
|
53388
|
+
{
|
|
53389
|
+
onClick: () => {
|
|
53390
|
+
handleReturnToLive();
|
|
53391
|
+
setShowDatePicker(false);
|
|
53392
|
+
},
|
|
53393
|
+
className: "flex-1 px-3 py-2 text-sm text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors",
|
|
53394
|
+
children: "Return to Live"
|
|
53395
|
+
}
|
|
53396
|
+
),
|
|
53397
|
+
/* @__PURE__ */ jsx(
|
|
53398
|
+
"button",
|
|
53399
|
+
{
|
|
53400
|
+
onClick: () => setShowDatePicker(false),
|
|
53401
|
+
className: "flex-1 px-3 py-2 text-sm text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors",
|
|
53402
|
+
children: "Done"
|
|
53403
|
+
}
|
|
53404
|
+
)
|
|
53405
|
+
] })
|
|
53406
|
+
] }) })
|
|
53407
|
+
] }),
|
|
53006
53408
|
/* @__PURE__ */ jsx(
|
|
53007
53409
|
WorkspaceUptimeDetailModal_default,
|
|
53008
53410
|
{
|
|
53009
53411
|
workspace: selectedWorkspace,
|
|
53010
53412
|
isOpen: Boolean(selectedWorkspace),
|
|
53011
53413
|
onClose: handleCloseDetails,
|
|
53012
|
-
shiftConfig:
|
|
53414
|
+
shiftConfig: modalShiftConfig,
|
|
53013
53415
|
date: selectedDate,
|
|
53014
53416
|
shiftId: selectedShiftId
|
|
53015
53417
|
}
|
|
@@ -54152,7 +54554,7 @@ function DailyBarChart({
|
|
|
54152
54554
|
axisLine: false,
|
|
54153
54555
|
tick: (props) => {
|
|
54154
54556
|
const { x, y, payload } = props;
|
|
54155
|
-
if (payload.value === 0) return
|
|
54557
|
+
if (payload.value === 0) return /* @__PURE__ */ jsx("g", {});
|
|
54156
54558
|
const hours = Math.round(payload.value / (1e3 * 60 * 60) * 10) / 10;
|
|
54157
54559
|
return /* @__PURE__ */ jsxs("text", { x, y, dy: 4, textAnchor: "end", className: "text-xs fill-gray-400", children: [
|
|
54158
54560
|
hours,
|
|
@@ -54507,7 +54909,7 @@ var UserManagementTable = ({
|
|
|
54507
54909
|
}
|
|
54508
54910
|
),
|
|
54509
54911
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
|
|
54510
|
-
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "
|
|
54912
|
+
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily usage" }),
|
|
54511
54913
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
|
|
54512
54914
|
] }) }),
|
|
54513
54915
|
/* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
|
|
@@ -54519,12 +54921,25 @@ var UserManagementTable = ({
|
|
|
54519
54921
|
const canChangeRole = permissions.canChangeRole(user);
|
|
54520
54922
|
const canRemove = permissions.canRemoveUser(user);
|
|
54521
54923
|
const hasActions = canChangeRole || canRemove;
|
|
54924
|
+
const canShowUsageModal = showUsageStats && (user.role_level === "plant_head" || user.role_level === "supervisor");
|
|
54925
|
+
const handleRowClick = (e) => {
|
|
54926
|
+
const target = e.target;
|
|
54927
|
+
if (target.closest("button") || target.closest("select") || target.closest("input") || target.closest('[role="button"]') || target.closest("[data-interactive]")) {
|
|
54928
|
+
return;
|
|
54929
|
+
}
|
|
54930
|
+
if (canShowUsageModal) {
|
|
54931
|
+
setUsageDetailUserId(user.user_id);
|
|
54932
|
+
setShowUsageDetailModal(true);
|
|
54933
|
+
}
|
|
54934
|
+
};
|
|
54522
54935
|
return /* @__PURE__ */ jsxs(
|
|
54523
54936
|
"tr",
|
|
54524
54937
|
{
|
|
54938
|
+
onClick: handleRowClick,
|
|
54525
54939
|
className: cn(
|
|
54526
|
-
"
|
|
54527
|
-
isDeactivated && "opacity-60"
|
|
54940
|
+
"transition-all duration-150",
|
|
54941
|
+
isDeactivated && "opacity-60",
|
|
54942
|
+
canShowUsageModal ? "cursor-pointer hover:bg-blue-50/50 hover:shadow-sm" : "hover:bg-gray-50"
|
|
54528
54943
|
),
|
|
54529
54944
|
children: [
|
|
54530
54945
|
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
@@ -54590,10 +55005,7 @@ var UserManagementTable = ({
|
|
|
54590
55005
|
setShowUsageDetailModal(true);
|
|
54591
55006
|
},
|
|
54592
55007
|
className: "group flex items-center gap-3 hover:bg-gray-50 -mx-2 px-2 py-1.5 rounded-lg transition-all duration-200",
|
|
54593
|
-
children: isUsageLoading ? /* @__PURE__ */ jsx("div", { className: "animate-pulse bg-gray-200 h-5 w-20 rounded" }) : /* @__PURE__ */
|
|
54594
|
-
/* @__PURE__ */ jsx("span", { className: `text-base font-semibold ${avgDailyUsage?.[user.user_id] && avgDailyUsage[user.user_id] > 0 ? "text-gray-900 group-hover:text-blue-600" : "text-gray-400"} transition-colors`, children: formatDuration3(avgDailyUsage?.[user.user_id] || 0) }),
|
|
54595
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "w-4 h-4 text-gray-400 group-hover:text-blue-600 group-hover:translate-x-0.5 transition-all" })
|
|
54596
|
-
] })
|
|
55008
|
+
children: isUsageLoading ? /* @__PURE__ */ jsx("div", { className: "animate-pulse bg-gray-200 h-5 w-20 rounded" }) : /* @__PURE__ */ jsx("span", { className: `text-base font-semibold ${avgDailyUsage?.[user.user_id] && avgDailyUsage[user.user_id] > 0 ? "text-gray-900 group-hover:text-blue-600" : "text-gray-400"} transition-colors`, children: formatDuration3(avgDailyUsage?.[user.user_id] || 0) })
|
|
54597
55009
|
}
|
|
54598
55010
|
) : /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-400", children: "-" }) }),
|
|
54599
55011
|
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
|
|
@@ -54641,25 +55053,50 @@ var UserManagementTable = ({
|
|
|
54641
55053
|
children: /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
|
|
54642
55054
|
(() => {
|
|
54643
55055
|
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
54644
|
-
const
|
|
54645
|
-
|
|
54646
|
-
return canChangeRole && /* @__PURE__ */ jsxs(
|
|
55056
|
+
const canShowUsage = showUsageStats && user && (user.role_level === "plant_head" || user.role_level === "supervisor");
|
|
55057
|
+
return canShowUsage && /* @__PURE__ */ jsxs(
|
|
54647
55058
|
"button",
|
|
54648
55059
|
{
|
|
54649
55060
|
onClick: () => {
|
|
54650
55061
|
if (user) {
|
|
54651
|
-
|
|
55062
|
+
setUsageDetailUserId(user.user_id);
|
|
55063
|
+
setShowUsageDetailModal(true);
|
|
55064
|
+
handleCloseActionMenu();
|
|
54652
55065
|
}
|
|
54653
55066
|
},
|
|
54654
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2
|
|
54655
|
-
disabled: isCurrentUser,
|
|
55067
|
+
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
54656
55068
|
children: [
|
|
54657
|
-
/* @__PURE__ */ jsx(
|
|
54658
|
-
"
|
|
55069
|
+
/* @__PURE__ */ jsx(BarChart3, { className: "w-4 h-4" }),
|
|
55070
|
+
"View Detailed Usage"
|
|
54659
55071
|
]
|
|
54660
55072
|
}
|
|
54661
55073
|
);
|
|
54662
55074
|
})(),
|
|
55075
|
+
(() => {
|
|
55076
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
55077
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
55078
|
+
const canChangeRole = user && permissions.canChangeRole(user);
|
|
55079
|
+
const canShowUsage = showUsageStats && user && (user.role_level === "plant_head" || user.role_level === "supervisor");
|
|
55080
|
+
return canChangeRole && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
55081
|
+
canShowUsage && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
55082
|
+
/* @__PURE__ */ jsxs(
|
|
55083
|
+
"button",
|
|
55084
|
+
{
|
|
55085
|
+
onClick: () => {
|
|
55086
|
+
if (user) {
|
|
55087
|
+
handleChangeRole(user);
|
|
55088
|
+
}
|
|
55089
|
+
},
|
|
55090
|
+
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
55091
|
+
disabled: isCurrentUser,
|
|
55092
|
+
children: [
|
|
55093
|
+
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
55094
|
+
"Change Role"
|
|
55095
|
+
]
|
|
55096
|
+
}
|
|
55097
|
+
)
|
|
55098
|
+
] });
|
|
55099
|
+
})(),
|
|
54663
55100
|
(() => {
|
|
54664
55101
|
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
54665
55102
|
const isCurrentUser = user?.user_id === currentUserId;
|
|
@@ -55201,7 +55638,7 @@ var TeamManagementView = ({
|
|
|
55201
55638
|
}, {});
|
|
55202
55639
|
}, [usageData, usageDateRange.daysElapsed]);
|
|
55203
55640
|
const pageTitle = user?.role_level === "optifye" ? "Platform Users" : "Team Management";
|
|
55204
|
-
const pageDescription = user?.role_level === "optifye" ? "Manage users across all companies" : user?.role_level === "owner" ? "Manage
|
|
55641
|
+
const pageDescription = user?.role_level === "optifye" ? "Manage users across all companies" : user?.role_level === "owner" ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
55205
55642
|
const hasAccess = user ? user.role_level === void 0 || ["optifye", "owner", "plant_head"].includes(user.role_level) : false;
|
|
55206
55643
|
const loadData = useCallback(async () => {
|
|
55207
55644
|
if (!supabase) {
|
|
@@ -57046,4 +57483,4 @@ function shuffleArray(array) {
|
|
|
57046
57483
|
return shuffled;
|
|
57047
57484
|
}
|
|
57048
57485
|
|
|
57049
|
-
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
|
|
57486
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, buildKPIsFromLineMetricsRow, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
|