@optifye/dashboard-core 6.10.47 → 6.10.48
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 +4 -0
- package/dist/index.d.mts +29 -2
- package/dist/index.d.ts +29 -2
- package/dist/index.js +618 -248
- package/dist/index.mjs +618 -248
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3947,6 +3947,10 @@ var AuthService = class {
|
|
|
3947
3947
|
email: enrichedUser.email,
|
|
3948
3948
|
role: enrichedUser.role,
|
|
3949
3949
|
role_level: enrichedUser.role,
|
|
3950
|
+
scope_mode: enrichedUser.scope_mode,
|
|
3951
|
+
scope_generated_at: enrichedUser.scope_generated_at,
|
|
3952
|
+
scope_ttl_seconds: enrichedUser.scope_ttl_seconds,
|
|
3953
|
+
access_scope: enrichedUser.access_scope,
|
|
3950
3954
|
company_id: enrichedUser.company_id,
|
|
3951
3955
|
first_login_completed: enrichedUser.profile.first_login_completed,
|
|
3952
3956
|
properties: {
|
|
@@ -8153,6 +8157,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8153
8157
|
const isFetchingRef = React26.useRef(false);
|
|
8154
8158
|
const lastProcessedSessionRef = React26.useRef(null);
|
|
8155
8159
|
const hasAuthenticatedRef = React26.useRef(false);
|
|
8160
|
+
const scopeFetchedAtRef = React26.useRef(0);
|
|
8161
|
+
const scopeTtlSecondsRef = React26.useRef(300);
|
|
8156
8162
|
const fetchSession = React26.useCallback(async (supabaseSession, isReauth = false, allowRetry = true) => {
|
|
8157
8163
|
if (isFetchingRef.current) {
|
|
8158
8164
|
console.log("[AuthContext] Already fetching, skipping duplicate request");
|
|
@@ -8165,6 +8171,9 @@ var AuthProvider = ({ children }) => {
|
|
|
8165
8171
|
}
|
|
8166
8172
|
try {
|
|
8167
8173
|
const enrichedUser = await AuthService.getSession(supabaseSession.access_token);
|
|
8174
|
+
const parsedScopeGeneratedAt = enrichedUser.scope_generated_at ? Date.parse(enrichedUser.scope_generated_at) : NaN;
|
|
8175
|
+
scopeFetchedAtRef.current = Number.isFinite(parsedScopeGeneratedAt) ? parsedScopeGeneratedAt : Date.now();
|
|
8176
|
+
scopeTtlSecondsRef.current = enrichedUser.scope_ttl_seconds || 300;
|
|
8168
8177
|
const authUser = AuthService.toAuthUser(enrichedUser);
|
|
8169
8178
|
setUser(authUser);
|
|
8170
8179
|
setSession(supabaseSession);
|
|
@@ -8254,6 +8263,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8254
8263
|
setError(null);
|
|
8255
8264
|
setShowOnboarding(false);
|
|
8256
8265
|
hasAuthenticatedRef.current = false;
|
|
8266
|
+
scopeFetchedAtRef.current = 0;
|
|
8267
|
+
scopeTtlSecondsRef.current = 300;
|
|
8257
8268
|
router$1.push("/login");
|
|
8258
8269
|
} catch (err) {
|
|
8259
8270
|
console.error("[AuthContext] Sign out error:", err);
|
|
@@ -8316,6 +8327,44 @@ var AuthProvider = ({ children }) => {
|
|
|
8316
8327
|
clearInterval(intervalId);
|
|
8317
8328
|
};
|
|
8318
8329
|
}, [session, supabase]);
|
|
8330
|
+
React26.useEffect(() => {
|
|
8331
|
+
if (!session || !user) {
|
|
8332
|
+
return;
|
|
8333
|
+
}
|
|
8334
|
+
const refreshScopeIfExpired = async () => {
|
|
8335
|
+
if (!session || isFetchingRef.current) {
|
|
8336
|
+
return;
|
|
8337
|
+
}
|
|
8338
|
+
const ttlSeconds = user.scope_ttl_seconds || scopeTtlSecondsRef.current || 300;
|
|
8339
|
+
const fetchedAt = scopeFetchedAtRef.current || 0;
|
|
8340
|
+
const shouldRefresh = fetchedAt === 0 || Date.now() - fetchedAt >= ttlSeconds * 1e3;
|
|
8341
|
+
if (!shouldRefresh) {
|
|
8342
|
+
return;
|
|
8343
|
+
}
|
|
8344
|
+
try {
|
|
8345
|
+
console.log("[AuthContext] Scope TTL expired, refreshing session scope");
|
|
8346
|
+
await fetchSession(session, true);
|
|
8347
|
+
} catch (err) {
|
|
8348
|
+
console.error("[AuthContext] Scope refresh failed:", err);
|
|
8349
|
+
}
|
|
8350
|
+
};
|
|
8351
|
+
refreshScopeIfExpired();
|
|
8352
|
+
const intervalId = setInterval(refreshScopeIfExpired, 6e4);
|
|
8353
|
+
return () => clearInterval(intervalId);
|
|
8354
|
+
}, [session, user?.id, user?.scope_ttl_seconds, fetchSession]);
|
|
8355
|
+
React26.useEffect(() => {
|
|
8356
|
+
if (typeof window === "undefined") {
|
|
8357
|
+
return;
|
|
8358
|
+
}
|
|
8359
|
+
const handleScopeRefresh = () => {
|
|
8360
|
+
if (!session) {
|
|
8361
|
+
return;
|
|
8362
|
+
}
|
|
8363
|
+
void fetchSession(session, true);
|
|
8364
|
+
};
|
|
8365
|
+
window.addEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
8366
|
+
return () => window.removeEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
8367
|
+
}, [session, fetchSession]);
|
|
8319
8368
|
React26.useEffect(() => {
|
|
8320
8369
|
if (!supabase) {
|
|
8321
8370
|
console.log("[AuthContext] No Supabase client, skipping auth initialization");
|
|
@@ -8384,7 +8433,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8384
8433
|
} else if (event === "TOKEN_REFRESHED") {
|
|
8385
8434
|
console.log("[AuthContext] Token refreshed automatically");
|
|
8386
8435
|
if (currentSession) {
|
|
8387
|
-
|
|
8436
|
+
const isReauth = hasAuthenticatedRef.current;
|
|
8437
|
+
await fetchSession(currentSession, isReauth);
|
|
8388
8438
|
const expiresAt = currentSession.expires_at;
|
|
8389
8439
|
if (expiresAt) {
|
|
8390
8440
|
const minutesUntilExpiry = Math.floor((expiresAt * 1e3 - Date.now()) / 6e4);
|
|
@@ -10532,12 +10582,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10532
10582
|
const configuredLineIds = React26.useMemo(() => {
|
|
10533
10583
|
return getConfiguredLineIds(entityConfig);
|
|
10534
10584
|
}, [entityConfig]);
|
|
10585
|
+
const targetFactoryLineIds = React26.useMemo(() => {
|
|
10586
|
+
const sourceLineIds = userAccessibleLineIds !== void 0 ? userAccessibleLineIds : configuredLineIds;
|
|
10587
|
+
return Array.from(new Set((sourceLineIds || []).filter(Boolean)));
|
|
10588
|
+
}, [userAccessibleLineIds, configuredLineIds]);
|
|
10535
10589
|
const { shiftConfig: staticShiftConfig } = useDashboardConfig();
|
|
10536
10590
|
const {
|
|
10537
10591
|
shiftConfigMap: multiLineShiftConfigMap,
|
|
10538
10592
|
isLoading: isMultiLineShiftConfigLoading
|
|
10539
10593
|
} = useMultiLineShiftConfigs(
|
|
10540
|
-
isFactoryView ?
|
|
10594
|
+
isFactoryView ? targetFactoryLineIds : [],
|
|
10541
10595
|
staticShiftConfig
|
|
10542
10596
|
);
|
|
10543
10597
|
const {
|
|
@@ -10632,7 +10686,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10632
10686
|
return;
|
|
10633
10687
|
}
|
|
10634
10688
|
const isFactory = currentLineIdToUse === factoryViewId;
|
|
10635
|
-
const targetLineIds = isFactory ?
|
|
10689
|
+
const targetLineIds = isFactory ? targetFactoryLineIds : [currentLineIdToUse];
|
|
10636
10690
|
const targetLineIdsKey = targetLineIds.slice().sort().join(",");
|
|
10637
10691
|
const usesShiftGroups = isFactory && shiftGroups.length > 0;
|
|
10638
10692
|
const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
|
|
@@ -10681,7 +10735,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10681
10735
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
10682
10736
|
}
|
|
10683
10737
|
if (targetLineIds.length === 0) {
|
|
10684
|
-
|
|
10738
|
+
logDebug("[useDashboardMetrics] Skipping fetch: no target line IDs after scope filtering");
|
|
10739
|
+
setMetrics({
|
|
10740
|
+
workspaceMetrics: [],
|
|
10741
|
+
lineMetrics: [],
|
|
10742
|
+
metadata: void 0,
|
|
10743
|
+
efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
|
|
10744
|
+
});
|
|
10745
|
+
setMetricsLineId(requestLineId);
|
|
10746
|
+
lastFetchKeyRef.current = inFlightFetchKeyRef.current;
|
|
10747
|
+
return;
|
|
10685
10748
|
}
|
|
10686
10749
|
let allWorkspaceMetrics = [];
|
|
10687
10750
|
let allLineMetrics = [];
|
|
@@ -10699,11 +10762,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10699
10762
|
lineCount: group.lineIds.length
|
|
10700
10763
|
}))
|
|
10701
10764
|
});
|
|
10765
|
+
const targetLineIdSet = new Set(targetLineIds);
|
|
10702
10766
|
const metricsPromises = shiftGroups.map(async (group) => {
|
|
10703
|
-
const
|
|
10767
|
+
const groupLineIds = group.lineIds.filter((lineGroupId) => targetLineIdSet.has(lineGroupId));
|
|
10768
|
+
if (groupLineIds.length === 0) {
|
|
10769
|
+
return null;
|
|
10770
|
+
}
|
|
10771
|
+
const lineIdsParam = `line_ids=${groupLineIds.join(",")}`;
|
|
10704
10772
|
const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${companyId}${forceParam}`;
|
|
10705
10773
|
logDebug(`[useDashboardMetrics] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
|
|
10706
|
-
lineIds:
|
|
10774
|
+
lineIds: groupLineIds,
|
|
10707
10775
|
date: group.date
|
|
10708
10776
|
});
|
|
10709
10777
|
const response = await fetch(url, {
|
|
@@ -10727,7 +10795,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10727
10795
|
throw new Error(`Invalid JSON response from backend`);
|
|
10728
10796
|
}
|
|
10729
10797
|
});
|
|
10730
|
-
const results = await Promise.all(metricsPromises);
|
|
10798
|
+
const results = (await Promise.all(metricsPromises)).filter((result) => !!result);
|
|
10731
10799
|
hasFlowBuffers = results.some((result) => result?.metadata?.has_flow_buffers);
|
|
10732
10800
|
results.forEach((result) => {
|
|
10733
10801
|
const idleMap = result?.metadata?.idle_time_vlm_by_line;
|
|
@@ -10905,6 +10973,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10905
10973
|
shiftGroups,
|
|
10906
10974
|
shiftGroupsKey,
|
|
10907
10975
|
configuredLineIds,
|
|
10976
|
+
targetFactoryLineIds,
|
|
10908
10977
|
isFactoryView,
|
|
10909
10978
|
multiLineShiftConfigMap,
|
|
10910
10979
|
staticShiftConfig,
|
|
@@ -10997,8 +11066,14 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10997
11066
|
logDebug("[useDashboardMetrics] Setting up group subscriptions:", {
|
|
10998
11067
|
groupCount: currentShiftGroups.length
|
|
10999
11068
|
});
|
|
11069
|
+
const targetFactoryLineIdsForSubscriptions = currentUserAccessibleLineIds !== void 0 ? currentUserAccessibleLineIds : currentConfiguredLineIds;
|
|
11070
|
+
const targetFactoryLineIdSet = new Set(
|
|
11071
|
+
(targetFactoryLineIdsForSubscriptions || []).filter(Boolean)
|
|
11072
|
+
);
|
|
11000
11073
|
currentShiftGroups.forEach((group, index) => {
|
|
11001
|
-
const groupLineIds = group.lineIds.filter(
|
|
11074
|
+
const groupLineIds = group.lineIds.filter(
|
|
11075
|
+
(id3) => id3 && id3 !== factoryViewIdentifier && targetFactoryLineIdSet.has(id3)
|
|
11076
|
+
);
|
|
11002
11077
|
if (groupLineIds.length === 0) {
|
|
11003
11078
|
logDebug("[useDashboardMetrics] Skipping group subscription: no line IDs after filtering", {
|
|
11004
11079
|
groupIndex: index,
|
|
@@ -16177,6 +16252,7 @@ function useAccessControl() {
|
|
|
16177
16252
|
function useTeamManagementPermissions() {
|
|
16178
16253
|
const { user } = useAuth();
|
|
16179
16254
|
const currentRole = user?.role_level;
|
|
16255
|
+
const isSuperAdminOptifye = currentRole === "optifye" && (user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin);
|
|
16180
16256
|
return {
|
|
16181
16257
|
/**
|
|
16182
16258
|
* Can the current user assign lines to this user?
|
|
@@ -16232,7 +16308,10 @@ function useTeamManagementPermissions() {
|
|
|
16232
16308
|
availableRolesToAssign: () => {
|
|
16233
16309
|
if (!currentRole) return [];
|
|
16234
16310
|
if (currentRole === "optifye") {
|
|
16235
|
-
|
|
16311
|
+
if (isSuperAdminOptifye) {
|
|
16312
|
+
return ["owner", "it", "plant_head", "supervisor"];
|
|
16313
|
+
}
|
|
16314
|
+
return ["it", "plant_head", "supervisor"];
|
|
16236
16315
|
}
|
|
16237
16316
|
if (currentRole === "owner") {
|
|
16238
16317
|
return ["it", "plant_head", "supervisor"];
|
|
@@ -16250,7 +16329,12 @@ function useTeamManagementPermissions() {
|
|
|
16250
16329
|
*/
|
|
16251
16330
|
canInviteRole: (role) => {
|
|
16252
16331
|
if (!currentRole) return false;
|
|
16253
|
-
if (currentRole === "optifye")
|
|
16332
|
+
if (currentRole === "optifye") {
|
|
16333
|
+
if (role === "owner") {
|
|
16334
|
+
return isSuperAdminOptifye;
|
|
16335
|
+
}
|
|
16336
|
+
return true;
|
|
16337
|
+
}
|
|
16254
16338
|
if (currentRole === "owner") {
|
|
16255
16339
|
return ["it", "plant_head", "supervisor"].includes(role);
|
|
16256
16340
|
}
|
|
@@ -16284,40 +16368,133 @@ function useTeamManagementPermissions() {
|
|
|
16284
16368
|
}
|
|
16285
16369
|
function useUserLineAccess(configLineIds) {
|
|
16286
16370
|
const { user } = useAuth();
|
|
16371
|
+
const { lines: dbLines } = useLines();
|
|
16287
16372
|
const { accessibleLineIds, defaultLineId } = React26.useMemo(() => {
|
|
16373
|
+
const dbLineIds = dbLines.map((line) => line.id);
|
|
16374
|
+
const dbLineIdSet = new Set(dbLineIds);
|
|
16375
|
+
const fallbackAllLineIds = configLineIds && configLineIds.length > 0 ? configLineIds : dbLineIds;
|
|
16376
|
+
const normalizeIds = (value) => {
|
|
16377
|
+
if (Array.isArray(value)) {
|
|
16378
|
+
return value.filter((id3) => typeof id3 === "string" && !!id3);
|
|
16379
|
+
}
|
|
16380
|
+
if (typeof value === "string" && value) {
|
|
16381
|
+
return [value];
|
|
16382
|
+
}
|
|
16383
|
+
return [];
|
|
16384
|
+
};
|
|
16385
|
+
const uniqueIds = (ids) => Array.from(new Set(ids.filter(Boolean)));
|
|
16386
|
+
const filterToEnabledDb = (lineIds, options = {}) => {
|
|
16387
|
+
const deduped = uniqueIds(lineIds);
|
|
16388
|
+
if (dbLineIdSet.size === 0) {
|
|
16389
|
+
return options.allowWhenDbUnavailable ? deduped : [];
|
|
16390
|
+
}
|
|
16391
|
+
return deduped.filter((lineId) => dbLineIdSet.has(lineId));
|
|
16392
|
+
};
|
|
16288
16393
|
if (!user) {
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
|
|
16394
|
+
const fallback = filterToEnabledDb(fallbackAllLineIds, { allowWhenDbUnavailable: true });
|
|
16395
|
+
return { accessibleLineIds: fallback, defaultLineId: fallback[0] };
|
|
16396
|
+
}
|
|
16397
|
+
const configSet = new Set(configLineIds || []);
|
|
16398
|
+
const hasScopePayload = !!user.access_scope || !!user.scope_mode;
|
|
16399
|
+
const role = user.role_level || user.role;
|
|
16400
|
+
const scopedLineIds = normalizeIds(user.access_scope?.line_ids);
|
|
16401
|
+
const scopedFactoryIds = normalizeIds(user.access_scope?.factory_ids);
|
|
16402
|
+
const properties = user.properties || {};
|
|
16403
|
+
const propertyLineIds = normalizeIds(properties?.line_ids || properties?.line_id);
|
|
16404
|
+
const propertyFactoryIds = normalizeIds(properties?.factory_ids || properties?.factory_id);
|
|
16405
|
+
const propertyCompanyId = typeof properties?.company_id === "string" ? properties.company_id : user.company_id;
|
|
16406
|
+
const scopedCompanyId = user.access_scope?.company_id || propertyCompanyId;
|
|
16407
|
+
const isExplicitSuperAdmin = user.scope_mode === "SUPER_ADMIN" || !!user.access_scope?.is_super_admin;
|
|
16408
|
+
const isLegacyOptifyeGlobal = !hasScopePayload && role === "optifye" && propertyLineIds.length === 0 && propertyFactoryIds.length === 0 && !propertyCompanyId;
|
|
16409
|
+
const lineFactoryMap = /* @__PURE__ */ new Map();
|
|
16410
|
+
dbLines.forEach((line) => {
|
|
16411
|
+
lineFactoryMap.set(line.id, line.factory_id);
|
|
16412
|
+
});
|
|
16413
|
+
const applyConfigFilter = (lineIds) => {
|
|
16414
|
+
const dedupedLineIds = uniqueIds(lineIds);
|
|
16415
|
+
if (!configLineIds || configLineIds.length === 0) {
|
|
16416
|
+
return dedupedLineIds;
|
|
16299
16417
|
}
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16418
|
+
const filtered = dedupedLineIds.filter((id3) => configSet.has(id3));
|
|
16419
|
+
return filtered.length > 0 ? filtered : dedupedLineIds;
|
|
16420
|
+
};
|
|
16421
|
+
const resolveFactoryScopedLines = (factoryIds) => {
|
|
16422
|
+
const factorySet = new Set(factoryIds);
|
|
16423
|
+
const baseLineIds = configLineIds && configLineIds.length > 0 ? configLineIds : dbLineIds;
|
|
16424
|
+
const filtered = baseLineIds.filter((lineId) => {
|
|
16425
|
+
const factoryId = lineFactoryMap.get(lineId);
|
|
16426
|
+
return !!factoryId && factorySet.has(factoryId);
|
|
16304
16427
|
});
|
|
16428
|
+
if (filtered.length > 0) {
|
|
16429
|
+
return uniqueIds(filtered);
|
|
16430
|
+
}
|
|
16431
|
+
const fromDb = dbLines.filter((line) => !!line.factory_id && factorySet.has(line.factory_id)).map((line) => line.id);
|
|
16432
|
+
if (fromDb.length > 0) {
|
|
16433
|
+
return uniqueIds(fromDb);
|
|
16434
|
+
}
|
|
16435
|
+
return [];
|
|
16436
|
+
};
|
|
16437
|
+
if (isExplicitSuperAdmin || isLegacyOptifyeGlobal) {
|
|
16438
|
+
const all = filterToEnabledDb(fallbackAllLineIds, { allowWhenDbUnavailable: true });
|
|
16305
16439
|
return {
|
|
16306
|
-
accessibleLineIds:
|
|
16307
|
-
defaultLineId:
|
|
16440
|
+
accessibleLineIds: all,
|
|
16441
|
+
defaultLineId: all[0]
|
|
16308
16442
|
};
|
|
16309
16443
|
}
|
|
16310
|
-
if (
|
|
16444
|
+
if (scopedLineIds.length > 0) {
|
|
16445
|
+
const filtered = applyConfigFilter(scopedLineIds);
|
|
16446
|
+
const scoped = filterToEnabledDb(filtered.length > 0 ? filtered : uniqueIds(scopedLineIds));
|
|
16311
16447
|
return {
|
|
16312
|
-
accessibleLineIds:
|
|
16313
|
-
defaultLineId:
|
|
16448
|
+
accessibleLineIds: scoped,
|
|
16449
|
+
defaultLineId: scoped[0]
|
|
16314
16450
|
};
|
|
16315
16451
|
}
|
|
16316
|
-
|
|
16317
|
-
|
|
16318
|
-
|
|
16319
|
-
|
|
16320
|
-
|
|
16452
|
+
if (scopedFactoryIds.length > 0) {
|
|
16453
|
+
const scoped = filterToEnabledDb(resolveFactoryScopedLines(scopedFactoryIds));
|
|
16454
|
+
return {
|
|
16455
|
+
accessibleLineIds: scoped,
|
|
16456
|
+
defaultLineId: scoped[0]
|
|
16457
|
+
};
|
|
16458
|
+
}
|
|
16459
|
+
if (scopedCompanyId) {
|
|
16460
|
+
const dbCompanyLineIds = dbLines.filter((line) => !line.company_id || line.company_id === scopedCompanyId).map((line) => line.id);
|
|
16461
|
+
const companyScopedLines = configLineIds && configLineIds.length > 0 ? (() => {
|
|
16462
|
+
const dbCompanySet = new Set(dbCompanyLineIds);
|
|
16463
|
+
const filtered = configLineIds.filter((lineId) => dbCompanySet.has(lineId));
|
|
16464
|
+
if (filtered.length > 0) return filtered;
|
|
16465
|
+
return dbCompanyLineIds;
|
|
16466
|
+
})() : dbCompanyLineIds;
|
|
16467
|
+
const scoped = filterToEnabledDb(companyScopedLines);
|
|
16468
|
+
return {
|
|
16469
|
+
accessibleLineIds: scoped,
|
|
16470
|
+
defaultLineId: scoped[0]
|
|
16471
|
+
};
|
|
16472
|
+
}
|
|
16473
|
+
if (propertyLineIds.length > 0) {
|
|
16474
|
+
const filtered = applyConfigFilter(propertyLineIds);
|
|
16475
|
+
const scoped = filterToEnabledDb(filtered);
|
|
16476
|
+
return {
|
|
16477
|
+
accessibleLineIds: scoped,
|
|
16478
|
+
defaultLineId: scoped[0]
|
|
16479
|
+
};
|
|
16480
|
+
}
|
|
16481
|
+
if (propertyFactoryIds.length > 0) {
|
|
16482
|
+
const scoped = filterToEnabledDb(resolveFactoryScopedLines(propertyFactoryIds));
|
|
16483
|
+
return {
|
|
16484
|
+
accessibleLineIds: scoped,
|
|
16485
|
+
defaultLineId: scoped[0]
|
|
16486
|
+
};
|
|
16487
|
+
}
|
|
16488
|
+
if (role === "owner" || role === "it" || role === "optifye") {
|
|
16489
|
+
const companyScopedLines = scopedCompanyId ? dbLines.filter((line) => !line.company_id || line.company_id === scopedCompanyId).map((line) => line.id) : [];
|
|
16490
|
+
const scoped = filterToEnabledDb(companyScopedLines);
|
|
16491
|
+
return {
|
|
16492
|
+
accessibleLineIds: scoped,
|
|
16493
|
+
defaultLineId: scoped[0]
|
|
16494
|
+
};
|
|
16495
|
+
}
|
|
16496
|
+
return { accessibleLineIds: [], defaultLineId: void 0 };
|
|
16497
|
+
}, [user, configLineIds, dbLines]);
|
|
16321
16498
|
return { accessibleLineIds, defaultLineId, user };
|
|
16322
16499
|
}
|
|
16323
16500
|
function useActiveLineId(queryLineId, configLineIds) {
|
|
@@ -16509,12 +16686,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
16509
16686
|
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
16510
16687
|
const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
|
|
16511
16688
|
if (useBackend && resolvedCompanyId) {
|
|
16512
|
-
const searchParams = new URLSearchParams({
|
|
16513
|
-
company_id: resolvedCompanyId
|
|
16514
|
-
});
|
|
16515
|
-
if (lineIdsKey) {
|
|
16516
|
-
searchParams.set("line_ids", lineIdsKey);
|
|
16517
|
-
}
|
|
16689
|
+
const searchParams = new URLSearchParams({ company_id: resolvedCompanyId });
|
|
16518
16690
|
const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
|
|
16519
16691
|
(data?.supervisors || []).forEach((row) => {
|
|
16520
16692
|
const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
|
|
@@ -28334,8 +28506,52 @@ function withAccessControl(WrappedComponent2, options = {}) {
|
|
|
28334
28506
|
const WithAccessControlComponent = (props) => {
|
|
28335
28507
|
const router$1 = router.useRouter();
|
|
28336
28508
|
const { user, loading: authLoading } = useAuth();
|
|
28337
|
-
const
|
|
28338
|
-
|
|
28509
|
+
const role = user?.role_level;
|
|
28510
|
+
const scope = user?.access_scope;
|
|
28511
|
+
const isSuperAdmin = !!scope?.is_super_admin || user?.scope_mode === "SUPER_ADMIN";
|
|
28512
|
+
const getBasePath = (path) => {
|
|
28513
|
+
const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
|
|
28514
|
+
return firstSegment ? `/${firstSegment}` : "/";
|
|
28515
|
+
};
|
|
28516
|
+
const getIdFromPath = (path, key) => {
|
|
28517
|
+
const queryValue = router$1.query[key];
|
|
28518
|
+
if (typeof queryValue === "string") return queryValue;
|
|
28519
|
+
if (Array.isArray(queryValue) && queryValue.length > 0) return queryValue[0];
|
|
28520
|
+
const segments = path.split("?")[0].split("/").filter(Boolean);
|
|
28521
|
+
const keyIndex = segments.findIndex((segment) => segment.toLowerCase() === key.toLowerCase());
|
|
28522
|
+
if (keyIndex >= 0 && keyIndex < segments.length - 1) return segments[keyIndex + 1];
|
|
28523
|
+
return null;
|
|
28524
|
+
};
|
|
28525
|
+
const roleAccessMap = {
|
|
28526
|
+
optifye: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28527
|
+
owner: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28528
|
+
it: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28529
|
+
plant_head: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28530
|
+
supervisor: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/skus", "/help", "/health", "/profile", "/workspace", "/improvement-center", "/tickets"]
|
|
28531
|
+
};
|
|
28532
|
+
const canAccessPath = (path) => {
|
|
28533
|
+
if (!user || !role) return false;
|
|
28534
|
+
if (isSuperAdmin) return true;
|
|
28535
|
+
const basePath = getBasePath(path);
|
|
28536
|
+
const allowed = roleAccessMap[role] || [];
|
|
28537
|
+
if (!allowed.includes(basePath)) {
|
|
28538
|
+
return false;
|
|
28539
|
+
}
|
|
28540
|
+
const workspaceId = getIdFromPath(path, "workspace");
|
|
28541
|
+
if (workspaceId && scope?.workspace_ids?.length) {
|
|
28542
|
+
return scope.workspace_ids.includes(workspaceId);
|
|
28543
|
+
}
|
|
28544
|
+
const lineId = getIdFromPath(path, "kpis") || getIdFromPath(path, "line");
|
|
28545
|
+
if (lineId && scope?.line_ids?.length) {
|
|
28546
|
+
return scope.line_ids.includes(lineId);
|
|
28547
|
+
}
|
|
28548
|
+
const factoryId = getIdFromPath(path, "factory");
|
|
28549
|
+
if (factoryId && scope?.factory_ids?.length) {
|
|
28550
|
+
return scope.factory_ids.includes(factoryId);
|
|
28551
|
+
}
|
|
28552
|
+
return true;
|
|
28553
|
+
};
|
|
28554
|
+
const pathToCheck = requiredPath || router$1.asPath || router$1.pathname;
|
|
28339
28555
|
if (authLoading) {
|
|
28340
28556
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
28341
28557
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }),
|
|
@@ -28345,6 +28561,24 @@ function withAccessControl(WrappedComponent2, options = {}) {
|
|
|
28345
28561
|
if (!user) {
|
|
28346
28562
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 mb-4", children: "Please log in to continue" }) }) });
|
|
28347
28563
|
}
|
|
28564
|
+
const hasAccess = canAccessPath(pathToCheck);
|
|
28565
|
+
if (!hasAccess) {
|
|
28566
|
+
if (UnauthorizedComponent) {
|
|
28567
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UnauthorizedComponent, {});
|
|
28568
|
+
}
|
|
28569
|
+
if (typeof window !== "undefined") {
|
|
28570
|
+
router$1.replace(redirectTo);
|
|
28571
|
+
}
|
|
28572
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
28573
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Access Denied" }),
|
|
28574
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 mb-4", children: "You don't have permission to access this page." }),
|
|
28575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500", children: [
|
|
28576
|
+
"Your role: ",
|
|
28577
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: role || "Unknown" })
|
|
28578
|
+
] }),
|
|
28579
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Redirecting to home page..." })
|
|
28580
|
+
] }) });
|
|
28581
|
+
}
|
|
28348
28582
|
return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent2, { ...props });
|
|
28349
28583
|
};
|
|
28350
28584
|
WithAccessControlComponent.displayName = `withAccessControl(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
@@ -48658,9 +48892,29 @@ var SideNavBar = React26.memo(({
|
|
|
48658
48892
|
}) => {
|
|
48659
48893
|
const router$1 = router.useRouter();
|
|
48660
48894
|
const { navigate } = useNavigation();
|
|
48661
|
-
const { signOut } = useAuth();
|
|
48895
|
+
const { signOut, user } = useAuth();
|
|
48662
48896
|
const entityConfig = useEntityConfig();
|
|
48663
48897
|
const dashboardConfig = useDashboardConfig();
|
|
48898
|
+
const isSuperAdmin = user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin;
|
|
48899
|
+
const role = user?.role_level;
|
|
48900
|
+
const roleAccessMap = {
|
|
48901
|
+
optifye: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48902
|
+
owner: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48903
|
+
it: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48904
|
+
plant_head: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48905
|
+
supervisor: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/skus", "/help", "/health", "/profile", "/workspace", "/tickets", "/improvement-center"]
|
|
48906
|
+
};
|
|
48907
|
+
const getBasePath = React26.useCallback((path) => {
|
|
48908
|
+
const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
|
|
48909
|
+
return firstSegment ? `/${firstSegment}` : "/";
|
|
48910
|
+
}, []);
|
|
48911
|
+
const canAccessPath = React26.useCallback((path) => {
|
|
48912
|
+
if (!role) return false;
|
|
48913
|
+
if (isSuperAdmin) return true;
|
|
48914
|
+
const basePath = getBasePath(path);
|
|
48915
|
+
const allowedPaths = roleAccessMap[role] || [];
|
|
48916
|
+
return allowedPaths.includes(basePath);
|
|
48917
|
+
}, [role, isSuperAdmin, getBasePath]);
|
|
48664
48918
|
const lineId = entityConfig.defaultLineId || LINE_1_UUID;
|
|
48665
48919
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
48666
48920
|
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
@@ -48826,7 +49080,7 @@ var SideNavBar = React26.memo(({
|
|
|
48826
49080
|
const settingsTriggerRef = React26.useRef(null);
|
|
48827
49081
|
const settingsItems = React26.useMemo(() => {
|
|
48828
49082
|
const items = [
|
|
48829
|
-
{
|
|
49083
|
+
...canAccessPath("/targets") ? [{
|
|
48830
49084
|
key: "targets",
|
|
48831
49085
|
label: "Targets",
|
|
48832
49086
|
icon: outline.AdjustmentsHorizontalIcon,
|
|
@@ -48835,8 +49089,8 @@ var SideNavBar = React26.memo(({
|
|
|
48835
49089
|
setIsSettingsOpen(false);
|
|
48836
49090
|
},
|
|
48837
49091
|
isActive: pathname === "/targets" || pathname.startsWith("/targets/")
|
|
48838
|
-
},
|
|
48839
|
-
{
|
|
49092
|
+
}] : [],
|
|
49093
|
+
...canAccessPath("/shifts") ? [{
|
|
48840
49094
|
key: "shifts",
|
|
48841
49095
|
label: "Shifts",
|
|
48842
49096
|
icon: outline.ClockIcon,
|
|
@@ -48845,8 +49099,8 @@ var SideNavBar = React26.memo(({
|
|
|
48845
49099
|
setIsSettingsOpen(false);
|
|
48846
49100
|
},
|
|
48847
49101
|
isActive: pathname === "/shifts" || pathname.startsWith("/shifts/")
|
|
48848
|
-
},
|
|
48849
|
-
{
|
|
49102
|
+
}] : [],
|
|
49103
|
+
...canAccessPath("/team-management") ? [{
|
|
48850
49104
|
key: "teams",
|
|
48851
49105
|
label: "Teams",
|
|
48852
49106
|
icon: outline.UsersIcon,
|
|
@@ -48855,8 +49109,8 @@ var SideNavBar = React26.memo(({
|
|
|
48855
49109
|
setIsSettingsOpen(false);
|
|
48856
49110
|
},
|
|
48857
49111
|
isActive: pathname === "/team-management" || pathname.startsWith("/team-management/")
|
|
48858
|
-
},
|
|
48859
|
-
{
|
|
49112
|
+
}] : [],
|
|
49113
|
+
...canAccessPath("/profile") ? [{
|
|
48860
49114
|
key: "profile",
|
|
48861
49115
|
label: "Profile",
|
|
48862
49116
|
icon: outline.UserCircleIcon,
|
|
@@ -48865,9 +49119,9 @@ var SideNavBar = React26.memo(({
|
|
|
48865
49119
|
setIsSettingsOpen(false);
|
|
48866
49120
|
},
|
|
48867
49121
|
isActive: pathname === "/profile" || pathname.startsWith("/profile/")
|
|
48868
|
-
}
|
|
49122
|
+
}] : []
|
|
48869
49123
|
];
|
|
48870
|
-
if (ticketsEnabled) {
|
|
49124
|
+
if (ticketsEnabled && canAccessPath("/tickets")) {
|
|
48871
49125
|
items.push({
|
|
48872
49126
|
key: "tickets",
|
|
48873
49127
|
label: "Tickets",
|
|
@@ -48879,18 +49133,20 @@ var SideNavBar = React26.memo(({
|
|
|
48879
49133
|
isActive: pathname === "/tickets" || pathname.startsWith("/tickets/")
|
|
48880
49134
|
});
|
|
48881
49135
|
}
|
|
48882
|
-
|
|
48883
|
-
|
|
48884
|
-
|
|
48885
|
-
|
|
48886
|
-
|
|
48887
|
-
|
|
48888
|
-
|
|
48889
|
-
|
|
48890
|
-
|
|
48891
|
-
|
|
49136
|
+
if (canAccessPath("/help")) {
|
|
49137
|
+
items.push({
|
|
49138
|
+
key: "help",
|
|
49139
|
+
label: "Help",
|
|
49140
|
+
icon: outline.QuestionMarkCircleIcon,
|
|
49141
|
+
onClick: () => {
|
|
49142
|
+
handleHelpClick();
|
|
49143
|
+
setIsSettingsOpen(false);
|
|
49144
|
+
},
|
|
49145
|
+
isActive: pathname === "/help" || pathname.startsWith("/help/")
|
|
49146
|
+
});
|
|
49147
|
+
}
|
|
48892
49148
|
return items;
|
|
48893
|
-
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleHelpClick, pathname, ticketsEnabled]);
|
|
49149
|
+
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleHelpClick, pathname, ticketsEnabled, canAccessPath]);
|
|
48894
49150
|
const handleLogout = React26.useCallback(async () => {
|
|
48895
49151
|
setIsSettingsOpen(false);
|
|
48896
49152
|
try {
|
|
@@ -48928,7 +49184,7 @@ var SideNavBar = React26.memo(({
|
|
|
48928
49184
|
}
|
|
48929
49185
|
) }),
|
|
48930
49186
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: [
|
|
48931
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49187
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6", children: canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48932
49188
|
"button",
|
|
48933
49189
|
{
|
|
48934
49190
|
onClick: handleHomeClick,
|
|
@@ -48944,7 +49200,7 @@ var SideNavBar = React26.memo(({
|
|
|
48944
49200
|
}
|
|
48945
49201
|
) }),
|
|
48946
49202
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
48947
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49203
|
+
canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48948
49204
|
"button",
|
|
48949
49205
|
{
|
|
48950
49206
|
onClick: handleLeaderboardClick,
|
|
@@ -48959,7 +49215,7 @@ var SideNavBar = React26.memo(({
|
|
|
48959
49215
|
]
|
|
48960
49216
|
}
|
|
48961
49217
|
),
|
|
48962
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49218
|
+
canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48963
49219
|
"button",
|
|
48964
49220
|
{
|
|
48965
49221
|
onClick: handleKPIsClick,
|
|
@@ -48974,7 +49230,7 @@ var SideNavBar = React26.memo(({
|
|
|
48974
49230
|
]
|
|
48975
49231
|
}
|
|
48976
49232
|
),
|
|
48977
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49233
|
+
canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48978
49234
|
"button",
|
|
48979
49235
|
{
|
|
48980
49236
|
onClick: handleImprovementClick,
|
|
@@ -48990,7 +49246,7 @@ var SideNavBar = React26.memo(({
|
|
|
48990
49246
|
}
|
|
48991
49247
|
),
|
|
48992
49248
|
showSupervisorManagement,
|
|
48993
|
-
skuEnabled &&
|
|
49249
|
+
skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48994
49250
|
"button",
|
|
48995
49251
|
{
|
|
48996
49252
|
onClick: handleSKUsClick,
|
|
@@ -49005,7 +49261,7 @@ var SideNavBar = React26.memo(({
|
|
|
49005
49261
|
]
|
|
49006
49262
|
}
|
|
49007
49263
|
),
|
|
49008
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49264
|
+
canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49009
49265
|
"button",
|
|
49010
49266
|
{
|
|
49011
49267
|
onClick: handleHealthClick,
|
|
@@ -49022,7 +49278,7 @@ var SideNavBar = React26.memo(({
|
|
|
49022
49278
|
)
|
|
49023
49279
|
] })
|
|
49024
49280
|
] }),
|
|
49025
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full py-5 px-4 border-t border-gray-100 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49281
|
+
settingsItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full py-5 px-4 border-t border-gray-100 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49026
49282
|
"button",
|
|
49027
49283
|
{
|
|
49028
49284
|
ref: settingsTriggerRef,
|
|
@@ -49060,7 +49316,7 @@ var SideNavBar = React26.memo(({
|
|
|
49060
49316
|
};
|
|
49061
49317
|
};
|
|
49062
49318
|
return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "px-5 py-6", children: [
|
|
49063
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49319
|
+
canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49064
49320
|
"button",
|
|
49065
49321
|
{
|
|
49066
49322
|
onClick: handleMobileNavClick(handleHomeClick),
|
|
@@ -49073,7 +49329,7 @@ var SideNavBar = React26.memo(({
|
|
|
49073
49329
|
}
|
|
49074
49330
|
),
|
|
49075
49331
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 space-y-2", children: [
|
|
49076
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49332
|
+
canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49077
49333
|
"button",
|
|
49078
49334
|
{
|
|
49079
49335
|
onClick: handleMobileNavClick(handleLeaderboardClick),
|
|
@@ -49085,7 +49341,7 @@ var SideNavBar = React26.memo(({
|
|
|
49085
49341
|
]
|
|
49086
49342
|
}
|
|
49087
49343
|
),
|
|
49088
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49344
|
+
canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49089
49345
|
"button",
|
|
49090
49346
|
{
|
|
49091
49347
|
onClick: handleMobileNavClick(handleKPIsClick),
|
|
@@ -49097,7 +49353,7 @@ var SideNavBar = React26.memo(({
|
|
|
49097
49353
|
]
|
|
49098
49354
|
}
|
|
49099
49355
|
),
|
|
49100
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49356
|
+
canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49101
49357
|
"button",
|
|
49102
49358
|
{
|
|
49103
49359
|
onClick: handleMobileNavClick(handleImprovementClick),
|
|
@@ -49110,7 +49366,7 @@ var SideNavBar = React26.memo(({
|
|
|
49110
49366
|
}
|
|
49111
49367
|
),
|
|
49112
49368
|
showSupervisorManagement,
|
|
49113
|
-
skuEnabled &&
|
|
49369
|
+
skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49114
49370
|
"button",
|
|
49115
49371
|
{
|
|
49116
49372
|
onClick: handleMobileNavClick(handleSKUsClick),
|
|
@@ -49122,7 +49378,7 @@ var SideNavBar = React26.memo(({
|
|
|
49122
49378
|
]
|
|
49123
49379
|
}
|
|
49124
49380
|
),
|
|
49125
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49381
|
+
canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49126
49382
|
"button",
|
|
49127
49383
|
{
|
|
49128
49384
|
onClick: handleMobileNavClick(handleHealthClick),
|
|
@@ -49135,10 +49391,10 @@ var SideNavBar = React26.memo(({
|
|
|
49135
49391
|
}
|
|
49136
49392
|
)
|
|
49137
49393
|
] }),
|
|
49138
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 pt-6 border-t border-gray-100", children: [
|
|
49394
|
+
settingsItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 pt-6 border-t border-gray-100", children: [
|
|
49139
49395
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "px-5 mb-3 text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Settings & Support" }),
|
|
49140
49396
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
49141
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49397
|
+
canAccessPath("/targets") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49142
49398
|
"button",
|
|
49143
49399
|
{
|
|
49144
49400
|
onClick: handleMobileNavClick(handleTargetsClick),
|
|
@@ -49150,7 +49406,7 @@ var SideNavBar = React26.memo(({
|
|
|
49150
49406
|
]
|
|
49151
49407
|
}
|
|
49152
49408
|
),
|
|
49153
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49409
|
+
canAccessPath("/shifts") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49154
49410
|
"button",
|
|
49155
49411
|
{
|
|
49156
49412
|
onClick: handleMobileNavClick(handleShiftsClick),
|
|
@@ -49162,7 +49418,7 @@ var SideNavBar = React26.memo(({
|
|
|
49162
49418
|
]
|
|
49163
49419
|
}
|
|
49164
49420
|
),
|
|
49165
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49421
|
+
canAccessPath("/team-management") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49166
49422
|
"button",
|
|
49167
49423
|
{
|
|
49168
49424
|
onClick: handleMobileNavClick(handleTeamManagementClick),
|
|
@@ -49174,7 +49430,7 @@ var SideNavBar = React26.memo(({
|
|
|
49174
49430
|
]
|
|
49175
49431
|
}
|
|
49176
49432
|
),
|
|
49177
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49433
|
+
canAccessPath("/profile") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49178
49434
|
"button",
|
|
49179
49435
|
{
|
|
49180
49436
|
onClick: handleMobileNavClick(handleProfileClick),
|
|
@@ -49186,7 +49442,7 @@ var SideNavBar = React26.memo(({
|
|
|
49186
49442
|
]
|
|
49187
49443
|
}
|
|
49188
49444
|
),
|
|
49189
|
-
ticketsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49445
|
+
ticketsEnabled && canAccessPath("/tickets") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49190
49446
|
"button",
|
|
49191
49447
|
{
|
|
49192
49448
|
onClick: handleMobileNavClick(handleTicketsClick),
|
|
@@ -49198,7 +49454,7 @@ var SideNavBar = React26.memo(({
|
|
|
49198
49454
|
]
|
|
49199
49455
|
}
|
|
49200
49456
|
),
|
|
49201
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49457
|
+
canAccessPath("/help") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49202
49458
|
"button",
|
|
49203
49459
|
{
|
|
49204
49460
|
onClick: handleMobileNavClick(handleHelpClick),
|
|
@@ -51458,9 +51714,19 @@ var InviteUserDialog = ({
|
|
|
51458
51714
|
const [factorySearch, setFactorySearch] = React26.useState("");
|
|
51459
51715
|
const [isSubmitting, setIsSubmitting] = React26.useState(false);
|
|
51460
51716
|
const [error, setError] = React26.useState(null);
|
|
51717
|
+
const isSuperAdminOptifye = user?.role_level === "optifye" && (user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin);
|
|
51718
|
+
const canInviteOwner = isSuperAdminOptifye;
|
|
51461
51719
|
const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
51462
51720
|
const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
51463
51721
|
const canInviteSupervisor = ["owner", "it", "plant_head", "optifye"].includes(user?.role_level || "");
|
|
51722
|
+
const invitableRoles = React26.useMemo(() => {
|
|
51723
|
+
const roles = [];
|
|
51724
|
+
if (canInviteOwner) roles.push("owner");
|
|
51725
|
+
if (canInviteIT) roles.push("it");
|
|
51726
|
+
if (canInvitePlantHead) roles.push("plant_head");
|
|
51727
|
+
if (canInviteSupervisor) roles.push("supervisor");
|
|
51728
|
+
return roles;
|
|
51729
|
+
}, [canInviteOwner, canInviteIT, canInvitePlantHead, canInviteSupervisor]);
|
|
51464
51730
|
const filteredLines = React26.useMemo(() => {
|
|
51465
51731
|
const search = lineSearch.trim().toLowerCase();
|
|
51466
51732
|
if (!search) return availableLines;
|
|
@@ -51492,6 +51758,14 @@ var InviteUserDialog = ({
|
|
|
51492
51758
|
setError(null);
|
|
51493
51759
|
}
|
|
51494
51760
|
}, [isOpen]);
|
|
51761
|
+
React26.useEffect(() => {
|
|
51762
|
+
if (!isOpen || invitableRoles.length === 0) {
|
|
51763
|
+
return;
|
|
51764
|
+
}
|
|
51765
|
+
if (!invitableRoles.includes(selectedRole)) {
|
|
51766
|
+
setSelectedRole(invitableRoles[0]);
|
|
51767
|
+
}
|
|
51768
|
+
}, [isOpen, invitableRoles, selectedRole]);
|
|
51495
51769
|
const validateEmail = (email2) => {
|
|
51496
51770
|
const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/;
|
|
51497
51771
|
return emailRegex.test(email2);
|
|
@@ -51521,6 +51795,14 @@ var InviteUserDialog = ({
|
|
|
51521
51795
|
setError("Please enter a valid email address");
|
|
51522
51796
|
return;
|
|
51523
51797
|
}
|
|
51798
|
+
if (!invitableRoles.includes(selectedRole)) {
|
|
51799
|
+
setError("You do not have permission to assign this role.");
|
|
51800
|
+
return;
|
|
51801
|
+
}
|
|
51802
|
+
if (selectedRole === "owner" && !canInviteOwner) {
|
|
51803
|
+
setError("Only super-admin Optifye users can create owner users.");
|
|
51804
|
+
return;
|
|
51805
|
+
}
|
|
51524
51806
|
const companyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
51525
51807
|
if (!companyId) {
|
|
51526
51808
|
setError("Company ID not found. Please refresh and try again.");
|
|
@@ -51701,6 +51983,33 @@ var InviteUserDialog = ({
|
|
|
51701
51983
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
|
|
51702
51984
|
] }),
|
|
51703
51985
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
51986
|
+
canInviteOwner && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51987
|
+
"label",
|
|
51988
|
+
{
|
|
51989
|
+
className: cn(
|
|
51990
|
+
"flex items-start gap-3 p-4 border-2 rounded-lg cursor-pointer transition-all duration-200",
|
|
51991
|
+
selectedRole === "owner" ? "border-amber-500 bg-amber-50" : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
|
51992
|
+
),
|
|
51993
|
+
children: [
|
|
51994
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51995
|
+
"input",
|
|
51996
|
+
{
|
|
51997
|
+
type: "radio",
|
|
51998
|
+
name: "role",
|
|
51999
|
+
value: "owner",
|
|
52000
|
+
checked: selectedRole === "owner",
|
|
52001
|
+
onChange: (e) => setSelectedRole(e.target.value),
|
|
52002
|
+
className: "mt-1",
|
|
52003
|
+
disabled: isSubmitting
|
|
52004
|
+
}
|
|
52005
|
+
),
|
|
52006
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
52007
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 mb-1", children: /* @__PURE__ */ jsxRuntime.jsx(RoleBadge_default, { role: "owner", size: "sm" }) }),
|
|
52008
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: "Company owner access. Full management permissions for the selected company dashboard." })
|
|
52009
|
+
] })
|
|
52010
|
+
]
|
|
52011
|
+
}
|
|
52012
|
+
),
|
|
51704
52013
|
canInviteIT && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51705
52014
|
"label",
|
|
51706
52015
|
{
|
|
@@ -53572,11 +53881,32 @@ var UserManagementTable = ({
|
|
|
53572
53881
|
setSortDirection("asc");
|
|
53573
53882
|
}
|
|
53574
53883
|
};
|
|
53884
|
+
const getFactoryIdsFromUser = (user) => {
|
|
53885
|
+
const properties = user.properties || {};
|
|
53886
|
+
const rawFactoryIds = properties.factory_ids ?? properties.factory_id;
|
|
53887
|
+
if (Array.isArray(rawFactoryIds)) {
|
|
53888
|
+
return rawFactoryIds.filter((factoryId) => typeof factoryId === "string" && factoryId.length > 0);
|
|
53889
|
+
}
|
|
53890
|
+
if (typeof rawFactoryIds === "string" && rawFactoryIds.length > 0) {
|
|
53891
|
+
return [rawFactoryIds];
|
|
53892
|
+
}
|
|
53893
|
+
return [];
|
|
53894
|
+
};
|
|
53575
53895
|
const formatAssignments = (user) => {
|
|
53576
53896
|
if (user.role_level === "owner" || user.role_level === "it") return "Company-wide";
|
|
53577
53897
|
if (user.role_level === "optifye") return "All companies";
|
|
53578
|
-
if (user.role_level === "plant_head"
|
|
53579
|
-
|
|
53898
|
+
if (user.role_level === "plant_head") {
|
|
53899
|
+
if (user.assigned_factories && user.assigned_factories.length > 0) {
|
|
53900
|
+
return user.assigned_factories.join(", ");
|
|
53901
|
+
}
|
|
53902
|
+
const assignedFactoryIds = getFactoryIdsFromUser(user);
|
|
53903
|
+
if (assignedFactoryIds.length === 1) {
|
|
53904
|
+
return "1 factory assigned";
|
|
53905
|
+
}
|
|
53906
|
+
if (assignedFactoryIds.length > 1) {
|
|
53907
|
+
return `${assignedFactoryIds.length} factories assigned`;
|
|
53908
|
+
}
|
|
53909
|
+
return "No factories assigned";
|
|
53580
53910
|
}
|
|
53581
53911
|
if (user.role_level === "supervisor" && user.assigned_lines) {
|
|
53582
53912
|
return user.assigned_lines.join(", ") || "No lines assigned";
|
|
@@ -53768,22 +54098,28 @@ var UserManagementTable = ({
|
|
|
53768
54098
|
onUpdate: onLineAssignmentUpdate || (async () => {
|
|
53769
54099
|
})
|
|
53770
54100
|
}
|
|
53771
|
-
) : user.role_level === "plant_head" ?
|
|
53772
|
-
|
|
53773
|
-
{
|
|
53774
|
-
|
|
53775
|
-
currentFactoryIds: user.properties?.factory_ids || [],
|
|
53776
|
-
availableFactories: (
|
|
53777
|
-
// Filter factories to only show those from the target user's company
|
|
53778
|
-
user.properties?.company_id ? availableFactories.filter(
|
|
53779
|
-
(factory) => factory.company_id === user.properties?.company_id
|
|
53780
|
-
) : availableFactories
|
|
53781
|
-
),
|
|
53782
|
-
canEdit: permissions.canAssignFactories(user) && !!onFactoryAssignmentUpdate,
|
|
53783
|
-
onUpdate: onFactoryAssignmentUpdate || (async () => {
|
|
53784
|
-
})
|
|
54101
|
+
) : user.role_level === "plant_head" ? (() => {
|
|
54102
|
+
const canEditFactoryAssignments = permissions.canAssignFactories(user) && !!onFactoryAssignmentUpdate;
|
|
54103
|
+
if (!canEditFactoryAssignments) {
|
|
54104
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) });
|
|
53785
54105
|
}
|
|
53786
|
-
|
|
54106
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
54107
|
+
FactoryAssignmentDropdown,
|
|
54108
|
+
{
|
|
54109
|
+
userId: user.user_id,
|
|
54110
|
+
currentFactoryIds: getFactoryIdsFromUser(user),
|
|
54111
|
+
availableFactories: (
|
|
54112
|
+
// Filter factories to only show those from the target user's company
|
|
54113
|
+
user.properties?.company_id ? availableFactories.filter(
|
|
54114
|
+
(factory) => factory.company_id === user.properties?.company_id
|
|
54115
|
+
) : availableFactories
|
|
54116
|
+
),
|
|
54117
|
+
canEdit: canEditFactoryAssignments,
|
|
54118
|
+
onUpdate: onFactoryAssignmentUpdate || (async () => {
|
|
54119
|
+
})
|
|
54120
|
+
}
|
|
54121
|
+
);
|
|
54122
|
+
})() : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
53787
54123
|
showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4", children: user.role_level === "plant_head" || user.role_level === "supervisor" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
53788
54124
|
"button",
|
|
53789
54125
|
{
|
|
@@ -54948,52 +55284,18 @@ function HomeView({
|
|
|
54948
55284
|
});
|
|
54949
55285
|
return merged;
|
|
54950
55286
|
}, [lineNames, dbLines]);
|
|
54951
|
-
const
|
|
54952
|
-
|
|
55287
|
+
const enabledLineIdSet = React26.useMemo(
|
|
55288
|
+
() => new Set(dbLines.filter((line) => line.enable).map((line) => line.id)),
|
|
55289
|
+
[dbLines]
|
|
55290
|
+
);
|
|
54953
55291
|
const isSupervisor = user?.role_level === "supervisor";
|
|
54954
|
-
const assignedLineIds = React26.useMemo(() => {
|
|
54955
|
-
const rawLineIds = user?.properties?.line_id || user?.properties?.line_ids || [];
|
|
54956
|
-
return Array.isArray(rawLineIds) ? rawLineIds : [];
|
|
54957
|
-
}, [user]);
|
|
54958
|
-
const assignedLineIdsInConfig = React26.useMemo(() => {
|
|
54959
|
-
if (assignedLineIds.length === 0) {
|
|
54960
|
-
return [];
|
|
54961
|
-
}
|
|
54962
|
-
return assignedLineIds.filter((id3) => allLineIds.includes(id3));
|
|
54963
|
-
}, [assignedLineIds, allLineIds]);
|
|
54964
|
-
const assignedFactoryIds = React26.useMemo(() => {
|
|
54965
|
-
const rawFactoryIds = user?.properties?.factory_id || user?.properties?.factory_ids || [];
|
|
54966
|
-
return Array.isArray(rawFactoryIds) ? rawFactoryIds : [];
|
|
54967
|
-
}, [user]);
|
|
54968
|
-
const lineFactoryMap = React26.useMemo(() => {
|
|
54969
|
-
const map = /* @__PURE__ */ new Map();
|
|
54970
|
-
dbLines.forEach((line) => {
|
|
54971
|
-
map.set(line.id, line.factory_id);
|
|
54972
|
-
});
|
|
54973
|
-
return map;
|
|
54974
|
-
}, [dbLines]);
|
|
54975
|
-
const plantHeadLineIds = React26.useMemo(() => {
|
|
54976
|
-
if (!isPlantHead || assignedFactoryIds.length === 0) {
|
|
54977
|
-
return [];
|
|
54978
|
-
}
|
|
54979
|
-
const assignedFactoryIdSet = new Set(assignedFactoryIds);
|
|
54980
|
-
return allLineIds.filter((lineId) => assignedFactoryIdSet.has(lineFactoryMap.get(lineId) || ""));
|
|
54981
|
-
}, [isPlantHead, assignedFactoryIds, allLineIds, lineFactoryMap]);
|
|
54982
55292
|
const visibleLineIds = React26.useMemo(() => {
|
|
54983
|
-
|
|
54984
|
-
|
|
55293
|
+
const scoped = Array.from(new Set(allLineIds.filter(Boolean)));
|
|
55294
|
+
if (enabledLineIdSet.size === 0) {
|
|
55295
|
+
return scoped;
|
|
54985
55296
|
}
|
|
54986
|
-
|
|
54987
|
-
|
|
54988
|
-
plantHeadLineIds.forEach((lineId) => combinedLineIds.add(lineId));
|
|
54989
|
-
assignedLineIdsInConfig.forEach((lineId) => combinedLineIds.add(lineId));
|
|
54990
|
-
return Array.from(combinedLineIds);
|
|
54991
|
-
}
|
|
54992
|
-
if (isSupervisor) {
|
|
54993
|
-
return assignedLineIdsInConfig;
|
|
54994
|
-
}
|
|
54995
|
-
return allLineIds;
|
|
54996
|
-
}, [isOwner, isPlantHead, isSupervisor, allLineIds, plantHeadLineIds, assignedLineIdsInConfig]);
|
|
55297
|
+
return scoped.filter((lineId) => enabledLineIdSet.has(lineId));
|
|
55298
|
+
}, [allLineIds, enabledLineIdSet]);
|
|
54997
55299
|
const fallbackLineId = visibleLineIds[0] || defaultLineId;
|
|
54998
55300
|
const defaultHomeLineId = fallbackLineId;
|
|
54999
55301
|
const availableLineIds = React26.useMemo(() => {
|
|
@@ -55020,7 +55322,7 @@ function HomeView({
|
|
|
55020
55322
|
return defaultHomeLineId;
|
|
55021
55323
|
});
|
|
55022
55324
|
React26.useEffect(() => {
|
|
55023
|
-
if (!user ||
|
|
55325
|
+
if (!user || availableLineIds.length === 0) {
|
|
55024
55326
|
return;
|
|
55025
55327
|
}
|
|
55026
55328
|
try {
|
|
@@ -55034,13 +55336,14 @@ function HomeView({
|
|
|
55034
55336
|
} catch (error) {
|
|
55035
55337
|
console.warn("Failed to read line filter from sessionStorage:", error);
|
|
55036
55338
|
}
|
|
55339
|
+
if (availableLineIds.includes(selectedLineId)) {
|
|
55340
|
+
return;
|
|
55341
|
+
}
|
|
55037
55342
|
if (defaultHomeLineId !== selectedLineId) {
|
|
55038
55343
|
setSelectedLineId(defaultHomeLineId);
|
|
55039
55344
|
}
|
|
55040
55345
|
}, [
|
|
55041
55346
|
user,
|
|
55042
|
-
isSupervisor,
|
|
55043
|
-
isPlantHead,
|
|
55044
55347
|
availableLineIds,
|
|
55045
55348
|
defaultHomeLineId,
|
|
55046
55349
|
selectedLineId,
|
|
@@ -55059,14 +55362,14 @@ function HomeView({
|
|
|
55059
55362
|
const [diagnoses, setDiagnoses] = React26.useState([]);
|
|
55060
55363
|
const timezone = useAppTimezone();
|
|
55061
55364
|
const { shiftConfigMap: lineShiftConfigs } = useMultiLineShiftConfigs(
|
|
55062
|
-
|
|
55365
|
+
visibleLineIds,
|
|
55063
55366
|
dashboardConfig?.shiftConfig
|
|
55064
55367
|
);
|
|
55065
55368
|
React26.useEffect(() => {
|
|
55066
55369
|
const initDisplayNames = async () => {
|
|
55067
55370
|
try {
|
|
55068
55371
|
if (selectedLineId === factoryViewId) {
|
|
55069
|
-
for (const lineId of
|
|
55372
|
+
for (const lineId of visibleLineIds) {
|
|
55070
55373
|
await preInitializeWorkspaceDisplayNames(lineId);
|
|
55071
55374
|
}
|
|
55072
55375
|
} else {
|
|
@@ -55079,7 +55382,7 @@ function HomeView({
|
|
|
55079
55382
|
}
|
|
55080
55383
|
};
|
|
55081
55384
|
initDisplayNames();
|
|
55082
|
-
}, [selectedLineId, factoryViewId,
|
|
55385
|
+
}, [selectedLineId, factoryViewId, visibleLineIds]);
|
|
55083
55386
|
const displayNameLineId = selectedLineId === factoryViewId ? void 0 : selectedLineId;
|
|
55084
55387
|
const {
|
|
55085
55388
|
displayNames: workspaceDisplayNames,
|
|
@@ -58830,8 +59133,32 @@ var KPIsOverviewView = ({
|
|
|
58830
59133
|
const dbTimezone = useAppTimezone();
|
|
58831
59134
|
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
58832
59135
|
const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
|
|
58833
|
-
const
|
|
58834
|
-
const
|
|
59136
|
+
const isSuperAdmin = user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin;
|
|
59137
|
+
const scopedLineIds = React26__namespace.default.useMemo(
|
|
59138
|
+
() => Array.isArray(user?.access_scope?.line_ids) ? user.access_scope.line_ids.filter((lineId) => typeof lineId === "string" && lineId.length > 0) : [],
|
|
59139
|
+
[user?.access_scope?.line_ids]
|
|
59140
|
+
);
|
|
59141
|
+
const hasCanonicalScope = !!user?.scope_mode || !!user?.access_scope;
|
|
59142
|
+
const scopeRole = (user?.role_level || user?.role || "").toLowerCase();
|
|
59143
|
+
const isStrictLineScopedRole = scopeRole === "supervisor" || scopeRole === "plant_head";
|
|
59144
|
+
const resolvedAssignedLineIds = React26__namespace.default.useMemo(() => {
|
|
59145
|
+
if (isSuperAdmin) return [];
|
|
59146
|
+
if (scopedLineIds.length > 0) return scopedLineIds;
|
|
59147
|
+
if (lineIds && lineIds.length > 0) return lineIds;
|
|
59148
|
+
if (isStrictLineScopedRole && hasCanonicalScope) return [];
|
|
59149
|
+
return [];
|
|
59150
|
+
}, [isSuperAdmin, scopedLineIds, lineIds, isStrictLineScopedRole, hasCanonicalScope]);
|
|
59151
|
+
const assignedLineIdSet = React26__namespace.default.useMemo(
|
|
59152
|
+
() => new Set(resolvedAssignedLineIds),
|
|
59153
|
+
[resolvedAssignedLineIds]
|
|
59154
|
+
);
|
|
59155
|
+
const metricsLineIds = React26__namespace.default.useMemo(() => {
|
|
59156
|
+
if (isSuperAdmin) {
|
|
59157
|
+
return lineIds ?? [];
|
|
59158
|
+
}
|
|
59159
|
+
return resolvedAssignedLineIds;
|
|
59160
|
+
}, [isSuperAdmin, lineIds, resolvedAssignedLineIds]);
|
|
59161
|
+
const assignedLineIdsForLeaderboard = isSuperAdmin ? void 0 : resolvedAssignedLineIds;
|
|
58835
59162
|
const leaderboardLinesForView = React26__namespace.default.useMemo(() => {
|
|
58836
59163
|
const targetMode = viewType === "machine" ? "uptime" : "output";
|
|
58837
59164
|
return leaderboardLines.filter((line) => (line.monitoring_mode ?? "output") === targetMode);
|
|
@@ -58883,7 +59210,7 @@ var KPIsOverviewView = ({
|
|
|
58883
59210
|
error: metricsError
|
|
58884
59211
|
} = useDashboardMetrics({
|
|
58885
59212
|
lineId: factoryViewId,
|
|
58886
|
-
userAccessibleLineIds:
|
|
59213
|
+
userAccessibleLineIds: metricsLineIds
|
|
58887
59214
|
});
|
|
58888
59215
|
const defaultKPIs = React26__namespace.default.useMemo(() => createDefaultKPIs(), []);
|
|
58889
59216
|
const kpisByLineId = React26__namespace.default.useMemo(() => {
|
|
@@ -58937,16 +59264,16 @@ var KPIsOverviewView = ({
|
|
|
58937
59264
|
console.log("[KPIsOverviewView] Fetching lines with lineIds filter:", lineIds);
|
|
58938
59265
|
const allLines = await dashboardService.getAllLines();
|
|
58939
59266
|
let filteredLines = allLines;
|
|
58940
|
-
if (
|
|
58941
|
-
filteredLines = allLines.filter((line) =>
|
|
58942
|
-
console.log("[KPIsOverviewView]
|
|
59267
|
+
if (!isSuperAdmin) {
|
|
59268
|
+
filteredLines = allLines.filter((line) => assignedLineIdSet.has(line.id));
|
|
59269
|
+
console.log("[KPIsOverviewView] Applied scoped line filter:", {
|
|
58943
59270
|
total: allLines.length,
|
|
58944
59271
|
filtered: filteredLines.length,
|
|
58945
|
-
|
|
59272
|
+
allowedLineIds: resolvedAssignedLineIds,
|
|
58946
59273
|
filteredLineIds: filteredLines.map((l) => l.id)
|
|
58947
59274
|
});
|
|
58948
59275
|
} else {
|
|
58949
|
-
console.log("[KPIsOverviewView]
|
|
59276
|
+
console.log("[KPIsOverviewView] Super admin view, showing all lines:", allLines.length);
|
|
58950
59277
|
}
|
|
58951
59278
|
setLines(filteredLines);
|
|
58952
59279
|
} catch (err) {
|
|
@@ -58957,34 +59284,36 @@ var KPIsOverviewView = ({
|
|
|
58957
59284
|
}
|
|
58958
59285
|
};
|
|
58959
59286
|
fetchLines();
|
|
58960
|
-
}, [supabase, dashboardConfig, lineIds]);
|
|
59287
|
+
}, [supabase, dashboardConfig, lineIds, isSuperAdmin, assignedLineIdSet, resolvedAssignedLineIds]);
|
|
58961
59288
|
React26.useEffect(() => {
|
|
58962
59289
|
let isMounted = true;
|
|
58963
59290
|
const fetchLeaderboardLines = async () => {
|
|
58964
59291
|
if (!supabase || !resolvedCompanyId) {
|
|
58965
|
-
setLeaderboardLines(lines);
|
|
59292
|
+
setLeaderboardLines(lines.filter((line) => line.enable !== false));
|
|
58966
59293
|
return;
|
|
58967
59294
|
}
|
|
58968
59295
|
setLeaderboardLinesLoading(true);
|
|
58969
59296
|
try {
|
|
58970
|
-
const
|
|
58971
|
-
|
|
59297
|
+
const data = await fetchBackendJson(
|
|
59298
|
+
supabase,
|
|
59299
|
+
`/api/dashboard/leaderboard-lines?company_id=${encodeURIComponent(resolvedCompanyId)}`
|
|
59300
|
+
);
|
|
58972
59301
|
if (!isMounted) return;
|
|
58973
|
-
const transformed =
|
|
59302
|
+
const transformed = (data.lines || []).filter((line) => line.enable !== false).map((line) => ({
|
|
58974
59303
|
id: line.id,
|
|
58975
|
-
line_name: line.
|
|
58976
|
-
factory_id: line.
|
|
59304
|
+
line_name: line.line_name,
|
|
59305
|
+
factory_id: line.factory_id || "",
|
|
58977
59306
|
factory_name: "N/A",
|
|
58978
|
-
company_id: line.
|
|
59307
|
+
company_id: line.company_id,
|
|
58979
59308
|
company_name: "",
|
|
58980
|
-
enable: line.
|
|
58981
|
-
monitoring_mode: line.
|
|
59309
|
+
enable: line.enable ?? true,
|
|
59310
|
+
monitoring_mode: line.monitoring_mode ?? "output"
|
|
58982
59311
|
}));
|
|
58983
59312
|
setLeaderboardLines(transformed);
|
|
58984
59313
|
} catch (err) {
|
|
58985
59314
|
console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
|
|
58986
59315
|
if (!isMounted) return;
|
|
58987
|
-
setLeaderboardLines(lines);
|
|
59316
|
+
setLeaderboardLines(lines.filter((line) => line.enable !== false));
|
|
58988
59317
|
} finally {
|
|
58989
59318
|
if (isMounted) setLeaderboardLinesLoading(false);
|
|
58990
59319
|
}
|
|
@@ -59065,11 +59394,10 @@ var KPIsOverviewView = ({
|
|
|
59065
59394
|
lineMode: viewType === "machine" ? "uptime" : "output"
|
|
59066
59395
|
});
|
|
59067
59396
|
const nextMap = /* @__PURE__ */ new Map();
|
|
59397
|
+
targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
|
|
59068
59398
|
entries.forEach((entry) => {
|
|
59069
59399
|
const value = Number(entry.avg_efficiency);
|
|
59070
|
-
|
|
59071
|
-
nextMap.set(entry.line_id, value);
|
|
59072
|
-
}
|
|
59400
|
+
nextMap.set(entry.line_id, Number.isFinite(value) ? value : 0);
|
|
59073
59401
|
});
|
|
59074
59402
|
setTodayEfficiencyByLineId(nextMap);
|
|
59075
59403
|
} catch (err) {
|
|
@@ -59152,6 +59480,9 @@ var KPIsOverviewView = ({
|
|
|
59152
59480
|
};
|
|
59153
59481
|
};
|
|
59154
59482
|
const handleLineClick = (line, kpis) => {
|
|
59483
|
+
if (!isSuperAdmin && !assignedLineIdSet.has(line.id)) {
|
|
59484
|
+
return;
|
|
59485
|
+
}
|
|
59155
59486
|
const trackProps = {
|
|
59156
59487
|
line_id: line.id,
|
|
59157
59488
|
line_name: line.line_name,
|
|
@@ -59664,6 +59995,7 @@ var MobileWorkspaceCard = React26.memo(({
|
|
|
59664
59995
|
workspace,
|
|
59665
59996
|
rank,
|
|
59666
59997
|
cardClass,
|
|
59998
|
+
isClickable,
|
|
59667
59999
|
onWorkspaceClick,
|
|
59668
60000
|
getMedalIcon,
|
|
59669
60001
|
efficiencyLabel
|
|
@@ -59676,8 +60008,8 @@ var MobileWorkspaceCard = React26.memo(({
|
|
|
59676
60008
|
transition: {
|
|
59677
60009
|
layout: { duration: 0.3, ease: "easeInOut" }
|
|
59678
60010
|
},
|
|
59679
|
-
onClick: () => onWorkspaceClick(workspace, rank),
|
|
59680
|
-
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] cursor-pointer`,
|
|
60011
|
+
onClick: isClickable ? () => onWorkspaceClick(workspace, rank) : void 0,
|
|
60012
|
+
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] ${isClickable ? "cursor-pointer" : "cursor-not-allowed opacity-75"}`,
|
|
59681
60013
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
59682
60014
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
59683
60015
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -59699,13 +60031,14 @@ var MobileWorkspaceCard = React26.memo(({
|
|
|
59699
60031
|
] })
|
|
59700
60032
|
}
|
|
59701
60033
|
), (prevProps, nextProps) => {
|
|
59702
|
-
return prevProps.efficiencyLabel === nextProps.efficiencyLabel && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
|
|
60034
|
+
return prevProps.efficiencyLabel === nextProps.efficiencyLabel && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
|
|
59703
60035
|
});
|
|
59704
60036
|
MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
|
|
59705
60037
|
var DesktopWorkspaceRow = React26.memo(({
|
|
59706
60038
|
workspace,
|
|
59707
60039
|
index,
|
|
59708
60040
|
rowClass,
|
|
60041
|
+
isClickable,
|
|
59709
60042
|
onWorkspaceClick,
|
|
59710
60043
|
getMedalIcon
|
|
59711
60044
|
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -59715,8 +60048,8 @@ var DesktopWorkspaceRow = React26.memo(({
|
|
|
59715
60048
|
layoutId: `row-${workspace.workspace_uuid}`,
|
|
59716
60049
|
initial: false,
|
|
59717
60050
|
transition: { layout: { duration: 0.3, ease: "easeInOut" } },
|
|
59718
|
-
onClick: () => onWorkspaceClick(workspace, index + 1),
|
|
59719
|
-
className: `${rowClass} hover:bg-gray-50/90
|
|
60051
|
+
onClick: isClickable ? () => onWorkspaceClick(workspace, index + 1) : void 0,
|
|
60052
|
+
className: `${rowClass} transition-colors duration-150 ${isClickable ? "hover:bg-gray-50/90 cursor-pointer group" : "cursor-not-allowed opacity-75"}`,
|
|
59720
60053
|
children: [
|
|
59721
60054
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap group-hover:font-medium", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
59722
60055
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: index + 1 }),
|
|
@@ -59728,7 +60061,7 @@ var DesktopWorkspaceRow = React26.memo(({
|
|
|
59728
60061
|
]
|
|
59729
60062
|
}
|
|
59730
60063
|
), (prevProps, nextProps) => {
|
|
59731
|
-
return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
|
|
60064
|
+
return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
|
|
59732
60065
|
});
|
|
59733
60066
|
DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
|
|
59734
60067
|
var LeaderboardDetailView = React26.memo(({
|
|
@@ -59888,6 +60221,16 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
59888
60221
|
}
|
|
59889
60222
|
return allLineIds;
|
|
59890
60223
|
}, [entityConfig, userAccessibleLineIds]);
|
|
60224
|
+
const accessibleLineIdSet = React26.useMemo(
|
|
60225
|
+
() => new Set((userAccessibleLineIds || []).filter(Boolean)),
|
|
60226
|
+
[userAccessibleLineIds]
|
|
60227
|
+
);
|
|
60228
|
+
const canOpenWorkspace = React26.useCallback((workspaceLineId) => {
|
|
60229
|
+
if (!workspaceLineId) return false;
|
|
60230
|
+
if (!userAccessibleLineIds) return true;
|
|
60231
|
+
if (accessibleLineIdSet.size === 0) return false;
|
|
60232
|
+
return accessibleLineIdSet.has(workspaceLineId);
|
|
60233
|
+
}, [accessibleLineIdSet, userAccessibleLineIds]);
|
|
59891
60234
|
const { hasUptime: lineModeHasUptime, hasOutput: lineModeHasOutput } = React26.useMemo(() => {
|
|
59892
60235
|
if (!lines || lines.length === 0) {
|
|
59893
60236
|
return { hasUptime: false, hasOutput: false };
|
|
@@ -60285,6 +60628,9 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
60285
60628
|
return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
|
|
60286
60629
|
}, [normalizedRange]);
|
|
60287
60630
|
const handleWorkspaceClick = React26.useCallback((workspace, rank) => {
|
|
60631
|
+
if (!canOpenWorkspace(workspace.line_id)) {
|
|
60632
|
+
return;
|
|
60633
|
+
}
|
|
60288
60634
|
trackCoreEvent("Workspace from Leaderboard Clicked", {
|
|
60289
60635
|
workspace_name: workspace.workspace_name,
|
|
60290
60636
|
workspace_id: workspace.workspace_uuid,
|
|
@@ -60317,7 +60663,7 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
60317
60663
|
const combinedParams = navParams ? `${navParams}&${contextParamString}` : `?${contextParamString}`;
|
|
60318
60664
|
navigation.navigate(`/workspace/${workspace.workspace_uuid}${combinedParams}`);
|
|
60319
60665
|
}
|
|
60320
|
-
}, [onWorkspaceClick, navigation, date, shiftId]);
|
|
60666
|
+
}, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId]);
|
|
60321
60667
|
React26.useEffect(() => {
|
|
60322
60668
|
workspacesLengthRef.current = activeEntries.length || 0;
|
|
60323
60669
|
}, [activeEntries.length]);
|
|
@@ -60608,6 +60954,7 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
60608
60954
|
workspace: ws,
|
|
60609
60955
|
rank,
|
|
60610
60956
|
cardClass,
|
|
60957
|
+
isClickable: canOpenWorkspace(ws.line_id),
|
|
60611
60958
|
onWorkspaceClick: stableHandleWorkspaceClick,
|
|
60612
60959
|
getMedalIcon: stableGetMedalIcon,
|
|
60613
60960
|
efficiencyLabel
|
|
@@ -60632,6 +60979,7 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
60632
60979
|
workspace: ws,
|
|
60633
60980
|
index,
|
|
60634
60981
|
rowClass,
|
|
60982
|
+
isClickable: canOpenWorkspace(ws.line_id),
|
|
60635
60983
|
onWorkspaceClick: stableHandleWorkspaceClick,
|
|
60636
60984
|
getMedalIcon: stableGetMedalIcon
|
|
60637
60985
|
},
|
|
@@ -60642,7 +60990,7 @@ var LeaderboardDetailView = React26.memo(({
|
|
|
60642
60990
|
) })
|
|
60643
60991
|
] });
|
|
60644
60992
|
}, (prevProps, nextProps) => {
|
|
60645
|
-
return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className;
|
|
60993
|
+
return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && JSON.stringify(prevProps.userAccessibleLineIds) === JSON.stringify(nextProps.userAccessibleLineIds) && prevProps.className === nextProps.className;
|
|
60646
60994
|
});
|
|
60647
60995
|
LeaderboardDetailView.displayName = "LeaderboardDetailView";
|
|
60648
60996
|
var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
|
|
@@ -66403,9 +66751,38 @@ var TeamManagementView = ({
|
|
|
66403
66751
|
optifye: 0
|
|
66404
66752
|
});
|
|
66405
66753
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = React26.useState(false);
|
|
66754
|
+
const normalizeIds = React26.useCallback((value) => {
|
|
66755
|
+
if (Array.isArray(value)) {
|
|
66756
|
+
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
66757
|
+
}
|
|
66758
|
+
if (typeof value === "string" && value.length > 0) {
|
|
66759
|
+
return [value];
|
|
66760
|
+
}
|
|
66761
|
+
return [];
|
|
66762
|
+
}, []);
|
|
66763
|
+
const plantHeadFactoryIds = React26.useMemo(() => {
|
|
66764
|
+
if (user?.role_level !== "plant_head") return [];
|
|
66765
|
+
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
66766
|
+
if (scopedFactoryIds.length > 0) {
|
|
66767
|
+
return Array.from(new Set(scopedFactoryIds));
|
|
66768
|
+
}
|
|
66769
|
+
const propertyFactoryIds = normalizeIds(
|
|
66770
|
+
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
66771
|
+
);
|
|
66772
|
+
if (propertyFactoryIds.length > 0) {
|
|
66773
|
+
return Array.from(new Set(propertyFactoryIds));
|
|
66774
|
+
}
|
|
66775
|
+
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
66776
|
+
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
66777
|
+
const notifyScopeRefresh = React26.useCallback(() => {
|
|
66778
|
+
if (typeof window !== "undefined") {
|
|
66779
|
+
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
66780
|
+
}
|
|
66781
|
+
}, []);
|
|
66406
66782
|
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "plant_head" || user?.role_level === "optifye";
|
|
66407
66783
|
const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
66408
|
-
const
|
|
66784
|
+
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
66785
|
+
const companyIdForUsage = pageCompanyId;
|
|
66409
66786
|
const usageDateRange = React26.useMemo(() => {
|
|
66410
66787
|
const today = /* @__PURE__ */ new Date();
|
|
66411
66788
|
const dayOfWeek = today.getDay();
|
|
@@ -66436,8 +66813,8 @@ var TeamManagementView = ({
|
|
|
66436
66813
|
return acc;
|
|
66437
66814
|
}, {});
|
|
66438
66815
|
}, [usageData, usageDateRange.daysElapsed]);
|
|
66439
|
-
const pageTitle =
|
|
66440
|
-
const pageDescription = user?.role_level === "
|
|
66816
|
+
const pageTitle = "Team Management";
|
|
66817
|
+
const pageDescription = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye" ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
66441
66818
|
const hasAccess = user ? user.role_level === void 0 || ["optifye", "owner", "it", "plant_head"].includes(user.role_level) : false;
|
|
66442
66819
|
const loadData = React26.useCallback(async () => {
|
|
66443
66820
|
if (!supabase) {
|
|
@@ -66445,20 +66822,16 @@ var TeamManagementView = ({
|
|
|
66445
66822
|
setIsLoading(false);
|
|
66446
66823
|
return;
|
|
66447
66824
|
}
|
|
66448
|
-
const
|
|
66449
|
-
if (!
|
|
66450
|
-
|
|
66451
|
-
|
|
66452
|
-
|
|
66453
|
-
setIsLoading(false);
|
|
66454
|
-
return;
|
|
66455
|
-
}
|
|
66825
|
+
const companyId = pageCompanyId;
|
|
66826
|
+
if (!companyId) {
|
|
66827
|
+
setError("Company not found. Please contact your administrator.");
|
|
66828
|
+
setIsLoading(false);
|
|
66829
|
+
return;
|
|
66456
66830
|
}
|
|
66457
|
-
const companyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
66458
66831
|
console.log("[TeamManagementView] Loading data:", {
|
|
66459
66832
|
role: user?.role_level,
|
|
66460
|
-
isOptifye:
|
|
66461
|
-
companyId
|
|
66833
|
+
isOptifye: user?.role_level === "optifye",
|
|
66834
|
+
companyId
|
|
66462
66835
|
});
|
|
66463
66836
|
setIsLoading(true);
|
|
66464
66837
|
setError(void 0);
|
|
@@ -66473,23 +66846,7 @@ var TeamManagementView = ({
|
|
|
66473
66846
|
if (!backendUrl) {
|
|
66474
66847
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
66475
66848
|
}
|
|
66476
|
-
if (
|
|
66477
|
-
const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").order("factory_name");
|
|
66478
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines`, {
|
|
66479
|
-
headers: {
|
|
66480
|
-
"Authorization": `Bearer ${token}`,
|
|
66481
|
-
"Content-Type": "application/json"
|
|
66482
|
-
}
|
|
66483
|
-
});
|
|
66484
|
-
if (!linesResponse.ok) {
|
|
66485
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
66486
|
-
}
|
|
66487
|
-
const linesData = await linesResponse.json();
|
|
66488
|
-
const lines = linesData.lines || [];
|
|
66489
|
-
setAvailableFactories(factories || []);
|
|
66490
|
-
setAvailableLines(lines);
|
|
66491
|
-
console.log("[TeamManagementView] Optifye - Loaded factories:", factories?.length, "lines:", lines?.length, "Sample lines:", lines?.slice(0, 3));
|
|
66492
|
-
} else if ((user?.role_level === "owner" || user?.role_level === "it") && companyId) {
|
|
66849
|
+
if ((user?.role_level === "optifye" || user?.role_level === "owner" || user?.role_level === "it") && companyId) {
|
|
66493
66850
|
const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").eq("company_id", companyId).order("factory_name");
|
|
66494
66851
|
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
66495
66852
|
headers: {
|
|
@@ -66504,9 +66861,8 @@ var TeamManagementView = ({
|
|
|
66504
66861
|
const lines = linesData.lines || [];
|
|
66505
66862
|
setAvailableFactories(factories || []);
|
|
66506
66863
|
setAvailableLines(lines);
|
|
66507
|
-
console.log("[TeamManagementView]
|
|
66864
|
+
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
66508
66865
|
} else if (user?.role_level === "plant_head") {
|
|
66509
|
-
const plantHeadFactoryIds = user?.properties?.factory_ids || [];
|
|
66510
66866
|
if (plantHeadFactoryIds.length > 0) {
|
|
66511
66867
|
if (companyId) {
|
|
66512
66868
|
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
@@ -66559,35 +66915,46 @@ var TeamManagementView = ({
|
|
|
66559
66915
|
setAvailableFactories([]);
|
|
66560
66916
|
console.log("[TeamManagementView] Fallback - Company:", companyId, "Loaded lines:", lines?.length);
|
|
66561
66917
|
}
|
|
66562
|
-
|
|
66563
|
-
|
|
66564
|
-
|
|
66565
|
-
|
|
66566
|
-
|
|
66567
|
-
|
|
66568
|
-
|
|
66569
|
-
|
|
66570
|
-
|
|
66571
|
-
|
|
66572
|
-
|
|
66573
|
-
|
|
66574
|
-
|
|
66575
|
-
|
|
66576
|
-
|
|
66577
|
-
|
|
66578
|
-
|
|
66579
|
-
|
|
66580
|
-
|
|
66581
|
-
|
|
66582
|
-
|
|
66583
|
-
|
|
66584
|
-
|
|
66585
|
-
|
|
66586
|
-
|
|
66587
|
-
|
|
66588
|
-
|
|
66589
|
-
|
|
66590
|
-
|
|
66918
|
+
const usersPromise = user?.role_level === "plant_head" ? (async () => {
|
|
66919
|
+
if (plantHeadFactoryIds.length === 0) {
|
|
66920
|
+
return [];
|
|
66921
|
+
}
|
|
66922
|
+
const results = await Promise.allSettled(
|
|
66923
|
+
plantHeadFactoryIds.map((factoryId) => userManagementService.getFactoryUsers(factoryId))
|
|
66924
|
+
);
|
|
66925
|
+
const successful = results.filter(
|
|
66926
|
+
(result) => result.status === "fulfilled"
|
|
66927
|
+
).flatMap((result) => result.value || []);
|
|
66928
|
+
if (successful.length > 0) {
|
|
66929
|
+
const byUserId = /* @__PURE__ */ new Map();
|
|
66930
|
+
successful.forEach((u) => {
|
|
66931
|
+
if (u?.user_id) {
|
|
66932
|
+
byUserId.set(u.user_id, u);
|
|
66933
|
+
}
|
|
66934
|
+
});
|
|
66935
|
+
return Array.from(byUserId.values());
|
|
66936
|
+
}
|
|
66937
|
+
const firstRejected = results.find(
|
|
66938
|
+
(result) => result.status === "rejected"
|
|
66939
|
+
);
|
|
66940
|
+
if (firstRejected) {
|
|
66941
|
+
throw firstRejected.reason;
|
|
66942
|
+
}
|
|
66943
|
+
return [];
|
|
66944
|
+
})() : userManagementService.getCompanyUsers(companyId);
|
|
66945
|
+
const [usersData, userStats] = await Promise.all([
|
|
66946
|
+
usersPromise,
|
|
66947
|
+
userManagementService.getUserStats(companyId)
|
|
66948
|
+
]);
|
|
66949
|
+
setUsers(usersData);
|
|
66950
|
+
setStats({
|
|
66951
|
+
totalUsers: userStats.total,
|
|
66952
|
+
owners: userStats.owners,
|
|
66953
|
+
it: userStats.it,
|
|
66954
|
+
plantHeads: userStats.plant_heads,
|
|
66955
|
+
supervisors: userStats.supervisors,
|
|
66956
|
+
optifye: 0
|
|
66957
|
+
});
|
|
66591
66958
|
} catch (err) {
|
|
66592
66959
|
console.error("Error loading team management data:", err);
|
|
66593
66960
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -66595,16 +66962,16 @@ var TeamManagementView = ({
|
|
|
66595
66962
|
} finally {
|
|
66596
66963
|
setIsLoading(false);
|
|
66597
66964
|
}
|
|
66598
|
-
}, [supabase, user, entityConfig]);
|
|
66965
|
+
}, [supabase, user, pageCompanyId, entityConfig?.factoryId, plantHeadFactoryIds]);
|
|
66599
66966
|
React26.useEffect(() => {
|
|
66600
|
-
const companyId =
|
|
66601
|
-
const canLoad = hasAccess && user &&
|
|
66967
|
+
const companyId = pageCompanyId;
|
|
66968
|
+
const canLoad = hasAccess && user && !!companyId;
|
|
66602
66969
|
if (canLoad) {
|
|
66603
66970
|
loadData();
|
|
66604
66971
|
} else if (!user) {
|
|
66605
66972
|
setIsLoading(true);
|
|
66606
66973
|
}
|
|
66607
|
-
}, [hasAccess, loadData, user,
|
|
66974
|
+
}, [hasAccess, loadData, user, pageCompanyId]);
|
|
66608
66975
|
const handleUserAdded = React26.useCallback(() => {
|
|
66609
66976
|
loadData();
|
|
66610
66977
|
}, [loadData]);
|
|
@@ -66618,12 +66985,13 @@ var TeamManagementView = ({
|
|
|
66618
66985
|
updated_by: user.id
|
|
66619
66986
|
});
|
|
66620
66987
|
sonner.toast.success("User role updated successfully");
|
|
66988
|
+
notifyScopeRefresh();
|
|
66621
66989
|
loadData();
|
|
66622
66990
|
} catch (err) {
|
|
66623
66991
|
console.error("Error updating user role:", err);
|
|
66624
66992
|
sonner.toast.error("Failed to update user role");
|
|
66625
66993
|
}
|
|
66626
|
-
}, [supabase, user, loadData]);
|
|
66994
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66627
66995
|
const handleRemoveUser = React26.useCallback(async (userId) => {
|
|
66628
66996
|
if (!supabase || !user) return;
|
|
66629
66997
|
try {
|
|
@@ -66646,12 +67014,13 @@ var TeamManagementView = ({
|
|
|
66646
67014
|
assigned_by: user.id
|
|
66647
67015
|
});
|
|
66648
67016
|
sonner.toast.success("Line assignments updated successfully");
|
|
67017
|
+
notifyScopeRefresh();
|
|
66649
67018
|
loadData();
|
|
66650
67019
|
} catch (err) {
|
|
66651
67020
|
console.error("Error updating line assignments:", err);
|
|
66652
67021
|
sonner.toast.error("Failed to update line assignments");
|
|
66653
67022
|
}
|
|
66654
|
-
}, [supabase, user, loadData]);
|
|
67023
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66655
67024
|
const handleFactoryAssignmentUpdate = React26.useCallback(async (userId, factoryIds) => {
|
|
66656
67025
|
if (!supabase || !user) return;
|
|
66657
67026
|
try {
|
|
@@ -66662,12 +67031,13 @@ var TeamManagementView = ({
|
|
|
66662
67031
|
assigned_by: user.id
|
|
66663
67032
|
});
|
|
66664
67033
|
sonner.toast.success("Factory assignments updated successfully");
|
|
67034
|
+
notifyScopeRefresh();
|
|
66665
67035
|
loadData();
|
|
66666
67036
|
} catch (err) {
|
|
66667
67037
|
console.error("Error updating factory assignments:", err);
|
|
66668
67038
|
sonner.toast.error("Failed to update factory assignments");
|
|
66669
67039
|
}
|
|
66670
|
-
}, [supabase, user, loadData]);
|
|
67040
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66671
67041
|
const handleProfileUpdate = React26.useCallback(async (userId, firstName, lastName, profilePhotoUrl) => {
|
|
66672
67042
|
if (!supabase || !user) return;
|
|
66673
67043
|
try {
|