@optifye/dashboard-core 6.10.47 → 6.10.49
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 +619 -252
- package/dist/index.mjs +619 -252
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3918,6 +3918,10 @@ var AuthService = class {
|
|
|
3918
3918
|
email: enrichedUser.email,
|
|
3919
3919
|
role: enrichedUser.role,
|
|
3920
3920
|
role_level: enrichedUser.role,
|
|
3921
|
+
scope_mode: enrichedUser.scope_mode,
|
|
3922
|
+
scope_generated_at: enrichedUser.scope_generated_at,
|
|
3923
|
+
scope_ttl_seconds: enrichedUser.scope_ttl_seconds,
|
|
3924
|
+
access_scope: enrichedUser.access_scope,
|
|
3921
3925
|
company_id: enrichedUser.company_id,
|
|
3922
3926
|
first_login_completed: enrichedUser.profile.first_login_completed,
|
|
3923
3927
|
properties: {
|
|
@@ -8124,6 +8128,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8124
8128
|
const isFetchingRef = useRef(false);
|
|
8125
8129
|
const lastProcessedSessionRef = useRef(null);
|
|
8126
8130
|
const hasAuthenticatedRef = useRef(false);
|
|
8131
|
+
const scopeFetchedAtRef = useRef(0);
|
|
8132
|
+
const scopeTtlSecondsRef = useRef(300);
|
|
8127
8133
|
const fetchSession = useCallback(async (supabaseSession, isReauth = false, allowRetry = true) => {
|
|
8128
8134
|
if (isFetchingRef.current) {
|
|
8129
8135
|
console.log("[AuthContext] Already fetching, skipping duplicate request");
|
|
@@ -8136,6 +8142,9 @@ var AuthProvider = ({ children }) => {
|
|
|
8136
8142
|
}
|
|
8137
8143
|
try {
|
|
8138
8144
|
const enrichedUser = await AuthService.getSession(supabaseSession.access_token);
|
|
8145
|
+
const parsedScopeGeneratedAt = enrichedUser.scope_generated_at ? Date.parse(enrichedUser.scope_generated_at) : NaN;
|
|
8146
|
+
scopeFetchedAtRef.current = Number.isFinite(parsedScopeGeneratedAt) ? parsedScopeGeneratedAt : Date.now();
|
|
8147
|
+
scopeTtlSecondsRef.current = enrichedUser.scope_ttl_seconds || 300;
|
|
8139
8148
|
const authUser = AuthService.toAuthUser(enrichedUser);
|
|
8140
8149
|
setUser(authUser);
|
|
8141
8150
|
setSession(supabaseSession);
|
|
@@ -8225,6 +8234,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8225
8234
|
setError(null);
|
|
8226
8235
|
setShowOnboarding(false);
|
|
8227
8236
|
hasAuthenticatedRef.current = false;
|
|
8237
|
+
scopeFetchedAtRef.current = 0;
|
|
8238
|
+
scopeTtlSecondsRef.current = 300;
|
|
8228
8239
|
router.push("/login");
|
|
8229
8240
|
} catch (err) {
|
|
8230
8241
|
console.error("[AuthContext] Sign out error:", err);
|
|
@@ -8287,6 +8298,44 @@ var AuthProvider = ({ children }) => {
|
|
|
8287
8298
|
clearInterval(intervalId);
|
|
8288
8299
|
};
|
|
8289
8300
|
}, [session, supabase]);
|
|
8301
|
+
useEffect(() => {
|
|
8302
|
+
if (!session || !user) {
|
|
8303
|
+
return;
|
|
8304
|
+
}
|
|
8305
|
+
const refreshScopeIfExpired = async () => {
|
|
8306
|
+
if (!session || isFetchingRef.current) {
|
|
8307
|
+
return;
|
|
8308
|
+
}
|
|
8309
|
+
const ttlSeconds = user.scope_ttl_seconds || scopeTtlSecondsRef.current || 300;
|
|
8310
|
+
const fetchedAt = scopeFetchedAtRef.current || 0;
|
|
8311
|
+
const shouldRefresh = fetchedAt === 0 || Date.now() - fetchedAt >= ttlSeconds * 1e3;
|
|
8312
|
+
if (!shouldRefresh) {
|
|
8313
|
+
return;
|
|
8314
|
+
}
|
|
8315
|
+
try {
|
|
8316
|
+
console.log("[AuthContext] Scope TTL expired, refreshing session scope");
|
|
8317
|
+
await fetchSession(session, true);
|
|
8318
|
+
} catch (err) {
|
|
8319
|
+
console.error("[AuthContext] Scope refresh failed:", err);
|
|
8320
|
+
}
|
|
8321
|
+
};
|
|
8322
|
+
refreshScopeIfExpired();
|
|
8323
|
+
const intervalId = setInterval(refreshScopeIfExpired, 6e4);
|
|
8324
|
+
return () => clearInterval(intervalId);
|
|
8325
|
+
}, [session, user?.id, user?.scope_ttl_seconds, fetchSession]);
|
|
8326
|
+
useEffect(() => {
|
|
8327
|
+
if (typeof window === "undefined") {
|
|
8328
|
+
return;
|
|
8329
|
+
}
|
|
8330
|
+
const handleScopeRefresh = () => {
|
|
8331
|
+
if (!session) {
|
|
8332
|
+
return;
|
|
8333
|
+
}
|
|
8334
|
+
void fetchSession(session, true);
|
|
8335
|
+
};
|
|
8336
|
+
window.addEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
8337
|
+
return () => window.removeEventListener("rbac:refresh-scope", handleScopeRefresh);
|
|
8338
|
+
}, [session, fetchSession]);
|
|
8290
8339
|
useEffect(() => {
|
|
8291
8340
|
if (!supabase) {
|
|
8292
8341
|
console.log("[AuthContext] No Supabase client, skipping auth initialization");
|
|
@@ -8355,7 +8404,8 @@ var AuthProvider = ({ children }) => {
|
|
|
8355
8404
|
} else if (event === "TOKEN_REFRESHED") {
|
|
8356
8405
|
console.log("[AuthContext] Token refreshed automatically");
|
|
8357
8406
|
if (currentSession) {
|
|
8358
|
-
|
|
8407
|
+
const isReauth = hasAuthenticatedRef.current;
|
|
8408
|
+
await fetchSession(currentSession, isReauth);
|
|
8359
8409
|
const expiresAt = currentSession.expires_at;
|
|
8360
8410
|
if (expiresAt) {
|
|
8361
8411
|
const minutesUntilExpiry = Math.floor((expiresAt * 1e3 - Date.now()) / 6e4);
|
|
@@ -10503,12 +10553,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10503
10553
|
const configuredLineIds = useMemo(() => {
|
|
10504
10554
|
return getConfiguredLineIds(entityConfig);
|
|
10505
10555
|
}, [entityConfig]);
|
|
10556
|
+
const targetFactoryLineIds = useMemo(() => {
|
|
10557
|
+
const sourceLineIds = userAccessibleLineIds !== void 0 ? userAccessibleLineIds : configuredLineIds;
|
|
10558
|
+
return Array.from(new Set((sourceLineIds || []).filter(Boolean)));
|
|
10559
|
+
}, [userAccessibleLineIds, configuredLineIds]);
|
|
10506
10560
|
const { shiftConfig: staticShiftConfig } = useDashboardConfig();
|
|
10507
10561
|
const {
|
|
10508
10562
|
shiftConfigMap: multiLineShiftConfigMap,
|
|
10509
10563
|
isLoading: isMultiLineShiftConfigLoading
|
|
10510
10564
|
} = useMultiLineShiftConfigs(
|
|
10511
|
-
isFactoryView ?
|
|
10565
|
+
isFactoryView ? targetFactoryLineIds : [],
|
|
10512
10566
|
staticShiftConfig
|
|
10513
10567
|
);
|
|
10514
10568
|
const {
|
|
@@ -10603,7 +10657,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10603
10657
|
return;
|
|
10604
10658
|
}
|
|
10605
10659
|
const isFactory = currentLineIdToUse === factoryViewId;
|
|
10606
|
-
const targetLineIds = isFactory ?
|
|
10660
|
+
const targetLineIds = isFactory ? targetFactoryLineIds : [currentLineIdToUse];
|
|
10607
10661
|
const targetLineIdsKey = targetLineIds.slice().sort().join(",");
|
|
10608
10662
|
const usesShiftGroups = isFactory && shiftGroups.length > 0;
|
|
10609
10663
|
const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
|
|
@@ -10652,7 +10706,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10652
10706
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
10653
10707
|
}
|
|
10654
10708
|
if (targetLineIds.length === 0) {
|
|
10655
|
-
|
|
10709
|
+
logDebug("[useDashboardMetrics] Skipping fetch: no target line IDs after scope filtering");
|
|
10710
|
+
setMetrics({
|
|
10711
|
+
workspaceMetrics: [],
|
|
10712
|
+
lineMetrics: [],
|
|
10713
|
+
metadata: void 0,
|
|
10714
|
+
efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
|
|
10715
|
+
});
|
|
10716
|
+
setMetricsLineId(requestLineId);
|
|
10717
|
+
lastFetchKeyRef.current = inFlightFetchKeyRef.current;
|
|
10718
|
+
return;
|
|
10656
10719
|
}
|
|
10657
10720
|
let allWorkspaceMetrics = [];
|
|
10658
10721
|
let allLineMetrics = [];
|
|
@@ -10670,11 +10733,16 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10670
10733
|
lineCount: group.lineIds.length
|
|
10671
10734
|
}))
|
|
10672
10735
|
});
|
|
10736
|
+
const targetLineIdSet = new Set(targetLineIds);
|
|
10673
10737
|
const metricsPromises = shiftGroups.map(async (group) => {
|
|
10674
|
-
const
|
|
10738
|
+
const groupLineIds = group.lineIds.filter((lineGroupId) => targetLineIdSet.has(lineGroupId));
|
|
10739
|
+
if (groupLineIds.length === 0) {
|
|
10740
|
+
return null;
|
|
10741
|
+
}
|
|
10742
|
+
const lineIdsParam = `line_ids=${groupLineIds.join(",")}`;
|
|
10675
10743
|
const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${companyId}${forceParam}`;
|
|
10676
10744
|
logDebug(`[useDashboardMetrics] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
|
|
10677
|
-
lineIds:
|
|
10745
|
+
lineIds: groupLineIds,
|
|
10678
10746
|
date: group.date
|
|
10679
10747
|
});
|
|
10680
10748
|
const response = await fetch(url, {
|
|
@@ -10698,7 +10766,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10698
10766
|
throw new Error(`Invalid JSON response from backend`);
|
|
10699
10767
|
}
|
|
10700
10768
|
});
|
|
10701
|
-
const results = await Promise.all(metricsPromises);
|
|
10769
|
+
const results = (await Promise.all(metricsPromises)).filter((result) => !!result);
|
|
10702
10770
|
hasFlowBuffers = results.some((result) => result?.metadata?.has_flow_buffers);
|
|
10703
10771
|
results.forEach((result) => {
|
|
10704
10772
|
const idleMap = result?.metadata?.idle_time_vlm_by_line;
|
|
@@ -10876,6 +10944,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10876
10944
|
shiftGroups,
|
|
10877
10945
|
shiftGroupsKey,
|
|
10878
10946
|
configuredLineIds,
|
|
10947
|
+
targetFactoryLineIds,
|
|
10879
10948
|
isFactoryView,
|
|
10880
10949
|
multiLineShiftConfigMap,
|
|
10881
10950
|
staticShiftConfig,
|
|
@@ -10968,8 +11037,14 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
10968
11037
|
logDebug("[useDashboardMetrics] Setting up group subscriptions:", {
|
|
10969
11038
|
groupCount: currentShiftGroups.length
|
|
10970
11039
|
});
|
|
11040
|
+
const targetFactoryLineIdsForSubscriptions = currentUserAccessibleLineIds !== void 0 ? currentUserAccessibleLineIds : currentConfiguredLineIds;
|
|
11041
|
+
const targetFactoryLineIdSet = new Set(
|
|
11042
|
+
(targetFactoryLineIdsForSubscriptions || []).filter(Boolean)
|
|
11043
|
+
);
|
|
10971
11044
|
currentShiftGroups.forEach((group, index) => {
|
|
10972
|
-
const groupLineIds = group.lineIds.filter(
|
|
11045
|
+
const groupLineIds = group.lineIds.filter(
|
|
11046
|
+
(id3) => id3 && id3 !== factoryViewIdentifier && targetFactoryLineIdSet.has(id3)
|
|
11047
|
+
);
|
|
10973
11048
|
if (groupLineIds.length === 0) {
|
|
10974
11049
|
logDebug("[useDashboardMetrics] Skipping group subscription: no line IDs after filtering", {
|
|
10975
11050
|
groupIndex: index,
|
|
@@ -16148,6 +16223,7 @@ function useAccessControl() {
|
|
|
16148
16223
|
function useTeamManagementPermissions() {
|
|
16149
16224
|
const { user } = useAuth();
|
|
16150
16225
|
const currentRole = user?.role_level;
|
|
16226
|
+
const isSuperAdminOptifye = currentRole === "optifye" && (user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin);
|
|
16151
16227
|
return {
|
|
16152
16228
|
/**
|
|
16153
16229
|
* Can the current user assign lines to this user?
|
|
@@ -16203,7 +16279,10 @@ function useTeamManagementPermissions() {
|
|
|
16203
16279
|
availableRolesToAssign: () => {
|
|
16204
16280
|
if (!currentRole) return [];
|
|
16205
16281
|
if (currentRole === "optifye") {
|
|
16206
|
-
|
|
16282
|
+
if (isSuperAdminOptifye) {
|
|
16283
|
+
return ["owner", "it", "plant_head", "supervisor"];
|
|
16284
|
+
}
|
|
16285
|
+
return ["it", "plant_head", "supervisor"];
|
|
16207
16286
|
}
|
|
16208
16287
|
if (currentRole === "owner") {
|
|
16209
16288
|
return ["it", "plant_head", "supervisor"];
|
|
@@ -16221,7 +16300,12 @@ function useTeamManagementPermissions() {
|
|
|
16221
16300
|
*/
|
|
16222
16301
|
canInviteRole: (role) => {
|
|
16223
16302
|
if (!currentRole) return false;
|
|
16224
|
-
if (currentRole === "optifye")
|
|
16303
|
+
if (currentRole === "optifye") {
|
|
16304
|
+
if (role === "owner") {
|
|
16305
|
+
return isSuperAdminOptifye;
|
|
16306
|
+
}
|
|
16307
|
+
return true;
|
|
16308
|
+
}
|
|
16225
16309
|
if (currentRole === "owner") {
|
|
16226
16310
|
return ["it", "plant_head", "supervisor"].includes(role);
|
|
16227
16311
|
}
|
|
@@ -16255,40 +16339,133 @@ function useTeamManagementPermissions() {
|
|
|
16255
16339
|
}
|
|
16256
16340
|
function useUserLineAccess(configLineIds) {
|
|
16257
16341
|
const { user } = useAuth();
|
|
16342
|
+
const { lines: dbLines } = useLines();
|
|
16258
16343
|
const { accessibleLineIds, defaultLineId } = useMemo(() => {
|
|
16344
|
+
const dbLineIds = dbLines.map((line) => line.id);
|
|
16345
|
+
const dbLineIdSet = new Set(dbLineIds);
|
|
16346
|
+
const fallbackAllLineIds = configLineIds && configLineIds.length > 0 ? configLineIds : dbLineIds;
|
|
16347
|
+
const normalizeIds = (value) => {
|
|
16348
|
+
if (Array.isArray(value)) {
|
|
16349
|
+
return value.filter((id3) => typeof id3 === "string" && !!id3);
|
|
16350
|
+
}
|
|
16351
|
+
if (typeof value === "string" && value) {
|
|
16352
|
+
return [value];
|
|
16353
|
+
}
|
|
16354
|
+
return [];
|
|
16355
|
+
};
|
|
16356
|
+
const uniqueIds = (ids) => Array.from(new Set(ids.filter(Boolean)));
|
|
16357
|
+
const filterToEnabledDb = (lineIds, options = {}) => {
|
|
16358
|
+
const deduped = uniqueIds(lineIds);
|
|
16359
|
+
if (dbLineIdSet.size === 0) {
|
|
16360
|
+
return options.allowWhenDbUnavailable ? deduped : [];
|
|
16361
|
+
}
|
|
16362
|
+
return deduped.filter((lineId) => dbLineIdSet.has(lineId));
|
|
16363
|
+
};
|
|
16259
16364
|
if (!user) {
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16264
|
-
|
|
16265
|
-
|
|
16266
|
-
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16365
|
+
const fallback = filterToEnabledDb(fallbackAllLineIds, { allowWhenDbUnavailable: true });
|
|
16366
|
+
return { accessibleLineIds: fallback, defaultLineId: fallback[0] };
|
|
16367
|
+
}
|
|
16368
|
+
const configSet = new Set(configLineIds || []);
|
|
16369
|
+
const hasScopePayload = !!user.access_scope || !!user.scope_mode;
|
|
16370
|
+
const role = user.role_level || user.role;
|
|
16371
|
+
const scopedLineIds = normalizeIds(user.access_scope?.line_ids);
|
|
16372
|
+
const scopedFactoryIds = normalizeIds(user.access_scope?.factory_ids);
|
|
16373
|
+
const properties = user.properties || {};
|
|
16374
|
+
const propertyLineIds = normalizeIds(properties?.line_ids || properties?.line_id);
|
|
16375
|
+
const propertyFactoryIds = normalizeIds(properties?.factory_ids || properties?.factory_id);
|
|
16376
|
+
const propertyCompanyId = typeof properties?.company_id === "string" ? properties.company_id : user.company_id;
|
|
16377
|
+
const scopedCompanyId = user.access_scope?.company_id || propertyCompanyId;
|
|
16378
|
+
const isExplicitSuperAdmin = user.scope_mode === "SUPER_ADMIN" || !!user.access_scope?.is_super_admin;
|
|
16379
|
+
const isLegacyOptifyeGlobal = !hasScopePayload && role === "optifye" && propertyLineIds.length === 0 && propertyFactoryIds.length === 0 && !propertyCompanyId;
|
|
16380
|
+
const lineFactoryMap = /* @__PURE__ */ new Map();
|
|
16381
|
+
dbLines.forEach((line) => {
|
|
16382
|
+
lineFactoryMap.set(line.id, line.factory_id);
|
|
16383
|
+
});
|
|
16384
|
+
const applyConfigFilter = (lineIds) => {
|
|
16385
|
+
const dedupedLineIds = uniqueIds(lineIds);
|
|
16386
|
+
if (!configLineIds || configLineIds.length === 0) {
|
|
16387
|
+
return dedupedLineIds;
|
|
16270
16388
|
}
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16274
|
-
|
|
16389
|
+
const filtered = dedupedLineIds.filter((id3) => configSet.has(id3));
|
|
16390
|
+
return filtered.length > 0 ? filtered : dedupedLineIds;
|
|
16391
|
+
};
|
|
16392
|
+
const resolveFactoryScopedLines = (factoryIds) => {
|
|
16393
|
+
const factorySet = new Set(factoryIds);
|
|
16394
|
+
const baseLineIds = configLineIds && configLineIds.length > 0 ? configLineIds : dbLineIds;
|
|
16395
|
+
const filtered = baseLineIds.filter((lineId) => {
|
|
16396
|
+
const factoryId = lineFactoryMap.get(lineId);
|
|
16397
|
+
return !!factoryId && factorySet.has(factoryId);
|
|
16275
16398
|
});
|
|
16399
|
+
if (filtered.length > 0) {
|
|
16400
|
+
return uniqueIds(filtered);
|
|
16401
|
+
}
|
|
16402
|
+
const fromDb = dbLines.filter((line) => !!line.factory_id && factorySet.has(line.factory_id)).map((line) => line.id);
|
|
16403
|
+
if (fromDb.length > 0) {
|
|
16404
|
+
return uniqueIds(fromDb);
|
|
16405
|
+
}
|
|
16406
|
+
return [];
|
|
16407
|
+
};
|
|
16408
|
+
if (isExplicitSuperAdmin || isLegacyOptifyeGlobal) {
|
|
16409
|
+
const all = filterToEnabledDb(fallbackAllLineIds, { allowWhenDbUnavailable: true });
|
|
16276
16410
|
return {
|
|
16277
|
-
accessibleLineIds:
|
|
16278
|
-
defaultLineId:
|
|
16411
|
+
accessibleLineIds: all,
|
|
16412
|
+
defaultLineId: all[0]
|
|
16279
16413
|
};
|
|
16280
16414
|
}
|
|
16281
|
-
if (
|
|
16415
|
+
if (scopedLineIds.length > 0) {
|
|
16416
|
+
const filtered = applyConfigFilter(scopedLineIds);
|
|
16417
|
+
const scoped = filterToEnabledDb(filtered.length > 0 ? filtered : uniqueIds(scopedLineIds));
|
|
16282
16418
|
return {
|
|
16283
|
-
accessibleLineIds:
|
|
16284
|
-
defaultLineId:
|
|
16419
|
+
accessibleLineIds: scoped,
|
|
16420
|
+
defaultLineId: scoped[0]
|
|
16285
16421
|
};
|
|
16286
16422
|
}
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16423
|
+
if (scopedFactoryIds.length > 0) {
|
|
16424
|
+
const scoped = filterToEnabledDb(resolveFactoryScopedLines(scopedFactoryIds));
|
|
16425
|
+
return {
|
|
16426
|
+
accessibleLineIds: scoped,
|
|
16427
|
+
defaultLineId: scoped[0]
|
|
16428
|
+
};
|
|
16429
|
+
}
|
|
16430
|
+
if (scopedCompanyId) {
|
|
16431
|
+
const dbCompanyLineIds = dbLines.filter((line) => !line.company_id || line.company_id === scopedCompanyId).map((line) => line.id);
|
|
16432
|
+
const companyScopedLines = configLineIds && configLineIds.length > 0 ? (() => {
|
|
16433
|
+
const dbCompanySet = new Set(dbCompanyLineIds);
|
|
16434
|
+
const filtered = configLineIds.filter((lineId) => dbCompanySet.has(lineId));
|
|
16435
|
+
if (filtered.length > 0) return filtered;
|
|
16436
|
+
return dbCompanyLineIds;
|
|
16437
|
+
})() : dbCompanyLineIds;
|
|
16438
|
+
const scoped = filterToEnabledDb(companyScopedLines);
|
|
16439
|
+
return {
|
|
16440
|
+
accessibleLineIds: scoped,
|
|
16441
|
+
defaultLineId: scoped[0]
|
|
16442
|
+
};
|
|
16443
|
+
}
|
|
16444
|
+
if (propertyLineIds.length > 0) {
|
|
16445
|
+
const filtered = applyConfigFilter(propertyLineIds);
|
|
16446
|
+
const scoped = filterToEnabledDb(filtered);
|
|
16447
|
+
return {
|
|
16448
|
+
accessibleLineIds: scoped,
|
|
16449
|
+
defaultLineId: scoped[0]
|
|
16450
|
+
};
|
|
16451
|
+
}
|
|
16452
|
+
if (propertyFactoryIds.length > 0) {
|
|
16453
|
+
const scoped = filterToEnabledDb(resolveFactoryScopedLines(propertyFactoryIds));
|
|
16454
|
+
return {
|
|
16455
|
+
accessibleLineIds: scoped,
|
|
16456
|
+
defaultLineId: scoped[0]
|
|
16457
|
+
};
|
|
16458
|
+
}
|
|
16459
|
+
if (role === "owner" || role === "it" || role === "optifye") {
|
|
16460
|
+
const companyScopedLines = scopedCompanyId ? dbLines.filter((line) => !line.company_id || line.company_id === scopedCompanyId).map((line) => line.id) : [];
|
|
16461
|
+
const scoped = filterToEnabledDb(companyScopedLines);
|
|
16462
|
+
return {
|
|
16463
|
+
accessibleLineIds: scoped,
|
|
16464
|
+
defaultLineId: scoped[0]
|
|
16465
|
+
};
|
|
16466
|
+
}
|
|
16467
|
+
return { accessibleLineIds: [], defaultLineId: void 0 };
|
|
16468
|
+
}, [user, configLineIds, dbLines]);
|
|
16292
16469
|
return { accessibleLineIds, defaultLineId, user };
|
|
16293
16470
|
}
|
|
16294
16471
|
function useActiveLineId(queryLineId, configLineIds) {
|
|
@@ -16480,12 +16657,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
16480
16657
|
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
16481
16658
|
const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
|
|
16482
16659
|
if (useBackend && resolvedCompanyId) {
|
|
16483
|
-
const searchParams = new URLSearchParams({
|
|
16484
|
-
company_id: resolvedCompanyId
|
|
16485
|
-
});
|
|
16486
|
-
if (lineIdsKey) {
|
|
16487
|
-
searchParams.set("line_ids", lineIdsKey);
|
|
16488
|
-
}
|
|
16660
|
+
const searchParams = new URLSearchParams({ company_id: resolvedCompanyId });
|
|
16489
16661
|
const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
|
|
16490
16662
|
(data?.supervisors || []).forEach((row) => {
|
|
16491
16663
|
const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
|
|
@@ -28305,17 +28477,76 @@ function withAccessControl(WrappedComponent2, options = {}) {
|
|
|
28305
28477
|
const WithAccessControlComponent = (props) => {
|
|
28306
28478
|
const router = useRouter();
|
|
28307
28479
|
const { user, loading: authLoading } = useAuth();
|
|
28308
|
-
const
|
|
28309
|
-
|
|
28480
|
+
const role = user?.role_level;
|
|
28481
|
+
const scope = user?.access_scope;
|
|
28482
|
+
const isSuperAdmin = !!scope?.is_super_admin || user?.scope_mode === "SUPER_ADMIN";
|
|
28483
|
+
const getBasePath = (path) => {
|
|
28484
|
+
const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
|
|
28485
|
+
return firstSegment ? `/${firstSegment}` : "/";
|
|
28486
|
+
};
|
|
28487
|
+
const getIdFromPath = (path, key) => {
|
|
28488
|
+
const queryValue = router.query[key];
|
|
28489
|
+
if (typeof queryValue === "string") return queryValue;
|
|
28490
|
+
if (Array.isArray(queryValue) && queryValue.length > 0) return queryValue[0];
|
|
28491
|
+
const segments = path.split("?")[0].split("/").filter(Boolean);
|
|
28492
|
+
const keyIndex = segments.findIndex((segment) => segment.toLowerCase() === key.toLowerCase());
|
|
28493
|
+
if (keyIndex >= 0 && keyIndex < segments.length - 1) return segments[keyIndex + 1];
|
|
28494
|
+
return null;
|
|
28495
|
+
};
|
|
28496
|
+
const roleAccessMap = {
|
|
28497
|
+
optifye: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28498
|
+
owner: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28499
|
+
it: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28500
|
+
plant_head: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/improvement-center", "/tickets"],
|
|
28501
|
+
supervisor: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/skus", "/help", "/health", "/profile", "/workspace", "/improvement-center", "/tickets"]
|
|
28502
|
+
};
|
|
28503
|
+
const canAccessPath = (path) => {
|
|
28504
|
+
if (!user || !role) return false;
|
|
28505
|
+
if (isSuperAdmin) return true;
|
|
28506
|
+
const basePath = getBasePath(path);
|
|
28507
|
+
const allowed = roleAccessMap[role] || [];
|
|
28508
|
+
if (!allowed.includes(basePath)) {
|
|
28509
|
+
return false;
|
|
28510
|
+
}
|
|
28511
|
+
const workspaceId = getIdFromPath(path, "workspace");
|
|
28512
|
+
if (workspaceId && scope?.workspace_ids?.length) {
|
|
28513
|
+
return scope.workspace_ids.includes(workspaceId);
|
|
28514
|
+
}
|
|
28515
|
+
const lineId = getIdFromPath(path, "kpis") || getIdFromPath(path, "line");
|
|
28516
|
+
if (lineId && scope?.line_ids?.length) {
|
|
28517
|
+
return scope.line_ids.includes(lineId);
|
|
28518
|
+
}
|
|
28519
|
+
const factoryId = getIdFromPath(path, "factory");
|
|
28520
|
+
if (factoryId && scope?.factory_ids?.length) {
|
|
28521
|
+
return scope.factory_ids.includes(factoryId);
|
|
28522
|
+
}
|
|
28523
|
+
return true;
|
|
28524
|
+
};
|
|
28525
|
+
const pathToCheck = requiredPath || router.asPath || router.pathname;
|
|
28310
28526
|
if (authLoading) {
|
|
28311
|
-
return /* @__PURE__ */ jsx("div", { className: "flex h-screen
|
|
28312
|
-
/* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }),
|
|
28313
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Loading..." })
|
|
28314
|
-
] }) });
|
|
28527
|
+
return /* @__PURE__ */ jsx("div", { className: "flex min-h-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }) });
|
|
28315
28528
|
}
|
|
28316
28529
|
if (!user) {
|
|
28317
28530
|
return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-4", children: "Please log in to continue" }) }) });
|
|
28318
28531
|
}
|
|
28532
|
+
const hasAccess = canAccessPath(pathToCheck);
|
|
28533
|
+
if (!hasAccess) {
|
|
28534
|
+
if (UnauthorizedComponent) {
|
|
28535
|
+
return /* @__PURE__ */ jsx(UnauthorizedComponent, {});
|
|
28536
|
+
}
|
|
28537
|
+
if (typeof window !== "undefined") {
|
|
28538
|
+
router.replace(redirectTo);
|
|
28539
|
+
}
|
|
28540
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
28541
|
+
/* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Access Denied" }),
|
|
28542
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-4", children: "You don't have permission to access this page." }),
|
|
28543
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500", children: [
|
|
28544
|
+
"Your role: ",
|
|
28545
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: role || "Unknown" })
|
|
28546
|
+
] }),
|
|
28547
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Redirecting to home page..." })
|
|
28548
|
+
] }) });
|
|
28549
|
+
}
|
|
28319
28550
|
return /* @__PURE__ */ jsx(WrappedComponent2, { ...props });
|
|
28320
28551
|
};
|
|
28321
28552
|
WithAccessControlComponent.displayName = `withAccessControl(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
@@ -48629,9 +48860,29 @@ var SideNavBar = memo$1(({
|
|
|
48629
48860
|
}) => {
|
|
48630
48861
|
const router = useRouter();
|
|
48631
48862
|
const { navigate } = useNavigation();
|
|
48632
|
-
const { signOut } = useAuth();
|
|
48863
|
+
const { signOut, user } = useAuth();
|
|
48633
48864
|
const entityConfig = useEntityConfig();
|
|
48634
48865
|
const dashboardConfig = useDashboardConfig();
|
|
48866
|
+
const isSuperAdmin = user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin;
|
|
48867
|
+
const role = user?.role_level;
|
|
48868
|
+
const roleAccessMap = {
|
|
48869
|
+
optifye: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48870
|
+
owner: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48871
|
+
it: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48872
|
+
plant_head: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/supervisor-management", "/skus", "/help", "/health", "/profile", "/workspace", "/factory-view", "/team-management", "/tickets", "/improvement-center"],
|
|
48873
|
+
supervisor: ["/", "/leaderboard", "/kpis", "/targets", "/shifts", "/skus", "/help", "/health", "/profile", "/workspace", "/tickets", "/improvement-center"]
|
|
48874
|
+
};
|
|
48875
|
+
const getBasePath = useCallback((path) => {
|
|
48876
|
+
const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
|
|
48877
|
+
return firstSegment ? `/${firstSegment}` : "/";
|
|
48878
|
+
}, []);
|
|
48879
|
+
const canAccessPath = useCallback((path) => {
|
|
48880
|
+
if (!role) return false;
|
|
48881
|
+
if (isSuperAdmin) return true;
|
|
48882
|
+
const basePath = getBasePath(path);
|
|
48883
|
+
const allowedPaths = roleAccessMap[role] || [];
|
|
48884
|
+
return allowedPaths.includes(basePath);
|
|
48885
|
+
}, [role, isSuperAdmin, getBasePath]);
|
|
48635
48886
|
const lineId = entityConfig.defaultLineId || LINE_1_UUID;
|
|
48636
48887
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
48637
48888
|
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
@@ -48797,7 +49048,7 @@ var SideNavBar = memo$1(({
|
|
|
48797
49048
|
const settingsTriggerRef = useRef(null);
|
|
48798
49049
|
const settingsItems = useMemo(() => {
|
|
48799
49050
|
const items = [
|
|
48800
|
-
{
|
|
49051
|
+
...canAccessPath("/targets") ? [{
|
|
48801
49052
|
key: "targets",
|
|
48802
49053
|
label: "Targets",
|
|
48803
49054
|
icon: AdjustmentsHorizontalIcon,
|
|
@@ -48806,8 +49057,8 @@ var SideNavBar = memo$1(({
|
|
|
48806
49057
|
setIsSettingsOpen(false);
|
|
48807
49058
|
},
|
|
48808
49059
|
isActive: pathname === "/targets" || pathname.startsWith("/targets/")
|
|
48809
|
-
},
|
|
48810
|
-
{
|
|
49060
|
+
}] : [],
|
|
49061
|
+
...canAccessPath("/shifts") ? [{
|
|
48811
49062
|
key: "shifts",
|
|
48812
49063
|
label: "Shifts",
|
|
48813
49064
|
icon: ClockIcon,
|
|
@@ -48816,8 +49067,8 @@ var SideNavBar = memo$1(({
|
|
|
48816
49067
|
setIsSettingsOpen(false);
|
|
48817
49068
|
},
|
|
48818
49069
|
isActive: pathname === "/shifts" || pathname.startsWith("/shifts/")
|
|
48819
|
-
},
|
|
48820
|
-
{
|
|
49070
|
+
}] : [],
|
|
49071
|
+
...canAccessPath("/team-management") ? [{
|
|
48821
49072
|
key: "teams",
|
|
48822
49073
|
label: "Teams",
|
|
48823
49074
|
icon: UsersIcon,
|
|
@@ -48826,8 +49077,8 @@ var SideNavBar = memo$1(({
|
|
|
48826
49077
|
setIsSettingsOpen(false);
|
|
48827
49078
|
},
|
|
48828
49079
|
isActive: pathname === "/team-management" || pathname.startsWith("/team-management/")
|
|
48829
|
-
},
|
|
48830
|
-
{
|
|
49080
|
+
}] : [],
|
|
49081
|
+
...canAccessPath("/profile") ? [{
|
|
48831
49082
|
key: "profile",
|
|
48832
49083
|
label: "Profile",
|
|
48833
49084
|
icon: UserCircleIcon,
|
|
@@ -48836,9 +49087,9 @@ var SideNavBar = memo$1(({
|
|
|
48836
49087
|
setIsSettingsOpen(false);
|
|
48837
49088
|
},
|
|
48838
49089
|
isActive: pathname === "/profile" || pathname.startsWith("/profile/")
|
|
48839
|
-
}
|
|
49090
|
+
}] : []
|
|
48840
49091
|
];
|
|
48841
|
-
if (ticketsEnabled) {
|
|
49092
|
+
if (ticketsEnabled && canAccessPath("/tickets")) {
|
|
48842
49093
|
items.push({
|
|
48843
49094
|
key: "tickets",
|
|
48844
49095
|
label: "Tickets",
|
|
@@ -48850,18 +49101,20 @@ var SideNavBar = memo$1(({
|
|
|
48850
49101
|
isActive: pathname === "/tickets" || pathname.startsWith("/tickets/")
|
|
48851
49102
|
});
|
|
48852
49103
|
}
|
|
48853
|
-
|
|
48854
|
-
|
|
48855
|
-
|
|
48856
|
-
|
|
48857
|
-
|
|
48858
|
-
|
|
48859
|
-
|
|
48860
|
-
|
|
48861
|
-
|
|
48862
|
-
|
|
49104
|
+
if (canAccessPath("/help")) {
|
|
49105
|
+
items.push({
|
|
49106
|
+
key: "help",
|
|
49107
|
+
label: "Help",
|
|
49108
|
+
icon: QuestionMarkCircleIcon,
|
|
49109
|
+
onClick: () => {
|
|
49110
|
+
handleHelpClick();
|
|
49111
|
+
setIsSettingsOpen(false);
|
|
49112
|
+
},
|
|
49113
|
+
isActive: pathname === "/help" || pathname.startsWith("/help/")
|
|
49114
|
+
});
|
|
49115
|
+
}
|
|
48863
49116
|
return items;
|
|
48864
|
-
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleHelpClick, pathname, ticketsEnabled]);
|
|
49117
|
+
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleHelpClick, pathname, ticketsEnabled, canAccessPath]);
|
|
48865
49118
|
const handleLogout = useCallback(async () => {
|
|
48866
49119
|
setIsSettingsOpen(false);
|
|
48867
49120
|
try {
|
|
@@ -48899,7 +49152,7 @@ var SideNavBar = memo$1(({
|
|
|
48899
49152
|
}
|
|
48900
49153
|
) }),
|
|
48901
49154
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: [
|
|
48902
|
-
/* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsxs(
|
|
49155
|
+
/* @__PURE__ */ jsx("div", { className: "mb-6", children: canAccessPath("/") && /* @__PURE__ */ jsxs(
|
|
48903
49156
|
"button",
|
|
48904
49157
|
{
|
|
48905
49158
|
onClick: handleHomeClick,
|
|
@@ -48915,7 +49168,7 @@ var SideNavBar = memo$1(({
|
|
|
48915
49168
|
}
|
|
48916
49169
|
) }),
|
|
48917
49170
|
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
48918
|
-
/* @__PURE__ */ jsxs(
|
|
49171
|
+
canAccessPath("/leaderboard") && /* @__PURE__ */ jsxs(
|
|
48919
49172
|
"button",
|
|
48920
49173
|
{
|
|
48921
49174
|
onClick: handleLeaderboardClick,
|
|
@@ -48930,7 +49183,7 @@ var SideNavBar = memo$1(({
|
|
|
48930
49183
|
]
|
|
48931
49184
|
}
|
|
48932
49185
|
),
|
|
48933
|
-
/* @__PURE__ */ jsxs(
|
|
49186
|
+
canAccessPath("/kpis") && /* @__PURE__ */ jsxs(
|
|
48934
49187
|
"button",
|
|
48935
49188
|
{
|
|
48936
49189
|
onClick: handleKPIsClick,
|
|
@@ -48945,7 +49198,7 @@ var SideNavBar = memo$1(({
|
|
|
48945
49198
|
]
|
|
48946
49199
|
}
|
|
48947
49200
|
),
|
|
48948
|
-
/* @__PURE__ */ jsxs(
|
|
49201
|
+
canAccessPath("/improvement-center") && /* @__PURE__ */ jsxs(
|
|
48949
49202
|
"button",
|
|
48950
49203
|
{
|
|
48951
49204
|
onClick: handleImprovementClick,
|
|
@@ -48961,7 +49214,7 @@ var SideNavBar = memo$1(({
|
|
|
48961
49214
|
}
|
|
48962
49215
|
),
|
|
48963
49216
|
showSupervisorManagement,
|
|
48964
|
-
skuEnabled &&
|
|
49217
|
+
skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxs(
|
|
48965
49218
|
"button",
|
|
48966
49219
|
{
|
|
48967
49220
|
onClick: handleSKUsClick,
|
|
@@ -48976,7 +49229,7 @@ var SideNavBar = memo$1(({
|
|
|
48976
49229
|
]
|
|
48977
49230
|
}
|
|
48978
49231
|
),
|
|
48979
|
-
/* @__PURE__ */ jsxs(
|
|
49232
|
+
canAccessPath("/health") && /* @__PURE__ */ jsxs(
|
|
48980
49233
|
"button",
|
|
48981
49234
|
{
|
|
48982
49235
|
onClick: handleHealthClick,
|
|
@@ -48993,7 +49246,7 @@ var SideNavBar = memo$1(({
|
|
|
48993
49246
|
)
|
|
48994
49247
|
] })
|
|
48995
49248
|
] }),
|
|
48996
|
-
/* @__PURE__ */ jsx("div", { className: "w-full py-5 px-4 border-t border-gray-100 flex-shrink-0", children: /* @__PURE__ */ jsxs(
|
|
49249
|
+
settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "w-full py-5 px-4 border-t border-gray-100 flex-shrink-0", children: /* @__PURE__ */ jsxs(
|
|
48997
49250
|
"button",
|
|
48998
49251
|
{
|
|
48999
49252
|
ref: settingsTriggerRef,
|
|
@@ -49031,7 +49284,7 @@ var SideNavBar = memo$1(({
|
|
|
49031
49284
|
};
|
|
49032
49285
|
};
|
|
49033
49286
|
return /* @__PURE__ */ jsxs("nav", { className: "px-5 py-6", children: [
|
|
49034
|
-
/* @__PURE__ */ jsxs(
|
|
49287
|
+
canAccessPath("/") && /* @__PURE__ */ jsxs(
|
|
49035
49288
|
"button",
|
|
49036
49289
|
{
|
|
49037
49290
|
onClick: handleMobileNavClick(handleHomeClick),
|
|
@@ -49044,7 +49297,7 @@ var SideNavBar = memo$1(({
|
|
|
49044
49297
|
}
|
|
49045
49298
|
),
|
|
49046
49299
|
/* @__PURE__ */ jsxs("div", { className: "mt-6 space-y-2", children: [
|
|
49047
|
-
/* @__PURE__ */ jsxs(
|
|
49300
|
+
canAccessPath("/leaderboard") && /* @__PURE__ */ jsxs(
|
|
49048
49301
|
"button",
|
|
49049
49302
|
{
|
|
49050
49303
|
onClick: handleMobileNavClick(handleLeaderboardClick),
|
|
@@ -49056,7 +49309,7 @@ var SideNavBar = memo$1(({
|
|
|
49056
49309
|
]
|
|
49057
49310
|
}
|
|
49058
49311
|
),
|
|
49059
|
-
/* @__PURE__ */ jsxs(
|
|
49312
|
+
canAccessPath("/kpis") && /* @__PURE__ */ jsxs(
|
|
49060
49313
|
"button",
|
|
49061
49314
|
{
|
|
49062
49315
|
onClick: handleMobileNavClick(handleKPIsClick),
|
|
@@ -49068,7 +49321,7 @@ var SideNavBar = memo$1(({
|
|
|
49068
49321
|
]
|
|
49069
49322
|
}
|
|
49070
49323
|
),
|
|
49071
|
-
/* @__PURE__ */ jsxs(
|
|
49324
|
+
canAccessPath("/improvement-center") && /* @__PURE__ */ jsxs(
|
|
49072
49325
|
"button",
|
|
49073
49326
|
{
|
|
49074
49327
|
onClick: handleMobileNavClick(handleImprovementClick),
|
|
@@ -49081,7 +49334,7 @@ var SideNavBar = memo$1(({
|
|
|
49081
49334
|
}
|
|
49082
49335
|
),
|
|
49083
49336
|
showSupervisorManagement,
|
|
49084
|
-
skuEnabled &&
|
|
49337
|
+
skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxs(
|
|
49085
49338
|
"button",
|
|
49086
49339
|
{
|
|
49087
49340
|
onClick: handleMobileNavClick(handleSKUsClick),
|
|
@@ -49093,7 +49346,7 @@ var SideNavBar = memo$1(({
|
|
|
49093
49346
|
]
|
|
49094
49347
|
}
|
|
49095
49348
|
),
|
|
49096
|
-
/* @__PURE__ */ jsxs(
|
|
49349
|
+
canAccessPath("/health") && /* @__PURE__ */ jsxs(
|
|
49097
49350
|
"button",
|
|
49098
49351
|
{
|
|
49099
49352
|
onClick: handleMobileNavClick(handleHealthClick),
|
|
@@ -49106,10 +49359,10 @@ var SideNavBar = memo$1(({
|
|
|
49106
49359
|
}
|
|
49107
49360
|
)
|
|
49108
49361
|
] }),
|
|
49109
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-8 pt-6 border-t border-gray-100", children: [
|
|
49362
|
+
settingsItems.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-8 pt-6 border-t border-gray-100", children: [
|
|
49110
49363
|
/* @__PURE__ */ jsx("h3", { className: "px-5 mb-3 text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Settings & Support" }),
|
|
49111
49364
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
49112
|
-
/* @__PURE__ */ jsxs(
|
|
49365
|
+
canAccessPath("/targets") && /* @__PURE__ */ jsxs(
|
|
49113
49366
|
"button",
|
|
49114
49367
|
{
|
|
49115
49368
|
onClick: handleMobileNavClick(handleTargetsClick),
|
|
@@ -49121,7 +49374,7 @@ var SideNavBar = memo$1(({
|
|
|
49121
49374
|
]
|
|
49122
49375
|
}
|
|
49123
49376
|
),
|
|
49124
|
-
/* @__PURE__ */ jsxs(
|
|
49377
|
+
canAccessPath("/shifts") && /* @__PURE__ */ jsxs(
|
|
49125
49378
|
"button",
|
|
49126
49379
|
{
|
|
49127
49380
|
onClick: handleMobileNavClick(handleShiftsClick),
|
|
@@ -49133,7 +49386,7 @@ var SideNavBar = memo$1(({
|
|
|
49133
49386
|
]
|
|
49134
49387
|
}
|
|
49135
49388
|
),
|
|
49136
|
-
/* @__PURE__ */ jsxs(
|
|
49389
|
+
canAccessPath("/team-management") && /* @__PURE__ */ jsxs(
|
|
49137
49390
|
"button",
|
|
49138
49391
|
{
|
|
49139
49392
|
onClick: handleMobileNavClick(handleTeamManagementClick),
|
|
@@ -49145,7 +49398,7 @@ var SideNavBar = memo$1(({
|
|
|
49145
49398
|
]
|
|
49146
49399
|
}
|
|
49147
49400
|
),
|
|
49148
|
-
/* @__PURE__ */ jsxs(
|
|
49401
|
+
canAccessPath("/profile") && /* @__PURE__ */ jsxs(
|
|
49149
49402
|
"button",
|
|
49150
49403
|
{
|
|
49151
49404
|
onClick: handleMobileNavClick(handleProfileClick),
|
|
@@ -49157,7 +49410,7 @@ var SideNavBar = memo$1(({
|
|
|
49157
49410
|
]
|
|
49158
49411
|
}
|
|
49159
49412
|
),
|
|
49160
|
-
ticketsEnabled && /* @__PURE__ */ jsxs(
|
|
49413
|
+
ticketsEnabled && canAccessPath("/tickets") && /* @__PURE__ */ jsxs(
|
|
49161
49414
|
"button",
|
|
49162
49415
|
{
|
|
49163
49416
|
onClick: handleMobileNavClick(handleTicketsClick),
|
|
@@ -49169,7 +49422,7 @@ var SideNavBar = memo$1(({
|
|
|
49169
49422
|
]
|
|
49170
49423
|
}
|
|
49171
49424
|
),
|
|
49172
|
-
/* @__PURE__ */ jsxs(
|
|
49425
|
+
canAccessPath("/help") && /* @__PURE__ */ jsxs(
|
|
49173
49426
|
"button",
|
|
49174
49427
|
{
|
|
49175
49428
|
onClick: handleMobileNavClick(handleHelpClick),
|
|
@@ -51429,9 +51682,19 @@ var InviteUserDialog = ({
|
|
|
51429
51682
|
const [factorySearch, setFactorySearch] = useState("");
|
|
51430
51683
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
51431
51684
|
const [error, setError] = useState(null);
|
|
51685
|
+
const isSuperAdminOptifye = user?.role_level === "optifye" && (user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin);
|
|
51686
|
+
const canInviteOwner = isSuperAdminOptifye;
|
|
51432
51687
|
const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
51433
51688
|
const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
51434
51689
|
const canInviteSupervisor = ["owner", "it", "plant_head", "optifye"].includes(user?.role_level || "");
|
|
51690
|
+
const invitableRoles = useMemo(() => {
|
|
51691
|
+
const roles = [];
|
|
51692
|
+
if (canInviteOwner) roles.push("owner");
|
|
51693
|
+
if (canInviteIT) roles.push("it");
|
|
51694
|
+
if (canInvitePlantHead) roles.push("plant_head");
|
|
51695
|
+
if (canInviteSupervisor) roles.push("supervisor");
|
|
51696
|
+
return roles;
|
|
51697
|
+
}, [canInviteOwner, canInviteIT, canInvitePlantHead, canInviteSupervisor]);
|
|
51435
51698
|
const filteredLines = useMemo(() => {
|
|
51436
51699
|
const search = lineSearch.trim().toLowerCase();
|
|
51437
51700
|
if (!search) return availableLines;
|
|
@@ -51463,6 +51726,14 @@ var InviteUserDialog = ({
|
|
|
51463
51726
|
setError(null);
|
|
51464
51727
|
}
|
|
51465
51728
|
}, [isOpen]);
|
|
51729
|
+
useEffect(() => {
|
|
51730
|
+
if (!isOpen || invitableRoles.length === 0) {
|
|
51731
|
+
return;
|
|
51732
|
+
}
|
|
51733
|
+
if (!invitableRoles.includes(selectedRole)) {
|
|
51734
|
+
setSelectedRole(invitableRoles[0]);
|
|
51735
|
+
}
|
|
51736
|
+
}, [isOpen, invitableRoles, selectedRole]);
|
|
51466
51737
|
const validateEmail = (email2) => {
|
|
51467
51738
|
const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/;
|
|
51468
51739
|
return emailRegex.test(email2);
|
|
@@ -51492,6 +51763,14 @@ var InviteUserDialog = ({
|
|
|
51492
51763
|
setError("Please enter a valid email address");
|
|
51493
51764
|
return;
|
|
51494
51765
|
}
|
|
51766
|
+
if (!invitableRoles.includes(selectedRole)) {
|
|
51767
|
+
setError("You do not have permission to assign this role.");
|
|
51768
|
+
return;
|
|
51769
|
+
}
|
|
51770
|
+
if (selectedRole === "owner" && !canInviteOwner) {
|
|
51771
|
+
setError("Only super-admin Optifye users can create owner users.");
|
|
51772
|
+
return;
|
|
51773
|
+
}
|
|
51495
51774
|
const companyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
51496
51775
|
if (!companyId) {
|
|
51497
51776
|
setError("Company ID not found. Please refresh and try again.");
|
|
@@ -51672,6 +51951,33 @@ var InviteUserDialog = ({
|
|
|
51672
51951
|
/* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
|
|
51673
51952
|
] }),
|
|
51674
51953
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
51954
|
+
canInviteOwner && /* @__PURE__ */ jsxs(
|
|
51955
|
+
"label",
|
|
51956
|
+
{
|
|
51957
|
+
className: cn(
|
|
51958
|
+
"flex items-start gap-3 p-4 border-2 rounded-lg cursor-pointer transition-all duration-200",
|
|
51959
|
+
selectedRole === "owner" ? "border-amber-500 bg-amber-50" : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
|
51960
|
+
),
|
|
51961
|
+
children: [
|
|
51962
|
+
/* @__PURE__ */ jsx(
|
|
51963
|
+
"input",
|
|
51964
|
+
{
|
|
51965
|
+
type: "radio",
|
|
51966
|
+
name: "role",
|
|
51967
|
+
value: "owner",
|
|
51968
|
+
checked: selectedRole === "owner",
|
|
51969
|
+
onChange: (e) => setSelectedRole(e.target.value),
|
|
51970
|
+
className: "mt-1",
|
|
51971
|
+
disabled: isSubmitting
|
|
51972
|
+
}
|
|
51973
|
+
),
|
|
51974
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
51975
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mb-1", children: /* @__PURE__ */ jsx(RoleBadge_default, { role: "owner", size: "sm" }) }),
|
|
51976
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Company owner access. Full management permissions for the selected company dashboard." })
|
|
51977
|
+
] })
|
|
51978
|
+
]
|
|
51979
|
+
}
|
|
51980
|
+
),
|
|
51675
51981
|
canInviteIT && /* @__PURE__ */ jsxs(
|
|
51676
51982
|
"label",
|
|
51677
51983
|
{
|
|
@@ -53543,11 +53849,32 @@ var UserManagementTable = ({
|
|
|
53543
53849
|
setSortDirection("asc");
|
|
53544
53850
|
}
|
|
53545
53851
|
};
|
|
53852
|
+
const getFactoryIdsFromUser = (user) => {
|
|
53853
|
+
const properties = user.properties || {};
|
|
53854
|
+
const rawFactoryIds = properties.factory_ids ?? properties.factory_id;
|
|
53855
|
+
if (Array.isArray(rawFactoryIds)) {
|
|
53856
|
+
return rawFactoryIds.filter((factoryId) => typeof factoryId === "string" && factoryId.length > 0);
|
|
53857
|
+
}
|
|
53858
|
+
if (typeof rawFactoryIds === "string" && rawFactoryIds.length > 0) {
|
|
53859
|
+
return [rawFactoryIds];
|
|
53860
|
+
}
|
|
53861
|
+
return [];
|
|
53862
|
+
};
|
|
53546
53863
|
const formatAssignments = (user) => {
|
|
53547
53864
|
if (user.role_level === "owner" || user.role_level === "it") return "Company-wide";
|
|
53548
53865
|
if (user.role_level === "optifye") return "All companies";
|
|
53549
|
-
if (user.role_level === "plant_head"
|
|
53550
|
-
|
|
53866
|
+
if (user.role_level === "plant_head") {
|
|
53867
|
+
if (user.assigned_factories && user.assigned_factories.length > 0) {
|
|
53868
|
+
return user.assigned_factories.join(", ");
|
|
53869
|
+
}
|
|
53870
|
+
const assignedFactoryIds = getFactoryIdsFromUser(user);
|
|
53871
|
+
if (assignedFactoryIds.length === 1) {
|
|
53872
|
+
return "1 factory assigned";
|
|
53873
|
+
}
|
|
53874
|
+
if (assignedFactoryIds.length > 1) {
|
|
53875
|
+
return `${assignedFactoryIds.length} factories assigned`;
|
|
53876
|
+
}
|
|
53877
|
+
return "No factories assigned";
|
|
53551
53878
|
}
|
|
53552
53879
|
if (user.role_level === "supervisor" && user.assigned_lines) {
|
|
53553
53880
|
return user.assigned_lines.join(", ") || "No lines assigned";
|
|
@@ -53739,22 +54066,28 @@ var UserManagementTable = ({
|
|
|
53739
54066
|
onUpdate: onLineAssignmentUpdate || (async () => {
|
|
53740
54067
|
})
|
|
53741
54068
|
}
|
|
53742
|
-
) : user.role_level === "plant_head" ?
|
|
53743
|
-
|
|
53744
|
-
{
|
|
53745
|
-
|
|
53746
|
-
currentFactoryIds: user.properties?.factory_ids || [],
|
|
53747
|
-
availableFactories: (
|
|
53748
|
-
// Filter factories to only show those from the target user's company
|
|
53749
|
-
user.properties?.company_id ? availableFactories.filter(
|
|
53750
|
-
(factory) => factory.company_id === user.properties?.company_id
|
|
53751
|
-
) : availableFactories
|
|
53752
|
-
),
|
|
53753
|
-
canEdit: permissions.canAssignFactories(user) && !!onFactoryAssignmentUpdate,
|
|
53754
|
-
onUpdate: onFactoryAssignmentUpdate || (async () => {
|
|
53755
|
-
})
|
|
54069
|
+
) : user.role_level === "plant_head" ? (() => {
|
|
54070
|
+
const canEditFactoryAssignments = permissions.canAssignFactories(user) && !!onFactoryAssignmentUpdate;
|
|
54071
|
+
if (!canEditFactoryAssignments) {
|
|
54072
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) });
|
|
53756
54073
|
}
|
|
53757
|
-
|
|
54074
|
+
return /* @__PURE__ */ jsx(
|
|
54075
|
+
FactoryAssignmentDropdown,
|
|
54076
|
+
{
|
|
54077
|
+
userId: user.user_id,
|
|
54078
|
+
currentFactoryIds: getFactoryIdsFromUser(user),
|
|
54079
|
+
availableFactories: (
|
|
54080
|
+
// Filter factories to only show those from the target user's company
|
|
54081
|
+
user.properties?.company_id ? availableFactories.filter(
|
|
54082
|
+
(factory) => factory.company_id === user.properties?.company_id
|
|
54083
|
+
) : availableFactories
|
|
54084
|
+
),
|
|
54085
|
+
canEdit: canEditFactoryAssignments,
|
|
54086
|
+
onUpdate: onFactoryAssignmentUpdate || (async () => {
|
|
54087
|
+
})
|
|
54088
|
+
}
|
|
54089
|
+
);
|
|
54090
|
+
})() : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
53758
54091
|
showUsageStats && /* @__PURE__ */ jsx("td", { className: "px-6 py-4", children: user.role_level === "plant_head" || user.role_level === "supervisor" ? /* @__PURE__ */ jsx(
|
|
53759
54092
|
"button",
|
|
53760
54093
|
{
|
|
@@ -54919,52 +55252,18 @@ function HomeView({
|
|
|
54919
55252
|
});
|
|
54920
55253
|
return merged;
|
|
54921
55254
|
}, [lineNames, dbLines]);
|
|
54922
|
-
const
|
|
54923
|
-
|
|
55255
|
+
const enabledLineIdSet = useMemo(
|
|
55256
|
+
() => new Set(dbLines.filter((line) => line.enable).map((line) => line.id)),
|
|
55257
|
+
[dbLines]
|
|
55258
|
+
);
|
|
54924
55259
|
const isSupervisor = user?.role_level === "supervisor";
|
|
54925
|
-
const assignedLineIds = useMemo(() => {
|
|
54926
|
-
const rawLineIds = user?.properties?.line_id || user?.properties?.line_ids || [];
|
|
54927
|
-
return Array.isArray(rawLineIds) ? rawLineIds : [];
|
|
54928
|
-
}, [user]);
|
|
54929
|
-
const assignedLineIdsInConfig = useMemo(() => {
|
|
54930
|
-
if (assignedLineIds.length === 0) {
|
|
54931
|
-
return [];
|
|
54932
|
-
}
|
|
54933
|
-
return assignedLineIds.filter((id3) => allLineIds.includes(id3));
|
|
54934
|
-
}, [assignedLineIds, allLineIds]);
|
|
54935
|
-
const assignedFactoryIds = useMemo(() => {
|
|
54936
|
-
const rawFactoryIds = user?.properties?.factory_id || user?.properties?.factory_ids || [];
|
|
54937
|
-
return Array.isArray(rawFactoryIds) ? rawFactoryIds : [];
|
|
54938
|
-
}, [user]);
|
|
54939
|
-
const lineFactoryMap = useMemo(() => {
|
|
54940
|
-
const map = /* @__PURE__ */ new Map();
|
|
54941
|
-
dbLines.forEach((line) => {
|
|
54942
|
-
map.set(line.id, line.factory_id);
|
|
54943
|
-
});
|
|
54944
|
-
return map;
|
|
54945
|
-
}, [dbLines]);
|
|
54946
|
-
const plantHeadLineIds = useMemo(() => {
|
|
54947
|
-
if (!isPlantHead || assignedFactoryIds.length === 0) {
|
|
54948
|
-
return [];
|
|
54949
|
-
}
|
|
54950
|
-
const assignedFactoryIdSet = new Set(assignedFactoryIds);
|
|
54951
|
-
return allLineIds.filter((lineId) => assignedFactoryIdSet.has(lineFactoryMap.get(lineId) || ""));
|
|
54952
|
-
}, [isPlantHead, assignedFactoryIds, allLineIds, lineFactoryMap]);
|
|
54953
55260
|
const visibleLineIds = useMemo(() => {
|
|
54954
|
-
|
|
54955
|
-
|
|
54956
|
-
|
|
54957
|
-
if (isPlantHead) {
|
|
54958
|
-
const combinedLineIds = /* @__PURE__ */ new Set();
|
|
54959
|
-
plantHeadLineIds.forEach((lineId) => combinedLineIds.add(lineId));
|
|
54960
|
-
assignedLineIdsInConfig.forEach((lineId) => combinedLineIds.add(lineId));
|
|
54961
|
-
return Array.from(combinedLineIds);
|
|
55261
|
+
const scoped = Array.from(new Set(allLineIds.filter(Boolean)));
|
|
55262
|
+
if (enabledLineIdSet.size === 0) {
|
|
55263
|
+
return scoped;
|
|
54962
55264
|
}
|
|
54963
|
-
|
|
54964
|
-
|
|
54965
|
-
}
|
|
54966
|
-
return allLineIds;
|
|
54967
|
-
}, [isOwner, isPlantHead, isSupervisor, allLineIds, plantHeadLineIds, assignedLineIdsInConfig]);
|
|
55265
|
+
return scoped.filter((lineId) => enabledLineIdSet.has(lineId));
|
|
55266
|
+
}, [allLineIds, enabledLineIdSet]);
|
|
54968
55267
|
const fallbackLineId = visibleLineIds[0] || defaultLineId;
|
|
54969
55268
|
const defaultHomeLineId = fallbackLineId;
|
|
54970
55269
|
const availableLineIds = useMemo(() => {
|
|
@@ -54991,7 +55290,7 @@ function HomeView({
|
|
|
54991
55290
|
return defaultHomeLineId;
|
|
54992
55291
|
});
|
|
54993
55292
|
useEffect(() => {
|
|
54994
|
-
if (!user ||
|
|
55293
|
+
if (!user || availableLineIds.length === 0) {
|
|
54995
55294
|
return;
|
|
54996
55295
|
}
|
|
54997
55296
|
try {
|
|
@@ -55005,13 +55304,14 @@ function HomeView({
|
|
|
55005
55304
|
} catch (error) {
|
|
55006
55305
|
console.warn("Failed to read line filter from sessionStorage:", error);
|
|
55007
55306
|
}
|
|
55307
|
+
if (availableLineIds.includes(selectedLineId)) {
|
|
55308
|
+
return;
|
|
55309
|
+
}
|
|
55008
55310
|
if (defaultHomeLineId !== selectedLineId) {
|
|
55009
55311
|
setSelectedLineId(defaultHomeLineId);
|
|
55010
55312
|
}
|
|
55011
55313
|
}, [
|
|
55012
55314
|
user,
|
|
55013
|
-
isSupervisor,
|
|
55014
|
-
isPlantHead,
|
|
55015
55315
|
availableLineIds,
|
|
55016
55316
|
defaultHomeLineId,
|
|
55017
55317
|
selectedLineId,
|
|
@@ -55030,14 +55330,14 @@ function HomeView({
|
|
|
55030
55330
|
const [diagnoses, setDiagnoses] = useState([]);
|
|
55031
55331
|
const timezone = useAppTimezone();
|
|
55032
55332
|
const { shiftConfigMap: lineShiftConfigs } = useMultiLineShiftConfigs(
|
|
55033
|
-
|
|
55333
|
+
visibleLineIds,
|
|
55034
55334
|
dashboardConfig?.shiftConfig
|
|
55035
55335
|
);
|
|
55036
55336
|
useEffect(() => {
|
|
55037
55337
|
const initDisplayNames = async () => {
|
|
55038
55338
|
try {
|
|
55039
55339
|
if (selectedLineId === factoryViewId) {
|
|
55040
|
-
for (const lineId of
|
|
55340
|
+
for (const lineId of visibleLineIds) {
|
|
55041
55341
|
await preInitializeWorkspaceDisplayNames(lineId);
|
|
55042
55342
|
}
|
|
55043
55343
|
} else {
|
|
@@ -55050,7 +55350,7 @@ function HomeView({
|
|
|
55050
55350
|
}
|
|
55051
55351
|
};
|
|
55052
55352
|
initDisplayNames();
|
|
55053
|
-
}, [selectedLineId, factoryViewId,
|
|
55353
|
+
}, [selectedLineId, factoryViewId, visibleLineIds]);
|
|
55054
55354
|
const displayNameLineId = selectedLineId === factoryViewId ? void 0 : selectedLineId;
|
|
55055
55355
|
const {
|
|
55056
55356
|
displayNames: workspaceDisplayNames,
|
|
@@ -58801,8 +59101,32 @@ var KPIsOverviewView = ({
|
|
|
58801
59101
|
const dbTimezone = useAppTimezone();
|
|
58802
59102
|
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
58803
59103
|
const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
|
|
58804
|
-
const
|
|
58805
|
-
const
|
|
59104
|
+
const isSuperAdmin = user?.scope_mode === "SUPER_ADMIN" || !!user?.access_scope?.is_super_admin;
|
|
59105
|
+
const scopedLineIds = React26__default.useMemo(
|
|
59106
|
+
() => Array.isArray(user?.access_scope?.line_ids) ? user.access_scope.line_ids.filter((lineId) => typeof lineId === "string" && lineId.length > 0) : [],
|
|
59107
|
+
[user?.access_scope?.line_ids]
|
|
59108
|
+
);
|
|
59109
|
+
const hasCanonicalScope = !!user?.scope_mode || !!user?.access_scope;
|
|
59110
|
+
const scopeRole = (user?.role_level || user?.role || "").toLowerCase();
|
|
59111
|
+
const isStrictLineScopedRole = scopeRole === "supervisor" || scopeRole === "plant_head";
|
|
59112
|
+
const resolvedAssignedLineIds = React26__default.useMemo(() => {
|
|
59113
|
+
if (isSuperAdmin) return [];
|
|
59114
|
+
if (scopedLineIds.length > 0) return scopedLineIds;
|
|
59115
|
+
if (lineIds && lineIds.length > 0) return lineIds;
|
|
59116
|
+
if (isStrictLineScopedRole && hasCanonicalScope) return [];
|
|
59117
|
+
return [];
|
|
59118
|
+
}, [isSuperAdmin, scopedLineIds, lineIds, isStrictLineScopedRole, hasCanonicalScope]);
|
|
59119
|
+
const assignedLineIdSet = React26__default.useMemo(
|
|
59120
|
+
() => new Set(resolvedAssignedLineIds),
|
|
59121
|
+
[resolvedAssignedLineIds]
|
|
59122
|
+
);
|
|
59123
|
+
const metricsLineIds = React26__default.useMemo(() => {
|
|
59124
|
+
if (isSuperAdmin) {
|
|
59125
|
+
return lineIds ?? [];
|
|
59126
|
+
}
|
|
59127
|
+
return resolvedAssignedLineIds;
|
|
59128
|
+
}, [isSuperAdmin, lineIds, resolvedAssignedLineIds]);
|
|
59129
|
+
const assignedLineIdsForLeaderboard = isSuperAdmin ? void 0 : resolvedAssignedLineIds;
|
|
58806
59130
|
const leaderboardLinesForView = React26__default.useMemo(() => {
|
|
58807
59131
|
const targetMode = viewType === "machine" ? "uptime" : "output";
|
|
58808
59132
|
return leaderboardLines.filter((line) => (line.monitoring_mode ?? "output") === targetMode);
|
|
@@ -58854,7 +59178,7 @@ var KPIsOverviewView = ({
|
|
|
58854
59178
|
error: metricsError
|
|
58855
59179
|
} = useDashboardMetrics({
|
|
58856
59180
|
lineId: factoryViewId,
|
|
58857
|
-
userAccessibleLineIds:
|
|
59181
|
+
userAccessibleLineIds: metricsLineIds
|
|
58858
59182
|
});
|
|
58859
59183
|
const defaultKPIs = React26__default.useMemo(() => createDefaultKPIs(), []);
|
|
58860
59184
|
const kpisByLineId = React26__default.useMemo(() => {
|
|
@@ -58908,16 +59232,16 @@ var KPIsOverviewView = ({
|
|
|
58908
59232
|
console.log("[KPIsOverviewView] Fetching lines with lineIds filter:", lineIds);
|
|
58909
59233
|
const allLines = await dashboardService.getAllLines();
|
|
58910
59234
|
let filteredLines = allLines;
|
|
58911
|
-
if (
|
|
58912
|
-
filteredLines = allLines.filter((line) =>
|
|
58913
|
-
console.log("[KPIsOverviewView]
|
|
59235
|
+
if (!isSuperAdmin) {
|
|
59236
|
+
filteredLines = allLines.filter((line) => assignedLineIdSet.has(line.id));
|
|
59237
|
+
console.log("[KPIsOverviewView] Applied scoped line filter:", {
|
|
58914
59238
|
total: allLines.length,
|
|
58915
59239
|
filtered: filteredLines.length,
|
|
58916
|
-
|
|
59240
|
+
allowedLineIds: resolvedAssignedLineIds,
|
|
58917
59241
|
filteredLineIds: filteredLines.map((l) => l.id)
|
|
58918
59242
|
});
|
|
58919
59243
|
} else {
|
|
58920
|
-
console.log("[KPIsOverviewView]
|
|
59244
|
+
console.log("[KPIsOverviewView] Super admin view, showing all lines:", allLines.length);
|
|
58921
59245
|
}
|
|
58922
59246
|
setLines(filteredLines);
|
|
58923
59247
|
} catch (err) {
|
|
@@ -58928,34 +59252,36 @@ var KPIsOverviewView = ({
|
|
|
58928
59252
|
}
|
|
58929
59253
|
};
|
|
58930
59254
|
fetchLines();
|
|
58931
|
-
}, [supabase, dashboardConfig, lineIds]);
|
|
59255
|
+
}, [supabase, dashboardConfig, lineIds, isSuperAdmin, assignedLineIdSet, resolvedAssignedLineIds]);
|
|
58932
59256
|
useEffect(() => {
|
|
58933
59257
|
let isMounted = true;
|
|
58934
59258
|
const fetchLeaderboardLines = async () => {
|
|
58935
59259
|
if (!supabase || !resolvedCompanyId) {
|
|
58936
|
-
setLeaderboardLines(lines);
|
|
59260
|
+
setLeaderboardLines(lines.filter((line) => line.enable !== false));
|
|
58937
59261
|
return;
|
|
58938
59262
|
}
|
|
58939
59263
|
setLeaderboardLinesLoading(true);
|
|
58940
59264
|
try {
|
|
58941
|
-
const
|
|
58942
|
-
|
|
59265
|
+
const data = await fetchBackendJson(
|
|
59266
|
+
supabase,
|
|
59267
|
+
`/api/dashboard/leaderboard-lines?company_id=${encodeURIComponent(resolvedCompanyId)}`
|
|
59268
|
+
);
|
|
58943
59269
|
if (!isMounted) return;
|
|
58944
|
-
const transformed =
|
|
59270
|
+
const transformed = (data.lines || []).filter((line) => line.enable !== false).map((line) => ({
|
|
58945
59271
|
id: line.id,
|
|
58946
|
-
line_name: line.
|
|
58947
|
-
factory_id: line.
|
|
59272
|
+
line_name: line.line_name,
|
|
59273
|
+
factory_id: line.factory_id || "",
|
|
58948
59274
|
factory_name: "N/A",
|
|
58949
|
-
company_id: line.
|
|
59275
|
+
company_id: line.company_id,
|
|
58950
59276
|
company_name: "",
|
|
58951
|
-
enable: line.
|
|
58952
|
-
monitoring_mode: line.
|
|
59277
|
+
enable: line.enable ?? true,
|
|
59278
|
+
monitoring_mode: line.monitoring_mode ?? "output"
|
|
58953
59279
|
}));
|
|
58954
59280
|
setLeaderboardLines(transformed);
|
|
58955
59281
|
} catch (err) {
|
|
58956
59282
|
console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
|
|
58957
59283
|
if (!isMounted) return;
|
|
58958
|
-
setLeaderboardLines(lines);
|
|
59284
|
+
setLeaderboardLines(lines.filter((line) => line.enable !== false));
|
|
58959
59285
|
} finally {
|
|
58960
59286
|
if (isMounted) setLeaderboardLinesLoading(false);
|
|
58961
59287
|
}
|
|
@@ -59036,11 +59362,10 @@ var KPIsOverviewView = ({
|
|
|
59036
59362
|
lineMode: viewType === "machine" ? "uptime" : "output"
|
|
59037
59363
|
});
|
|
59038
59364
|
const nextMap = /* @__PURE__ */ new Map();
|
|
59365
|
+
targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
|
|
59039
59366
|
entries.forEach((entry) => {
|
|
59040
59367
|
const value = Number(entry.avg_efficiency);
|
|
59041
|
-
|
|
59042
|
-
nextMap.set(entry.line_id, value);
|
|
59043
|
-
}
|
|
59368
|
+
nextMap.set(entry.line_id, Number.isFinite(value) ? value : 0);
|
|
59044
59369
|
});
|
|
59045
59370
|
setTodayEfficiencyByLineId(nextMap);
|
|
59046
59371
|
} catch (err) {
|
|
@@ -59123,6 +59448,9 @@ var KPIsOverviewView = ({
|
|
|
59123
59448
|
};
|
|
59124
59449
|
};
|
|
59125
59450
|
const handleLineClick = (line, kpis) => {
|
|
59451
|
+
if (!isSuperAdmin && !assignedLineIdSet.has(line.id)) {
|
|
59452
|
+
return;
|
|
59453
|
+
}
|
|
59126
59454
|
const trackProps = {
|
|
59127
59455
|
line_id: line.id,
|
|
59128
59456
|
line_name: line.line_name,
|
|
@@ -59635,6 +59963,7 @@ var MobileWorkspaceCard = memo$1(({
|
|
|
59635
59963
|
workspace,
|
|
59636
59964
|
rank,
|
|
59637
59965
|
cardClass,
|
|
59966
|
+
isClickable,
|
|
59638
59967
|
onWorkspaceClick,
|
|
59639
59968
|
getMedalIcon,
|
|
59640
59969
|
efficiencyLabel
|
|
@@ -59647,8 +59976,8 @@ var MobileWorkspaceCard = memo$1(({
|
|
|
59647
59976
|
transition: {
|
|
59648
59977
|
layout: { duration: 0.3, ease: "easeInOut" }
|
|
59649
59978
|
},
|
|
59650
|
-
onClick: () => onWorkspaceClick(workspace, rank),
|
|
59651
|
-
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] cursor-pointer`,
|
|
59979
|
+
onClick: isClickable ? () => onWorkspaceClick(workspace, rank) : void 0,
|
|
59980
|
+
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] ${isClickable ? "cursor-pointer" : "cursor-not-allowed opacity-75"}`,
|
|
59652
59981
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
59653
59982
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
59654
59983
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -59670,13 +59999,14 @@ var MobileWorkspaceCard = memo$1(({
|
|
|
59670
59999
|
] })
|
|
59671
60000
|
}
|
|
59672
60001
|
), (prevProps, nextProps) => {
|
|
59673
|
-
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;
|
|
60002
|
+
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;
|
|
59674
60003
|
});
|
|
59675
60004
|
MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
|
|
59676
60005
|
var DesktopWorkspaceRow = memo$1(({
|
|
59677
60006
|
workspace,
|
|
59678
60007
|
index,
|
|
59679
60008
|
rowClass,
|
|
60009
|
+
isClickable,
|
|
59680
60010
|
onWorkspaceClick,
|
|
59681
60011
|
getMedalIcon
|
|
59682
60012
|
}) => /* @__PURE__ */ jsxs(
|
|
@@ -59686,8 +60016,8 @@ var DesktopWorkspaceRow = memo$1(({
|
|
|
59686
60016
|
layoutId: `row-${workspace.workspace_uuid}`,
|
|
59687
60017
|
initial: false,
|
|
59688
60018
|
transition: { layout: { duration: 0.3, ease: "easeInOut" } },
|
|
59689
|
-
onClick: () => onWorkspaceClick(workspace, index + 1),
|
|
59690
|
-
className: `${rowClass} hover:bg-gray-50/90
|
|
60019
|
+
onClick: isClickable ? () => onWorkspaceClick(workspace, index + 1) : void 0,
|
|
60020
|
+
className: `${rowClass} transition-colors duration-150 ${isClickable ? "hover:bg-gray-50/90 cursor-pointer group" : "cursor-not-allowed opacity-75"}`,
|
|
59691
60021
|
children: [
|
|
59692
60022
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap group-hover:font-medium", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
59693
60023
|
/* @__PURE__ */ jsx("span", { children: index + 1 }),
|
|
@@ -59699,7 +60029,7 @@ var DesktopWorkspaceRow = memo$1(({
|
|
|
59699
60029
|
]
|
|
59700
60030
|
}
|
|
59701
60031
|
), (prevProps, nextProps) => {
|
|
59702
|
-
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;
|
|
60032
|
+
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;
|
|
59703
60033
|
});
|
|
59704
60034
|
DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
|
|
59705
60035
|
var LeaderboardDetailView = memo$1(({
|
|
@@ -59859,6 +60189,16 @@ var LeaderboardDetailView = memo$1(({
|
|
|
59859
60189
|
}
|
|
59860
60190
|
return allLineIds;
|
|
59861
60191
|
}, [entityConfig, userAccessibleLineIds]);
|
|
60192
|
+
const accessibleLineIdSet = useMemo(
|
|
60193
|
+
() => new Set((userAccessibleLineIds || []).filter(Boolean)),
|
|
60194
|
+
[userAccessibleLineIds]
|
|
60195
|
+
);
|
|
60196
|
+
const canOpenWorkspace = useCallback((workspaceLineId) => {
|
|
60197
|
+
if (!workspaceLineId) return false;
|
|
60198
|
+
if (!userAccessibleLineIds) return true;
|
|
60199
|
+
if (accessibleLineIdSet.size === 0) return false;
|
|
60200
|
+
return accessibleLineIdSet.has(workspaceLineId);
|
|
60201
|
+
}, [accessibleLineIdSet, userAccessibleLineIds]);
|
|
59862
60202
|
const { hasUptime: lineModeHasUptime, hasOutput: lineModeHasOutput } = useMemo(() => {
|
|
59863
60203
|
if (!lines || lines.length === 0) {
|
|
59864
60204
|
return { hasUptime: false, hasOutput: false };
|
|
@@ -60256,6 +60596,9 @@ var LeaderboardDetailView = memo$1(({
|
|
|
60256
60596
|
return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
|
|
60257
60597
|
}, [normalizedRange]);
|
|
60258
60598
|
const handleWorkspaceClick = useCallback((workspace, rank) => {
|
|
60599
|
+
if (!canOpenWorkspace(workspace.line_id)) {
|
|
60600
|
+
return;
|
|
60601
|
+
}
|
|
60259
60602
|
trackCoreEvent("Workspace from Leaderboard Clicked", {
|
|
60260
60603
|
workspace_name: workspace.workspace_name,
|
|
60261
60604
|
workspace_id: workspace.workspace_uuid,
|
|
@@ -60288,7 +60631,7 @@ var LeaderboardDetailView = memo$1(({
|
|
|
60288
60631
|
const combinedParams = navParams ? `${navParams}&${contextParamString}` : `?${contextParamString}`;
|
|
60289
60632
|
navigation.navigate(`/workspace/${workspace.workspace_uuid}${combinedParams}`);
|
|
60290
60633
|
}
|
|
60291
|
-
}, [onWorkspaceClick, navigation, date, shiftId]);
|
|
60634
|
+
}, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId]);
|
|
60292
60635
|
useEffect(() => {
|
|
60293
60636
|
workspacesLengthRef.current = activeEntries.length || 0;
|
|
60294
60637
|
}, [activeEntries.length]);
|
|
@@ -60579,6 +60922,7 @@ var LeaderboardDetailView = memo$1(({
|
|
|
60579
60922
|
workspace: ws,
|
|
60580
60923
|
rank,
|
|
60581
60924
|
cardClass,
|
|
60925
|
+
isClickable: canOpenWorkspace(ws.line_id),
|
|
60582
60926
|
onWorkspaceClick: stableHandleWorkspaceClick,
|
|
60583
60927
|
getMedalIcon: stableGetMedalIcon,
|
|
60584
60928
|
efficiencyLabel
|
|
@@ -60603,6 +60947,7 @@ var LeaderboardDetailView = memo$1(({
|
|
|
60603
60947
|
workspace: ws,
|
|
60604
60948
|
index,
|
|
60605
60949
|
rowClass,
|
|
60950
|
+
isClickable: canOpenWorkspace(ws.line_id),
|
|
60606
60951
|
onWorkspaceClick: stableHandleWorkspaceClick,
|
|
60607
60952
|
getMedalIcon: stableGetMedalIcon
|
|
60608
60953
|
},
|
|
@@ -60613,7 +60958,7 @@ var LeaderboardDetailView = memo$1(({
|
|
|
60613
60958
|
) })
|
|
60614
60959
|
] });
|
|
60615
60960
|
}, (prevProps, nextProps) => {
|
|
60616
|
-
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;
|
|
60961
|
+
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;
|
|
60617
60962
|
});
|
|
60618
60963
|
LeaderboardDetailView.displayName = "LeaderboardDetailView";
|
|
60619
60964
|
var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
|
|
@@ -66374,9 +66719,38 @@ var TeamManagementView = ({
|
|
|
66374
66719
|
optifye: 0
|
|
66375
66720
|
});
|
|
66376
66721
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
|
|
66722
|
+
const normalizeIds = useCallback((value) => {
|
|
66723
|
+
if (Array.isArray(value)) {
|
|
66724
|
+
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
66725
|
+
}
|
|
66726
|
+
if (typeof value === "string" && value.length > 0) {
|
|
66727
|
+
return [value];
|
|
66728
|
+
}
|
|
66729
|
+
return [];
|
|
66730
|
+
}, []);
|
|
66731
|
+
const plantHeadFactoryIds = useMemo(() => {
|
|
66732
|
+
if (user?.role_level !== "plant_head") return [];
|
|
66733
|
+
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
66734
|
+
if (scopedFactoryIds.length > 0) {
|
|
66735
|
+
return Array.from(new Set(scopedFactoryIds));
|
|
66736
|
+
}
|
|
66737
|
+
const propertyFactoryIds = normalizeIds(
|
|
66738
|
+
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
66739
|
+
);
|
|
66740
|
+
if (propertyFactoryIds.length > 0) {
|
|
66741
|
+
return Array.from(new Set(propertyFactoryIds));
|
|
66742
|
+
}
|
|
66743
|
+
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
66744
|
+
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
66745
|
+
const notifyScopeRefresh = useCallback(() => {
|
|
66746
|
+
if (typeof window !== "undefined") {
|
|
66747
|
+
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
66748
|
+
}
|
|
66749
|
+
}, []);
|
|
66377
66750
|
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "plant_head" || user?.role_level === "optifye";
|
|
66378
66751
|
const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
66379
|
-
const
|
|
66752
|
+
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
66753
|
+
const companyIdForUsage = pageCompanyId;
|
|
66380
66754
|
const usageDateRange = useMemo(() => {
|
|
66381
66755
|
const today = /* @__PURE__ */ new Date();
|
|
66382
66756
|
const dayOfWeek = today.getDay();
|
|
@@ -66407,8 +66781,8 @@ var TeamManagementView = ({
|
|
|
66407
66781
|
return acc;
|
|
66408
66782
|
}, {});
|
|
66409
66783
|
}, [usageData, usageDateRange.daysElapsed]);
|
|
66410
|
-
const pageTitle =
|
|
66411
|
-
const pageDescription = user?.role_level === "
|
|
66784
|
+
const pageTitle = "Team Management";
|
|
66785
|
+
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";
|
|
66412
66786
|
const hasAccess = user ? user.role_level === void 0 || ["optifye", "owner", "it", "plant_head"].includes(user.role_level) : false;
|
|
66413
66787
|
const loadData = useCallback(async () => {
|
|
66414
66788
|
if (!supabase) {
|
|
@@ -66416,20 +66790,16 @@ var TeamManagementView = ({
|
|
|
66416
66790
|
setIsLoading(false);
|
|
66417
66791
|
return;
|
|
66418
66792
|
}
|
|
66419
|
-
const
|
|
66420
|
-
if (!
|
|
66421
|
-
|
|
66422
|
-
|
|
66423
|
-
|
|
66424
|
-
setIsLoading(false);
|
|
66425
|
-
return;
|
|
66426
|
-
}
|
|
66793
|
+
const companyId = pageCompanyId;
|
|
66794
|
+
if (!companyId) {
|
|
66795
|
+
setError("Company not found. Please contact your administrator.");
|
|
66796
|
+
setIsLoading(false);
|
|
66797
|
+
return;
|
|
66427
66798
|
}
|
|
66428
|
-
const companyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
66429
66799
|
console.log("[TeamManagementView] Loading data:", {
|
|
66430
66800
|
role: user?.role_level,
|
|
66431
|
-
isOptifye:
|
|
66432
|
-
companyId
|
|
66801
|
+
isOptifye: user?.role_level === "optifye",
|
|
66802
|
+
companyId
|
|
66433
66803
|
});
|
|
66434
66804
|
setIsLoading(true);
|
|
66435
66805
|
setError(void 0);
|
|
@@ -66444,23 +66814,7 @@ var TeamManagementView = ({
|
|
|
66444
66814
|
if (!backendUrl) {
|
|
66445
66815
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
66446
66816
|
}
|
|
66447
|
-
if (
|
|
66448
|
-
const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").order("factory_name");
|
|
66449
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines`, {
|
|
66450
|
-
headers: {
|
|
66451
|
-
"Authorization": `Bearer ${token}`,
|
|
66452
|
-
"Content-Type": "application/json"
|
|
66453
|
-
}
|
|
66454
|
-
});
|
|
66455
|
-
if (!linesResponse.ok) {
|
|
66456
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
66457
|
-
}
|
|
66458
|
-
const linesData = await linesResponse.json();
|
|
66459
|
-
const lines = linesData.lines || [];
|
|
66460
|
-
setAvailableFactories(factories || []);
|
|
66461
|
-
setAvailableLines(lines);
|
|
66462
|
-
console.log("[TeamManagementView] Optifye - Loaded factories:", factories?.length, "lines:", lines?.length, "Sample lines:", lines?.slice(0, 3));
|
|
66463
|
-
} else if ((user?.role_level === "owner" || user?.role_level === "it") && companyId) {
|
|
66817
|
+
if ((user?.role_level === "optifye" || user?.role_level === "owner" || user?.role_level === "it") && companyId) {
|
|
66464
66818
|
const { data: factories } = await supabase.from("factories").select("id, factory_name, company_id").eq("company_id", companyId).order("factory_name");
|
|
66465
66819
|
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
66466
66820
|
headers: {
|
|
@@ -66475,9 +66829,8 @@ var TeamManagementView = ({
|
|
|
66475
66829
|
const lines = linesData.lines || [];
|
|
66476
66830
|
setAvailableFactories(factories || []);
|
|
66477
66831
|
setAvailableLines(lines);
|
|
66478
|
-
console.log("[TeamManagementView]
|
|
66832
|
+
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
66479
66833
|
} else if (user?.role_level === "plant_head") {
|
|
66480
|
-
const plantHeadFactoryIds = user?.properties?.factory_ids || [];
|
|
66481
66834
|
if (plantHeadFactoryIds.length > 0) {
|
|
66482
66835
|
if (companyId) {
|
|
66483
66836
|
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
@@ -66530,35 +66883,46 @@ var TeamManagementView = ({
|
|
|
66530
66883
|
setAvailableFactories([]);
|
|
66531
66884
|
console.log("[TeamManagementView] Fallback - Company:", companyId, "Loaded lines:", lines?.length);
|
|
66532
66885
|
}
|
|
66533
|
-
|
|
66534
|
-
|
|
66535
|
-
|
|
66536
|
-
|
|
66537
|
-
|
|
66538
|
-
|
|
66539
|
-
|
|
66540
|
-
|
|
66541
|
-
|
|
66542
|
-
|
|
66543
|
-
|
|
66544
|
-
|
|
66545
|
-
|
|
66546
|
-
|
|
66547
|
-
|
|
66548
|
-
|
|
66549
|
-
|
|
66550
|
-
|
|
66551
|
-
|
|
66552
|
-
|
|
66553
|
-
|
|
66554
|
-
|
|
66555
|
-
|
|
66556
|
-
|
|
66557
|
-
|
|
66558
|
-
|
|
66559
|
-
|
|
66560
|
-
|
|
66561
|
-
|
|
66886
|
+
const usersPromise = user?.role_level === "plant_head" ? (async () => {
|
|
66887
|
+
if (plantHeadFactoryIds.length === 0) {
|
|
66888
|
+
return [];
|
|
66889
|
+
}
|
|
66890
|
+
const results = await Promise.allSettled(
|
|
66891
|
+
plantHeadFactoryIds.map((factoryId) => userManagementService.getFactoryUsers(factoryId))
|
|
66892
|
+
);
|
|
66893
|
+
const successful = results.filter(
|
|
66894
|
+
(result) => result.status === "fulfilled"
|
|
66895
|
+
).flatMap((result) => result.value || []);
|
|
66896
|
+
if (successful.length > 0) {
|
|
66897
|
+
const byUserId = /* @__PURE__ */ new Map();
|
|
66898
|
+
successful.forEach((u) => {
|
|
66899
|
+
if (u?.user_id) {
|
|
66900
|
+
byUserId.set(u.user_id, u);
|
|
66901
|
+
}
|
|
66902
|
+
});
|
|
66903
|
+
return Array.from(byUserId.values());
|
|
66904
|
+
}
|
|
66905
|
+
const firstRejected = results.find(
|
|
66906
|
+
(result) => result.status === "rejected"
|
|
66907
|
+
);
|
|
66908
|
+
if (firstRejected) {
|
|
66909
|
+
throw firstRejected.reason;
|
|
66910
|
+
}
|
|
66911
|
+
return [];
|
|
66912
|
+
})() : userManagementService.getCompanyUsers(companyId);
|
|
66913
|
+
const [usersData, userStats] = await Promise.all([
|
|
66914
|
+
usersPromise,
|
|
66915
|
+
userManagementService.getUserStats(companyId)
|
|
66916
|
+
]);
|
|
66917
|
+
setUsers(usersData);
|
|
66918
|
+
setStats({
|
|
66919
|
+
totalUsers: userStats.total,
|
|
66920
|
+
owners: userStats.owners,
|
|
66921
|
+
it: userStats.it,
|
|
66922
|
+
plantHeads: userStats.plant_heads,
|
|
66923
|
+
supervisors: userStats.supervisors,
|
|
66924
|
+
optifye: 0
|
|
66925
|
+
});
|
|
66562
66926
|
} catch (err) {
|
|
66563
66927
|
console.error("Error loading team management data:", err);
|
|
66564
66928
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -66566,16 +66930,16 @@ var TeamManagementView = ({
|
|
|
66566
66930
|
} finally {
|
|
66567
66931
|
setIsLoading(false);
|
|
66568
66932
|
}
|
|
66569
|
-
}, [supabase, user, entityConfig]);
|
|
66933
|
+
}, [supabase, user, pageCompanyId, entityConfig?.factoryId, plantHeadFactoryIds]);
|
|
66570
66934
|
useEffect(() => {
|
|
66571
|
-
const companyId =
|
|
66572
|
-
const canLoad = hasAccess && user &&
|
|
66935
|
+
const companyId = pageCompanyId;
|
|
66936
|
+
const canLoad = hasAccess && user && !!companyId;
|
|
66573
66937
|
if (canLoad) {
|
|
66574
66938
|
loadData();
|
|
66575
66939
|
} else if (!user) {
|
|
66576
66940
|
setIsLoading(true);
|
|
66577
66941
|
}
|
|
66578
|
-
}, [hasAccess, loadData, user,
|
|
66942
|
+
}, [hasAccess, loadData, user, pageCompanyId]);
|
|
66579
66943
|
const handleUserAdded = useCallback(() => {
|
|
66580
66944
|
loadData();
|
|
66581
66945
|
}, [loadData]);
|
|
@@ -66589,12 +66953,13 @@ var TeamManagementView = ({
|
|
|
66589
66953
|
updated_by: user.id
|
|
66590
66954
|
});
|
|
66591
66955
|
toast.success("User role updated successfully");
|
|
66956
|
+
notifyScopeRefresh();
|
|
66592
66957
|
loadData();
|
|
66593
66958
|
} catch (err) {
|
|
66594
66959
|
console.error("Error updating user role:", err);
|
|
66595
66960
|
toast.error("Failed to update user role");
|
|
66596
66961
|
}
|
|
66597
|
-
}, [supabase, user, loadData]);
|
|
66962
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66598
66963
|
const handleRemoveUser = useCallback(async (userId) => {
|
|
66599
66964
|
if (!supabase || !user) return;
|
|
66600
66965
|
try {
|
|
@@ -66617,12 +66982,13 @@ var TeamManagementView = ({
|
|
|
66617
66982
|
assigned_by: user.id
|
|
66618
66983
|
});
|
|
66619
66984
|
toast.success("Line assignments updated successfully");
|
|
66985
|
+
notifyScopeRefresh();
|
|
66620
66986
|
loadData();
|
|
66621
66987
|
} catch (err) {
|
|
66622
66988
|
console.error("Error updating line assignments:", err);
|
|
66623
66989
|
toast.error("Failed to update line assignments");
|
|
66624
66990
|
}
|
|
66625
|
-
}, [supabase, user, loadData]);
|
|
66991
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66626
66992
|
const handleFactoryAssignmentUpdate = useCallback(async (userId, factoryIds) => {
|
|
66627
66993
|
if (!supabase || !user) return;
|
|
66628
66994
|
try {
|
|
@@ -66633,12 +66999,13 @@ var TeamManagementView = ({
|
|
|
66633
66999
|
assigned_by: user.id
|
|
66634
67000
|
});
|
|
66635
67001
|
toast.success("Factory assignments updated successfully");
|
|
67002
|
+
notifyScopeRefresh();
|
|
66636
67003
|
loadData();
|
|
66637
67004
|
} catch (err) {
|
|
66638
67005
|
console.error("Error updating factory assignments:", err);
|
|
66639
67006
|
toast.error("Failed to update factory assignments");
|
|
66640
67007
|
}
|
|
66641
|
-
}, [supabase, user, loadData]);
|
|
67008
|
+
}, [supabase, user, loadData, notifyScopeRefresh]);
|
|
66642
67009
|
const handleProfileUpdate = useCallback(async (userId, firstName, lastName, profilePhotoUrl) => {
|
|
66643
67010
|
if (!supabase || !user) return;
|
|
66644
67011
|
try {
|