@optifye/dashboard-core 6.9.1 → 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 +4 -8
- package/dist/index.d.mts +51 -25
- package/dist/index.d.ts +51 -25
- package/dist/index.js +575 -678
- package/dist/index.mjs +575 -678
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -198,7 +198,7 @@ var DEFAULT_DATE_TIME_CONFIG = {
|
|
|
198
198
|
};
|
|
199
199
|
var DEFAULT_ENDPOINTS_CONFIG = {
|
|
200
200
|
whatsapp: "/api/send-whatsapp-direct",
|
|
201
|
-
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://
|
|
201
|
+
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app",
|
|
202
202
|
// Default AGNO API URL
|
|
203
203
|
// Use environment variable for Slack webhook URL for privacy/security
|
|
204
204
|
// Note: SLACK_WEBHOOK_URL is server-side only, NEXT_PUBLIC_SLACK_WEBHOOK_URL works client-side but is less secure
|
|
@@ -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 {
|
|
@@ -3216,7 +3269,7 @@ var SSEChatClient = class {
|
|
|
3216
3269
|
user_id: userId,
|
|
3217
3270
|
context
|
|
3218
3271
|
});
|
|
3219
|
-
const agnoApiUrl = this.baseUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
3272
|
+
const agnoApiUrl = this.baseUrl || (process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app");
|
|
3220
3273
|
const endpoint = `${agnoApiUrl}/api/v2/chat`;
|
|
3221
3274
|
console.log("[SSEClient] Posting directly to AGNO:", endpoint);
|
|
3222
3275
|
const response = await fetch(endpoint, {
|
|
@@ -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();
|
|
@@ -10709,23 +10736,18 @@ function formatRelativeTime(timestamp) {
|
|
|
10709
10736
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10710
10737
|
const diffDays = Math.floor(diffHours / 24);
|
|
10711
10738
|
if (diffSeconds < 60) {
|
|
10712
|
-
return
|
|
10739
|
+
return "Less than a minute ago";
|
|
10713
10740
|
}
|
|
10714
10741
|
if (diffMinutes < 60) {
|
|
10715
|
-
const
|
|
10716
|
-
|
|
10717
|
-
return `${diffMinutes}m ago`;
|
|
10718
|
-
}
|
|
10719
|
-
return `${diffMinutes}m ${remainingSeconds}s ago`;
|
|
10742
|
+
const minuteLabel = diffMinutes === 1 ? "minute" : "minutes";
|
|
10743
|
+
return `${diffMinutes} ${minuteLabel} ago`;
|
|
10720
10744
|
}
|
|
10721
10745
|
if (diffHours < 24) {
|
|
10722
|
-
const
|
|
10723
|
-
|
|
10724
|
-
return `${diffHours}h ago`;
|
|
10725
|
-
}
|
|
10726
|
-
return `${diffHours}h ${remainingMinutes}m ago`;
|
|
10746
|
+
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
10747
|
+
return `${diffHours} ${hourLabel} ago`;
|
|
10727
10748
|
}
|
|
10728
|
-
|
|
10749
|
+
const dayLabel = diffDays === 1 ? "day" : "days";
|
|
10750
|
+
return `${diffDays} ${dayLabel} ago`;
|
|
10729
10751
|
} catch (error) {
|
|
10730
10752
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
10731
10753
|
return "Unknown";
|
|
@@ -10741,8 +10763,8 @@ function getNextUpdateInterval(timestamp) {
|
|
|
10741
10763
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
10742
10764
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
10743
10765
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10744
|
-
if (diffSeconds < 60) return
|
|
10745
|
-
if (diffMinutes < 60) return
|
|
10766
|
+
if (diffSeconds < 60) return 1e4;
|
|
10767
|
+
if (diffMinutes < 60) return 6e4;
|
|
10746
10768
|
if (diffHours < 24) return 6e4;
|
|
10747
10769
|
return 36e5;
|
|
10748
10770
|
} catch (error) {
|
|
@@ -10919,50 +10941,129 @@ function useAccessControl() {
|
|
|
10919
10941
|
const userRole = useMemo(() => {
|
|
10920
10942
|
if (!user?.role_level) return null;
|
|
10921
10943
|
const roleLevel = user.role_level;
|
|
10922
|
-
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
10944
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
|
|
10923
10945
|
return roleLevel;
|
|
10924
10946
|
}
|
|
10925
10947
|
return "supervisor";
|
|
10926
10948
|
}, [user?.role_level]);
|
|
10927
|
-
const
|
|
10928
|
-
|
|
10929
|
-
"
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
"
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10949
|
+
const assignedLineIds = useMemo(() => {
|
|
10950
|
+
if (!user) return [];
|
|
10951
|
+
if (user.role_level === "supervisor") {
|
|
10952
|
+
const lines = user.properties?.line_id || user.properties?.line_ids || [];
|
|
10953
|
+
return Array.isArray(lines) ? lines : [];
|
|
10954
|
+
}
|
|
10955
|
+
return [];
|
|
10956
|
+
}, [user]);
|
|
10957
|
+
const assignedFactoryIds = useMemo(() => {
|
|
10958
|
+
if (!user) return [];
|
|
10959
|
+
if (user.role_level === "plant_head") {
|
|
10960
|
+
const factories = user.properties?.factory_id || user.properties?.factory_ids || [];
|
|
10961
|
+
return Array.isArray(factories) ? factories : [];
|
|
10962
|
+
}
|
|
10963
|
+
return [];
|
|
10964
|
+
}, [user]);
|
|
10965
|
+
const roleAccessMap = {
|
|
10966
|
+
optifye: [
|
|
10967
|
+
"/",
|
|
10968
|
+
"/leaderboard",
|
|
10969
|
+
"/kpis",
|
|
10970
|
+
"/targets",
|
|
10971
|
+
"/shifts",
|
|
10972
|
+
"/supervisor-management",
|
|
10973
|
+
"/skus",
|
|
10974
|
+
"/ai-agent",
|
|
10975
|
+
"/help",
|
|
10976
|
+
"/health",
|
|
10977
|
+
"/profile",
|
|
10978
|
+
"/workspace",
|
|
10979
|
+
"/factory-view",
|
|
10980
|
+
"/team-management"
|
|
10981
|
+
],
|
|
10982
|
+
owner: [
|
|
10983
|
+
"/",
|
|
10984
|
+
"/leaderboard",
|
|
10985
|
+
"/kpis",
|
|
10986
|
+
"/targets",
|
|
10987
|
+
"/shifts",
|
|
10988
|
+
"/supervisor-management",
|
|
10989
|
+
"/skus",
|
|
10990
|
+
"/ai-agent",
|
|
10991
|
+
"/help",
|
|
10992
|
+
"/health",
|
|
10993
|
+
"/profile",
|
|
10994
|
+
"/workspace",
|
|
10995
|
+
"/factory-view",
|
|
10996
|
+
"/team-management"
|
|
10997
|
+
],
|
|
10998
|
+
plant_head: [
|
|
10999
|
+
"/",
|
|
11000
|
+
"/leaderboard",
|
|
11001
|
+
"/kpis",
|
|
11002
|
+
"/targets",
|
|
11003
|
+
"/shifts",
|
|
11004
|
+
"/supervisor-management",
|
|
11005
|
+
"/skus",
|
|
11006
|
+
"/ai-agent",
|
|
11007
|
+
"/help",
|
|
11008
|
+
"/health",
|
|
11009
|
+
"/profile",
|
|
11010
|
+
"/workspace",
|
|
11011
|
+
"/factory-view",
|
|
11012
|
+
"/team-management"
|
|
11013
|
+
],
|
|
11014
|
+
supervisor: [
|
|
11015
|
+
"/",
|
|
11016
|
+
"/leaderboard",
|
|
11017
|
+
"/kpis",
|
|
11018
|
+
"/targets",
|
|
11019
|
+
"/shifts",
|
|
11020
|
+
"/skus",
|
|
11021
|
+
"/ai-agent",
|
|
11022
|
+
"/help",
|
|
11023
|
+
"/health",
|
|
11024
|
+
"/profile",
|
|
11025
|
+
"/workspace"
|
|
11026
|
+
]
|
|
11027
|
+
};
|
|
10942
11028
|
const accessiblePages = useMemo(() => {
|
|
10943
|
-
return
|
|
10944
|
-
|
|
11029
|
+
if (!userRole) return [];
|
|
11030
|
+
return roleAccessMap[userRole] || [];
|
|
11031
|
+
}, [userRole]);
|
|
10945
11032
|
const hasAccess = useMemo(() => {
|
|
10946
11033
|
return (path) => {
|
|
10947
|
-
return
|
|
11034
|
+
if (!userRole) return false;
|
|
11035
|
+
const basePath = path.split("?")[0].split("/").slice(0, 2).join("/");
|
|
11036
|
+
const hasBaseAccess = accessiblePages.includes(basePath) || accessiblePages.includes("/");
|
|
11037
|
+
if (userRole === "supervisor" && assignedLineIds.length > 0) {
|
|
11038
|
+
if (path.includes("/kpis/") || path.includes("/workspace/")) {
|
|
11039
|
+
const lineIdMatch = path.match(/\/kpis\/([^/?]+)/);
|
|
11040
|
+
if (lineIdMatch) {
|
|
11041
|
+
const pathLineId = lineIdMatch[1];
|
|
11042
|
+
return assignedLineIds.includes(pathLineId);
|
|
11043
|
+
}
|
|
11044
|
+
}
|
|
11045
|
+
}
|
|
11046
|
+
return hasBaseAccess;
|
|
10948
11047
|
};
|
|
10949
|
-
}, []);
|
|
11048
|
+
}, [userRole, accessiblePages, assignedLineIds]);
|
|
10950
11049
|
const isPageVisible = useMemo(() => {
|
|
10951
11050
|
return (path) => {
|
|
10952
|
-
return
|
|
11051
|
+
return hasAccess(path);
|
|
10953
11052
|
};
|
|
10954
|
-
}, []);
|
|
11053
|
+
}, [hasAccess]);
|
|
10955
11054
|
const canAccessPage = useMemo(() => {
|
|
10956
11055
|
return (path) => {
|
|
10957
|
-
return
|
|
11056
|
+
return hasAccess(path);
|
|
10958
11057
|
};
|
|
10959
|
-
}, []);
|
|
11058
|
+
}, [hasAccess]);
|
|
10960
11059
|
return {
|
|
10961
11060
|
userRole,
|
|
10962
11061
|
hasAccess,
|
|
10963
11062
|
accessiblePages,
|
|
10964
11063
|
isPageVisible,
|
|
10965
|
-
canAccessPage
|
|
11064
|
+
canAccessPage,
|
|
11065
|
+
assignedLineIds,
|
|
11066
|
+
assignedFactoryIds
|
|
10966
11067
|
};
|
|
10967
11068
|
}
|
|
10968
11069
|
|
|
@@ -11126,45 +11227,51 @@ function useHasLineAccess(lineId, configLineIds) {
|
|
|
11126
11227
|
function useLineSupervisor(lineId) {
|
|
11127
11228
|
const supabase = useSupabase();
|
|
11128
11229
|
const [supervisor, setSupervisor] = useState(null);
|
|
11230
|
+
const [supervisors, setSupervisors] = useState([]);
|
|
11129
11231
|
const [isLoading, setIsLoading] = useState(true);
|
|
11130
11232
|
const [error, setError] = useState(null);
|
|
11131
|
-
console.log("[useLineSupervisor] Hook initialized for lineId:", lineId);
|
|
11132
11233
|
const fetchSupervisor = useCallback(async () => {
|
|
11133
11234
|
if (!lineId || !supabase) {
|
|
11134
|
-
console.log("[useLineSupervisor] Skipping fetch - lineId or supabase missing:", { lineId, hasSupabase: !!supabase });
|
|
11135
11235
|
setIsLoading(false);
|
|
11136
11236
|
return;
|
|
11137
11237
|
}
|
|
11138
11238
|
try {
|
|
11139
11239
|
setIsLoading(true);
|
|
11140
11240
|
setError(null);
|
|
11141
|
-
|
|
11142
|
-
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor").filter("properties->line_id", "@>", `["${lineId}"]`).limit(1);
|
|
11143
|
-
console.log("[useLineSupervisor] Query result:", { data, error: fetchError, lineId });
|
|
11241
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor");
|
|
11144
11242
|
if (fetchError) {
|
|
11145
11243
|
console.error("[useLineSupervisor] Query error:", fetchError);
|
|
11146
11244
|
throw fetchError;
|
|
11147
11245
|
}
|
|
11148
11246
|
if (data && data.length > 0) {
|
|
11149
|
-
const
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
line_id_in_properties: supervisorData.properties?.line_id
|
|
11154
|
-
});
|
|
11155
|
-
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11156
|
-
setSupervisor({
|
|
11157
|
-
userId: supervisorData.user_id,
|
|
11158
|
-
email: supervisorData.email,
|
|
11159
|
-
displayName
|
|
11247
|
+
const supervisorsForLine = data.filter((supervisorData) => {
|
|
11248
|
+
const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
|
|
11249
|
+
const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
|
|
11250
|
+
return isAssigned;
|
|
11160
11251
|
});
|
|
11252
|
+
if (supervisorsForLine.length > 0) {
|
|
11253
|
+
const allSupervisors = supervisorsForLine.map((supervisorData) => {
|
|
11254
|
+
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11255
|
+
return {
|
|
11256
|
+
userId: supervisorData.user_id,
|
|
11257
|
+
email: supervisorData.email,
|
|
11258
|
+
displayName
|
|
11259
|
+
};
|
|
11260
|
+
});
|
|
11261
|
+
setSupervisors(allSupervisors);
|
|
11262
|
+
setSupervisor(allSupervisors[0]);
|
|
11263
|
+
} else {
|
|
11264
|
+
setSupervisors([]);
|
|
11265
|
+
setSupervisor(null);
|
|
11266
|
+
}
|
|
11161
11267
|
} else {
|
|
11162
|
-
|
|
11268
|
+
setSupervisors([]);
|
|
11163
11269
|
setSupervisor(null);
|
|
11164
11270
|
}
|
|
11165
11271
|
} catch (err) {
|
|
11166
|
-
console.error("[useLineSupervisor] Error fetching
|
|
11167
|
-
setError(err instanceof Error ? err : new Error("Failed to fetch
|
|
11272
|
+
console.error("[useLineSupervisor] Error fetching supervisors:", err);
|
|
11273
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
11274
|
+
setSupervisors([]);
|
|
11168
11275
|
setSupervisor(null);
|
|
11169
11276
|
} finally {
|
|
11170
11277
|
setIsLoading(false);
|
|
@@ -11189,7 +11296,6 @@ function useLineSupervisor(lineId) {
|
|
|
11189
11296
|
filter: `role_level=eq.supervisor`
|
|
11190
11297
|
},
|
|
11191
11298
|
(payload) => {
|
|
11192
|
-
console.log("[useLineSupervisor] Real-time update received:", payload);
|
|
11193
11299
|
fetchSupervisor();
|
|
11194
11300
|
}
|
|
11195
11301
|
).subscribe();
|
|
@@ -11201,9 +11307,11 @@ function useLineSupervisor(lineId) {
|
|
|
11201
11307
|
}
|
|
11202
11308
|
};
|
|
11203
11309
|
}, [lineId, supabase, fetchSupervisor]);
|
|
11310
|
+
const supervisorName = supervisors.length > 0 ? supervisors.map((s) => s.displayName).join(", ") : null;
|
|
11204
11311
|
return {
|
|
11205
|
-
supervisorName
|
|
11312
|
+
supervisorName,
|
|
11206
11313
|
supervisor,
|
|
11314
|
+
supervisors,
|
|
11207
11315
|
isLoading,
|
|
11208
11316
|
error
|
|
11209
11317
|
};
|
|
@@ -34074,14 +34182,16 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
34074
34182
|
};
|
|
34075
34183
|
var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
34076
34184
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
34185
|
+
const entityConfig = useEntityConfig();
|
|
34077
34186
|
const generatePDF = async () => {
|
|
34078
34187
|
setIsGenerating(true);
|
|
34079
34188
|
try {
|
|
34189
|
+
const lineName = workspace.line_name || getLineDisplayName(entityConfig, workspace.line_id);
|
|
34080
34190
|
trackCoreEvent("Workspace PDF Export Clicked", {
|
|
34081
34191
|
workspace_id: workspace.workspace_id,
|
|
34082
34192
|
line_id: workspace.line_id,
|
|
34083
34193
|
workspace_name: workspace.workspace_name,
|
|
34084
|
-
line_name:
|
|
34194
|
+
line_name: lineName
|
|
34085
34195
|
});
|
|
34086
34196
|
const doc = new jsPDF$1();
|
|
34087
34197
|
doc.setFontSize(14);
|
|
@@ -34102,7 +34212,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34102
34212
|
doc.setFontSize(32);
|
|
34103
34213
|
doc.setFont("helvetica", "bold");
|
|
34104
34214
|
doc.setTextColor(0, 0, 0);
|
|
34105
|
-
doc.text(
|
|
34215
|
+
doc.text(lineName, 20, 40);
|
|
34106
34216
|
doc.setFontSize(22);
|
|
34107
34217
|
doc.setFont("helvetica", "normal");
|
|
34108
34218
|
doc.setTextColor(40, 40, 40);
|
|
@@ -38802,7 +38912,7 @@ var AIAgentView = () => {
|
|
|
38802
38912
|
const renderedContentCache = useRef(/* @__PURE__ */ new Map());
|
|
38803
38913
|
const { createThread, mutate: mutateThreads } = useThreads();
|
|
38804
38914
|
const { messages, addMessage, setMessages } = useMessages(activeThreadId);
|
|
38805
|
-
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://
|
|
38915
|
+
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
38806
38916
|
const sseClient = useMemo(() => {
|
|
38807
38917
|
console.log("[AIAgentView] Using AGNO API URL:", agnoApiUrl);
|
|
38808
38918
|
return new SSEChatClient(agnoApiUrl);
|
|
@@ -41336,7 +41446,6 @@ function HomeView({
|
|
|
41336
41446
|
factoryName = "Simba Beer - Line 1"
|
|
41337
41447
|
}) {
|
|
41338
41448
|
const [isHydrated, setIsHydrated] = useState(false);
|
|
41339
|
-
const availableLineIds = useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
|
|
41340
41449
|
const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
|
|
41341
41450
|
const [isChangingFilter, setIsChangingFilter] = useState(false);
|
|
41342
41451
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
@@ -41346,6 +41455,21 @@ function HomeView({
|
|
|
41346
41455
|
const entityConfig = useEntityConfig();
|
|
41347
41456
|
const supabaseClient = useSupabaseClient();
|
|
41348
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]);
|
|
41349
41473
|
const userCompanyId = useMemo(() => {
|
|
41350
41474
|
return user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
41351
41475
|
}, [user, entityConfig.companyId]);
|
|
@@ -41406,7 +41530,9 @@ function HomeView({
|
|
|
41406
41530
|
refetch: refetchMetrics
|
|
41407
41531
|
} = useDashboardMetrics({
|
|
41408
41532
|
lineId: selectedLineId,
|
|
41409
|
-
onLineMetricsUpdate
|
|
41533
|
+
onLineMetricsUpdate,
|
|
41534
|
+
userAccessibleLineIds: allLineIds
|
|
41535
|
+
// Pass user's accessible lines for supervisor filtering
|
|
41410
41536
|
});
|
|
41411
41537
|
const {
|
|
41412
41538
|
activeBreaks: allActiveBreaks,
|
|
@@ -43465,7 +43591,8 @@ var LeaderboardDetailView = memo(({
|
|
|
43465
43591
|
line1Id = "",
|
|
43466
43592
|
line2Id = "",
|
|
43467
43593
|
lineNames = {},
|
|
43468
|
-
className = ""
|
|
43594
|
+
className = "",
|
|
43595
|
+
userAccessibleLineIds
|
|
43469
43596
|
}) => {
|
|
43470
43597
|
const navigation = useNavigation();
|
|
43471
43598
|
const entityConfig = useEntityConfig();
|
|
@@ -43505,7 +43632,9 @@ var LeaderboardDetailView = memo(({
|
|
|
43505
43632
|
refreshWorkspaces
|
|
43506
43633
|
} = useAllWorkspaceMetrics({
|
|
43507
43634
|
initialDate: date,
|
|
43508
|
-
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
|
|
43509
43638
|
});
|
|
43510
43639
|
const getShiftName = useCallback((shiftId2) => {
|
|
43511
43640
|
if (shiftId2 === void 0) return "Day";
|
|
@@ -44861,12 +44990,17 @@ var ACTION_NAMES = {
|
|
|
44861
44990
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
44862
44991
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
44863
44992
|
const pph = 3600 / cycleTime;
|
|
44864
|
-
return
|
|
44993
|
+
return Math.round(pph);
|
|
44865
44994
|
};
|
|
44866
44995
|
var calculateDayOutput = (pph, shiftHours, breaks = []) => {
|
|
44867
44996
|
if (pph === "") return "";
|
|
44868
44997
|
return Math.round(pph * shiftHours);
|
|
44869
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
|
+
};
|
|
44870
45004
|
var formatWorkspaceName = (name, lineId) => {
|
|
44871
45005
|
return getWorkspaceDisplayName(name, lineId);
|
|
44872
45006
|
};
|
|
@@ -44878,20 +45012,6 @@ var getStoredLineState2 = (lineId) => {
|
|
|
44878
45012
|
return false;
|
|
44879
45013
|
}
|
|
44880
45014
|
};
|
|
44881
|
-
var calculateShiftHours2 = (startTime, endTime, breaks = []) => {
|
|
44882
|
-
if (!startTime || !endTime) return 8;
|
|
44883
|
-
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
44884
|
-
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
44885
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
44886
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
44887
|
-
if (endMinutes < startMinutes) {
|
|
44888
|
-
endMinutes += 24 * 60;
|
|
44889
|
-
}
|
|
44890
|
-
const safeBreaks = Array.isArray(breaks) ? breaks : [];
|
|
44891
|
-
const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
|
|
44892
|
-
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
44893
|
-
return Number(hoursDiff.toFixed(1));
|
|
44894
|
-
};
|
|
44895
45015
|
var BulkConfigureModal = ({
|
|
44896
45016
|
isOpen,
|
|
44897
45017
|
onClose,
|
|
@@ -46099,121 +46219,6 @@ var TargetsView = ({
|
|
|
46099
46219
|
const dashboardConfig = useDashboardConfig();
|
|
46100
46220
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
46101
46221
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
46102
|
-
const loadOperatingHours = useCallback(async (lineId, shiftId) => {
|
|
46103
|
-
try {
|
|
46104
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
46105
|
-
if (!session?.access_token) {
|
|
46106
|
-
console.error("No authentication token available");
|
|
46107
|
-
return {
|
|
46108
|
-
startTime: "08:00",
|
|
46109
|
-
// Default values
|
|
46110
|
-
endTime: "19:00",
|
|
46111
|
-
breaks: []
|
|
46112
|
-
};
|
|
46113
|
-
}
|
|
46114
|
-
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
46115
|
-
if (!backendUrl) {
|
|
46116
|
-
console.error("Backend URL is not configured");
|
|
46117
|
-
return {
|
|
46118
|
-
startTime: "08:00",
|
|
46119
|
-
// Default values
|
|
46120
|
-
endTime: "19:00",
|
|
46121
|
-
breaks: []
|
|
46122
|
-
};
|
|
46123
|
-
}
|
|
46124
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}/operating-hours?shift_id=${shiftId}`, {
|
|
46125
|
-
headers: {
|
|
46126
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46127
|
-
"Content-Type": "application/json"
|
|
46128
|
-
}
|
|
46129
|
-
});
|
|
46130
|
-
if (!response.ok) {
|
|
46131
|
-
console.log(`No operating hours found for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46132
|
-
return {
|
|
46133
|
-
startTime: "08:00",
|
|
46134
|
-
// Default values
|
|
46135
|
-
endTime: "19:00",
|
|
46136
|
-
breaks: []
|
|
46137
|
-
};
|
|
46138
|
-
}
|
|
46139
|
-
const data = await response.json();
|
|
46140
|
-
let breaks = [];
|
|
46141
|
-
if (data?.breaks) {
|
|
46142
|
-
if (Array.isArray(data.breaks)) {
|
|
46143
|
-
breaks = data.breaks.map((breakItem) => {
|
|
46144
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46145
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46146
|
-
const calculateDuration = (start, end) => {
|
|
46147
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46148
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46149
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46150
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46151
|
-
if (endMinutes < startMinutes) {
|
|
46152
|
-
endMinutes += 24 * 60;
|
|
46153
|
-
}
|
|
46154
|
-
return endMinutes - startMinutes;
|
|
46155
|
-
};
|
|
46156
|
-
return {
|
|
46157
|
-
startTime,
|
|
46158
|
-
endTime,
|
|
46159
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46160
|
-
};
|
|
46161
|
-
});
|
|
46162
|
-
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
46163
|
-
breaks = data.breaks.breaks.map((breakItem) => {
|
|
46164
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46165
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46166
|
-
const calculateDuration = (start, end) => {
|
|
46167
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46168
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46169
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46170
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46171
|
-
if (endMinutes < startMinutes) {
|
|
46172
|
-
endMinutes += 24 * 60;
|
|
46173
|
-
}
|
|
46174
|
-
return endMinutes - startMinutes;
|
|
46175
|
-
};
|
|
46176
|
-
return {
|
|
46177
|
-
startTime,
|
|
46178
|
-
endTime,
|
|
46179
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46180
|
-
};
|
|
46181
|
-
});
|
|
46182
|
-
} else if (typeof data.breaks === "string") {
|
|
46183
|
-
try {
|
|
46184
|
-
const parsedBreaks = JSON.parse(data.breaks);
|
|
46185
|
-
if (Array.isArray(parsedBreaks)) {
|
|
46186
|
-
breaks = parsedBreaks.map((breakItem) => ({
|
|
46187
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46188
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46189
|
-
duration: breakItem.duration || 0
|
|
46190
|
-
}));
|
|
46191
|
-
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
46192
|
-
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
46193
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46194
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46195
|
-
duration: breakItem.duration || 0
|
|
46196
|
-
}));
|
|
46197
|
-
}
|
|
46198
|
-
} catch (e) {
|
|
46199
|
-
console.error("Error parsing breaks data:", e);
|
|
46200
|
-
}
|
|
46201
|
-
}
|
|
46202
|
-
}
|
|
46203
|
-
return {
|
|
46204
|
-
startTime: data?.start_time || "08:00",
|
|
46205
|
-
endTime: data?.end_time || "19:00",
|
|
46206
|
-
breaks
|
|
46207
|
-
};
|
|
46208
|
-
} catch (e) {
|
|
46209
|
-
console.error("Exception when loading operating hours:", e);
|
|
46210
|
-
return {
|
|
46211
|
-
startTime: "08:00",
|
|
46212
|
-
endTime: "19:00",
|
|
46213
|
-
breaks: []
|
|
46214
|
-
};
|
|
46215
|
-
}
|
|
46216
|
-
}, [supabase]);
|
|
46217
46222
|
useEffect(() => {
|
|
46218
46223
|
console.log("[TargetsView] Component mounted/re-rendered", {
|
|
46219
46224
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -46225,221 +46230,108 @@ var TargetsView = ({
|
|
|
46225
46230
|
};
|
|
46226
46231
|
}, []);
|
|
46227
46232
|
useEffect(() => {
|
|
46228
|
-
let timeoutId;
|
|
46229
|
-
let retryCount = 0;
|
|
46230
|
-
const MAX_RETRIES2 = 2;
|
|
46231
|
-
const LOADING_TIMEOUT = 15e3;
|
|
46232
46233
|
const fetchInitialData = async () => {
|
|
46233
|
-
if (lineIds.length === 0) return;
|
|
46234
|
-
console.log("[TargetsView] Starting fetchInitialData");
|
|
46234
|
+
if (lineIds.length === 0 || !companyId) return;
|
|
46235
|
+
console.log("[TargetsView] Starting optimized fetchInitialData with bulk endpoint");
|
|
46235
46236
|
setIsLoading(true);
|
|
46236
|
-
timeoutId = setTimeout(() => {
|
|
46237
|
-
console.error("Loading timeout reached");
|
|
46238
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46239
|
-
retryCount++;
|
|
46240
|
-
console.log(`Retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46241
|
-
toast.warning("Loading is taking longer than expected. Retrying...");
|
|
46242
|
-
fetchInitialData();
|
|
46243
|
-
} else {
|
|
46244
|
-
setIsLoading(false);
|
|
46245
|
-
toast.error("Failed to load data. Please refresh the page.");
|
|
46246
|
-
}
|
|
46247
|
-
}, LOADING_TIMEOUT);
|
|
46248
46237
|
try {
|
|
46249
|
-
const
|
|
46250
|
-
|
|
46251
|
-
|
|
46252
|
-
|
|
46253
|
-
|
|
46254
|
-
|
|
46255
|
-
|
|
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");
|
|
46256
46250
|
}
|
|
46257
|
-
const
|
|
46258
|
-
|
|
46259
|
-
|
|
46260
|
-
try {
|
|
46261
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}`, {
|
|
46262
|
-
headers: {
|
|
46263
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46264
|
-
"Content-Type": "application/json"
|
|
46265
|
-
}
|
|
46266
|
-
});
|
|
46267
|
-
if (!response.ok) {
|
|
46268
|
-
console.error(`Error fetching line data for ${lineId}:`, response.statusText);
|
|
46269
|
-
return { lineId, factoryId: void 0 };
|
|
46270
|
-
}
|
|
46271
|
-
const lineData = await response.json();
|
|
46272
|
-
return { lineId, factoryId: lineData.factory_id };
|
|
46273
|
-
} catch (err) {
|
|
46274
|
-
console.error(`Exception fetching line data for ${lineId}:`, err);
|
|
46275
|
-
return { lineId, factoryId: void 0 };
|
|
46276
|
-
}
|
|
46277
|
-
})),
|
|
46278
|
-
// Fetch action IDs
|
|
46279
|
-
actionService.getActionsByName(
|
|
46280
|
-
[ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
|
|
46281
|
-
companyId
|
|
46282
|
-
)
|
|
46283
|
-
]);
|
|
46284
|
-
const factoryResults = linesResponse;
|
|
46285
|
-
const assemblyAction = actions.find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46286
|
-
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);
|
|
46287
46254
|
if (!assemblyAction || !packagingAction) {
|
|
46288
|
-
throw new Error("Could not find required actions");
|
|
46255
|
+
throw new Error("Could not find required actions in bulk response");
|
|
46289
46256
|
}
|
|
46290
46257
|
const actionIdsData = {
|
|
46291
46258
|
assembly: assemblyAction.id,
|
|
46292
46259
|
packaging: packagingAction.id
|
|
46293
46260
|
};
|
|
46294
46261
|
setActionIds(actionIdsData);
|
|
46295
|
-
const
|
|
46296
|
-
|
|
46297
|
-
|
|
46298
|
-
|
|
46299
|
-
|
|
46300
|
-
|
|
46301
|
-
|
|
46302
|
-
|
|
46303
|
-
if (!updatedLineWorkspaces[lineId]?.factoryId) {
|
|
46304
|
-
console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
|
|
46305
|
-
continue;
|
|
46306
|
-
}
|
|
46307
|
-
try {
|
|
46308
|
-
const workspacesData = await workspaceService.getWorkspaces(lineId);
|
|
46309
|
-
const enabledWorkspaces = workspacesData.filter((ws) => ws.enable === true);
|
|
46310
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46311
|
-
lineId,
|
|
46312
|
-
currentDate,
|
|
46313
|
-
0
|
|
46314
|
-
// Always use day shift for initial load
|
|
46315
|
-
);
|
|
46316
|
-
const operatingHoursData = await loadOperatingHours(lineId, 0);
|
|
46317
|
-
if (operatingHoursData) {
|
|
46318
|
-
updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
|
|
46319
|
-
updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
|
|
46320
|
-
updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
|
|
46321
|
-
updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
|
|
46322
|
-
operatingHoursData.startTime,
|
|
46323
|
-
operatingHoursData.endTime,
|
|
46324
|
-
operatingHoursData.breaks
|
|
46325
|
-
);
|
|
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;
|
|
46326
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);
|
|
46327
46288
|
const mappedWorkspaces = enabledWorkspaces.map((ws) => {
|
|
46328
|
-
const threshold =
|
|
46329
|
-
if (!dbValues[0][lineId]) {
|
|
46330
|
-
dbValues[0][lineId] = {};
|
|
46331
|
-
}
|
|
46289
|
+
const threshold = shiftData.thresholds.find((t) => t.workspace_id === ws.id);
|
|
46332
46290
|
if (threshold) {
|
|
46333
|
-
|
|
46334
|
-
targetPPH: threshold.pph_threshold,
|
|
46335
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46336
|
-
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 ?? ""
|
|
46337
46295
|
};
|
|
46338
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
|
+
}
|
|
46339
46306
|
return {
|
|
46340
46307
|
id: ws.id,
|
|
46341
46308
|
name: ws.workspace_id,
|
|
46342
|
-
targetPPH: threshold?.pph_threshold
|
|
46343
|
-
targetCycleTime: threshold?.ideal_cycle_time ??
|
|
46344
|
-
targetDayOutput: threshold?.total_day_output ??
|
|
46345
|
-
actionType
|
|
46346
|
-
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
|
|
46347
46314
|
};
|
|
46348
46315
|
}).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
46349
|
-
|
|
46350
|
-
}
|
|
46351
|
-
|
|
46352
|
-
|
|
46353
|
-
|
|
46354
|
-
|
|
46355
|
-
|
|
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
|
+
});
|
|
46356
46326
|
} catch (error) {
|
|
46357
|
-
console.error("Error fetching
|
|
46358
|
-
|
|
46359
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46360
|
-
retryCount++;
|
|
46361
|
-
console.log(`Error occurred, retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46362
|
-
toast.warning("Error loading data. Retrying...");
|
|
46363
|
-
setTimeout(() => fetchInitialData(), 1e3);
|
|
46364
|
-
} else {
|
|
46365
|
-
toast.error("Failed to load initial data");
|
|
46366
|
-
setIsLoading(false);
|
|
46367
|
-
}
|
|
46327
|
+
console.error("[TargetsView] Error fetching bulk data:", error);
|
|
46328
|
+
toast.error("Failed to load targets data. Please refresh the page.");
|
|
46368
46329
|
} finally {
|
|
46369
|
-
|
|
46370
|
-
if (retryCount === 0 || retryCount >= MAX_RETRIES2) {
|
|
46371
|
-
setIsLoading(false);
|
|
46372
|
-
}
|
|
46330
|
+
setIsLoading(false);
|
|
46373
46331
|
}
|
|
46374
46332
|
};
|
|
46375
46333
|
fetchInitialData();
|
|
46376
|
-
|
|
46377
|
-
clearTimeout(timeoutId);
|
|
46378
|
-
};
|
|
46379
|
-
}, [lineIds, companyId, loadOperatingHours]);
|
|
46380
|
-
const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
|
|
46381
|
-
if (!supabase) return;
|
|
46382
|
-
const currentDate = getOperationalDate(timezone);
|
|
46383
|
-
const newAllShiftsData = {
|
|
46384
|
-
0: JSON.parse(JSON.stringify(currentWorkspaces)),
|
|
46385
|
-
// Deep clone for day shift
|
|
46386
|
-
1: JSON.parse(JSON.stringify(currentWorkspaces))
|
|
46387
|
-
// Deep clone for night shift
|
|
46388
|
-
};
|
|
46389
|
-
const newDbValues = { 0: {}, 1: {} };
|
|
46390
|
-
for (const shiftId of [0, 1]) {
|
|
46391
|
-
for (const lineId of lineIds) {
|
|
46392
|
-
try {
|
|
46393
|
-
const operatingHoursData = await loadOperatingHours(lineId, shiftId);
|
|
46394
|
-
if (!operatingHoursData) {
|
|
46395
|
-
console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46396
|
-
continue;
|
|
46397
|
-
}
|
|
46398
|
-
const { startTime, endTime, breaks } = operatingHoursData;
|
|
46399
|
-
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
46400
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46401
|
-
lineId,
|
|
46402
|
-
currentDate,
|
|
46403
|
-
shiftId
|
|
46404
|
-
);
|
|
46405
|
-
if (!newDbValues[shiftId][lineId]) {
|
|
46406
|
-
newDbValues[shiftId][lineId] = {};
|
|
46407
|
-
}
|
|
46408
|
-
const existingLine = newAllShiftsData[shiftId][lineId];
|
|
46409
|
-
if (existingLine) {
|
|
46410
|
-
newAllShiftsData[shiftId][lineId] = {
|
|
46411
|
-
...existingLine,
|
|
46412
|
-
shiftStartTime: startTime,
|
|
46413
|
-
shiftEndTime: endTime,
|
|
46414
|
-
breaks,
|
|
46415
|
-
shiftHours: Number(shiftHours),
|
|
46416
|
-
workspaces: existingLine.workspaces.map((ws) => {
|
|
46417
|
-
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
46418
|
-
if (threshold) {
|
|
46419
|
-
newDbValues[shiftId][lineId][ws.id] = {
|
|
46420
|
-
targetPPH: threshold.pph_threshold,
|
|
46421
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46422
|
-
targetDayOutput: threshold.total_day_output
|
|
46423
|
-
};
|
|
46424
|
-
return {
|
|
46425
|
-
...ws,
|
|
46426
|
-
targetPPH: threshold.pph_threshold,
|
|
46427
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46428
|
-
targetDayOutput: threshold.total_day_output
|
|
46429
|
-
};
|
|
46430
|
-
}
|
|
46431
|
-
return ws;
|
|
46432
|
-
})
|
|
46433
|
-
};
|
|
46434
|
-
}
|
|
46435
|
-
} catch (error) {
|
|
46436
|
-
console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
|
|
46437
|
-
}
|
|
46438
|
-
}
|
|
46439
|
-
}
|
|
46440
|
-
setAllShiftsData(newAllShiftsData);
|
|
46441
|
-
setDbValues(newDbValues);
|
|
46442
|
-
}, [supabase, lineIds, loadOperatingHours]);
|
|
46334
|
+
}, [lineIds, companyId, timezone, skuEnabled]);
|
|
46443
46335
|
const toggleLineDropdown = useCallback((lineId) => {
|
|
46444
46336
|
setDropdownStates((prev) => {
|
|
46445
46337
|
const newIsOpen = !prev[lineId];
|
|
@@ -46512,7 +46404,7 @@ var TargetsView = ({
|
|
|
46512
46404
|
if (value !== "") {
|
|
46513
46405
|
const pph = calculatePPH(value, prev[lineId].breaks, shiftHours);
|
|
46514
46406
|
updates.targetPPH = pph;
|
|
46515
|
-
updates.targetDayOutput =
|
|
46407
|
+
updates.targetDayOutput = calculateDayOutputFromCycleTime(value, shiftHours);
|
|
46516
46408
|
} else {
|
|
46517
46409
|
updates.targetPPH = "";
|
|
46518
46410
|
updates.targetDayOutput = "";
|
|
@@ -46630,7 +46522,8 @@ var TargetsView = ({
|
|
|
46630
46522
|
action_id: ws.actionId,
|
|
46631
46523
|
workspace_id: ws.id,
|
|
46632
46524
|
date: currentDate,
|
|
46633
|
-
pph_threshold: Number(ws.targetPPH)
|
|
46525
|
+
pph_threshold: ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0,
|
|
46526
|
+
// Round to whole number
|
|
46634
46527
|
ideal_cycle_time: Number(ws.targetCycleTime) || 0,
|
|
46635
46528
|
total_day_output: Number(ws.targetDayOutput) || 0,
|
|
46636
46529
|
action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
|
|
@@ -46649,7 +46542,8 @@ var TargetsView = ({
|
|
|
46649
46542
|
shift_id: selectedShift,
|
|
46650
46543
|
product_code: lineDataToSave.productId,
|
|
46651
46544
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
46652
|
-
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
|
|
46653
46547
|
...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
|
|
46654
46548
|
};
|
|
46655
46549
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
@@ -46908,6 +46802,7 @@ var WorkspaceDetailView = ({
|
|
|
46908
46802
|
const [showIdleTime, setShowIdleTime] = useState(false);
|
|
46909
46803
|
const dashboardConfig = useDashboardConfig();
|
|
46910
46804
|
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
46805
|
+
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
46911
46806
|
const {
|
|
46912
46807
|
workspace: workspaceHealth,
|
|
46913
46808
|
loading: healthLoading,
|
|
@@ -46916,6 +46811,36 @@ var WorkspaceDetailView = ({
|
|
|
46916
46811
|
enableRealtime: true,
|
|
46917
46812
|
refreshInterval: 3e4
|
|
46918
46813
|
});
|
|
46814
|
+
const {
|
|
46815
|
+
isHealthy: isWorkspaceHealthy,
|
|
46816
|
+
timeSinceUpdate,
|
|
46817
|
+
lastHeartbeat,
|
|
46818
|
+
loading: healthStatusLoading,
|
|
46819
|
+
error: healthStatusError
|
|
46820
|
+
} = useWorkspaceHealthStatus(workspaceId);
|
|
46821
|
+
const isLive = useMemo(() => {
|
|
46822
|
+
if (!lastHeartbeat) return false;
|
|
46823
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
46824
|
+
const heartbeat = new Date(lastHeartbeat);
|
|
46825
|
+
const minutesSince = (now2.getTime() - heartbeat.getTime()) / 6e4;
|
|
46826
|
+
return minutesSince < 5;
|
|
46827
|
+
}, [lastHeartbeat]);
|
|
46828
|
+
useEffect(() => {
|
|
46829
|
+
console.log("[WorkspaceDetailView] Workspace Health Status:", {
|
|
46830
|
+
workspaceId,
|
|
46831
|
+
// Old workspace_health table
|
|
46832
|
+
oldStatus: workspaceHealth?.status,
|
|
46833
|
+
oldLastHeartbeat: workspaceHealth?.last_heartbeat,
|
|
46834
|
+
oldTimeSinceLastUpdate: workspaceHealth?.timeSinceLastUpdate,
|
|
46835
|
+
// New workspace_health_status table
|
|
46836
|
+
isHealthy: isWorkspaceHealthy,
|
|
46837
|
+
isLive,
|
|
46838
|
+
lastHeartbeat,
|
|
46839
|
+
timeSinceUpdate,
|
|
46840
|
+
loading: healthLoading || healthStatusLoading,
|
|
46841
|
+
error: healthError || healthStatusError
|
|
46842
|
+
});
|
|
46843
|
+
}, [workspaceId, workspaceHealth, isWorkspaceHealthy, isLive, lastHeartbeat, timeSinceUpdate, healthLoading, healthStatusLoading, healthError, healthStatusError]);
|
|
46919
46844
|
const {
|
|
46920
46845
|
status: prefetchStatus,
|
|
46921
46846
|
data: prefetchData,
|
|
@@ -46952,6 +46877,7 @@ var WorkspaceDetailView = ({
|
|
|
46952
46877
|
const workspace = isHistoricView ? historicMetrics : liveMetrics;
|
|
46953
46878
|
const loading = isHistoricView ? historicLoading : liveLoading;
|
|
46954
46879
|
const error = isHistoricView ? historicError : liveError;
|
|
46880
|
+
const { supervisorName } = useLineSupervisor(workspace?.line_id || lineId);
|
|
46955
46881
|
useEffect(() => {
|
|
46956
46882
|
if (onTabChange) {
|
|
46957
46883
|
onTabChange(activeTab);
|
|
@@ -47174,7 +47100,7 @@ var WorkspaceDetailView = ({
|
|
|
47174
47100
|
initial: { opacity: 1 },
|
|
47175
47101
|
animate: { opacity: 1 },
|
|
47176
47102
|
children: /* @__PURE__ */ jsxs("div", { className: "min-h-screen w-full flex flex-col bg-slate-50", children: [
|
|
47177
|
-
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-
|
|
47103
|
+
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-3 sm:py-3 lg:py-3.5 flex flex-col shadow-sm bg-white", children: [
|
|
47178
47104
|
/* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
47179
47105
|
/* @__PURE__ */ jsx(
|
|
47180
47106
|
"button",
|
|
@@ -47188,15 +47114,15 @@ var WorkspaceDetailView = ({
|
|
|
47188
47114
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
|
|
47189
47115
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
47190
47116
|
/* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900 truncate max-w-[220px]", children: formattedWorkspaceName }),
|
|
47191
|
-
|
|
47192
|
-
|
|
47117
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
|
|
47118
|
+
isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47193
47119
|
/* @__PURE__ */ jsx("span", { className: clsx(
|
|
47194
47120
|
"relative inline-flex rounded-full h-2 w-2",
|
|
47195
|
-
|
|
47121
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47196
47122
|
) })
|
|
47197
47123
|
] }) })
|
|
47198
47124
|
] }),
|
|
47199
|
-
activeTab !== "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
|
|
47125
|
+
activeTab !== "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5 mt-1", children: [
|
|
47200
47126
|
workspaceHealth && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-500", children: workspaceHealth.timeSinceLastUpdate }),
|
|
47201
47127
|
/* @__PURE__ */ jsx(
|
|
47202
47128
|
WorkspaceHealthStatusBadge,
|
|
@@ -47223,14 +47149,11 @@ var WorkspaceDetailView = ({
|
|
|
47223
47149
|
) }),
|
|
47224
47150
|
/* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
47225
47151
|
/* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
|
|
47226
|
-
|
|
47227
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
47228
|
-
"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
|
|
47229
|
-
workspaceHealth.status === "healthy" ? "bg-green-400" : "bg-red-400"
|
|
47230
|
-
) }),
|
|
47152
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
|
|
47153
|
+
isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47231
47154
|
/* @__PURE__ */ jsx("span", { className: clsx(
|
|
47232
47155
|
"relative inline-flex rounded-full h-2.5 w-2.5",
|
|
47233
|
-
|
|
47156
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47234
47157
|
) })
|
|
47235
47158
|
] })
|
|
47236
47159
|
] }) }),
|
|
@@ -48372,94 +48295,14 @@ var SupervisorManagementView = ({
|
|
|
48372
48295
|
] });
|
|
48373
48296
|
};
|
|
48374
48297
|
var SupervisorManagementView_default = SupervisorManagementView;
|
|
48375
|
-
var
|
|
48376
|
-
|
|
48377
|
-
|
|
48378
|
-
|
|
48379
|
-
|
|
48380
|
-
|
|
48381
|
-
|
|
48382
|
-
|
|
48383
|
-
],
|
|
48384
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48385
|
-
},
|
|
48386
|
-
supervisor_to_owner: {
|
|
48387
|
-
title: "Supervisor \u2192 Owner",
|
|
48388
|
-
warnings: [
|
|
48389
|
-
"Will gain full company access",
|
|
48390
|
-
"Will lose line-specific restrictions",
|
|
48391
|
-
"Will be able to manage all users",
|
|
48392
|
-
"Will see all company data"
|
|
48393
|
-
],
|
|
48394
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48395
|
-
},
|
|
48396
|
-
plant_head_to_supervisor: {
|
|
48397
|
-
title: "Plant Head \u2192 Supervisor",
|
|
48398
|
-
warnings: [
|
|
48399
|
-
"Will lose factory access",
|
|
48400
|
-
"Will lose ability to manage others",
|
|
48401
|
-
"Will need line assignment",
|
|
48402
|
-
"Will only see assigned line data"
|
|
48403
|
-
],
|
|
48404
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48405
|
-
},
|
|
48406
|
-
plant_head_to_owner: {
|
|
48407
|
-
title: "Plant Head \u2192 Owner",
|
|
48408
|
-
warnings: [
|
|
48409
|
-
"Will gain full company access",
|
|
48410
|
-
"Will lose factory-specific restrictions",
|
|
48411
|
-
"Will be able to manage all users",
|
|
48412
|
-
"Will see all company data"
|
|
48413
|
-
],
|
|
48414
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48415
|
-
},
|
|
48416
|
-
owner_to_plant_head: {
|
|
48417
|
-
title: "Owner \u2192 Plant Head",
|
|
48418
|
-
warnings: [
|
|
48419
|
-
"Will lose company-wide access",
|
|
48420
|
-
"Will need factory assignment",
|
|
48421
|
-
"Will only manage supervisors",
|
|
48422
|
-
"Will only see factory data"
|
|
48423
|
-
],
|
|
48424
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48425
|
-
},
|
|
48426
|
-
owner_to_supervisor: {
|
|
48427
|
-
title: "Owner \u2192 Supervisor",
|
|
48428
|
-
warnings: [
|
|
48429
|
-
"Will lose company-wide access",
|
|
48430
|
-
"Will lose ability to manage others",
|
|
48431
|
-
"Will need line assignment",
|
|
48432
|
-
"Will only see assigned line data"
|
|
48433
|
-
],
|
|
48434
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48435
|
-
},
|
|
48436
|
-
to_optifye: {
|
|
48437
|
-
title: "Promoting to Optifye",
|
|
48438
|
-
warnings: [
|
|
48439
|
-
"Will gain access to ALL companies",
|
|
48440
|
-
"Will be able to manage all users globally",
|
|
48441
|
-
"Will see all data across the platform",
|
|
48442
|
-
"This is the highest privilege level"
|
|
48443
|
-
],
|
|
48444
|
-
colorClass: "text-purple-700 bg-purple-50 border-purple-200"
|
|
48445
|
-
},
|
|
48446
|
-
from_optifye: {
|
|
48447
|
-
title: "Demoting from Optifye",
|
|
48448
|
-
warnings: [
|
|
48449
|
-
"Will lose access to other companies",
|
|
48450
|
-
"Will only see assigned company data",
|
|
48451
|
-
"Will lose global management capabilities",
|
|
48452
|
-
"Will need appropriate assignments"
|
|
48453
|
-
],
|
|
48454
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48455
|
-
}
|
|
48456
|
-
};
|
|
48457
|
-
var getRoleChangeKey = (currentRole, newRole) => {
|
|
48458
|
-
if (currentRole === newRole) return null;
|
|
48459
|
-
if (newRole === "optifye") return "to_optifye";
|
|
48460
|
-
if (currentRole === "optifye") return "from_optifye";
|
|
48461
|
-
const key = `${currentRole}_to_${newRole}`;
|
|
48462
|
-
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;
|
|
48463
48306
|
};
|
|
48464
48307
|
var ChangeRoleDialog = ({
|
|
48465
48308
|
user,
|
|
@@ -48472,8 +48315,6 @@ var ChangeRoleDialog = ({
|
|
|
48472
48315
|
const [confirmed, setConfirmed] = useState(false);
|
|
48473
48316
|
const [isChanging, setIsChanging] = useState(false);
|
|
48474
48317
|
const roleChanged = selectedRole !== user.role_level;
|
|
48475
|
-
const changeKey = roleChanged ? getRoleChangeKey(user.role_level, selectedRole) : null;
|
|
48476
|
-
const impactInfo = changeKey ? roleChangeImpacts[changeKey] : null;
|
|
48477
48318
|
const handleNext = () => {
|
|
48478
48319
|
if (roleChanged) {
|
|
48479
48320
|
setStep("confirm");
|
|
@@ -48493,7 +48334,7 @@ var ChangeRoleDialog = ({
|
|
|
48493
48334
|
setStep("select");
|
|
48494
48335
|
setConfirmed(false);
|
|
48495
48336
|
};
|
|
48496
|
-
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: [
|
|
48497
48338
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48498
48339
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48499
48340
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(UserCog, { className: "w-5 h-5 text-blue-600" }) }),
|
|
@@ -48503,20 +48344,14 @@ var ChangeRoleDialog = ({
|
|
|
48503
48344
|
"button",
|
|
48504
48345
|
{
|
|
48505
48346
|
onClick: onClose,
|
|
48506
|
-
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",
|
|
48507
48348
|
disabled: isChanging,
|
|
48349
|
+
"aria-label": "Close dialog",
|
|
48508
48350
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48509
48351
|
}
|
|
48510
48352
|
)
|
|
48511
48353
|
] }),
|
|
48512
|
-
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-
|
|
48513
|
-
roleChanged && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4 bg-amber-50 border border-amber-200 rounded-lg", children: [
|
|
48514
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" }),
|
|
48515
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
48516
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-amber-900 text-sm", children: "Changing roles affects access" }),
|
|
48517
|
-
/* @__PURE__ */ jsx("p", { className: "text-amber-700 text-sm mt-1", children: "Review the impact carefully before confirming." })
|
|
48518
|
-
] })
|
|
48519
|
-
] }),
|
|
48354
|
+
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-5", children: step === "select" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
48520
48355
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48521
48356
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48522
48357
|
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-gray-50 rounded-lg", children: [
|
|
@@ -48527,7 +48362,7 @@ var ChangeRoleDialog = ({
|
|
|
48527
48362
|
] })
|
|
48528
48363
|
] })
|
|
48529
48364
|
] }),
|
|
48530
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48365
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48531
48366
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Select new role" }),
|
|
48532
48367
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: availableRoles.map((role) => /* @__PURE__ */ jsxs(
|
|
48533
48368
|
"label",
|
|
@@ -48551,27 +48386,24 @@ var ChangeRoleDialog = ({
|
|
|
48551
48386
|
role
|
|
48552
48387
|
)) })
|
|
48553
48388
|
] }),
|
|
48554
|
-
|
|
48555
|
-
|
|
48556
|
-
/* @__PURE__ */ jsx("
|
|
48557
|
-
|
|
48558
|
-
|
|
48559
|
-
|
|
48560
|
-
] })
|
|
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
|
+
] }) })
|
|
48561
48396
|
] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
48562
48397
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-3 p-4 bg-gray-50 rounded-lg", children: [
|
|
48563
48398
|
/* @__PURE__ */ jsx(RoleBadge, { role: user.role_level }),
|
|
48564
48399
|
/* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5 text-gray-400" }),
|
|
48565
48400
|
/* @__PURE__ */ jsx(RoleBadge, { role: selectedRole })
|
|
48566
48401
|
] }),
|
|
48567
|
-
|
|
48568
|
-
/* @__PURE__ */ jsx("p", { className: "
|
|
48569
|
-
/* @__PURE__ */ jsx("
|
|
48570
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5", children: "\u2022" }),
|
|
48571
|
-
/* @__PURE__ */ jsx("span", { children: warning6 })
|
|
48572
|
-
] }, 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 })
|
|
48573
48405
|
] }),
|
|
48574
|
-
/* @__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: [
|
|
48575
48407
|
/* @__PURE__ */ jsx(
|
|
48576
48408
|
"input",
|
|
48577
48409
|
{
|
|
@@ -48582,7 +48414,7 @@ var ChangeRoleDialog = ({
|
|
|
48582
48414
|
className: "mt-0.5 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
48583
48415
|
}
|
|
48584
48416
|
),
|
|
48585
|
-
/* @__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" })
|
|
48586
48418
|
] })
|
|
48587
48419
|
] }) }) }),
|
|
48588
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: [
|
|
@@ -48625,7 +48457,7 @@ var ChangeRoleDialog = ({
|
|
|
48625
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" })
|
|
48626
48458
|
] }),
|
|
48627
48459
|
"Changing..."
|
|
48628
|
-
] }) : "Change
|
|
48460
|
+
] }) : "Confirm Change"
|
|
48629
48461
|
}
|
|
48630
48462
|
)
|
|
48631
48463
|
] }) })
|
|
@@ -48648,32 +48480,34 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48648
48480
|
setIsRemoving(false);
|
|
48649
48481
|
}
|
|
48650
48482
|
};
|
|
48651
|
-
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: [
|
|
48652
48484
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48653
48485
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48654
48486
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-red-100 rounded-lg", children: /* @__PURE__ */ jsx(Trash2, { className: "w-5 h-5 text-red-600" }) }),
|
|
48655
|
-
/* @__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" })
|
|
48656
48488
|
] }),
|
|
48657
48489
|
/* @__PURE__ */ jsx(
|
|
48658
48490
|
"button",
|
|
48659
48491
|
{
|
|
48660
48492
|
onClick: onCancel,
|
|
48661
|
-
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",
|
|
48662
48496
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48663
48497
|
}
|
|
48664
48498
|
)
|
|
48665
48499
|
] }),
|
|
48666
|
-
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-
|
|
48667
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-
|
|
48668
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-
|
|
48669
|
-
/* @__PURE__ */ jsxs("
|
|
48670
|
-
/* @__PURE__ */ jsx("
|
|
48671
|
-
|
|
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."
|
|
48672
48506
|
] })
|
|
48673
48507
|
] }),
|
|
48674
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48675
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to
|
|
48676
|
-
/* @__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: [
|
|
48677
48511
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
48678
48512
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: "Email" }),
|
|
48679
48513
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
@@ -48698,28 +48532,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48698
48532
|
] })
|
|
48699
48533
|
] })
|
|
48700
48534
|
] }),
|
|
48701
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
48702
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "This will:" }),
|
|
48703
|
-
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm text-gray-600", children: [
|
|
48704
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48705
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48706
|
-
/* @__PURE__ */ jsx("span", { children: "Revoke all access immediately" })
|
|
48707
|
-
] }),
|
|
48708
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48709
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48710
|
-
/* @__PURE__ */ jsx("span", { children: "Remove all line and factory assignments" })
|
|
48711
|
-
] }),
|
|
48712
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48713
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48714
|
-
/* @__PURE__ */ jsx("span", { children: "Log the user out of all sessions" })
|
|
48715
|
-
] }),
|
|
48716
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48717
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48718
|
-
/* @__PURE__ */ jsx("span", { children: "User will not be able to access the dashboard" })
|
|
48719
|
-
] })
|
|
48720
|
-
] })
|
|
48721
|
-
] }),
|
|
48722
|
-
/* @__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: [
|
|
48723
48536
|
/* @__PURE__ */ jsx(
|
|
48724
48537
|
"input",
|
|
48725
48538
|
{
|
|
@@ -48730,7 +48543,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48730
48543
|
className: "mt-0.5 h-4 w-4 text-red-600 border-gray-300 rounded focus:ring-red-500"
|
|
48731
48544
|
}
|
|
48732
48545
|
),
|
|
48733
|
-
/* @__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" })
|
|
48734
48547
|
] })
|
|
48735
48548
|
] }),
|
|
48736
48549
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: [
|
|
@@ -48754,8 +48567,8 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48754
48567
|
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
48755
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" })
|
|
48756
48569
|
] }),
|
|
48757
|
-
"
|
|
48758
|
-
] }) : "
|
|
48570
|
+
"Deleting..."
|
|
48571
|
+
] }) : "Delete User"
|
|
48759
48572
|
}
|
|
48760
48573
|
)
|
|
48761
48574
|
] })
|
|
@@ -49178,7 +48991,6 @@ var FactoryAssignmentDropdown = ({
|
|
|
49178
48991
|
var UserManagementTable = ({
|
|
49179
48992
|
users,
|
|
49180
48993
|
isLoading = false,
|
|
49181
|
-
onUserClick,
|
|
49182
48994
|
onRoleChange,
|
|
49183
48995
|
onRemoveUser,
|
|
49184
48996
|
onLineAssignmentUpdate,
|
|
@@ -49204,9 +49016,48 @@ var UserManagementTable = ({
|
|
|
49204
49016
|
const [sortField, setSortField] = useState("email");
|
|
49205
49017
|
const [sortDirection, setSortDirection] = useState("asc");
|
|
49206
49018
|
const [openActionMenuId, setOpenActionMenuId] = useState(null);
|
|
49019
|
+
const [dropdownPosition, setDropdownPosition] = useState(null);
|
|
49020
|
+
const actionButtonRefs = useRef({});
|
|
49207
49021
|
const [selectedUser, setSelectedUser] = useState(null);
|
|
49208
49022
|
const [showChangeRoleDialog, setShowChangeRoleDialog] = useState(false);
|
|
49209
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]);
|
|
49210
49061
|
const filteredAndSortedUsers = useMemo(() => {
|
|
49211
49062
|
let filtered = users;
|
|
49212
49063
|
filtered = filtered.filter((user) => availableRoles.includes(user.role_level));
|
|
@@ -49397,7 +49248,7 @@ var UserManagementTable = ({
|
|
|
49397
49248
|
LineAssignmentDropdown,
|
|
49398
49249
|
{
|
|
49399
49250
|
userId: user.user_id,
|
|
49400
|
-
currentLineIds: user.properties?.
|
|
49251
|
+
currentLineIds: user.properties?.line_ids || [],
|
|
49401
49252
|
availableLines: (
|
|
49402
49253
|
// Filter lines to only show those from the target user's company
|
|
49403
49254
|
(() => {
|
|
@@ -49435,69 +49286,24 @@ var UserManagementTable = ({
|
|
|
49435
49286
|
}
|
|
49436
49287
|
) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
49437
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) }) }),
|
|
49438
|
-
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */
|
|
49439
|
-
|
|
49440
|
-
|
|
49441
|
-
{
|
|
49442
|
-
|
|
49443
|
-
|
|
49444
|
-
|
|
49445
|
-
|
|
49446
|
-
|
|
49447
|
-
|
|
49448
|
-
|
|
49449
|
-
/* @__PURE__ */ jsx(
|
|
49450
|
-
"div",
|
|
49451
|
-
{
|
|
49452
|
-
className: "fixed inset-0 z-[9998]",
|
|
49453
|
-
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);
|
|
49454
49300
|
}
|
|
49455
|
-
|
|
49456
|
-
|
|
49457
|
-
|
|
49458
|
-
|
|
49459
|
-
|
|
49460
|
-
|
|
49461
|
-
onUserClick?.(user);
|
|
49462
|
-
setOpenActionMenuId(null);
|
|
49463
|
-
},
|
|
49464
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49465
|
-
children: [
|
|
49466
|
-
/* @__PURE__ */ jsx(Eye, { className: "w-4 h-4" }),
|
|
49467
|
-
"View Details"
|
|
49468
|
-
]
|
|
49469
|
-
}
|
|
49470
|
-
),
|
|
49471
|
-
canChangeRole && /* @__PURE__ */ jsxs(
|
|
49472
|
-
"button",
|
|
49473
|
-
{
|
|
49474
|
-
onClick: () => handleChangeRole(user),
|
|
49475
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49476
|
-
disabled: isCurrentUser,
|
|
49477
|
-
children: [
|
|
49478
|
-
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
49479
|
-
"Change Role"
|
|
49480
|
-
]
|
|
49481
|
-
}
|
|
49482
|
-
),
|
|
49483
|
-
canRemove && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49484
|
-
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49485
|
-
/* @__PURE__ */ jsxs(
|
|
49486
|
-
"button",
|
|
49487
|
-
{
|
|
49488
|
-
onClick: () => handleRemoveUser(user),
|
|
49489
|
-
className: "w-full px-4 py-2 text-sm text-left text-red-600 hover:bg-red-50 flex items-center gap-2",
|
|
49490
|
-
disabled: isCurrentUser,
|
|
49491
|
-
children: [
|
|
49492
|
-
/* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
|
|
49493
|
-
"Remove User"
|
|
49494
|
-
]
|
|
49495
|
-
}
|
|
49496
|
-
)
|
|
49497
|
-
] })
|
|
49498
|
-
] }) })
|
|
49499
|
-
] })
|
|
49500
|
-
] }) })
|
|
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
|
+
) }) })
|
|
49501
49307
|
]
|
|
49502
49308
|
},
|
|
49503
49309
|
user.user_id
|
|
@@ -49505,6 +49311,75 @@ var UserManagementTable = ({
|
|
|
49505
49311
|
}) })
|
|
49506
49312
|
] }) }) })
|
|
49507
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
|
+
),
|
|
49508
49383
|
showChangeRoleDialog && selectedUser && /* @__PURE__ */ jsx(
|
|
49509
49384
|
ChangeRoleDialog,
|
|
49510
49385
|
{
|
|
@@ -49618,6 +49493,34 @@ var InviteUserDialog = ({
|
|
|
49618
49493
|
throw new Error(data.error);
|
|
49619
49494
|
}
|
|
49620
49495
|
toast.success(data?.message || "User added successfully!");
|
|
49496
|
+
try {
|
|
49497
|
+
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
49498
|
+
if (dashboardUrl) {
|
|
49499
|
+
console.log("Sending welcome email to:", email.trim());
|
|
49500
|
+
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
49501
|
+
body: {
|
|
49502
|
+
action: "send-welcome-email",
|
|
49503
|
+
email: email.trim(),
|
|
49504
|
+
company_id: companyId,
|
|
49505
|
+
dashboard_url: dashboardUrl
|
|
49506
|
+
}
|
|
49507
|
+
});
|
|
49508
|
+
if (emailError) {
|
|
49509
|
+
console.error("Failed to send welcome email:", emailError);
|
|
49510
|
+
toast.warning("User added successfully, but welcome email could not be sent");
|
|
49511
|
+
} else if (emailData?.success) {
|
|
49512
|
+
console.log("Welcome email sent successfully:", emailData);
|
|
49513
|
+
toast.success("User added and welcome email sent!");
|
|
49514
|
+
} else {
|
|
49515
|
+
console.log("Welcome email response:", emailData);
|
|
49516
|
+
}
|
|
49517
|
+
} else {
|
|
49518
|
+
console.warn("Dashboard URL not available, skipping welcome email");
|
|
49519
|
+
}
|
|
49520
|
+
} catch (emailErr) {
|
|
49521
|
+
console.error("Error sending welcome email:", emailErr);
|
|
49522
|
+
toast.info("User added successfully (email service unavailable)");
|
|
49523
|
+
}
|
|
49621
49524
|
onInviteSent?.();
|
|
49622
49525
|
onClose();
|
|
49623
49526
|
} catch (err) {
|
|
@@ -50128,15 +50031,12 @@ var TeamManagementView = ({
|
|
|
50128
50031
|
if (!supabase || !user) return;
|
|
50129
50032
|
try {
|
|
50130
50033
|
const userManagementService = createUserManagementService(supabase);
|
|
50131
|
-
await userManagementService.
|
|
50132
|
-
|
|
50133
|
-
deactivated_by: user.id
|
|
50134
|
-
});
|
|
50135
|
-
toast.success("User removed successfully");
|
|
50034
|
+
const result = await userManagementService.deleteUser(userId);
|
|
50035
|
+
toast.success(`User ${result.deleted_user_email} permanently deleted`);
|
|
50136
50036
|
loadData();
|
|
50137
50037
|
} catch (err) {
|
|
50138
|
-
console.error("Error
|
|
50139
|
-
toast.error("Failed to
|
|
50038
|
+
console.error("Error deleting user:", err);
|
|
50039
|
+
toast.error("Failed to delete user");
|
|
50140
50040
|
}
|
|
50141
50041
|
}, [supabase, user, loadData]);
|
|
50142
50042
|
const handleLineAssignmentUpdate = useCallback(async (userId, lineIds) => {
|
|
@@ -50230,9 +50130,6 @@ var TeamManagementView = ({
|
|
|
50230
50130
|
{
|
|
50231
50131
|
users,
|
|
50232
50132
|
currentUserId: user?.id,
|
|
50233
|
-
onUserClick: (clickedUser) => {
|
|
50234
|
-
console.log("View user details:", clickedUser);
|
|
50235
|
-
},
|
|
50236
50133
|
onRoleChange: handleRoleChange,
|
|
50237
50134
|
onRemoveUser: handleRemoveUser,
|
|
50238
50135
|
onLineAssignmentUpdate: handleLineAssignmentUpdate,
|
|
@@ -51410,7 +51307,7 @@ var S3Service = class {
|
|
|
51410
51307
|
};
|
|
51411
51308
|
|
|
51412
51309
|
// src/lib/api/optifye-agent.ts
|
|
51413
|
-
var OPTIFYE_API_URL = "https://
|
|
51310
|
+
var OPTIFYE_API_URL = process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app";
|
|
51414
51311
|
var OptifyeAgentClient = class {
|
|
51415
51312
|
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
51416
51313
|
this.apiUrl = apiUrl;
|