@optifye/dashboard-core 6.9.2 → 6.9.4
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 +0 -4
- package/dist/index.d.mts +39 -15
- package/dist/index.d.ts +39 -15
- package/dist/index.js +358 -601
- package/dist/index.mjs +358 -601
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2058,6 +2058,59 @@ var workspaceService = {
|
|
|
2058
2058
|
console.error("Error updating shift configuration:", error);
|
|
2059
2059
|
throw error;
|
|
2060
2060
|
}
|
|
2061
|
+
},
|
|
2062
|
+
/**
|
|
2063
|
+
* Fetch bulk targets data for multiple lines and shifts in a single API call
|
|
2064
|
+
* This replaces 120+ individual API calls with 1 optimized call
|
|
2065
|
+
*
|
|
2066
|
+
* @param params - Parameters for the bulk fetch
|
|
2067
|
+
* @returns Promise with complete targets data for all lines and shifts
|
|
2068
|
+
*/
|
|
2069
|
+
async fetchBulkTargets(params) {
|
|
2070
|
+
try {
|
|
2071
|
+
const token = await getAuthToken();
|
|
2072
|
+
const apiUrl = getBackendUrl();
|
|
2073
|
+
const {
|
|
2074
|
+
companyId,
|
|
2075
|
+
lineIds,
|
|
2076
|
+
date,
|
|
2077
|
+
shifts = [0, 1],
|
|
2078
|
+
includeSkus = true,
|
|
2079
|
+
includeActions = true
|
|
2080
|
+
} = params;
|
|
2081
|
+
const queryParams = new URLSearchParams({
|
|
2082
|
+
company_id: companyId,
|
|
2083
|
+
line_ids: lineIds.join(","),
|
|
2084
|
+
date,
|
|
2085
|
+
shifts: shifts.join(","),
|
|
2086
|
+
include_skus: includeSkus.toString(),
|
|
2087
|
+
include_actions: includeActions.toString()
|
|
2088
|
+
});
|
|
2089
|
+
const response = await fetch(
|
|
2090
|
+
`${apiUrl}/api/targets/bulk-load?${queryParams}`,
|
|
2091
|
+
{
|
|
2092
|
+
method: "GET",
|
|
2093
|
+
headers: {
|
|
2094
|
+
"Authorization": `Bearer ${token}`,
|
|
2095
|
+
"Content-Type": "application/json"
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
);
|
|
2099
|
+
if (!response.ok) {
|
|
2100
|
+
const errorText = await response.text();
|
|
2101
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
2102
|
+
}
|
|
2103
|
+
const data = await response.json();
|
|
2104
|
+
console.log("[fetchBulkTargets] Success:", {
|
|
2105
|
+
lineCount: data.metadata?.line_count,
|
|
2106
|
+
totalWorkspaces: data.metadata?.total_workspaces,
|
|
2107
|
+
timestamp: data.metadata?.timestamp
|
|
2108
|
+
});
|
|
2109
|
+
return data;
|
|
2110
|
+
} catch (error) {
|
|
2111
|
+
console.error("Error fetching bulk targets:", error);
|
|
2112
|
+
throw error;
|
|
2113
|
+
}
|
|
2061
2114
|
}
|
|
2062
2115
|
};
|
|
2063
2116
|
var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
@@ -5792,60 +5845,32 @@ var UserManagementService = class {
|
|
|
5792
5845
|
}
|
|
5793
5846
|
}
|
|
5794
5847
|
/**
|
|
5795
|
-
*
|
|
5796
|
-
* @param
|
|
5848
|
+
* Permanently delete a user
|
|
5849
|
+
* @param userId - The ID of the user to delete
|
|
5850
|
+
* @returns Promise with deletion response
|
|
5797
5851
|
*/
|
|
5798
|
-
async
|
|
5852
|
+
async deleteUser(userId) {
|
|
5799
5853
|
try {
|
|
5800
5854
|
const token = await this.getAuthToken();
|
|
5801
5855
|
const backendUrl = this.getBackendUrl();
|
|
5802
5856
|
const response = await fetch(
|
|
5803
|
-
`${backendUrl}/api/users
|
|
5804
|
-
{
|
|
5805
|
-
method: "POST",
|
|
5806
|
-
headers: {
|
|
5807
|
-
"Authorization": `Bearer ${token}`,
|
|
5808
|
-
"Content-Type": "application/json"
|
|
5809
|
-
},
|
|
5810
|
-
body: JSON.stringify(input)
|
|
5811
|
-
}
|
|
5812
|
-
);
|
|
5813
|
-
if (!response.ok) {
|
|
5814
|
-
const errorData = await response.json();
|
|
5815
|
-
throw new Error(`Failed to deactivate user: ${errorData.detail || response.statusText}`);
|
|
5816
|
-
}
|
|
5817
|
-
console.log("[UserManagementService] User deactivated successfully");
|
|
5818
|
-
} catch (error) {
|
|
5819
|
-
console.error("[UserManagementService] Error deactivating user:", error);
|
|
5820
|
-
throw error;
|
|
5821
|
-
}
|
|
5822
|
-
}
|
|
5823
|
-
/**
|
|
5824
|
-
* Reactivate a user
|
|
5825
|
-
* @param input - Reactivation input
|
|
5826
|
-
*/
|
|
5827
|
-
async reactivateUser(input) {
|
|
5828
|
-
try {
|
|
5829
|
-
const token = await this.getAuthToken();
|
|
5830
|
-
const backendUrl = this.getBackendUrl();
|
|
5831
|
-
const response = await fetch(
|
|
5832
|
-
`${backendUrl}/api/users/reactivate`,
|
|
5857
|
+
`${backendUrl}/api/users/${userId}`,
|
|
5833
5858
|
{
|
|
5834
|
-
method: "
|
|
5859
|
+
method: "DELETE",
|
|
5835
5860
|
headers: {
|
|
5836
|
-
"Authorization": `Bearer ${token}
|
|
5837
|
-
|
|
5838
|
-
},
|
|
5839
|
-
body: JSON.stringify(input)
|
|
5861
|
+
"Authorization": `Bearer ${token}`
|
|
5862
|
+
}
|
|
5840
5863
|
}
|
|
5841
5864
|
);
|
|
5842
5865
|
if (!response.ok) {
|
|
5843
5866
|
const errorData = await response.json();
|
|
5844
|
-
throw new Error(`Failed to
|
|
5867
|
+
throw new Error(`Failed to delete user: ${errorData.detail || response.statusText}`);
|
|
5845
5868
|
}
|
|
5846
|
-
|
|
5869
|
+
const result = await response.json();
|
|
5870
|
+
console.log("[UserManagementService] User permanently deleted:", result);
|
|
5871
|
+
return result;
|
|
5847
5872
|
} catch (error) {
|
|
5848
|
-
console.error("[UserManagementService] Error
|
|
5873
|
+
console.error("[UserManagementService] Error deleting user:", error);
|
|
5849
5874
|
throw error;
|
|
5850
5875
|
}
|
|
5851
5876
|
}
|
|
@@ -7658,7 +7683,7 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
|
|
|
7658
7683
|
refetch: fetchLeaderboard
|
|
7659
7684
|
};
|
|
7660
7685
|
};
|
|
7661
|
-
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
7686
|
+
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
|
|
7662
7687
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
7663
7688
|
const entityConfig = useEntityConfig();
|
|
7664
7689
|
const databaseConfig = useDatabaseConfig();
|
|
@@ -7703,7 +7728,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7703
7728
|
try {
|
|
7704
7729
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7705
7730
|
const operationalDate = getOperationalDate(defaultTimezone);
|
|
7706
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7731
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7707
7732
|
if (targetLineIds.length === 0 && currentLineIdToUse === (entityConfig.factoryViewId || "factory")) {
|
|
7708
7733
|
throw new Error("Factory view selected, but no lines are configured in entityConfig.");
|
|
7709
7734
|
}
|
|
@@ -7829,7 +7854,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7829
7854
|
}
|
|
7830
7855
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7831
7856
|
const operationalDateForSubscription = getOperationalDate(defaultTimezone);
|
|
7832
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7857
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7833
7858
|
if (targetLineIds.length === 0) return;
|
|
7834
7859
|
const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
7835
7860
|
const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
@@ -7867,7 +7892,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7867
7892
|
entityConfig?.companyId,
|
|
7868
7893
|
entityConfig?.factoryViewId,
|
|
7869
7894
|
defaultTimezone,
|
|
7870
|
-
lineId
|
|
7895
|
+
lineId,
|
|
7896
|
+
userAccessibleLineIds
|
|
7871
7897
|
]);
|
|
7872
7898
|
return {
|
|
7873
7899
|
workspaceMetrics: metrics2?.workspaceMetrics || [],
|
|
@@ -9857,7 +9883,8 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9857
9883
|
}
|
|
9858
9884
|
setError(null);
|
|
9859
9885
|
try {
|
|
9860
|
-
const
|
|
9886
|
+
const allConfiguredLineIds = getConfiguredLineIds(entityConfig);
|
|
9887
|
+
const configuredLineIds = options?.allowedLineIds ? allConfiguredLineIds.filter((id3) => options.allowedLineIds.includes(id3)) : allConfiguredLineIds;
|
|
9861
9888
|
let enabledWorkspaceIds = [];
|
|
9862
9889
|
for (const lineId of configuredLineIds) {
|
|
9863
9890
|
const workspaces2 = await workspaceService.getWorkspaces(lineId);
|
|
@@ -9920,7 +9947,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9920
9947
|
setLoading(false);
|
|
9921
9948
|
isFetchingRef.current = false;
|
|
9922
9949
|
}
|
|
9923
|
-
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
|
|
9950
|
+
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId, entityConfig, options?.allowedLineIds]);
|
|
9924
9951
|
useEffect(() => {
|
|
9925
9952
|
if (!initialized) {
|
|
9926
9953
|
fetchWorkspaceMetrics();
|
|
@@ -41419,7 +41446,6 @@ function HomeView({
|
|
|
41419
41446
|
factoryName = "Simba Beer - Line 1"
|
|
41420
41447
|
}) {
|
|
41421
41448
|
const [isHydrated, setIsHydrated] = useState(false);
|
|
41422
|
-
const availableLineIds = useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
|
|
41423
41449
|
const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
|
|
41424
41450
|
const [isChangingFilter, setIsChangingFilter] = useState(false);
|
|
41425
41451
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
@@ -41429,6 +41455,21 @@ function HomeView({
|
|
|
41429
41455
|
const entityConfig = useEntityConfig();
|
|
41430
41456
|
const supabaseClient = useSupabaseClient();
|
|
41431
41457
|
const { user } = useAuth();
|
|
41458
|
+
const isSupervisor = user?.role_level === "supervisor";
|
|
41459
|
+
const hasMultipleLines = allLineIds.length > 1;
|
|
41460
|
+
const availableLineIds = useMemo(() => {
|
|
41461
|
+
if (isSupervisor && !hasMultipleLines) {
|
|
41462
|
+
return allLineIds;
|
|
41463
|
+
}
|
|
41464
|
+
return [factoryViewId, ...allLineIds];
|
|
41465
|
+
}, [factoryViewId, allLineIds, isSupervisor, hasMultipleLines]);
|
|
41466
|
+
useEffect(() => {
|
|
41467
|
+
if (user) {
|
|
41468
|
+
if (isSupervisor && allLineIds.length > 0) {
|
|
41469
|
+
setSelectedLineId(allLineIds[0]);
|
|
41470
|
+
}
|
|
41471
|
+
}
|
|
41472
|
+
}, [user, isSupervisor, allLineIds]);
|
|
41432
41473
|
const userCompanyId = useMemo(() => {
|
|
41433
41474
|
return user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
41434
41475
|
}, [user, entityConfig.companyId]);
|
|
@@ -41489,7 +41530,9 @@ function HomeView({
|
|
|
41489
41530
|
refetch: refetchMetrics
|
|
41490
41531
|
} = useDashboardMetrics({
|
|
41491
41532
|
lineId: selectedLineId,
|
|
41492
|
-
onLineMetricsUpdate
|
|
41533
|
+
onLineMetricsUpdate,
|
|
41534
|
+
userAccessibleLineIds: allLineIds
|
|
41535
|
+
// Pass user's accessible lines for supervisor filtering
|
|
41493
41536
|
});
|
|
41494
41537
|
const {
|
|
41495
41538
|
activeBreaks: allActiveBreaks,
|
|
@@ -43548,7 +43591,8 @@ var LeaderboardDetailView = memo(({
|
|
|
43548
43591
|
line1Id = "",
|
|
43549
43592
|
line2Id = "",
|
|
43550
43593
|
lineNames = {},
|
|
43551
|
-
className = ""
|
|
43594
|
+
className = "",
|
|
43595
|
+
userAccessibleLineIds
|
|
43552
43596
|
}) => {
|
|
43553
43597
|
const navigation = useNavigation();
|
|
43554
43598
|
const entityConfig = useEntityConfig();
|
|
@@ -43588,7 +43632,9 @@ var LeaderboardDetailView = memo(({
|
|
|
43588
43632
|
refreshWorkspaces
|
|
43589
43633
|
} = useAllWorkspaceMetrics({
|
|
43590
43634
|
initialDate: date,
|
|
43591
|
-
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
|
|
43635
|
+
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
|
|
43636
|
+
allowedLineIds: userAccessibleLineIds
|
|
43637
|
+
// Filter to user's accessible lines only
|
|
43592
43638
|
});
|
|
43593
43639
|
const getShiftName = useCallback((shiftId2) => {
|
|
43594
43640
|
if (shiftId2 === void 0) return "Day";
|
|
@@ -44944,12 +44990,17 @@ var ACTION_NAMES = {
|
|
|
44944
44990
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
44945
44991
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
44946
44992
|
const pph = 3600 / cycleTime;
|
|
44947
|
-
return
|
|
44993
|
+
return Math.round(pph);
|
|
44948
44994
|
};
|
|
44949
44995
|
var calculateDayOutput = (pph, shiftHours, breaks = []) => {
|
|
44950
44996
|
if (pph === "") return "";
|
|
44951
44997
|
return Math.round(pph * shiftHours);
|
|
44952
44998
|
};
|
|
44999
|
+
var calculateDayOutputFromCycleTime = (cycleTime, shiftHours) => {
|
|
45000
|
+
if (cycleTime === "" || cycleTime === 0 || shiftHours === 0) return "";
|
|
45001
|
+
const dayOutput = 3600 / cycleTime * shiftHours;
|
|
45002
|
+
return Math.round(dayOutput);
|
|
45003
|
+
};
|
|
44953
45004
|
var formatWorkspaceName = (name, lineId) => {
|
|
44954
45005
|
return getWorkspaceDisplayName(name, lineId);
|
|
44955
45006
|
};
|
|
@@ -44961,20 +45012,6 @@ var getStoredLineState2 = (lineId) => {
|
|
|
44961
45012
|
return false;
|
|
44962
45013
|
}
|
|
44963
45014
|
};
|
|
44964
|
-
var calculateShiftHours2 = (startTime, endTime, breaks = []) => {
|
|
44965
|
-
if (!startTime || !endTime) return 8;
|
|
44966
|
-
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
44967
|
-
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
44968
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
44969
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
44970
|
-
if (endMinutes < startMinutes) {
|
|
44971
|
-
endMinutes += 24 * 60;
|
|
44972
|
-
}
|
|
44973
|
-
const safeBreaks = Array.isArray(breaks) ? breaks : [];
|
|
44974
|
-
const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
|
|
44975
|
-
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
44976
|
-
return Number(hoursDiff.toFixed(1));
|
|
44977
|
-
};
|
|
44978
45015
|
var BulkConfigureModal = ({
|
|
44979
45016
|
isOpen,
|
|
44980
45017
|
onClose,
|
|
@@ -46182,121 +46219,6 @@ var TargetsView = ({
|
|
|
46182
46219
|
const dashboardConfig = useDashboardConfig();
|
|
46183
46220
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
46184
46221
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
46185
|
-
const loadOperatingHours = useCallback(async (lineId, shiftId) => {
|
|
46186
|
-
try {
|
|
46187
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
46188
|
-
if (!session?.access_token) {
|
|
46189
|
-
console.error("No authentication token available");
|
|
46190
|
-
return {
|
|
46191
|
-
startTime: "08:00",
|
|
46192
|
-
// Default values
|
|
46193
|
-
endTime: "19:00",
|
|
46194
|
-
breaks: []
|
|
46195
|
-
};
|
|
46196
|
-
}
|
|
46197
|
-
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
46198
|
-
if (!backendUrl) {
|
|
46199
|
-
console.error("Backend URL is not configured");
|
|
46200
|
-
return {
|
|
46201
|
-
startTime: "08:00",
|
|
46202
|
-
// Default values
|
|
46203
|
-
endTime: "19:00",
|
|
46204
|
-
breaks: []
|
|
46205
|
-
};
|
|
46206
|
-
}
|
|
46207
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}/operating-hours?shift_id=${shiftId}`, {
|
|
46208
|
-
headers: {
|
|
46209
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46210
|
-
"Content-Type": "application/json"
|
|
46211
|
-
}
|
|
46212
|
-
});
|
|
46213
|
-
if (!response.ok) {
|
|
46214
|
-
console.log(`No operating hours found for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46215
|
-
return {
|
|
46216
|
-
startTime: "08:00",
|
|
46217
|
-
// Default values
|
|
46218
|
-
endTime: "19:00",
|
|
46219
|
-
breaks: []
|
|
46220
|
-
};
|
|
46221
|
-
}
|
|
46222
|
-
const data = await response.json();
|
|
46223
|
-
let breaks = [];
|
|
46224
|
-
if (data?.breaks) {
|
|
46225
|
-
if (Array.isArray(data.breaks)) {
|
|
46226
|
-
breaks = data.breaks.map((breakItem) => {
|
|
46227
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46228
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46229
|
-
const calculateDuration = (start, end) => {
|
|
46230
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46231
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46232
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46233
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46234
|
-
if (endMinutes < startMinutes) {
|
|
46235
|
-
endMinutes += 24 * 60;
|
|
46236
|
-
}
|
|
46237
|
-
return endMinutes - startMinutes;
|
|
46238
|
-
};
|
|
46239
|
-
return {
|
|
46240
|
-
startTime,
|
|
46241
|
-
endTime,
|
|
46242
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46243
|
-
};
|
|
46244
|
-
});
|
|
46245
|
-
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
46246
|
-
breaks = data.breaks.breaks.map((breakItem) => {
|
|
46247
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46248
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46249
|
-
const calculateDuration = (start, end) => {
|
|
46250
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46251
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46252
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46253
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46254
|
-
if (endMinutes < startMinutes) {
|
|
46255
|
-
endMinutes += 24 * 60;
|
|
46256
|
-
}
|
|
46257
|
-
return endMinutes - startMinutes;
|
|
46258
|
-
};
|
|
46259
|
-
return {
|
|
46260
|
-
startTime,
|
|
46261
|
-
endTime,
|
|
46262
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46263
|
-
};
|
|
46264
|
-
});
|
|
46265
|
-
} else if (typeof data.breaks === "string") {
|
|
46266
|
-
try {
|
|
46267
|
-
const parsedBreaks = JSON.parse(data.breaks);
|
|
46268
|
-
if (Array.isArray(parsedBreaks)) {
|
|
46269
|
-
breaks = parsedBreaks.map((breakItem) => ({
|
|
46270
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46271
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46272
|
-
duration: breakItem.duration || 0
|
|
46273
|
-
}));
|
|
46274
|
-
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
46275
|
-
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
46276
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46277
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46278
|
-
duration: breakItem.duration || 0
|
|
46279
|
-
}));
|
|
46280
|
-
}
|
|
46281
|
-
} catch (e) {
|
|
46282
|
-
console.error("Error parsing breaks data:", e);
|
|
46283
|
-
}
|
|
46284
|
-
}
|
|
46285
|
-
}
|
|
46286
|
-
return {
|
|
46287
|
-
startTime: data?.start_time || "08:00",
|
|
46288
|
-
endTime: data?.end_time || "19:00",
|
|
46289
|
-
breaks
|
|
46290
|
-
};
|
|
46291
|
-
} catch (e) {
|
|
46292
|
-
console.error("Exception when loading operating hours:", e);
|
|
46293
|
-
return {
|
|
46294
|
-
startTime: "08:00",
|
|
46295
|
-
endTime: "19:00",
|
|
46296
|
-
breaks: []
|
|
46297
|
-
};
|
|
46298
|
-
}
|
|
46299
|
-
}, [supabase]);
|
|
46300
46222
|
useEffect(() => {
|
|
46301
46223
|
console.log("[TargetsView] Component mounted/re-rendered", {
|
|
46302
46224
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -46308,221 +46230,108 @@ var TargetsView = ({
|
|
|
46308
46230
|
};
|
|
46309
46231
|
}, []);
|
|
46310
46232
|
useEffect(() => {
|
|
46311
|
-
let timeoutId;
|
|
46312
|
-
let retryCount = 0;
|
|
46313
|
-
const MAX_RETRIES2 = 2;
|
|
46314
|
-
const LOADING_TIMEOUT = 15e3;
|
|
46315
46233
|
const fetchInitialData = async () => {
|
|
46316
|
-
if (lineIds.length === 0) return;
|
|
46317
|
-
console.log("[TargetsView] Starting fetchInitialData");
|
|
46234
|
+
if (lineIds.length === 0 || !companyId) return;
|
|
46235
|
+
console.log("[TargetsView] Starting optimized fetchInitialData with bulk endpoint");
|
|
46318
46236
|
setIsLoading(true);
|
|
46319
|
-
timeoutId = setTimeout(() => {
|
|
46320
|
-
console.error("Loading timeout reached");
|
|
46321
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46322
|
-
retryCount++;
|
|
46323
|
-
console.log(`Retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46324
|
-
toast.warning("Loading is taking longer than expected. Retrying...");
|
|
46325
|
-
fetchInitialData();
|
|
46326
|
-
} else {
|
|
46327
|
-
setIsLoading(false);
|
|
46328
|
-
toast.error("Failed to load data. Please refresh the page.");
|
|
46329
|
-
}
|
|
46330
|
-
}, LOADING_TIMEOUT);
|
|
46331
46237
|
try {
|
|
46332
|
-
const
|
|
46333
|
-
|
|
46334
|
-
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
|
|
46238
|
+
const currentDate = getOperationalDate(timezone);
|
|
46239
|
+
const bulkResponse = await workspaceService.fetchBulkTargets({
|
|
46240
|
+
companyId,
|
|
46241
|
+
lineIds,
|
|
46242
|
+
date: currentDate,
|
|
46243
|
+
shifts: [0, 1],
|
|
46244
|
+
// Fetch both shifts at once
|
|
46245
|
+
includeSkus: skuEnabled,
|
|
46246
|
+
includeActions: true
|
|
46247
|
+
});
|
|
46248
|
+
if (!bulkResponse.success) {
|
|
46249
|
+
throw new Error("Failed to fetch bulk targets data");
|
|
46339
46250
|
}
|
|
46340
|
-
const
|
|
46341
|
-
|
|
46342
|
-
|
|
46343
|
-
try {
|
|
46344
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}`, {
|
|
46345
|
-
headers: {
|
|
46346
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46347
|
-
"Content-Type": "application/json"
|
|
46348
|
-
}
|
|
46349
|
-
});
|
|
46350
|
-
if (!response.ok) {
|
|
46351
|
-
console.error(`Error fetching line data for ${lineId}:`, response.statusText);
|
|
46352
|
-
return { lineId, factoryId: void 0 };
|
|
46353
|
-
}
|
|
46354
|
-
const lineData = await response.json();
|
|
46355
|
-
return { lineId, factoryId: lineData.factory_id };
|
|
46356
|
-
} catch (err) {
|
|
46357
|
-
console.error(`Exception fetching line data for ${lineId}:`, err);
|
|
46358
|
-
return { lineId, factoryId: void 0 };
|
|
46359
|
-
}
|
|
46360
|
-
})),
|
|
46361
|
-
// Fetch action IDs
|
|
46362
|
-
actionService.getActionsByName(
|
|
46363
|
-
[ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
|
|
46364
|
-
companyId
|
|
46365
|
-
)
|
|
46366
|
-
]);
|
|
46367
|
-
const factoryResults = linesResponse;
|
|
46368
|
-
const assemblyAction = actions.find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46369
|
-
const packagingAction = actions.find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46251
|
+
const { data } = bulkResponse;
|
|
46252
|
+
const assemblyAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46253
|
+
const packagingAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46370
46254
|
if (!assemblyAction || !packagingAction) {
|
|
46371
|
-
throw new Error("Could not find required actions");
|
|
46255
|
+
throw new Error("Could not find required actions in bulk response");
|
|
46372
46256
|
}
|
|
46373
46257
|
const actionIdsData = {
|
|
46374
46258
|
assembly: assemblyAction.id,
|
|
46375
46259
|
packaging: packagingAction.id
|
|
46376
46260
|
};
|
|
46377
46261
|
setActionIds(actionIdsData);
|
|
46378
|
-
const
|
|
46379
|
-
|
|
46380
|
-
|
|
46381
|
-
|
|
46382
|
-
|
|
46383
|
-
|
|
46384
|
-
|
|
46385
|
-
|
|
46386
|
-
if (!updatedLineWorkspaces[lineId]?.factoryId) {
|
|
46387
|
-
console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
|
|
46388
|
-
continue;
|
|
46389
|
-
}
|
|
46390
|
-
try {
|
|
46391
|
-
const workspacesData = await workspaceService.getWorkspaces(lineId);
|
|
46392
|
-
const enabledWorkspaces = workspacesData.filter((ws) => ws.enable === true);
|
|
46393
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46394
|
-
lineId,
|
|
46395
|
-
currentDate,
|
|
46396
|
-
0
|
|
46397
|
-
// Always use day shift for initial load
|
|
46398
|
-
);
|
|
46399
|
-
const operatingHoursData = await loadOperatingHours(lineId, 0);
|
|
46400
|
-
if (operatingHoursData) {
|
|
46401
|
-
updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
|
|
46402
|
-
updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
|
|
46403
|
-
updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
|
|
46404
|
-
updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
|
|
46405
|
-
operatingHoursData.startTime,
|
|
46406
|
-
operatingHoursData.endTime,
|
|
46407
|
-
operatingHoursData.breaks
|
|
46408
|
-
);
|
|
46262
|
+
const newAllShiftsData = { 0: {}, 1: {} };
|
|
46263
|
+
const newDbValues = { 0: {}, 1: {} };
|
|
46264
|
+
Object.entries(data.lines).forEach(([lineId, lineData]) => {
|
|
46265
|
+
[0, 1].forEach((shiftId) => {
|
|
46266
|
+
const shiftData = lineData.shifts[shiftId.toString()];
|
|
46267
|
+
if (!shiftData) {
|
|
46268
|
+
console.warn(`No shift ${shiftId} data for line ${lineId}`);
|
|
46269
|
+
return;
|
|
46409
46270
|
}
|
|
46271
|
+
const operatingHours = shiftData.operating_hours || {
|
|
46272
|
+
start_time: "08:00",
|
|
46273
|
+
end_time: "19:00",
|
|
46274
|
+
breaks: [],
|
|
46275
|
+
total_hours: 11
|
|
46276
|
+
};
|
|
46277
|
+
newAllShiftsData[shiftId][lineId] = {
|
|
46278
|
+
productId: "",
|
|
46279
|
+
shiftStartTime: operatingHours.start_time,
|
|
46280
|
+
shiftEndTime: operatingHours.end_time,
|
|
46281
|
+
breaks: operatingHours.breaks,
|
|
46282
|
+
shiftHours: operatingHours.total_hours,
|
|
46283
|
+
workspaces: [],
|
|
46284
|
+
factoryId: lineData.line_info.factory_id
|
|
46285
|
+
};
|
|
46286
|
+
newDbValues[shiftId][lineId] = {};
|
|
46287
|
+
const enabledWorkspaces = lineData.workspaces.filter((ws) => ws.enable === true);
|
|
46410
46288
|
const mappedWorkspaces = enabledWorkspaces.map((ws) => {
|
|
46411
|
-
const threshold =
|
|
46412
|
-
if (!dbValues[0][lineId]) {
|
|
46413
|
-
dbValues[0][lineId] = {};
|
|
46414
|
-
}
|
|
46289
|
+
const threshold = shiftData.thresholds.find((t) => t.workspace_id === ws.id);
|
|
46415
46290
|
if (threshold) {
|
|
46416
|
-
|
|
46417
|
-
targetPPH: threshold.pph_threshold,
|
|
46418
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46419
|
-
targetDayOutput: threshold.total_day_output
|
|
46291
|
+
newDbValues[shiftId][lineId][ws.id] = {
|
|
46292
|
+
targetPPH: threshold.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46293
|
+
targetCycleTime: threshold.ideal_cycle_time ?? "",
|
|
46294
|
+
targetDayOutput: threshold.total_day_output ?? ""
|
|
46420
46295
|
};
|
|
46421
46296
|
}
|
|
46297
|
+
let actionType = "assembly";
|
|
46298
|
+
let actionId = actionIdsData.assembly;
|
|
46299
|
+
if (ws.action_id === packagingAction.id || ws.action_type === "packaging") {
|
|
46300
|
+
actionType = "packaging";
|
|
46301
|
+
actionId = packagingAction.id;
|
|
46302
|
+
} else if (ws.action_id === assemblyAction.id || ws.action_type === "assembly") {
|
|
46303
|
+
actionType = "assembly";
|
|
46304
|
+
actionId = assemblyAction.id;
|
|
46305
|
+
}
|
|
46422
46306
|
return {
|
|
46423
46307
|
id: ws.id,
|
|
46424
46308
|
name: ws.workspace_id,
|
|
46425
|
-
targetPPH: threshold?.pph_threshold
|
|
46426
|
-
targetCycleTime: threshold?.ideal_cycle_time ??
|
|
46427
|
-
targetDayOutput: threshold?.total_day_output ??
|
|
46428
|
-
actionType
|
|
46429
|
-
actionId
|
|
46309
|
+
targetPPH: threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46310
|
+
targetCycleTime: threshold?.ideal_cycle_time ?? "",
|
|
46311
|
+
targetDayOutput: threshold?.total_day_output ?? "",
|
|
46312
|
+
actionType,
|
|
46313
|
+
actionId
|
|
46430
46314
|
};
|
|
46431
46315
|
}).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
46432
|
-
|
|
46433
|
-
}
|
|
46434
|
-
|
|
46435
|
-
|
|
46436
|
-
|
|
46437
|
-
|
|
46438
|
-
|
|
46316
|
+
newAllShiftsData[shiftId][lineId].workspaces = mappedWorkspaces;
|
|
46317
|
+
});
|
|
46318
|
+
});
|
|
46319
|
+
setAllShiftsData(newAllShiftsData);
|
|
46320
|
+
setDbValues(newDbValues);
|
|
46321
|
+
console.log("[TargetsView] Successfully loaded all data with bulk endpoint:", {
|
|
46322
|
+
lineCount: bulkResponse.metadata.line_count,
|
|
46323
|
+
totalWorkspaces: bulkResponse.metadata.total_workspaces,
|
|
46324
|
+
loadTime: "Fast!"
|
|
46325
|
+
});
|
|
46439
46326
|
} catch (error) {
|
|
46440
|
-
console.error("Error fetching
|
|
46441
|
-
|
|
46442
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46443
|
-
retryCount++;
|
|
46444
|
-
console.log(`Error occurred, retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46445
|
-
toast.warning("Error loading data. Retrying...");
|
|
46446
|
-
setTimeout(() => fetchInitialData(), 1e3);
|
|
46447
|
-
} else {
|
|
46448
|
-
toast.error("Failed to load initial data");
|
|
46449
|
-
setIsLoading(false);
|
|
46450
|
-
}
|
|
46327
|
+
console.error("[TargetsView] Error fetching bulk data:", error);
|
|
46328
|
+
toast.error("Failed to load targets data. Please refresh the page.");
|
|
46451
46329
|
} finally {
|
|
46452
|
-
|
|
46453
|
-
if (retryCount === 0 || retryCount >= MAX_RETRIES2) {
|
|
46454
|
-
setIsLoading(false);
|
|
46455
|
-
}
|
|
46330
|
+
setIsLoading(false);
|
|
46456
46331
|
}
|
|
46457
46332
|
};
|
|
46458
46333
|
fetchInitialData();
|
|
46459
|
-
|
|
46460
|
-
clearTimeout(timeoutId);
|
|
46461
|
-
};
|
|
46462
|
-
}, [lineIds, companyId, loadOperatingHours]);
|
|
46463
|
-
const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
|
|
46464
|
-
if (!supabase) return;
|
|
46465
|
-
const currentDate = getOperationalDate(timezone);
|
|
46466
|
-
const newAllShiftsData = {
|
|
46467
|
-
0: JSON.parse(JSON.stringify(currentWorkspaces)),
|
|
46468
|
-
// Deep clone for day shift
|
|
46469
|
-
1: JSON.parse(JSON.stringify(currentWorkspaces))
|
|
46470
|
-
// Deep clone for night shift
|
|
46471
|
-
};
|
|
46472
|
-
const newDbValues = { 0: {}, 1: {} };
|
|
46473
|
-
for (const shiftId of [0, 1]) {
|
|
46474
|
-
for (const lineId of lineIds) {
|
|
46475
|
-
try {
|
|
46476
|
-
const operatingHoursData = await loadOperatingHours(lineId, shiftId);
|
|
46477
|
-
if (!operatingHoursData) {
|
|
46478
|
-
console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46479
|
-
continue;
|
|
46480
|
-
}
|
|
46481
|
-
const { startTime, endTime, breaks } = operatingHoursData;
|
|
46482
|
-
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
46483
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46484
|
-
lineId,
|
|
46485
|
-
currentDate,
|
|
46486
|
-
shiftId
|
|
46487
|
-
);
|
|
46488
|
-
if (!newDbValues[shiftId][lineId]) {
|
|
46489
|
-
newDbValues[shiftId][lineId] = {};
|
|
46490
|
-
}
|
|
46491
|
-
const existingLine = newAllShiftsData[shiftId][lineId];
|
|
46492
|
-
if (existingLine) {
|
|
46493
|
-
newAllShiftsData[shiftId][lineId] = {
|
|
46494
|
-
...existingLine,
|
|
46495
|
-
shiftStartTime: startTime,
|
|
46496
|
-
shiftEndTime: endTime,
|
|
46497
|
-
breaks,
|
|
46498
|
-
shiftHours: Number(shiftHours),
|
|
46499
|
-
workspaces: existingLine.workspaces.map((ws) => {
|
|
46500
|
-
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
46501
|
-
if (threshold) {
|
|
46502
|
-
newDbValues[shiftId][lineId][ws.id] = {
|
|
46503
|
-
targetPPH: threshold.pph_threshold,
|
|
46504
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46505
|
-
targetDayOutput: threshold.total_day_output
|
|
46506
|
-
};
|
|
46507
|
-
return {
|
|
46508
|
-
...ws,
|
|
46509
|
-
targetPPH: threshold.pph_threshold,
|
|
46510
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46511
|
-
targetDayOutput: threshold.total_day_output
|
|
46512
|
-
};
|
|
46513
|
-
}
|
|
46514
|
-
return ws;
|
|
46515
|
-
})
|
|
46516
|
-
};
|
|
46517
|
-
}
|
|
46518
|
-
} catch (error) {
|
|
46519
|
-
console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
|
|
46520
|
-
}
|
|
46521
|
-
}
|
|
46522
|
-
}
|
|
46523
|
-
setAllShiftsData(newAllShiftsData);
|
|
46524
|
-
setDbValues(newDbValues);
|
|
46525
|
-
}, [supabase, lineIds, loadOperatingHours]);
|
|
46334
|
+
}, [lineIds, companyId, timezone, skuEnabled]);
|
|
46526
46335
|
const toggleLineDropdown = useCallback((lineId) => {
|
|
46527
46336
|
setDropdownStates((prev) => {
|
|
46528
46337
|
const newIsOpen = !prev[lineId];
|
|
@@ -46595,7 +46404,7 @@ var TargetsView = ({
|
|
|
46595
46404
|
if (value !== "") {
|
|
46596
46405
|
const pph = calculatePPH(value, prev[lineId].breaks, shiftHours);
|
|
46597
46406
|
updates.targetPPH = pph;
|
|
46598
|
-
updates.targetDayOutput =
|
|
46407
|
+
updates.targetDayOutput = calculateDayOutputFromCycleTime(value, shiftHours);
|
|
46599
46408
|
} else {
|
|
46600
46409
|
updates.targetPPH = "";
|
|
46601
46410
|
updates.targetDayOutput = "";
|
|
@@ -46713,7 +46522,8 @@ var TargetsView = ({
|
|
|
46713
46522
|
action_id: ws.actionId,
|
|
46714
46523
|
workspace_id: ws.id,
|
|
46715
46524
|
date: currentDate,
|
|
46716
|
-
pph_threshold: Number(ws.targetPPH)
|
|
46525
|
+
pph_threshold: ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0,
|
|
46526
|
+
// Round to whole number
|
|
46717
46527
|
ideal_cycle_time: Number(ws.targetCycleTime) || 0,
|
|
46718
46528
|
total_day_output: Number(ws.targetDayOutput) || 0,
|
|
46719
46529
|
action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
|
|
@@ -46732,7 +46542,8 @@ var TargetsView = ({
|
|
|
46732
46542
|
shift_id: selectedShift,
|
|
46733
46543
|
product_code: lineDataToSave.productId,
|
|
46734
46544
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
46735
|
-
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH)
|
|
46545
|
+
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
46546
|
+
// Round each PPH value
|
|
46736
46547
|
...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
|
|
46737
46548
|
};
|
|
46738
46549
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
@@ -48484,94 +48295,14 @@ var SupervisorManagementView = ({
|
|
|
48484
48295
|
] });
|
|
48485
48296
|
};
|
|
48486
48297
|
var SupervisorManagementView_default = SupervisorManagementView;
|
|
48487
|
-
var
|
|
48488
|
-
|
|
48489
|
-
|
|
48490
|
-
|
|
48491
|
-
|
|
48492
|
-
|
|
48493
|
-
|
|
48494
|
-
|
|
48495
|
-
],
|
|
48496
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48497
|
-
},
|
|
48498
|
-
supervisor_to_owner: {
|
|
48499
|
-
title: "Supervisor \u2192 Owner",
|
|
48500
|
-
warnings: [
|
|
48501
|
-
"Will gain full company access",
|
|
48502
|
-
"Will lose line-specific restrictions",
|
|
48503
|
-
"Will be able to manage all users",
|
|
48504
|
-
"Will see all company data"
|
|
48505
|
-
],
|
|
48506
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48507
|
-
},
|
|
48508
|
-
plant_head_to_supervisor: {
|
|
48509
|
-
title: "Plant Head \u2192 Supervisor",
|
|
48510
|
-
warnings: [
|
|
48511
|
-
"Will lose factory access",
|
|
48512
|
-
"Will lose ability to manage others",
|
|
48513
|
-
"Will need line assignment",
|
|
48514
|
-
"Will only see assigned line data"
|
|
48515
|
-
],
|
|
48516
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48517
|
-
},
|
|
48518
|
-
plant_head_to_owner: {
|
|
48519
|
-
title: "Plant Head \u2192 Owner",
|
|
48520
|
-
warnings: [
|
|
48521
|
-
"Will gain full company access",
|
|
48522
|
-
"Will lose factory-specific restrictions",
|
|
48523
|
-
"Will be able to manage all users",
|
|
48524
|
-
"Will see all company data"
|
|
48525
|
-
],
|
|
48526
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48527
|
-
},
|
|
48528
|
-
owner_to_plant_head: {
|
|
48529
|
-
title: "Owner \u2192 Plant Head",
|
|
48530
|
-
warnings: [
|
|
48531
|
-
"Will lose company-wide access",
|
|
48532
|
-
"Will need factory assignment",
|
|
48533
|
-
"Will only manage supervisors",
|
|
48534
|
-
"Will only see factory data"
|
|
48535
|
-
],
|
|
48536
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48537
|
-
},
|
|
48538
|
-
owner_to_supervisor: {
|
|
48539
|
-
title: "Owner \u2192 Supervisor",
|
|
48540
|
-
warnings: [
|
|
48541
|
-
"Will lose company-wide access",
|
|
48542
|
-
"Will lose ability to manage others",
|
|
48543
|
-
"Will need line assignment",
|
|
48544
|
-
"Will only see assigned line data"
|
|
48545
|
-
],
|
|
48546
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48547
|
-
},
|
|
48548
|
-
to_optifye: {
|
|
48549
|
-
title: "Promoting to Optifye",
|
|
48550
|
-
warnings: [
|
|
48551
|
-
"Will gain access to ALL companies",
|
|
48552
|
-
"Will be able to manage all users globally",
|
|
48553
|
-
"Will see all data across the platform",
|
|
48554
|
-
"This is the highest privilege level"
|
|
48555
|
-
],
|
|
48556
|
-
colorClass: "text-purple-700 bg-purple-50 border-purple-200"
|
|
48557
|
-
},
|
|
48558
|
-
from_optifye: {
|
|
48559
|
-
title: "Demoting from Optifye",
|
|
48560
|
-
warnings: [
|
|
48561
|
-
"Will lose access to other companies",
|
|
48562
|
-
"Will only see assigned company data",
|
|
48563
|
-
"Will lose global management capabilities",
|
|
48564
|
-
"Will need appropriate assignments"
|
|
48565
|
-
],
|
|
48566
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48567
|
-
}
|
|
48568
|
-
};
|
|
48569
|
-
var getRoleChangeKey = (currentRole, newRole) => {
|
|
48570
|
-
if (currentRole === newRole) return null;
|
|
48571
|
-
if (newRole === "optifye") return "to_optifye";
|
|
48572
|
-
if (currentRole === "optifye") return "from_optifye";
|
|
48573
|
-
const key = `${currentRole}_to_${newRole}`;
|
|
48574
|
-
return roleChangeImpacts[key] ? key : null;
|
|
48298
|
+
var getRoleLabel = (role) => {
|
|
48299
|
+
const labels = {
|
|
48300
|
+
"supervisor": "Supervisor",
|
|
48301
|
+
"plant_head": "Plant Head",
|
|
48302
|
+
"owner": "Owner",
|
|
48303
|
+
"optifye": "Optifye Admin"
|
|
48304
|
+
};
|
|
48305
|
+
return labels[role] || role;
|
|
48575
48306
|
};
|
|
48576
48307
|
var ChangeRoleDialog = ({
|
|
48577
48308
|
user,
|
|
@@ -48584,8 +48315,6 @@ var ChangeRoleDialog = ({
|
|
|
48584
48315
|
const [confirmed, setConfirmed] = useState(false);
|
|
48585
48316
|
const [isChanging, setIsChanging] = useState(false);
|
|
48586
48317
|
const roleChanged = selectedRole !== user.role_level;
|
|
48587
|
-
const changeKey = roleChanged ? getRoleChangeKey(user.role_level, selectedRole) : null;
|
|
48588
|
-
const impactInfo = changeKey ? roleChangeImpacts[changeKey] : null;
|
|
48589
48318
|
const handleNext = () => {
|
|
48590
48319
|
if (roleChanged) {
|
|
48591
48320
|
setStep("confirm");
|
|
@@ -48605,7 +48334,7 @@ var ChangeRoleDialog = ({
|
|
|
48605
48334
|
setStep("select");
|
|
48606
48335
|
setConfirmed(false);
|
|
48607
48336
|
};
|
|
48608
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-
|
|
48337
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48609
48338
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48610
48339
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48611
48340
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(UserCog, { className: "w-5 h-5 text-blue-600" }) }),
|
|
@@ -48615,20 +48344,14 @@ var ChangeRoleDialog = ({
|
|
|
48615
48344
|
"button",
|
|
48616
48345
|
{
|
|
48617
48346
|
onClick: onClose,
|
|
48618
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48347
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48619
48348
|
disabled: isChanging,
|
|
48349
|
+
"aria-label": "Close dialog",
|
|
48620
48350
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48621
48351
|
}
|
|
48622
48352
|
)
|
|
48623
48353
|
] }),
|
|
48624
|
-
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-
|
|
48625
|
-
roleChanged && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4 bg-amber-50 border border-amber-200 rounded-lg", children: [
|
|
48626
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" }),
|
|
48627
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
48628
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-amber-900 text-sm", children: "Changing roles affects access" }),
|
|
48629
|
-
/* @__PURE__ */ jsx("p", { className: "text-amber-700 text-sm mt-1", children: "Review the impact carefully before confirming." })
|
|
48630
|
-
] })
|
|
48631
|
-
] }),
|
|
48354
|
+
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-5", children: step === "select" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
48632
48355
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48633
48356
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48634
48357
|
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-gray-50 rounded-lg", children: [
|
|
@@ -48639,7 +48362,7 @@ var ChangeRoleDialog = ({
|
|
|
48639
48362
|
] })
|
|
48640
48363
|
] })
|
|
48641
48364
|
] }),
|
|
48642
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48365
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48643
48366
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Select new role" }),
|
|
48644
48367
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: availableRoles.map((role) => /* @__PURE__ */ jsxs(
|
|
48645
48368
|
"label",
|
|
@@ -48663,27 +48386,24 @@ var ChangeRoleDialog = ({
|
|
|
48663
48386
|
role
|
|
48664
48387
|
)) })
|
|
48665
48388
|
] }),
|
|
48666
|
-
|
|
48667
|
-
|
|
48668
|
-
/* @__PURE__ */ jsx("
|
|
48669
|
-
|
|
48670
|
-
|
|
48671
|
-
|
|
48672
|
-
] })
|
|
48389
|
+
roleChanged && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
48390
|
+
"Changing from ",
|
|
48391
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(user.role_level) }),
|
|
48392
|
+
" to ",
|
|
48393
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(selectedRole) }),
|
|
48394
|
+
" will modify user permissions."
|
|
48395
|
+
] }) })
|
|
48673
48396
|
] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
48674
48397
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-3 p-4 bg-gray-50 rounded-lg", children: [
|
|
48675
48398
|
/* @__PURE__ */ jsx(RoleBadge, { role: user.role_level }),
|
|
48676
48399
|
/* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5 text-gray-400" }),
|
|
48677
48400
|
/* @__PURE__ */ jsx(RoleBadge, { role: selectedRole })
|
|
48678
48401
|
] }),
|
|
48679
|
-
|
|
48680
|
-
/* @__PURE__ */ jsx("p", { className: "
|
|
48681
|
-
/* @__PURE__ */ jsx("
|
|
48682
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5", children: "\u2022" }),
|
|
48683
|
-
/* @__PURE__ */ jsx("span", { children: warning6 })
|
|
48684
|
-
] }, idx)) })
|
|
48402
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48403
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48404
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
48685
48405
|
] }),
|
|
48686
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-
|
|
48406
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48687
48407
|
/* @__PURE__ */ jsx(
|
|
48688
48408
|
"input",
|
|
48689
48409
|
{
|
|
@@ -48694,7 +48414,7 @@ var ChangeRoleDialog = ({
|
|
|
48694
48414
|
className: "mt-0.5 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
48695
48415
|
}
|
|
48696
48416
|
),
|
|
48697
|
-
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-role-change", className: "text-sm text-gray-700 cursor-pointer", children: "I
|
|
48417
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-role-change", className: "text-sm text-gray-700 cursor-pointer", children: "I confirm this role change and understand the permissions will be updated" })
|
|
48698
48418
|
] })
|
|
48699
48419
|
] }) }) }),
|
|
48700
48420
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: step === "select" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -48737,7 +48457,7 @@ var ChangeRoleDialog = ({
|
|
|
48737
48457
|
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
48738
48458
|
] }),
|
|
48739
48459
|
"Changing..."
|
|
48740
|
-
] }) : "Change
|
|
48460
|
+
] }) : "Confirm Change"
|
|
48741
48461
|
}
|
|
48742
48462
|
)
|
|
48743
48463
|
] }) })
|
|
@@ -48760,32 +48480,34 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48760
48480
|
setIsRemoving(false);
|
|
48761
48481
|
}
|
|
48762
48482
|
};
|
|
48763
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-
|
|
48483
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48764
48484
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48765
48485
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48766
48486
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-red-100 rounded-lg", children: /* @__PURE__ */ jsx(Trash2, { className: "w-5 h-5 text-red-600" }) }),
|
|
48767
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "
|
|
48487
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Permanently Delete User" })
|
|
48768
48488
|
] }),
|
|
48769
48489
|
/* @__PURE__ */ jsx(
|
|
48770
48490
|
"button",
|
|
48771
48491
|
{
|
|
48772
48492
|
onClick: onCancel,
|
|
48773
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48493
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48494
|
+
disabled: isRemoving,
|
|
48495
|
+
"aria-label": "Close dialog",
|
|
48774
48496
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48775
48497
|
}
|
|
48776
48498
|
)
|
|
48777
48499
|
] }),
|
|
48778
|
-
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-
|
|
48779
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-
|
|
48780
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-
|
|
48781
|
-
/* @__PURE__ */ jsxs("
|
|
48782
|
-
/* @__PURE__ */ jsx("
|
|
48783
|
-
|
|
48500
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-5", children: [
|
|
48501
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 bg-red-50 border border-red-200 rounded-lg", children: [
|
|
48502
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4 text-red-600 mt-0.5 flex-shrink-0" }),
|
|
48503
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-red-900", children: [
|
|
48504
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "This action cannot be undone." }),
|
|
48505
|
+
" This will permanently delete the user and all related data from the system."
|
|
48784
48506
|
] })
|
|
48785
48507
|
] }),
|
|
48786
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48787
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to
|
|
48788
|
-
/* @__PURE__ */ jsxs("div", { className: "p-
|
|
48508
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48509
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to permanently delete:" }),
|
|
48510
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-gray-50 rounded-lg space-y-2.5", children: [
|
|
48789
48511
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
48790
48512
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: "Email" }),
|
|
48791
48513
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
@@ -48810,28 +48532,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48810
48532
|
] })
|
|
48811
48533
|
] })
|
|
48812
48534
|
] }),
|
|
48813
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
48814
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "This will:" }),
|
|
48815
|
-
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm text-gray-600", children: [
|
|
48816
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48817
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48818
|
-
/* @__PURE__ */ jsx("span", { children: "Revoke all access immediately" })
|
|
48819
|
-
] }),
|
|
48820
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48821
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48822
|
-
/* @__PURE__ */ jsx("span", { children: "Remove all line and factory assignments" })
|
|
48823
|
-
] }),
|
|
48824
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48825
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48826
|
-
/* @__PURE__ */ jsx("span", { children: "Log the user out of all sessions" })
|
|
48827
|
-
] }),
|
|
48828
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48829
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48830
|
-
/* @__PURE__ */ jsx("span", { children: "User will not be able to access the dashboard" })
|
|
48831
|
-
] })
|
|
48832
|
-
] })
|
|
48833
|
-
] }),
|
|
48834
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4 border border-gray-200 rounded-lg", children: [
|
|
48535
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48835
48536
|
/* @__PURE__ */ jsx(
|
|
48836
48537
|
"input",
|
|
48837
48538
|
{
|
|
@@ -48842,7 +48543,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48842
48543
|
className: "mt-0.5 h-4 w-4 text-red-600 border-gray-300 rounded focus:ring-red-500"
|
|
48843
48544
|
}
|
|
48844
48545
|
),
|
|
48845
|
-
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-remove", className: "text-sm text-gray-700 cursor-pointer", children: "I understand this
|
|
48546
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-remove", className: "text-sm text-gray-700 cursor-pointer", children: "I understand this will permanently delete the user and all related data, and this action cannot be undone" })
|
|
48846
48547
|
] })
|
|
48847
48548
|
] }),
|
|
48848
48549
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: [
|
|
@@ -48866,8 +48567,8 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48866
48567
|
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
48867
48568
|
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
48868
48569
|
] }),
|
|
48869
|
-
"
|
|
48870
|
-
] }) : "
|
|
48570
|
+
"Deleting..."
|
|
48571
|
+
] }) : "Delete User"
|
|
48871
48572
|
}
|
|
48872
48573
|
)
|
|
48873
48574
|
] })
|
|
@@ -49290,7 +48991,6 @@ var FactoryAssignmentDropdown = ({
|
|
|
49290
48991
|
var UserManagementTable = ({
|
|
49291
48992
|
users,
|
|
49292
48993
|
isLoading = false,
|
|
49293
|
-
onUserClick,
|
|
49294
48994
|
onRoleChange,
|
|
49295
48995
|
onRemoveUser,
|
|
49296
48996
|
onLineAssignmentUpdate,
|
|
@@ -49316,9 +49016,48 @@ var UserManagementTable = ({
|
|
|
49316
49016
|
const [sortField, setSortField] = useState("email");
|
|
49317
49017
|
const [sortDirection, setSortDirection] = useState("asc");
|
|
49318
49018
|
const [openActionMenuId, setOpenActionMenuId] = useState(null);
|
|
49019
|
+
const [dropdownPosition, setDropdownPosition] = useState(null);
|
|
49020
|
+
const actionButtonRefs = useRef({});
|
|
49319
49021
|
const [selectedUser, setSelectedUser] = useState(null);
|
|
49320
49022
|
const [showChangeRoleDialog, setShowChangeRoleDialog] = useState(false);
|
|
49321
49023
|
const [showRemoveUserDialog, setShowRemoveUserDialog] = useState(false);
|
|
49024
|
+
const handleOpenActionMenu = (userId) => {
|
|
49025
|
+
const buttonRef = actionButtonRefs.current[userId];
|
|
49026
|
+
if (buttonRef) {
|
|
49027
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49028
|
+
setDropdownPosition({
|
|
49029
|
+
top: rect.bottom + 8,
|
|
49030
|
+
// 8px below the button
|
|
49031
|
+
left: rect.right - 192
|
|
49032
|
+
// 192px is the dropdown width (w-48 = 12rem = 192px)
|
|
49033
|
+
});
|
|
49034
|
+
setOpenActionMenuId(userId);
|
|
49035
|
+
}
|
|
49036
|
+
};
|
|
49037
|
+
const handleCloseActionMenu = () => {
|
|
49038
|
+
setOpenActionMenuId(null);
|
|
49039
|
+
setDropdownPosition(null);
|
|
49040
|
+
};
|
|
49041
|
+
useEffect(() => {
|
|
49042
|
+
if (openActionMenuId && actionButtonRefs.current[openActionMenuId]) {
|
|
49043
|
+
const updatePosition = () => {
|
|
49044
|
+
const buttonRef = actionButtonRefs.current[openActionMenuId];
|
|
49045
|
+
if (buttonRef) {
|
|
49046
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49047
|
+
setDropdownPosition({
|
|
49048
|
+
top: rect.bottom + 8,
|
|
49049
|
+
left: rect.right - 192
|
|
49050
|
+
});
|
|
49051
|
+
}
|
|
49052
|
+
};
|
|
49053
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
49054
|
+
window.addEventListener("resize", updatePosition);
|
|
49055
|
+
return () => {
|
|
49056
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
49057
|
+
window.removeEventListener("resize", updatePosition);
|
|
49058
|
+
};
|
|
49059
|
+
}
|
|
49060
|
+
}, [openActionMenuId]);
|
|
49322
49061
|
const filteredAndSortedUsers = useMemo(() => {
|
|
49323
49062
|
let filtered = users;
|
|
49324
49063
|
filtered = filtered.filter((user) => availableRoles.includes(user.role_level));
|
|
@@ -49509,7 +49248,7 @@ var UserManagementTable = ({
|
|
|
49509
49248
|
LineAssignmentDropdown,
|
|
49510
49249
|
{
|
|
49511
49250
|
userId: user.user_id,
|
|
49512
|
-
currentLineIds: user.properties?.
|
|
49251
|
+
currentLineIds: user.properties?.line_ids || [],
|
|
49513
49252
|
availableLines: (
|
|
49514
49253
|
// Filter lines to only show those from the target user's company
|
|
49515
49254
|
(() => {
|
|
@@ -49547,69 +49286,24 @@ var UserManagementTable = ({
|
|
|
49547
49286
|
}
|
|
49548
49287
|
) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
49549
49288
|
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: formatDate(user.created_at) }) }),
|
|
49550
|
-
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */
|
|
49551
|
-
|
|
49552
|
-
|
|
49553
|
-
{
|
|
49554
|
-
|
|
49555
|
-
|
|
49556
|
-
|
|
49557
|
-
|
|
49558
|
-
|
|
49559
|
-
|
|
49560
|
-
|
|
49561
|
-
/* @__PURE__ */ jsx(
|
|
49562
|
-
"div",
|
|
49563
|
-
{
|
|
49564
|
-
className: "fixed inset-0 z-[9998]",
|
|
49565
|
-
onClick: () => setOpenActionMenuId(null)
|
|
49289
|
+
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
|
|
49290
|
+
"button",
|
|
49291
|
+
{
|
|
49292
|
+
ref: (el) => {
|
|
49293
|
+
actionButtonRefs.current[user.user_id] = el;
|
|
49294
|
+
},
|
|
49295
|
+
onClick: () => {
|
|
49296
|
+
if (openActionMenuId === user.user_id) {
|
|
49297
|
+
handleCloseActionMenu();
|
|
49298
|
+
} else {
|
|
49299
|
+
handleOpenActionMenu(user.user_id);
|
|
49566
49300
|
}
|
|
49567
|
-
|
|
49568
|
-
|
|
49569
|
-
|
|
49570
|
-
|
|
49571
|
-
|
|
49572
|
-
|
|
49573
|
-
onUserClick?.(user);
|
|
49574
|
-
setOpenActionMenuId(null);
|
|
49575
|
-
},
|
|
49576
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49577
|
-
children: [
|
|
49578
|
-
/* @__PURE__ */ jsx(Eye, { className: "w-4 h-4" }),
|
|
49579
|
-
"View Details"
|
|
49580
|
-
]
|
|
49581
|
-
}
|
|
49582
|
-
),
|
|
49583
|
-
canChangeRole && /* @__PURE__ */ jsxs(
|
|
49584
|
-
"button",
|
|
49585
|
-
{
|
|
49586
|
-
onClick: () => handleChangeRole(user),
|
|
49587
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49588
|
-
disabled: isCurrentUser,
|
|
49589
|
-
children: [
|
|
49590
|
-
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
49591
|
-
"Change Role"
|
|
49592
|
-
]
|
|
49593
|
-
}
|
|
49594
|
-
),
|
|
49595
|
-
canRemove && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49596
|
-
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49597
|
-
/* @__PURE__ */ jsxs(
|
|
49598
|
-
"button",
|
|
49599
|
-
{
|
|
49600
|
-
onClick: () => handleRemoveUser(user),
|
|
49601
|
-
className: "w-full px-4 py-2 text-sm text-left text-red-600 hover:bg-red-50 flex items-center gap-2",
|
|
49602
|
-
disabled: isCurrentUser,
|
|
49603
|
-
children: [
|
|
49604
|
-
/* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
|
|
49605
|
-
"Remove User"
|
|
49606
|
-
]
|
|
49607
|
-
}
|
|
49608
|
-
)
|
|
49609
|
-
] })
|
|
49610
|
-
] }) })
|
|
49611
|
-
] })
|
|
49612
|
-
] }) })
|
|
49301
|
+
},
|
|
49302
|
+
className: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
49303
|
+
"aria-label": "User actions",
|
|
49304
|
+
children: /* @__PURE__ */ jsx(MoreVertical, { className: "w-4 h-4" })
|
|
49305
|
+
}
|
|
49306
|
+
) }) })
|
|
49613
49307
|
]
|
|
49614
49308
|
},
|
|
49615
49309
|
user.user_id
|
|
@@ -49617,6 +49311,75 @@ var UserManagementTable = ({
|
|
|
49617
49311
|
}) })
|
|
49618
49312
|
] }) }) })
|
|
49619
49313
|
] }),
|
|
49314
|
+
openActionMenuId && dropdownPosition && typeof document !== "undefined" && createPortal(
|
|
49315
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49316
|
+
/* @__PURE__ */ jsx(
|
|
49317
|
+
"div",
|
|
49318
|
+
{
|
|
49319
|
+
className: "fixed inset-0 z-[9998]",
|
|
49320
|
+
onClick: handleCloseActionMenu
|
|
49321
|
+
}
|
|
49322
|
+
),
|
|
49323
|
+
/* @__PURE__ */ jsx(
|
|
49324
|
+
"div",
|
|
49325
|
+
{
|
|
49326
|
+
className: "fixed w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-[9999]",
|
|
49327
|
+
style: {
|
|
49328
|
+
top: `${dropdownPosition.top}px`,
|
|
49329
|
+
left: `${dropdownPosition.left}px`
|
|
49330
|
+
},
|
|
49331
|
+
children: /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
|
|
49332
|
+
(() => {
|
|
49333
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49334
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49335
|
+
const canChangeRole = user && permissions.canChangeRole(user);
|
|
49336
|
+
return canChangeRole && /* @__PURE__ */ jsxs(
|
|
49337
|
+
"button",
|
|
49338
|
+
{
|
|
49339
|
+
onClick: () => {
|
|
49340
|
+
if (user) {
|
|
49341
|
+
handleChangeRole(user);
|
|
49342
|
+
}
|
|
49343
|
+
},
|
|
49344
|
+
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
49345
|
+
disabled: isCurrentUser,
|
|
49346
|
+
children: [
|
|
49347
|
+
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
49348
|
+
"Change Role"
|
|
49349
|
+
]
|
|
49350
|
+
}
|
|
49351
|
+
);
|
|
49352
|
+
})(),
|
|
49353
|
+
(() => {
|
|
49354
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49355
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49356
|
+
const canRemove = user && permissions.canRemoveUser(user);
|
|
49357
|
+
return canRemove && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49358
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49359
|
+
/* @__PURE__ */ jsxs(
|
|
49360
|
+
"button",
|
|
49361
|
+
{
|
|
49362
|
+
onClick: () => {
|
|
49363
|
+
if (user) {
|
|
49364
|
+
handleRemoveUser(user);
|
|
49365
|
+
}
|
|
49366
|
+
},
|
|
49367
|
+
className: "w-full px-4 py-2 text-sm text-left text-red-600 hover:bg-red-50 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
49368
|
+
disabled: isCurrentUser,
|
|
49369
|
+
children: [
|
|
49370
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
|
|
49371
|
+
"Delete User"
|
|
49372
|
+
]
|
|
49373
|
+
}
|
|
49374
|
+
)
|
|
49375
|
+
] });
|
|
49376
|
+
})()
|
|
49377
|
+
] })
|
|
49378
|
+
}
|
|
49379
|
+
)
|
|
49380
|
+
] }),
|
|
49381
|
+
document.body
|
|
49382
|
+
),
|
|
49620
49383
|
showChangeRoleDialog && selectedUser && /* @__PURE__ */ jsx(
|
|
49621
49384
|
ChangeRoleDialog,
|
|
49622
49385
|
{
|
|
@@ -50268,15 +50031,12 @@ var TeamManagementView = ({
|
|
|
50268
50031
|
if (!supabase || !user) return;
|
|
50269
50032
|
try {
|
|
50270
50033
|
const userManagementService = createUserManagementService(supabase);
|
|
50271
|
-
await userManagementService.
|
|
50272
|
-
|
|
50273
|
-
deactivated_by: user.id
|
|
50274
|
-
});
|
|
50275
|
-
toast.success("User removed successfully");
|
|
50034
|
+
const result = await userManagementService.deleteUser(userId);
|
|
50035
|
+
toast.success(`User ${result.deleted_user_email} permanently deleted`);
|
|
50276
50036
|
loadData();
|
|
50277
50037
|
} catch (err) {
|
|
50278
|
-
console.error("Error
|
|
50279
|
-
toast.error("Failed to
|
|
50038
|
+
console.error("Error deleting user:", err);
|
|
50039
|
+
toast.error("Failed to delete user");
|
|
50280
50040
|
}
|
|
50281
50041
|
}, [supabase, user, loadData]);
|
|
50282
50042
|
const handleLineAssignmentUpdate = useCallback(async (userId, lineIds) => {
|
|
@@ -50370,9 +50130,6 @@ var TeamManagementView = ({
|
|
|
50370
50130
|
{
|
|
50371
50131
|
users,
|
|
50372
50132
|
currentUserId: user?.id,
|
|
50373
|
-
onUserClick: (clickedUser) => {
|
|
50374
|
-
console.log("View user details:", clickedUser);
|
|
50375
|
-
},
|
|
50376
50133
|
onRoleChange: handleRoleChange,
|
|
50377
50134
|
onRemoveUser: handleRemoveUser,
|
|
50378
50135
|
onLineAssignmentUpdate: handleLineAssignmentUpdate,
|