@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.js
CHANGED
|
@@ -228,7 +228,7 @@ var DEFAULT_DATE_TIME_CONFIG = {
|
|
|
228
228
|
};
|
|
229
229
|
var DEFAULT_ENDPOINTS_CONFIG = {
|
|
230
230
|
whatsapp: "/api/send-whatsapp-direct",
|
|
231
|
-
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://
|
|
231
|
+
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app",
|
|
232
232
|
// Default AGNO API URL
|
|
233
233
|
// Use environment variable for Slack webhook URL for privacy/security
|
|
234
234
|
// Note: SLACK_WEBHOOK_URL is server-side only, NEXT_PUBLIC_SLACK_WEBHOOK_URL works client-side but is less secure
|
|
@@ -2088,6 +2088,59 @@ var workspaceService = {
|
|
|
2088
2088
|
console.error("Error updating shift configuration:", error);
|
|
2089
2089
|
throw error;
|
|
2090
2090
|
}
|
|
2091
|
+
},
|
|
2092
|
+
/**
|
|
2093
|
+
* Fetch bulk targets data for multiple lines and shifts in a single API call
|
|
2094
|
+
* This replaces 120+ individual API calls with 1 optimized call
|
|
2095
|
+
*
|
|
2096
|
+
* @param params - Parameters for the bulk fetch
|
|
2097
|
+
* @returns Promise with complete targets data for all lines and shifts
|
|
2098
|
+
*/
|
|
2099
|
+
async fetchBulkTargets(params) {
|
|
2100
|
+
try {
|
|
2101
|
+
const token = await getAuthToken();
|
|
2102
|
+
const apiUrl = getBackendUrl();
|
|
2103
|
+
const {
|
|
2104
|
+
companyId,
|
|
2105
|
+
lineIds,
|
|
2106
|
+
date,
|
|
2107
|
+
shifts = [0, 1],
|
|
2108
|
+
includeSkus = true,
|
|
2109
|
+
includeActions = true
|
|
2110
|
+
} = params;
|
|
2111
|
+
const queryParams = new URLSearchParams({
|
|
2112
|
+
company_id: companyId,
|
|
2113
|
+
line_ids: lineIds.join(","),
|
|
2114
|
+
date,
|
|
2115
|
+
shifts: shifts.join(","),
|
|
2116
|
+
include_skus: includeSkus.toString(),
|
|
2117
|
+
include_actions: includeActions.toString()
|
|
2118
|
+
});
|
|
2119
|
+
const response = await fetch(
|
|
2120
|
+
`${apiUrl}/api/targets/bulk-load?${queryParams}`,
|
|
2121
|
+
{
|
|
2122
|
+
method: "GET",
|
|
2123
|
+
headers: {
|
|
2124
|
+
"Authorization": `Bearer ${token}`,
|
|
2125
|
+
"Content-Type": "application/json"
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
);
|
|
2129
|
+
if (!response.ok) {
|
|
2130
|
+
const errorText = await response.text();
|
|
2131
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
2132
|
+
}
|
|
2133
|
+
const data = await response.json();
|
|
2134
|
+
console.log("[fetchBulkTargets] Success:", {
|
|
2135
|
+
lineCount: data.metadata?.line_count,
|
|
2136
|
+
totalWorkspaces: data.metadata?.total_workspaces,
|
|
2137
|
+
timestamp: data.metadata?.timestamp
|
|
2138
|
+
});
|
|
2139
|
+
return data;
|
|
2140
|
+
} catch (error) {
|
|
2141
|
+
console.error("Error fetching bulk targets:", error);
|
|
2142
|
+
throw error;
|
|
2143
|
+
}
|
|
2091
2144
|
}
|
|
2092
2145
|
};
|
|
2093
2146
|
var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
@@ -3246,7 +3299,7 @@ var SSEChatClient = class {
|
|
|
3246
3299
|
user_id: userId,
|
|
3247
3300
|
context
|
|
3248
3301
|
});
|
|
3249
|
-
const agnoApiUrl = this.baseUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
3302
|
+
const agnoApiUrl = this.baseUrl || (process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app");
|
|
3250
3303
|
const endpoint = `${agnoApiUrl}/api/v2/chat`;
|
|
3251
3304
|
console.log("[SSEClient] Posting directly to AGNO:", endpoint);
|
|
3252
3305
|
const response = await fetch(endpoint, {
|
|
@@ -5822,60 +5875,32 @@ var UserManagementService = class {
|
|
|
5822
5875
|
}
|
|
5823
5876
|
}
|
|
5824
5877
|
/**
|
|
5825
|
-
*
|
|
5826
|
-
* @param
|
|
5878
|
+
* Permanently delete a user
|
|
5879
|
+
* @param userId - The ID of the user to delete
|
|
5880
|
+
* @returns Promise with deletion response
|
|
5827
5881
|
*/
|
|
5828
|
-
async
|
|
5882
|
+
async deleteUser(userId) {
|
|
5829
5883
|
try {
|
|
5830
5884
|
const token = await this.getAuthToken();
|
|
5831
5885
|
const backendUrl = this.getBackendUrl();
|
|
5832
5886
|
const response = await fetch(
|
|
5833
|
-
`${backendUrl}/api/users
|
|
5834
|
-
{
|
|
5835
|
-
method: "POST",
|
|
5836
|
-
headers: {
|
|
5837
|
-
"Authorization": `Bearer ${token}`,
|
|
5838
|
-
"Content-Type": "application/json"
|
|
5839
|
-
},
|
|
5840
|
-
body: JSON.stringify(input)
|
|
5841
|
-
}
|
|
5842
|
-
);
|
|
5843
|
-
if (!response.ok) {
|
|
5844
|
-
const errorData = await response.json();
|
|
5845
|
-
throw new Error(`Failed to deactivate user: ${errorData.detail || response.statusText}`);
|
|
5846
|
-
}
|
|
5847
|
-
console.log("[UserManagementService] User deactivated successfully");
|
|
5848
|
-
} catch (error) {
|
|
5849
|
-
console.error("[UserManagementService] Error deactivating user:", error);
|
|
5850
|
-
throw error;
|
|
5851
|
-
}
|
|
5852
|
-
}
|
|
5853
|
-
/**
|
|
5854
|
-
* Reactivate a user
|
|
5855
|
-
* @param input - Reactivation input
|
|
5856
|
-
*/
|
|
5857
|
-
async reactivateUser(input) {
|
|
5858
|
-
try {
|
|
5859
|
-
const token = await this.getAuthToken();
|
|
5860
|
-
const backendUrl = this.getBackendUrl();
|
|
5861
|
-
const response = await fetch(
|
|
5862
|
-
`${backendUrl}/api/users/reactivate`,
|
|
5887
|
+
`${backendUrl}/api/users/${userId}`,
|
|
5863
5888
|
{
|
|
5864
|
-
method: "
|
|
5889
|
+
method: "DELETE",
|
|
5865
5890
|
headers: {
|
|
5866
|
-
"Authorization": `Bearer ${token}
|
|
5867
|
-
|
|
5868
|
-
},
|
|
5869
|
-
body: JSON.stringify(input)
|
|
5891
|
+
"Authorization": `Bearer ${token}`
|
|
5892
|
+
}
|
|
5870
5893
|
}
|
|
5871
5894
|
);
|
|
5872
5895
|
if (!response.ok) {
|
|
5873
5896
|
const errorData = await response.json();
|
|
5874
|
-
throw new Error(`Failed to
|
|
5897
|
+
throw new Error(`Failed to delete user: ${errorData.detail || response.statusText}`);
|
|
5875
5898
|
}
|
|
5876
|
-
|
|
5899
|
+
const result = await response.json();
|
|
5900
|
+
console.log("[UserManagementService] User permanently deleted:", result);
|
|
5901
|
+
return result;
|
|
5877
5902
|
} catch (error) {
|
|
5878
|
-
console.error("[UserManagementService] Error
|
|
5903
|
+
console.error("[UserManagementService] Error deleting user:", error);
|
|
5879
5904
|
throw error;
|
|
5880
5905
|
}
|
|
5881
5906
|
}
|
|
@@ -7688,7 +7713,7 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
|
|
|
7688
7713
|
refetch: fetchLeaderboard
|
|
7689
7714
|
};
|
|
7690
7715
|
};
|
|
7691
|
-
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
7716
|
+
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
|
|
7692
7717
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
7693
7718
|
const entityConfig = useEntityConfig();
|
|
7694
7719
|
const databaseConfig = useDatabaseConfig();
|
|
@@ -7733,7 +7758,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7733
7758
|
try {
|
|
7734
7759
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7735
7760
|
const operationalDate = getOperationalDate(defaultTimezone);
|
|
7736
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7761
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7737
7762
|
if (targetLineIds.length === 0 && currentLineIdToUse === (entityConfig.factoryViewId || "factory")) {
|
|
7738
7763
|
throw new Error("Factory view selected, but no lines are configured in entityConfig.");
|
|
7739
7764
|
}
|
|
@@ -7859,7 +7884,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7859
7884
|
}
|
|
7860
7885
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7861
7886
|
const operationalDateForSubscription = getOperationalDate(defaultTimezone);
|
|
7862
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7887
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7863
7888
|
if (targetLineIds.length === 0) return;
|
|
7864
7889
|
const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
7865
7890
|
const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
@@ -7897,7 +7922,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7897
7922
|
entityConfig?.companyId,
|
|
7898
7923
|
entityConfig?.factoryViewId,
|
|
7899
7924
|
defaultTimezone,
|
|
7900
|
-
lineId
|
|
7925
|
+
lineId,
|
|
7926
|
+
userAccessibleLineIds
|
|
7901
7927
|
]);
|
|
7902
7928
|
return {
|
|
7903
7929
|
workspaceMetrics: metrics2?.workspaceMetrics || [],
|
|
@@ -9887,7 +9913,8 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9887
9913
|
}
|
|
9888
9914
|
setError(null);
|
|
9889
9915
|
try {
|
|
9890
|
-
const
|
|
9916
|
+
const allConfiguredLineIds = getConfiguredLineIds(entityConfig);
|
|
9917
|
+
const configuredLineIds = options?.allowedLineIds ? allConfiguredLineIds.filter((id3) => options.allowedLineIds.includes(id3)) : allConfiguredLineIds;
|
|
9891
9918
|
let enabledWorkspaceIds = [];
|
|
9892
9919
|
for (const lineId of configuredLineIds) {
|
|
9893
9920
|
const workspaces2 = await workspaceService.getWorkspaces(lineId);
|
|
@@ -9950,7 +9977,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9950
9977
|
setLoading(false);
|
|
9951
9978
|
isFetchingRef.current = false;
|
|
9952
9979
|
}
|
|
9953
|
-
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
|
|
9980
|
+
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId, entityConfig, options?.allowedLineIds]);
|
|
9954
9981
|
React23.useEffect(() => {
|
|
9955
9982
|
if (!initialized) {
|
|
9956
9983
|
fetchWorkspaceMetrics();
|
|
@@ -10739,23 +10766,18 @@ function formatRelativeTime(timestamp) {
|
|
|
10739
10766
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10740
10767
|
const diffDays = Math.floor(diffHours / 24);
|
|
10741
10768
|
if (diffSeconds < 60) {
|
|
10742
|
-
return
|
|
10769
|
+
return "Less than a minute ago";
|
|
10743
10770
|
}
|
|
10744
10771
|
if (diffMinutes < 60) {
|
|
10745
|
-
const
|
|
10746
|
-
|
|
10747
|
-
return `${diffMinutes}m ago`;
|
|
10748
|
-
}
|
|
10749
|
-
return `${diffMinutes}m ${remainingSeconds}s ago`;
|
|
10772
|
+
const minuteLabel = diffMinutes === 1 ? "minute" : "minutes";
|
|
10773
|
+
return `${diffMinutes} ${minuteLabel} ago`;
|
|
10750
10774
|
}
|
|
10751
10775
|
if (diffHours < 24) {
|
|
10752
|
-
const
|
|
10753
|
-
|
|
10754
|
-
return `${diffHours}h ago`;
|
|
10755
|
-
}
|
|
10756
|
-
return `${diffHours}h ${remainingMinutes}m ago`;
|
|
10776
|
+
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
10777
|
+
return `${diffHours} ${hourLabel} ago`;
|
|
10757
10778
|
}
|
|
10758
|
-
|
|
10779
|
+
const dayLabel = diffDays === 1 ? "day" : "days";
|
|
10780
|
+
return `${diffDays} ${dayLabel} ago`;
|
|
10759
10781
|
} catch (error) {
|
|
10760
10782
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
10761
10783
|
return "Unknown";
|
|
@@ -10771,8 +10793,8 @@ function getNextUpdateInterval(timestamp) {
|
|
|
10771
10793
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
10772
10794
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
10773
10795
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10774
|
-
if (diffSeconds < 60) return
|
|
10775
|
-
if (diffMinutes < 60) return
|
|
10796
|
+
if (diffSeconds < 60) return 1e4;
|
|
10797
|
+
if (diffMinutes < 60) return 6e4;
|
|
10776
10798
|
if (diffHours < 24) return 6e4;
|
|
10777
10799
|
return 36e5;
|
|
10778
10800
|
} catch (error) {
|
|
@@ -10949,50 +10971,129 @@ function useAccessControl() {
|
|
|
10949
10971
|
const userRole = React23.useMemo(() => {
|
|
10950
10972
|
if (!user?.role_level) return null;
|
|
10951
10973
|
const roleLevel = user.role_level;
|
|
10952
|
-
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
10974
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
|
|
10953
10975
|
return roleLevel;
|
|
10954
10976
|
}
|
|
10955
10977
|
return "supervisor";
|
|
10956
10978
|
}, [user?.role_level]);
|
|
10957
|
-
const
|
|
10958
|
-
|
|
10959
|
-
"
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
"
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10979
|
+
const assignedLineIds = React23.useMemo(() => {
|
|
10980
|
+
if (!user) return [];
|
|
10981
|
+
if (user.role_level === "supervisor") {
|
|
10982
|
+
const lines = user.properties?.line_id || user.properties?.line_ids || [];
|
|
10983
|
+
return Array.isArray(lines) ? lines : [];
|
|
10984
|
+
}
|
|
10985
|
+
return [];
|
|
10986
|
+
}, [user]);
|
|
10987
|
+
const assignedFactoryIds = React23.useMemo(() => {
|
|
10988
|
+
if (!user) return [];
|
|
10989
|
+
if (user.role_level === "plant_head") {
|
|
10990
|
+
const factories = user.properties?.factory_id || user.properties?.factory_ids || [];
|
|
10991
|
+
return Array.isArray(factories) ? factories : [];
|
|
10992
|
+
}
|
|
10993
|
+
return [];
|
|
10994
|
+
}, [user]);
|
|
10995
|
+
const roleAccessMap = {
|
|
10996
|
+
optifye: [
|
|
10997
|
+
"/",
|
|
10998
|
+
"/leaderboard",
|
|
10999
|
+
"/kpis",
|
|
11000
|
+
"/targets",
|
|
11001
|
+
"/shifts",
|
|
11002
|
+
"/supervisor-management",
|
|
11003
|
+
"/skus",
|
|
11004
|
+
"/ai-agent",
|
|
11005
|
+
"/help",
|
|
11006
|
+
"/health",
|
|
11007
|
+
"/profile",
|
|
11008
|
+
"/workspace",
|
|
11009
|
+
"/factory-view",
|
|
11010
|
+
"/team-management"
|
|
11011
|
+
],
|
|
11012
|
+
owner: [
|
|
11013
|
+
"/",
|
|
11014
|
+
"/leaderboard",
|
|
11015
|
+
"/kpis",
|
|
11016
|
+
"/targets",
|
|
11017
|
+
"/shifts",
|
|
11018
|
+
"/supervisor-management",
|
|
11019
|
+
"/skus",
|
|
11020
|
+
"/ai-agent",
|
|
11021
|
+
"/help",
|
|
11022
|
+
"/health",
|
|
11023
|
+
"/profile",
|
|
11024
|
+
"/workspace",
|
|
11025
|
+
"/factory-view",
|
|
11026
|
+
"/team-management"
|
|
11027
|
+
],
|
|
11028
|
+
plant_head: [
|
|
11029
|
+
"/",
|
|
11030
|
+
"/leaderboard",
|
|
11031
|
+
"/kpis",
|
|
11032
|
+
"/targets",
|
|
11033
|
+
"/shifts",
|
|
11034
|
+
"/supervisor-management",
|
|
11035
|
+
"/skus",
|
|
11036
|
+
"/ai-agent",
|
|
11037
|
+
"/help",
|
|
11038
|
+
"/health",
|
|
11039
|
+
"/profile",
|
|
11040
|
+
"/workspace",
|
|
11041
|
+
"/factory-view",
|
|
11042
|
+
"/team-management"
|
|
11043
|
+
],
|
|
11044
|
+
supervisor: [
|
|
11045
|
+
"/",
|
|
11046
|
+
"/leaderboard",
|
|
11047
|
+
"/kpis",
|
|
11048
|
+
"/targets",
|
|
11049
|
+
"/shifts",
|
|
11050
|
+
"/skus",
|
|
11051
|
+
"/ai-agent",
|
|
11052
|
+
"/help",
|
|
11053
|
+
"/health",
|
|
11054
|
+
"/profile",
|
|
11055
|
+
"/workspace"
|
|
11056
|
+
]
|
|
11057
|
+
};
|
|
10972
11058
|
const accessiblePages = React23.useMemo(() => {
|
|
10973
|
-
return
|
|
10974
|
-
|
|
11059
|
+
if (!userRole) return [];
|
|
11060
|
+
return roleAccessMap[userRole] || [];
|
|
11061
|
+
}, [userRole]);
|
|
10975
11062
|
const hasAccess = React23.useMemo(() => {
|
|
10976
11063
|
return (path) => {
|
|
10977
|
-
return
|
|
11064
|
+
if (!userRole) return false;
|
|
11065
|
+
const basePath = path.split("?")[0].split("/").slice(0, 2).join("/");
|
|
11066
|
+
const hasBaseAccess = accessiblePages.includes(basePath) || accessiblePages.includes("/");
|
|
11067
|
+
if (userRole === "supervisor" && assignedLineIds.length > 0) {
|
|
11068
|
+
if (path.includes("/kpis/") || path.includes("/workspace/")) {
|
|
11069
|
+
const lineIdMatch = path.match(/\/kpis\/([^/?]+)/);
|
|
11070
|
+
if (lineIdMatch) {
|
|
11071
|
+
const pathLineId = lineIdMatch[1];
|
|
11072
|
+
return assignedLineIds.includes(pathLineId);
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
}
|
|
11076
|
+
return hasBaseAccess;
|
|
10978
11077
|
};
|
|
10979
|
-
}, []);
|
|
11078
|
+
}, [userRole, accessiblePages, assignedLineIds]);
|
|
10980
11079
|
const isPageVisible = React23.useMemo(() => {
|
|
10981
11080
|
return (path) => {
|
|
10982
|
-
return
|
|
11081
|
+
return hasAccess(path);
|
|
10983
11082
|
};
|
|
10984
|
-
}, []);
|
|
11083
|
+
}, [hasAccess]);
|
|
10985
11084
|
const canAccessPage = React23.useMemo(() => {
|
|
10986
11085
|
return (path) => {
|
|
10987
|
-
return
|
|
11086
|
+
return hasAccess(path);
|
|
10988
11087
|
};
|
|
10989
|
-
}, []);
|
|
11088
|
+
}, [hasAccess]);
|
|
10990
11089
|
return {
|
|
10991
11090
|
userRole,
|
|
10992
11091
|
hasAccess,
|
|
10993
11092
|
accessiblePages,
|
|
10994
11093
|
isPageVisible,
|
|
10995
|
-
canAccessPage
|
|
11094
|
+
canAccessPage,
|
|
11095
|
+
assignedLineIds,
|
|
11096
|
+
assignedFactoryIds
|
|
10996
11097
|
};
|
|
10997
11098
|
}
|
|
10998
11099
|
|
|
@@ -11156,45 +11257,51 @@ function useHasLineAccess(lineId, configLineIds) {
|
|
|
11156
11257
|
function useLineSupervisor(lineId) {
|
|
11157
11258
|
const supabase = useSupabase();
|
|
11158
11259
|
const [supervisor, setSupervisor] = React23.useState(null);
|
|
11260
|
+
const [supervisors, setSupervisors] = React23.useState([]);
|
|
11159
11261
|
const [isLoading, setIsLoading] = React23.useState(true);
|
|
11160
11262
|
const [error, setError] = React23.useState(null);
|
|
11161
|
-
console.log("[useLineSupervisor] Hook initialized for lineId:", lineId);
|
|
11162
11263
|
const fetchSupervisor = React23.useCallback(async () => {
|
|
11163
11264
|
if (!lineId || !supabase) {
|
|
11164
|
-
console.log("[useLineSupervisor] Skipping fetch - lineId or supabase missing:", { lineId, hasSupabase: !!supabase });
|
|
11165
11265
|
setIsLoading(false);
|
|
11166
11266
|
return;
|
|
11167
11267
|
}
|
|
11168
11268
|
try {
|
|
11169
11269
|
setIsLoading(true);
|
|
11170
11270
|
setError(null);
|
|
11171
|
-
|
|
11172
|
-
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);
|
|
11173
|
-
console.log("[useLineSupervisor] Query result:", { data, error: fetchError, lineId });
|
|
11271
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor");
|
|
11174
11272
|
if (fetchError) {
|
|
11175
11273
|
console.error("[useLineSupervisor] Query error:", fetchError);
|
|
11176
11274
|
throw fetchError;
|
|
11177
11275
|
}
|
|
11178
11276
|
if (data && data.length > 0) {
|
|
11179
|
-
const
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
line_id_in_properties: supervisorData.properties?.line_id
|
|
11184
|
-
});
|
|
11185
|
-
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11186
|
-
setSupervisor({
|
|
11187
|
-
userId: supervisorData.user_id,
|
|
11188
|
-
email: supervisorData.email,
|
|
11189
|
-
displayName
|
|
11277
|
+
const supervisorsForLine = data.filter((supervisorData) => {
|
|
11278
|
+
const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
|
|
11279
|
+
const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
|
|
11280
|
+
return isAssigned;
|
|
11190
11281
|
});
|
|
11282
|
+
if (supervisorsForLine.length > 0) {
|
|
11283
|
+
const allSupervisors = supervisorsForLine.map((supervisorData) => {
|
|
11284
|
+
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11285
|
+
return {
|
|
11286
|
+
userId: supervisorData.user_id,
|
|
11287
|
+
email: supervisorData.email,
|
|
11288
|
+
displayName
|
|
11289
|
+
};
|
|
11290
|
+
});
|
|
11291
|
+
setSupervisors(allSupervisors);
|
|
11292
|
+
setSupervisor(allSupervisors[0]);
|
|
11293
|
+
} else {
|
|
11294
|
+
setSupervisors([]);
|
|
11295
|
+
setSupervisor(null);
|
|
11296
|
+
}
|
|
11191
11297
|
} else {
|
|
11192
|
-
|
|
11298
|
+
setSupervisors([]);
|
|
11193
11299
|
setSupervisor(null);
|
|
11194
11300
|
}
|
|
11195
11301
|
} catch (err) {
|
|
11196
|
-
console.error("[useLineSupervisor] Error fetching
|
|
11197
|
-
setError(err instanceof Error ? err : new Error("Failed to fetch
|
|
11302
|
+
console.error("[useLineSupervisor] Error fetching supervisors:", err);
|
|
11303
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
11304
|
+
setSupervisors([]);
|
|
11198
11305
|
setSupervisor(null);
|
|
11199
11306
|
} finally {
|
|
11200
11307
|
setIsLoading(false);
|
|
@@ -11219,7 +11326,6 @@ function useLineSupervisor(lineId) {
|
|
|
11219
11326
|
filter: `role_level=eq.supervisor`
|
|
11220
11327
|
},
|
|
11221
11328
|
(payload) => {
|
|
11222
|
-
console.log("[useLineSupervisor] Real-time update received:", payload);
|
|
11223
11329
|
fetchSupervisor();
|
|
11224
11330
|
}
|
|
11225
11331
|
).subscribe();
|
|
@@ -11231,9 +11337,11 @@ function useLineSupervisor(lineId) {
|
|
|
11231
11337
|
}
|
|
11232
11338
|
};
|
|
11233
11339
|
}, [lineId, supabase, fetchSupervisor]);
|
|
11340
|
+
const supervisorName = supervisors.length > 0 ? supervisors.map((s) => s.displayName).join(", ") : null;
|
|
11234
11341
|
return {
|
|
11235
|
-
supervisorName
|
|
11342
|
+
supervisorName,
|
|
11236
11343
|
supervisor,
|
|
11344
|
+
supervisors,
|
|
11237
11345
|
isLoading,
|
|
11238
11346
|
error
|
|
11239
11347
|
};
|
|
@@ -34104,14 +34212,16 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
34104
34212
|
};
|
|
34105
34213
|
var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
34106
34214
|
const [isGenerating, setIsGenerating] = React23.useState(false);
|
|
34215
|
+
const entityConfig = useEntityConfig();
|
|
34107
34216
|
const generatePDF = async () => {
|
|
34108
34217
|
setIsGenerating(true);
|
|
34109
34218
|
try {
|
|
34219
|
+
const lineName = workspace.line_name || getLineDisplayName(entityConfig, workspace.line_id);
|
|
34110
34220
|
trackCoreEvent("Workspace PDF Export Clicked", {
|
|
34111
34221
|
workspace_id: workspace.workspace_id,
|
|
34112
34222
|
line_id: workspace.line_id,
|
|
34113
34223
|
workspace_name: workspace.workspace_name,
|
|
34114
|
-
line_name:
|
|
34224
|
+
line_name: lineName
|
|
34115
34225
|
});
|
|
34116
34226
|
const doc = new jsPDF.jsPDF();
|
|
34117
34227
|
doc.setFontSize(14);
|
|
@@ -34132,7 +34242,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34132
34242
|
doc.setFontSize(32);
|
|
34133
34243
|
doc.setFont("helvetica", "bold");
|
|
34134
34244
|
doc.setTextColor(0, 0, 0);
|
|
34135
|
-
doc.text(
|
|
34245
|
+
doc.text(lineName, 20, 40);
|
|
34136
34246
|
doc.setFontSize(22);
|
|
34137
34247
|
doc.setFont("helvetica", "normal");
|
|
34138
34248
|
doc.setTextColor(40, 40, 40);
|
|
@@ -38832,7 +38942,7 @@ var AIAgentView = () => {
|
|
|
38832
38942
|
const renderedContentCache = React23.useRef(/* @__PURE__ */ new Map());
|
|
38833
38943
|
const { createThread, mutate: mutateThreads } = useThreads();
|
|
38834
38944
|
const { messages, addMessage, setMessages } = useMessages(activeThreadId);
|
|
38835
|
-
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://
|
|
38945
|
+
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
38836
38946
|
const sseClient = React23.useMemo(() => {
|
|
38837
38947
|
console.log("[AIAgentView] Using AGNO API URL:", agnoApiUrl);
|
|
38838
38948
|
return new SSEChatClient(agnoApiUrl);
|
|
@@ -41366,7 +41476,6 @@ function HomeView({
|
|
|
41366
41476
|
factoryName = "Simba Beer - Line 1"
|
|
41367
41477
|
}) {
|
|
41368
41478
|
const [isHydrated, setIsHydrated] = React23.useState(false);
|
|
41369
|
-
const availableLineIds = React23.useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
|
|
41370
41479
|
const [selectedLineId, setSelectedLineId] = React23.useState(factoryViewId);
|
|
41371
41480
|
const [isChangingFilter, setIsChangingFilter] = React23.useState(false);
|
|
41372
41481
|
const [errorMessage, setErrorMessage] = React23.useState(null);
|
|
@@ -41376,6 +41485,21 @@ function HomeView({
|
|
|
41376
41485
|
const entityConfig = useEntityConfig();
|
|
41377
41486
|
const supabaseClient = useSupabaseClient();
|
|
41378
41487
|
const { user } = useAuth();
|
|
41488
|
+
const isSupervisor = user?.role_level === "supervisor";
|
|
41489
|
+
const hasMultipleLines = allLineIds.length > 1;
|
|
41490
|
+
const availableLineIds = React23.useMemo(() => {
|
|
41491
|
+
if (isSupervisor && !hasMultipleLines) {
|
|
41492
|
+
return allLineIds;
|
|
41493
|
+
}
|
|
41494
|
+
return [factoryViewId, ...allLineIds];
|
|
41495
|
+
}, [factoryViewId, allLineIds, isSupervisor, hasMultipleLines]);
|
|
41496
|
+
React23.useEffect(() => {
|
|
41497
|
+
if (user) {
|
|
41498
|
+
if (isSupervisor && allLineIds.length > 0) {
|
|
41499
|
+
setSelectedLineId(allLineIds[0]);
|
|
41500
|
+
}
|
|
41501
|
+
}
|
|
41502
|
+
}, [user, isSupervisor, allLineIds]);
|
|
41379
41503
|
const userCompanyId = React23.useMemo(() => {
|
|
41380
41504
|
return user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
41381
41505
|
}, [user, entityConfig.companyId]);
|
|
@@ -41436,7 +41560,9 @@ function HomeView({
|
|
|
41436
41560
|
refetch: refetchMetrics
|
|
41437
41561
|
} = useDashboardMetrics({
|
|
41438
41562
|
lineId: selectedLineId,
|
|
41439
|
-
onLineMetricsUpdate
|
|
41563
|
+
onLineMetricsUpdate,
|
|
41564
|
+
userAccessibleLineIds: allLineIds
|
|
41565
|
+
// Pass user's accessible lines for supervisor filtering
|
|
41440
41566
|
});
|
|
41441
41567
|
const {
|
|
41442
41568
|
activeBreaks: allActiveBreaks,
|
|
@@ -43495,7 +43621,8 @@ var LeaderboardDetailView = React23.memo(({
|
|
|
43495
43621
|
line1Id = "",
|
|
43496
43622
|
line2Id = "",
|
|
43497
43623
|
lineNames = {},
|
|
43498
|
-
className = ""
|
|
43624
|
+
className = "",
|
|
43625
|
+
userAccessibleLineIds
|
|
43499
43626
|
}) => {
|
|
43500
43627
|
const navigation = useNavigation();
|
|
43501
43628
|
const entityConfig = useEntityConfig();
|
|
@@ -43535,7 +43662,9 @@ var LeaderboardDetailView = React23.memo(({
|
|
|
43535
43662
|
refreshWorkspaces
|
|
43536
43663
|
} = useAllWorkspaceMetrics({
|
|
43537
43664
|
initialDate: date,
|
|
43538
|
-
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
|
|
43665
|
+
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
|
|
43666
|
+
allowedLineIds: userAccessibleLineIds
|
|
43667
|
+
// Filter to user's accessible lines only
|
|
43539
43668
|
});
|
|
43540
43669
|
const getShiftName = React23.useCallback((shiftId2) => {
|
|
43541
43670
|
if (shiftId2 === void 0) return "Day";
|
|
@@ -44891,12 +45020,17 @@ var ACTION_NAMES = {
|
|
|
44891
45020
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
44892
45021
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
44893
45022
|
const pph = 3600 / cycleTime;
|
|
44894
|
-
return
|
|
45023
|
+
return Math.round(pph);
|
|
44895
45024
|
};
|
|
44896
45025
|
var calculateDayOutput = (pph, shiftHours, breaks = []) => {
|
|
44897
45026
|
if (pph === "") return "";
|
|
44898
45027
|
return Math.round(pph * shiftHours);
|
|
44899
45028
|
};
|
|
45029
|
+
var calculateDayOutputFromCycleTime = (cycleTime, shiftHours) => {
|
|
45030
|
+
if (cycleTime === "" || cycleTime === 0 || shiftHours === 0) return "";
|
|
45031
|
+
const dayOutput = 3600 / cycleTime * shiftHours;
|
|
45032
|
+
return Math.round(dayOutput);
|
|
45033
|
+
};
|
|
44900
45034
|
var formatWorkspaceName = (name, lineId) => {
|
|
44901
45035
|
return getWorkspaceDisplayName(name, lineId);
|
|
44902
45036
|
};
|
|
@@ -44908,20 +45042,6 @@ var getStoredLineState2 = (lineId) => {
|
|
|
44908
45042
|
return false;
|
|
44909
45043
|
}
|
|
44910
45044
|
};
|
|
44911
|
-
var calculateShiftHours2 = (startTime, endTime, breaks = []) => {
|
|
44912
|
-
if (!startTime || !endTime) return 8;
|
|
44913
|
-
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
44914
|
-
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
44915
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
44916
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
44917
|
-
if (endMinutes < startMinutes) {
|
|
44918
|
-
endMinutes += 24 * 60;
|
|
44919
|
-
}
|
|
44920
|
-
const safeBreaks = Array.isArray(breaks) ? breaks : [];
|
|
44921
|
-
const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
|
|
44922
|
-
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
44923
|
-
return Number(hoursDiff.toFixed(1));
|
|
44924
|
-
};
|
|
44925
45045
|
var BulkConfigureModal = ({
|
|
44926
45046
|
isOpen,
|
|
44927
45047
|
onClose,
|
|
@@ -46129,121 +46249,6 @@ var TargetsView = ({
|
|
|
46129
46249
|
const dashboardConfig = useDashboardConfig();
|
|
46130
46250
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
46131
46251
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
46132
|
-
const loadOperatingHours = React23.useCallback(async (lineId, shiftId) => {
|
|
46133
|
-
try {
|
|
46134
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
46135
|
-
if (!session?.access_token) {
|
|
46136
|
-
console.error("No authentication token available");
|
|
46137
|
-
return {
|
|
46138
|
-
startTime: "08:00",
|
|
46139
|
-
// Default values
|
|
46140
|
-
endTime: "19:00",
|
|
46141
|
-
breaks: []
|
|
46142
|
-
};
|
|
46143
|
-
}
|
|
46144
|
-
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
46145
|
-
if (!backendUrl) {
|
|
46146
|
-
console.error("Backend URL is not configured");
|
|
46147
|
-
return {
|
|
46148
|
-
startTime: "08:00",
|
|
46149
|
-
// Default values
|
|
46150
|
-
endTime: "19:00",
|
|
46151
|
-
breaks: []
|
|
46152
|
-
};
|
|
46153
|
-
}
|
|
46154
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}/operating-hours?shift_id=${shiftId}`, {
|
|
46155
|
-
headers: {
|
|
46156
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46157
|
-
"Content-Type": "application/json"
|
|
46158
|
-
}
|
|
46159
|
-
});
|
|
46160
|
-
if (!response.ok) {
|
|
46161
|
-
console.log(`No operating hours found for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46162
|
-
return {
|
|
46163
|
-
startTime: "08:00",
|
|
46164
|
-
// Default values
|
|
46165
|
-
endTime: "19:00",
|
|
46166
|
-
breaks: []
|
|
46167
|
-
};
|
|
46168
|
-
}
|
|
46169
|
-
const data = await response.json();
|
|
46170
|
-
let breaks = [];
|
|
46171
|
-
if (data?.breaks) {
|
|
46172
|
-
if (Array.isArray(data.breaks)) {
|
|
46173
|
-
breaks = data.breaks.map((breakItem) => {
|
|
46174
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46175
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46176
|
-
const calculateDuration = (start, end) => {
|
|
46177
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46178
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46179
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46180
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46181
|
-
if (endMinutes < startMinutes) {
|
|
46182
|
-
endMinutes += 24 * 60;
|
|
46183
|
-
}
|
|
46184
|
-
return endMinutes - startMinutes;
|
|
46185
|
-
};
|
|
46186
|
-
return {
|
|
46187
|
-
startTime,
|
|
46188
|
-
endTime,
|
|
46189
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46190
|
-
};
|
|
46191
|
-
});
|
|
46192
|
-
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
46193
|
-
breaks = data.breaks.breaks.map((breakItem) => {
|
|
46194
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46195
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46196
|
-
const calculateDuration = (start, end) => {
|
|
46197
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46198
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46199
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46200
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46201
|
-
if (endMinutes < startMinutes) {
|
|
46202
|
-
endMinutes += 24 * 60;
|
|
46203
|
-
}
|
|
46204
|
-
return endMinutes - startMinutes;
|
|
46205
|
-
};
|
|
46206
|
-
return {
|
|
46207
|
-
startTime,
|
|
46208
|
-
endTime,
|
|
46209
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46210
|
-
};
|
|
46211
|
-
});
|
|
46212
|
-
} else if (typeof data.breaks === "string") {
|
|
46213
|
-
try {
|
|
46214
|
-
const parsedBreaks = JSON.parse(data.breaks);
|
|
46215
|
-
if (Array.isArray(parsedBreaks)) {
|
|
46216
|
-
breaks = parsedBreaks.map((breakItem) => ({
|
|
46217
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46218
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46219
|
-
duration: breakItem.duration || 0
|
|
46220
|
-
}));
|
|
46221
|
-
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
46222
|
-
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
46223
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46224
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46225
|
-
duration: breakItem.duration || 0
|
|
46226
|
-
}));
|
|
46227
|
-
}
|
|
46228
|
-
} catch (e) {
|
|
46229
|
-
console.error("Error parsing breaks data:", e);
|
|
46230
|
-
}
|
|
46231
|
-
}
|
|
46232
|
-
}
|
|
46233
|
-
return {
|
|
46234
|
-
startTime: data?.start_time || "08:00",
|
|
46235
|
-
endTime: data?.end_time || "19:00",
|
|
46236
|
-
breaks
|
|
46237
|
-
};
|
|
46238
|
-
} catch (e) {
|
|
46239
|
-
console.error("Exception when loading operating hours:", e);
|
|
46240
|
-
return {
|
|
46241
|
-
startTime: "08:00",
|
|
46242
|
-
endTime: "19:00",
|
|
46243
|
-
breaks: []
|
|
46244
|
-
};
|
|
46245
|
-
}
|
|
46246
|
-
}, [supabase]);
|
|
46247
46252
|
React23.useEffect(() => {
|
|
46248
46253
|
console.log("[TargetsView] Component mounted/re-rendered", {
|
|
46249
46254
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -46255,221 +46260,108 @@ var TargetsView = ({
|
|
|
46255
46260
|
};
|
|
46256
46261
|
}, []);
|
|
46257
46262
|
React23.useEffect(() => {
|
|
46258
|
-
let timeoutId;
|
|
46259
|
-
let retryCount = 0;
|
|
46260
|
-
const MAX_RETRIES2 = 2;
|
|
46261
|
-
const LOADING_TIMEOUT = 15e3;
|
|
46262
46263
|
const fetchInitialData = async () => {
|
|
46263
|
-
if (lineIds.length === 0) return;
|
|
46264
|
-
console.log("[TargetsView] Starting fetchInitialData");
|
|
46264
|
+
if (lineIds.length === 0 || !companyId) return;
|
|
46265
|
+
console.log("[TargetsView] Starting optimized fetchInitialData with bulk endpoint");
|
|
46265
46266
|
setIsLoading(true);
|
|
46266
|
-
timeoutId = setTimeout(() => {
|
|
46267
|
-
console.error("Loading timeout reached");
|
|
46268
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46269
|
-
retryCount++;
|
|
46270
|
-
console.log(`Retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46271
|
-
sonner.toast.warning("Loading is taking longer than expected. Retrying...");
|
|
46272
|
-
fetchInitialData();
|
|
46273
|
-
} else {
|
|
46274
|
-
setIsLoading(false);
|
|
46275
|
-
sonner.toast.error("Failed to load data. Please refresh the page.");
|
|
46276
|
-
}
|
|
46277
|
-
}, LOADING_TIMEOUT);
|
|
46278
46267
|
try {
|
|
46279
|
-
const
|
|
46280
|
-
|
|
46281
|
-
|
|
46282
|
-
|
|
46283
|
-
|
|
46284
|
-
|
|
46285
|
-
|
|
46268
|
+
const currentDate = getOperationalDate(timezone);
|
|
46269
|
+
const bulkResponse = await workspaceService.fetchBulkTargets({
|
|
46270
|
+
companyId,
|
|
46271
|
+
lineIds,
|
|
46272
|
+
date: currentDate,
|
|
46273
|
+
shifts: [0, 1],
|
|
46274
|
+
// Fetch both shifts at once
|
|
46275
|
+
includeSkus: skuEnabled,
|
|
46276
|
+
includeActions: true
|
|
46277
|
+
});
|
|
46278
|
+
if (!bulkResponse.success) {
|
|
46279
|
+
throw new Error("Failed to fetch bulk targets data");
|
|
46286
46280
|
}
|
|
46287
|
-
const
|
|
46288
|
-
|
|
46289
|
-
|
|
46290
|
-
try {
|
|
46291
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}`, {
|
|
46292
|
-
headers: {
|
|
46293
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46294
|
-
"Content-Type": "application/json"
|
|
46295
|
-
}
|
|
46296
|
-
});
|
|
46297
|
-
if (!response.ok) {
|
|
46298
|
-
console.error(`Error fetching line data for ${lineId}:`, response.statusText);
|
|
46299
|
-
return { lineId, factoryId: void 0 };
|
|
46300
|
-
}
|
|
46301
|
-
const lineData = await response.json();
|
|
46302
|
-
return { lineId, factoryId: lineData.factory_id };
|
|
46303
|
-
} catch (err) {
|
|
46304
|
-
console.error(`Exception fetching line data for ${lineId}:`, err);
|
|
46305
|
-
return { lineId, factoryId: void 0 };
|
|
46306
|
-
}
|
|
46307
|
-
})),
|
|
46308
|
-
// Fetch action IDs
|
|
46309
|
-
actionService.getActionsByName(
|
|
46310
|
-
[ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
|
|
46311
|
-
companyId
|
|
46312
|
-
)
|
|
46313
|
-
]);
|
|
46314
|
-
const factoryResults = linesResponse;
|
|
46315
|
-
const assemblyAction = actions.find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46316
|
-
const packagingAction = actions.find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46281
|
+
const { data } = bulkResponse;
|
|
46282
|
+
const assemblyAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46283
|
+
const packagingAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46317
46284
|
if (!assemblyAction || !packagingAction) {
|
|
46318
|
-
throw new Error("Could not find required actions");
|
|
46285
|
+
throw new Error("Could not find required actions in bulk response");
|
|
46319
46286
|
}
|
|
46320
46287
|
const actionIdsData = {
|
|
46321
46288
|
assembly: assemblyAction.id,
|
|
46322
46289
|
packaging: packagingAction.id
|
|
46323
46290
|
};
|
|
46324
46291
|
setActionIds(actionIdsData);
|
|
46325
|
-
const
|
|
46326
|
-
|
|
46327
|
-
|
|
46328
|
-
|
|
46329
|
-
|
|
46330
|
-
|
|
46331
|
-
|
|
46332
|
-
|
|
46333
|
-
if (!updatedLineWorkspaces[lineId]?.factoryId) {
|
|
46334
|
-
console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
|
|
46335
|
-
continue;
|
|
46336
|
-
}
|
|
46337
|
-
try {
|
|
46338
|
-
const workspacesData = await workspaceService.getWorkspaces(lineId);
|
|
46339
|
-
const enabledWorkspaces = workspacesData.filter((ws) => ws.enable === true);
|
|
46340
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46341
|
-
lineId,
|
|
46342
|
-
currentDate,
|
|
46343
|
-
0
|
|
46344
|
-
// Always use day shift for initial load
|
|
46345
|
-
);
|
|
46346
|
-
const operatingHoursData = await loadOperatingHours(lineId, 0);
|
|
46347
|
-
if (operatingHoursData) {
|
|
46348
|
-
updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
|
|
46349
|
-
updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
|
|
46350
|
-
updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
|
|
46351
|
-
updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
|
|
46352
|
-
operatingHoursData.startTime,
|
|
46353
|
-
operatingHoursData.endTime,
|
|
46354
|
-
operatingHoursData.breaks
|
|
46355
|
-
);
|
|
46292
|
+
const newAllShiftsData = { 0: {}, 1: {} };
|
|
46293
|
+
const newDbValues = { 0: {}, 1: {} };
|
|
46294
|
+
Object.entries(data.lines).forEach(([lineId, lineData]) => {
|
|
46295
|
+
[0, 1].forEach((shiftId) => {
|
|
46296
|
+
const shiftData = lineData.shifts[shiftId.toString()];
|
|
46297
|
+
if (!shiftData) {
|
|
46298
|
+
console.warn(`No shift ${shiftId} data for line ${lineId}`);
|
|
46299
|
+
return;
|
|
46356
46300
|
}
|
|
46301
|
+
const operatingHours = shiftData.operating_hours || {
|
|
46302
|
+
start_time: "08:00",
|
|
46303
|
+
end_time: "19:00",
|
|
46304
|
+
breaks: [],
|
|
46305
|
+
total_hours: 11
|
|
46306
|
+
};
|
|
46307
|
+
newAllShiftsData[shiftId][lineId] = {
|
|
46308
|
+
productId: "",
|
|
46309
|
+
shiftStartTime: operatingHours.start_time,
|
|
46310
|
+
shiftEndTime: operatingHours.end_time,
|
|
46311
|
+
breaks: operatingHours.breaks,
|
|
46312
|
+
shiftHours: operatingHours.total_hours,
|
|
46313
|
+
workspaces: [],
|
|
46314
|
+
factoryId: lineData.line_info.factory_id
|
|
46315
|
+
};
|
|
46316
|
+
newDbValues[shiftId][lineId] = {};
|
|
46317
|
+
const enabledWorkspaces = lineData.workspaces.filter((ws) => ws.enable === true);
|
|
46357
46318
|
const mappedWorkspaces = enabledWorkspaces.map((ws) => {
|
|
46358
|
-
const threshold =
|
|
46359
|
-
if (!dbValues[0][lineId]) {
|
|
46360
|
-
dbValues[0][lineId] = {};
|
|
46361
|
-
}
|
|
46319
|
+
const threshold = shiftData.thresholds.find((t) => t.workspace_id === ws.id);
|
|
46362
46320
|
if (threshold) {
|
|
46363
|
-
|
|
46364
|
-
targetPPH: threshold.pph_threshold,
|
|
46365
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46366
|
-
targetDayOutput: threshold.total_day_output
|
|
46321
|
+
newDbValues[shiftId][lineId][ws.id] = {
|
|
46322
|
+
targetPPH: threshold.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46323
|
+
targetCycleTime: threshold.ideal_cycle_time ?? "",
|
|
46324
|
+
targetDayOutput: threshold.total_day_output ?? ""
|
|
46367
46325
|
};
|
|
46368
46326
|
}
|
|
46327
|
+
let actionType = "assembly";
|
|
46328
|
+
let actionId = actionIdsData.assembly;
|
|
46329
|
+
if (ws.action_id === packagingAction.id || ws.action_type === "packaging") {
|
|
46330
|
+
actionType = "packaging";
|
|
46331
|
+
actionId = packagingAction.id;
|
|
46332
|
+
} else if (ws.action_id === assemblyAction.id || ws.action_type === "assembly") {
|
|
46333
|
+
actionType = "assembly";
|
|
46334
|
+
actionId = assemblyAction.id;
|
|
46335
|
+
}
|
|
46369
46336
|
return {
|
|
46370
46337
|
id: ws.id,
|
|
46371
46338
|
name: ws.workspace_id,
|
|
46372
|
-
targetPPH: threshold?.pph_threshold
|
|
46373
|
-
targetCycleTime: threshold?.ideal_cycle_time ??
|
|
46374
|
-
targetDayOutput: threshold?.total_day_output ??
|
|
46375
|
-
actionType
|
|
46376
|
-
actionId
|
|
46339
|
+
targetPPH: threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46340
|
+
targetCycleTime: threshold?.ideal_cycle_time ?? "",
|
|
46341
|
+
targetDayOutput: threshold?.total_day_output ?? "",
|
|
46342
|
+
actionType,
|
|
46343
|
+
actionId
|
|
46377
46344
|
};
|
|
46378
46345
|
}).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
46379
|
-
|
|
46380
|
-
}
|
|
46381
|
-
|
|
46382
|
-
|
|
46383
|
-
|
|
46384
|
-
|
|
46385
|
-
|
|
46346
|
+
newAllShiftsData[shiftId][lineId].workspaces = mappedWorkspaces;
|
|
46347
|
+
});
|
|
46348
|
+
});
|
|
46349
|
+
setAllShiftsData(newAllShiftsData);
|
|
46350
|
+
setDbValues(newDbValues);
|
|
46351
|
+
console.log("[TargetsView] Successfully loaded all data with bulk endpoint:", {
|
|
46352
|
+
lineCount: bulkResponse.metadata.line_count,
|
|
46353
|
+
totalWorkspaces: bulkResponse.metadata.total_workspaces,
|
|
46354
|
+
loadTime: "Fast!"
|
|
46355
|
+
});
|
|
46386
46356
|
} catch (error) {
|
|
46387
|
-
console.error("Error fetching
|
|
46388
|
-
|
|
46389
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46390
|
-
retryCount++;
|
|
46391
|
-
console.log(`Error occurred, retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46392
|
-
sonner.toast.warning("Error loading data. Retrying...");
|
|
46393
|
-
setTimeout(() => fetchInitialData(), 1e3);
|
|
46394
|
-
} else {
|
|
46395
|
-
sonner.toast.error("Failed to load initial data");
|
|
46396
|
-
setIsLoading(false);
|
|
46397
|
-
}
|
|
46357
|
+
console.error("[TargetsView] Error fetching bulk data:", error);
|
|
46358
|
+
sonner.toast.error("Failed to load targets data. Please refresh the page.");
|
|
46398
46359
|
} finally {
|
|
46399
|
-
|
|
46400
|
-
if (retryCount === 0 || retryCount >= MAX_RETRIES2) {
|
|
46401
|
-
setIsLoading(false);
|
|
46402
|
-
}
|
|
46360
|
+
setIsLoading(false);
|
|
46403
46361
|
}
|
|
46404
46362
|
};
|
|
46405
46363
|
fetchInitialData();
|
|
46406
|
-
|
|
46407
|
-
clearTimeout(timeoutId);
|
|
46408
|
-
};
|
|
46409
|
-
}, [lineIds, companyId, loadOperatingHours]);
|
|
46410
|
-
const fetchAllShiftsData = React23.useCallback(async (currentWorkspaces) => {
|
|
46411
|
-
if (!supabase) return;
|
|
46412
|
-
const currentDate = getOperationalDate(timezone);
|
|
46413
|
-
const newAllShiftsData = {
|
|
46414
|
-
0: JSON.parse(JSON.stringify(currentWorkspaces)),
|
|
46415
|
-
// Deep clone for day shift
|
|
46416
|
-
1: JSON.parse(JSON.stringify(currentWorkspaces))
|
|
46417
|
-
// Deep clone for night shift
|
|
46418
|
-
};
|
|
46419
|
-
const newDbValues = { 0: {}, 1: {} };
|
|
46420
|
-
for (const shiftId of [0, 1]) {
|
|
46421
|
-
for (const lineId of lineIds) {
|
|
46422
|
-
try {
|
|
46423
|
-
const operatingHoursData = await loadOperatingHours(lineId, shiftId);
|
|
46424
|
-
if (!operatingHoursData) {
|
|
46425
|
-
console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46426
|
-
continue;
|
|
46427
|
-
}
|
|
46428
|
-
const { startTime, endTime, breaks } = operatingHoursData;
|
|
46429
|
-
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
46430
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46431
|
-
lineId,
|
|
46432
|
-
currentDate,
|
|
46433
|
-
shiftId
|
|
46434
|
-
);
|
|
46435
|
-
if (!newDbValues[shiftId][lineId]) {
|
|
46436
|
-
newDbValues[shiftId][lineId] = {};
|
|
46437
|
-
}
|
|
46438
|
-
const existingLine = newAllShiftsData[shiftId][lineId];
|
|
46439
|
-
if (existingLine) {
|
|
46440
|
-
newAllShiftsData[shiftId][lineId] = {
|
|
46441
|
-
...existingLine,
|
|
46442
|
-
shiftStartTime: startTime,
|
|
46443
|
-
shiftEndTime: endTime,
|
|
46444
|
-
breaks,
|
|
46445
|
-
shiftHours: Number(shiftHours),
|
|
46446
|
-
workspaces: existingLine.workspaces.map((ws) => {
|
|
46447
|
-
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
46448
|
-
if (threshold) {
|
|
46449
|
-
newDbValues[shiftId][lineId][ws.id] = {
|
|
46450
|
-
targetPPH: threshold.pph_threshold,
|
|
46451
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46452
|
-
targetDayOutput: threshold.total_day_output
|
|
46453
|
-
};
|
|
46454
|
-
return {
|
|
46455
|
-
...ws,
|
|
46456
|
-
targetPPH: threshold.pph_threshold,
|
|
46457
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46458
|
-
targetDayOutput: threshold.total_day_output
|
|
46459
|
-
};
|
|
46460
|
-
}
|
|
46461
|
-
return ws;
|
|
46462
|
-
})
|
|
46463
|
-
};
|
|
46464
|
-
}
|
|
46465
|
-
} catch (error) {
|
|
46466
|
-
console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
|
|
46467
|
-
}
|
|
46468
|
-
}
|
|
46469
|
-
}
|
|
46470
|
-
setAllShiftsData(newAllShiftsData);
|
|
46471
|
-
setDbValues(newDbValues);
|
|
46472
|
-
}, [supabase, lineIds, loadOperatingHours]);
|
|
46364
|
+
}, [lineIds, companyId, timezone, skuEnabled]);
|
|
46473
46365
|
const toggleLineDropdown = React23.useCallback((lineId) => {
|
|
46474
46366
|
setDropdownStates((prev) => {
|
|
46475
46367
|
const newIsOpen = !prev[lineId];
|
|
@@ -46542,7 +46434,7 @@ var TargetsView = ({
|
|
|
46542
46434
|
if (value !== "") {
|
|
46543
46435
|
const pph = calculatePPH(value, prev[lineId].breaks, shiftHours);
|
|
46544
46436
|
updates.targetPPH = pph;
|
|
46545
|
-
updates.targetDayOutput =
|
|
46437
|
+
updates.targetDayOutput = calculateDayOutputFromCycleTime(value, shiftHours);
|
|
46546
46438
|
} else {
|
|
46547
46439
|
updates.targetPPH = "";
|
|
46548
46440
|
updates.targetDayOutput = "";
|
|
@@ -46660,7 +46552,8 @@ var TargetsView = ({
|
|
|
46660
46552
|
action_id: ws.actionId,
|
|
46661
46553
|
workspace_id: ws.id,
|
|
46662
46554
|
date: currentDate,
|
|
46663
|
-
pph_threshold: Number(ws.targetPPH)
|
|
46555
|
+
pph_threshold: ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0,
|
|
46556
|
+
// Round to whole number
|
|
46664
46557
|
ideal_cycle_time: Number(ws.targetCycleTime) || 0,
|
|
46665
46558
|
total_day_output: Number(ws.targetDayOutput) || 0,
|
|
46666
46559
|
action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
|
|
@@ -46679,7 +46572,8 @@ var TargetsView = ({
|
|
|
46679
46572
|
shift_id: selectedShift,
|
|
46680
46573
|
product_code: lineDataToSave.productId,
|
|
46681
46574
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
46682
|
-
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH)
|
|
46575
|
+
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
46576
|
+
// Round each PPH value
|
|
46683
46577
|
...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
|
|
46684
46578
|
};
|
|
46685
46579
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
@@ -46938,6 +46832,7 @@ var WorkspaceDetailView = ({
|
|
|
46938
46832
|
const [showIdleTime, setShowIdleTime] = React23.useState(false);
|
|
46939
46833
|
const dashboardConfig = useDashboardConfig();
|
|
46940
46834
|
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
46835
|
+
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
46941
46836
|
const {
|
|
46942
46837
|
workspace: workspaceHealth,
|
|
46943
46838
|
loading: healthLoading,
|
|
@@ -46946,6 +46841,36 @@ var WorkspaceDetailView = ({
|
|
|
46946
46841
|
enableRealtime: true,
|
|
46947
46842
|
refreshInterval: 3e4
|
|
46948
46843
|
});
|
|
46844
|
+
const {
|
|
46845
|
+
isHealthy: isWorkspaceHealthy,
|
|
46846
|
+
timeSinceUpdate,
|
|
46847
|
+
lastHeartbeat,
|
|
46848
|
+
loading: healthStatusLoading,
|
|
46849
|
+
error: healthStatusError
|
|
46850
|
+
} = useWorkspaceHealthStatus(workspaceId);
|
|
46851
|
+
const isLive = React23.useMemo(() => {
|
|
46852
|
+
if (!lastHeartbeat) return false;
|
|
46853
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
46854
|
+
const heartbeat = new Date(lastHeartbeat);
|
|
46855
|
+
const minutesSince = (now2.getTime() - heartbeat.getTime()) / 6e4;
|
|
46856
|
+
return minutesSince < 5;
|
|
46857
|
+
}, [lastHeartbeat]);
|
|
46858
|
+
React23.useEffect(() => {
|
|
46859
|
+
console.log("[WorkspaceDetailView] Workspace Health Status:", {
|
|
46860
|
+
workspaceId,
|
|
46861
|
+
// Old workspace_health table
|
|
46862
|
+
oldStatus: workspaceHealth?.status,
|
|
46863
|
+
oldLastHeartbeat: workspaceHealth?.last_heartbeat,
|
|
46864
|
+
oldTimeSinceLastUpdate: workspaceHealth?.timeSinceLastUpdate,
|
|
46865
|
+
// New workspace_health_status table
|
|
46866
|
+
isHealthy: isWorkspaceHealthy,
|
|
46867
|
+
isLive,
|
|
46868
|
+
lastHeartbeat,
|
|
46869
|
+
timeSinceUpdate,
|
|
46870
|
+
loading: healthLoading || healthStatusLoading,
|
|
46871
|
+
error: healthError || healthStatusError
|
|
46872
|
+
});
|
|
46873
|
+
}, [workspaceId, workspaceHealth, isWorkspaceHealthy, isLive, lastHeartbeat, timeSinceUpdate, healthLoading, healthStatusLoading, healthError, healthStatusError]);
|
|
46949
46874
|
const {
|
|
46950
46875
|
status: prefetchStatus,
|
|
46951
46876
|
data: prefetchData,
|
|
@@ -46982,6 +46907,7 @@ var WorkspaceDetailView = ({
|
|
|
46982
46907
|
const workspace = isHistoricView ? historicMetrics : liveMetrics;
|
|
46983
46908
|
const loading = isHistoricView ? historicLoading : liveLoading;
|
|
46984
46909
|
const error = isHistoricView ? historicError : liveError;
|
|
46910
|
+
const { supervisorName } = useLineSupervisor(workspace?.line_id || lineId);
|
|
46985
46911
|
React23.useEffect(() => {
|
|
46986
46912
|
if (onTabChange) {
|
|
46987
46913
|
onTabChange(activeTab);
|
|
@@ -47204,7 +47130,7 @@ var WorkspaceDetailView = ({
|
|
|
47204
47130
|
initial: { opacity: 1 },
|
|
47205
47131
|
animate: { opacity: 1 },
|
|
47206
47132
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen w-full flex flex-col bg-slate-50", children: [
|
|
47207
|
-
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-
|
|
47133
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
47208
47134
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
47209
47135
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47210
47136
|
"button",
|
|
@@ -47218,15 +47144,15 @@ var WorkspaceDetailView = ({
|
|
|
47218
47144
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
|
|
47219
47145
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
47220
47146
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base font-semibold text-gray-900 truncate max-w-[220px]", children: formattedWorkspaceName }),
|
|
47221
|
-
|
|
47222
|
-
|
|
47147
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-2 w-2", children: [
|
|
47148
|
+
isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47223
47149
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
47224
47150
|
"relative inline-flex rounded-full h-2 w-2",
|
|
47225
|
-
|
|
47151
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47226
47152
|
) })
|
|
47227
47153
|
] }) })
|
|
47228
47154
|
] }),
|
|
47229
|
-
activeTab !== "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
|
|
47155
|
+
activeTab !== "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5 mt-1", children: [
|
|
47230
47156
|
workspaceHealth && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-gray-500", children: workspaceHealth.timeSinceLastUpdate }),
|
|
47231
47157
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47232
47158
|
WorkspaceHealthStatusBadge,
|
|
@@ -47253,14 +47179,11 @@ var WorkspaceDetailView = ({
|
|
|
47253
47179
|
) }),
|
|
47254
47180
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
47255
47181
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
|
|
47256
|
-
|
|
47257
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className:
|
|
47258
|
-
"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
|
|
47259
|
-
workspaceHealth.status === "healthy" ? "bg-green-400" : "bg-red-400"
|
|
47260
|
-
) }),
|
|
47182
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
|
|
47183
|
+
isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47261
47184
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
47262
47185
|
"relative inline-flex rounded-full h-2.5 w-2.5",
|
|
47263
|
-
|
|
47186
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47264
47187
|
) })
|
|
47265
47188
|
] })
|
|
47266
47189
|
] }) }),
|
|
@@ -48402,94 +48325,14 @@ var SupervisorManagementView = ({
|
|
|
48402
48325
|
] });
|
|
48403
48326
|
};
|
|
48404
48327
|
var SupervisorManagementView_default = SupervisorManagementView;
|
|
48405
|
-
var
|
|
48406
|
-
|
|
48407
|
-
|
|
48408
|
-
|
|
48409
|
-
|
|
48410
|
-
|
|
48411
|
-
|
|
48412
|
-
|
|
48413
|
-
],
|
|
48414
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48415
|
-
},
|
|
48416
|
-
supervisor_to_owner: {
|
|
48417
|
-
title: "Supervisor \u2192 Owner",
|
|
48418
|
-
warnings: [
|
|
48419
|
-
"Will gain full company access",
|
|
48420
|
-
"Will lose line-specific restrictions",
|
|
48421
|
-
"Will be able to manage all users",
|
|
48422
|
-
"Will see all company data"
|
|
48423
|
-
],
|
|
48424
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48425
|
-
},
|
|
48426
|
-
plant_head_to_supervisor: {
|
|
48427
|
-
title: "Plant Head \u2192 Supervisor",
|
|
48428
|
-
warnings: [
|
|
48429
|
-
"Will lose factory access",
|
|
48430
|
-
"Will lose ability to manage others",
|
|
48431
|
-
"Will need line assignment",
|
|
48432
|
-
"Will only see assigned line data"
|
|
48433
|
-
],
|
|
48434
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48435
|
-
},
|
|
48436
|
-
plant_head_to_owner: {
|
|
48437
|
-
title: "Plant Head \u2192 Owner",
|
|
48438
|
-
warnings: [
|
|
48439
|
-
"Will gain full company access",
|
|
48440
|
-
"Will lose factory-specific restrictions",
|
|
48441
|
-
"Will be able to manage all users",
|
|
48442
|
-
"Will see all company data"
|
|
48443
|
-
],
|
|
48444
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48445
|
-
},
|
|
48446
|
-
owner_to_plant_head: {
|
|
48447
|
-
title: "Owner \u2192 Plant Head",
|
|
48448
|
-
warnings: [
|
|
48449
|
-
"Will lose company-wide access",
|
|
48450
|
-
"Will need factory assignment",
|
|
48451
|
-
"Will only manage supervisors",
|
|
48452
|
-
"Will only see factory data"
|
|
48453
|
-
],
|
|
48454
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48455
|
-
},
|
|
48456
|
-
owner_to_supervisor: {
|
|
48457
|
-
title: "Owner \u2192 Supervisor",
|
|
48458
|
-
warnings: [
|
|
48459
|
-
"Will lose company-wide access",
|
|
48460
|
-
"Will lose ability to manage others",
|
|
48461
|
-
"Will need line assignment",
|
|
48462
|
-
"Will only see assigned line data"
|
|
48463
|
-
],
|
|
48464
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48465
|
-
},
|
|
48466
|
-
to_optifye: {
|
|
48467
|
-
title: "Promoting to Optifye",
|
|
48468
|
-
warnings: [
|
|
48469
|
-
"Will gain access to ALL companies",
|
|
48470
|
-
"Will be able to manage all users globally",
|
|
48471
|
-
"Will see all data across the platform",
|
|
48472
|
-
"This is the highest privilege level"
|
|
48473
|
-
],
|
|
48474
|
-
colorClass: "text-purple-700 bg-purple-50 border-purple-200"
|
|
48475
|
-
},
|
|
48476
|
-
from_optifye: {
|
|
48477
|
-
title: "Demoting from Optifye",
|
|
48478
|
-
warnings: [
|
|
48479
|
-
"Will lose access to other companies",
|
|
48480
|
-
"Will only see assigned company data",
|
|
48481
|
-
"Will lose global management capabilities",
|
|
48482
|
-
"Will need appropriate assignments"
|
|
48483
|
-
],
|
|
48484
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48485
|
-
}
|
|
48486
|
-
};
|
|
48487
|
-
var getRoleChangeKey = (currentRole, newRole) => {
|
|
48488
|
-
if (currentRole === newRole) return null;
|
|
48489
|
-
if (newRole === "optifye") return "to_optifye";
|
|
48490
|
-
if (currentRole === "optifye") return "from_optifye";
|
|
48491
|
-
const key = `${currentRole}_to_${newRole}`;
|
|
48492
|
-
return roleChangeImpacts[key] ? key : null;
|
|
48328
|
+
var getRoleLabel = (role) => {
|
|
48329
|
+
const labels = {
|
|
48330
|
+
"supervisor": "Supervisor",
|
|
48331
|
+
"plant_head": "Plant Head",
|
|
48332
|
+
"owner": "Owner",
|
|
48333
|
+
"optifye": "Optifye Admin"
|
|
48334
|
+
};
|
|
48335
|
+
return labels[role] || role;
|
|
48493
48336
|
};
|
|
48494
48337
|
var ChangeRoleDialog = ({
|
|
48495
48338
|
user,
|
|
@@ -48502,8 +48345,6 @@ var ChangeRoleDialog = ({
|
|
|
48502
48345
|
const [confirmed, setConfirmed] = React23.useState(false);
|
|
48503
48346
|
const [isChanging, setIsChanging] = React23.useState(false);
|
|
48504
48347
|
const roleChanged = selectedRole !== user.role_level;
|
|
48505
|
-
const changeKey = roleChanged ? getRoleChangeKey(user.role_level, selectedRole) : null;
|
|
48506
|
-
const impactInfo = changeKey ? roleChangeImpacts[changeKey] : null;
|
|
48507
48348
|
const handleNext = () => {
|
|
48508
48349
|
if (roleChanged) {
|
|
48509
48350
|
setStep("confirm");
|
|
@@ -48523,7 +48364,7 @@ var ChangeRoleDialog = ({
|
|
|
48523
48364
|
setStep("select");
|
|
48524
48365
|
setConfirmed(false);
|
|
48525
48366
|
};
|
|
48526
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-
|
|
48367
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48527
48368
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48528
48369
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48529
48370
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 bg-blue-100 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserCog, { className: "w-5 h-5 text-blue-600" }) }),
|
|
@@ -48533,20 +48374,14 @@ var ChangeRoleDialog = ({
|
|
|
48533
48374
|
"button",
|
|
48534
48375
|
{
|
|
48535
48376
|
onClick: onClose,
|
|
48536
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48377
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48537
48378
|
disabled: isChanging,
|
|
48379
|
+
"aria-label": "Close dialog",
|
|
48538
48380
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
|
|
48539
48381
|
}
|
|
48540
48382
|
)
|
|
48541
48383
|
] }),
|
|
48542
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-
|
|
48543
|
-
roleChanged && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-4 bg-amber-50 border border-amber-200 rounded-lg", children: [
|
|
48544
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" }),
|
|
48545
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
48546
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-amber-900 text-sm", children: "Changing roles affects access" }),
|
|
48547
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-amber-700 text-sm mt-1", children: "Review the impact carefully before confirming." })
|
|
48548
|
-
] })
|
|
48549
|
-
] }),
|
|
48384
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-5", children: step === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
48550
48385
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
48551
48386
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48552
48387
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-gray-50 rounded-lg", children: [
|
|
@@ -48557,7 +48392,7 @@ var ChangeRoleDialog = ({
|
|
|
48557
48392
|
] })
|
|
48558
48393
|
] })
|
|
48559
48394
|
] }),
|
|
48560
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-
|
|
48395
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
48561
48396
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "Select new role" }),
|
|
48562
48397
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: availableRoles.map((role) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48563
48398
|
"label",
|
|
@@ -48581,27 +48416,24 @@ var ChangeRoleDialog = ({
|
|
|
48581
48416
|
role
|
|
48582
48417
|
)) })
|
|
48583
48418
|
] }),
|
|
48584
|
-
|
|
48585
|
-
|
|
48586
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
48587
|
-
|
|
48588
|
-
|
|
48589
|
-
|
|
48590
|
-
] })
|
|
48419
|
+
roleChanged && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
48420
|
+
"Changing from ",
|
|
48421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(user.role_level) }),
|
|
48422
|
+
" to ",
|
|
48423
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(selectedRole) }),
|
|
48424
|
+
" will modify user permissions."
|
|
48425
|
+
] }) })
|
|
48591
48426
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
48592
48427
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-3 p-4 bg-gray-50 rounded-lg", children: [
|
|
48593
48428
|
/* @__PURE__ */ jsxRuntime.jsx(RoleBadge, { role: user.role_level }),
|
|
48594
48429
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-5 h-5 text-gray-400" }),
|
|
48595
48430
|
/* @__PURE__ */ jsxRuntime.jsx(RoleBadge, { role: selectedRole })
|
|
48596
48431
|
] }),
|
|
48597
|
-
|
|
48598
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "
|
|
48599
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
48600
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5", children: "\u2022" }),
|
|
48601
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: warning6 })
|
|
48602
|
-
] }, idx)) })
|
|
48432
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
48433
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48434
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
48603
48435
|
] }),
|
|
48604
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-
|
|
48436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48605
48437
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48606
48438
|
"input",
|
|
48607
48439
|
{
|
|
@@ -48612,7 +48444,7 @@ var ChangeRoleDialog = ({
|
|
|
48612
48444
|
className: "mt-0.5 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
48613
48445
|
}
|
|
48614
48446
|
),
|
|
48615
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "confirm-role-change", className: "text-sm text-gray-700 cursor-pointer", children: "I
|
|
48447
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
48616
48448
|
] })
|
|
48617
48449
|
] }) }) }),
|
|
48618
48450
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: step === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -48655,7 +48487,7 @@ var ChangeRoleDialog = ({
|
|
|
48655
48487
|
/* @__PURE__ */ jsxRuntime.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" })
|
|
48656
48488
|
] }),
|
|
48657
48489
|
"Changing..."
|
|
48658
|
-
] }) : "Change
|
|
48490
|
+
] }) : "Confirm Change"
|
|
48659
48491
|
}
|
|
48660
48492
|
)
|
|
48661
48493
|
] }) })
|
|
@@ -48678,32 +48510,34 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48678
48510
|
setIsRemoving(false);
|
|
48679
48511
|
}
|
|
48680
48512
|
};
|
|
48681
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-
|
|
48513
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48682
48514
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48683
48515
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48684
48516
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 bg-red-100 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-5 h-5 text-red-600" }) }),
|
|
48685
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "
|
|
48517
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Permanently Delete User" })
|
|
48686
48518
|
] }),
|
|
48687
48519
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48688
48520
|
"button",
|
|
48689
48521
|
{
|
|
48690
48522
|
onClick: onCancel,
|
|
48691
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48523
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48524
|
+
disabled: isRemoving,
|
|
48525
|
+
"aria-label": "Close dialog",
|
|
48692
48526
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
|
|
48693
48527
|
}
|
|
48694
48528
|
)
|
|
48695
48529
|
] }),
|
|
48696
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-
|
|
48697
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-
|
|
48698
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "w-
|
|
48699
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
48700
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
48701
|
-
|
|
48530
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-5", children: [
|
|
48531
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-3 bg-red-50 border border-red-200 rounded-lg", children: [
|
|
48532
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "w-4 h-4 text-red-600 mt-0.5 flex-shrink-0" }),
|
|
48533
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-red-900", children: [
|
|
48534
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "This action cannot be undone." }),
|
|
48535
|
+
" This will permanently delete the user and all related data from the system."
|
|
48702
48536
|
] })
|
|
48703
48537
|
] }),
|
|
48704
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-
|
|
48705
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to
|
|
48706
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-
|
|
48538
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
48539
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to permanently delete:" }),
|
|
48540
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-gray-50 rounded-lg space-y-2.5", children: [
|
|
48707
48541
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
48708
48542
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-600", children: "Email" }),
|
|
48709
48543
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
@@ -48728,28 +48562,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48728
48562
|
] })
|
|
48729
48563
|
] })
|
|
48730
48564
|
] }),
|
|
48731
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
48732
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700", children: "This will:" }),
|
|
48733
|
-
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm text-gray-600", children: [
|
|
48734
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48735
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48736
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Revoke all access immediately" })
|
|
48737
|
-
] }),
|
|
48738
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48739
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48740
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Remove all line and factory assignments" })
|
|
48741
|
-
] }),
|
|
48742
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48743
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48744
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Log the user out of all sessions" })
|
|
48745
|
-
] }),
|
|
48746
|
-
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48747
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48748
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "User will not be able to access the dashboard" })
|
|
48749
|
-
] })
|
|
48750
|
-
] })
|
|
48751
|
-
] }),
|
|
48752
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-4 border border-gray-200 rounded-lg", children: [
|
|
48565
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48753
48566
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48754
48567
|
"input",
|
|
48755
48568
|
{
|
|
@@ -48760,7 +48573,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48760
48573
|
className: "mt-0.5 h-4 w-4 text-red-600 border-gray-300 rounded focus:ring-red-500"
|
|
48761
48574
|
}
|
|
48762
48575
|
),
|
|
48763
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "confirm-remove", className: "text-sm text-gray-700 cursor-pointer", children: "I understand this
|
|
48576
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
48764
48577
|
] })
|
|
48765
48578
|
] }),
|
|
48766
48579
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: [
|
|
@@ -48784,8 +48597,8 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48784
48597
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
48785
48598
|
/* @__PURE__ */ jsxRuntime.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" })
|
|
48786
48599
|
] }),
|
|
48787
|
-
"
|
|
48788
|
-
] }) : "
|
|
48600
|
+
"Deleting..."
|
|
48601
|
+
] }) : "Delete User"
|
|
48789
48602
|
}
|
|
48790
48603
|
)
|
|
48791
48604
|
] })
|
|
@@ -49208,7 +49021,6 @@ var FactoryAssignmentDropdown = ({
|
|
|
49208
49021
|
var UserManagementTable = ({
|
|
49209
49022
|
users,
|
|
49210
49023
|
isLoading = false,
|
|
49211
|
-
onUserClick,
|
|
49212
49024
|
onRoleChange,
|
|
49213
49025
|
onRemoveUser,
|
|
49214
49026
|
onLineAssignmentUpdate,
|
|
@@ -49234,9 +49046,48 @@ var UserManagementTable = ({
|
|
|
49234
49046
|
const [sortField, setSortField] = React23.useState("email");
|
|
49235
49047
|
const [sortDirection, setSortDirection] = React23.useState("asc");
|
|
49236
49048
|
const [openActionMenuId, setOpenActionMenuId] = React23.useState(null);
|
|
49049
|
+
const [dropdownPosition, setDropdownPosition] = React23.useState(null);
|
|
49050
|
+
const actionButtonRefs = React23.useRef({});
|
|
49237
49051
|
const [selectedUser, setSelectedUser] = React23.useState(null);
|
|
49238
49052
|
const [showChangeRoleDialog, setShowChangeRoleDialog] = React23.useState(false);
|
|
49239
49053
|
const [showRemoveUserDialog, setShowRemoveUserDialog] = React23.useState(false);
|
|
49054
|
+
const handleOpenActionMenu = (userId) => {
|
|
49055
|
+
const buttonRef = actionButtonRefs.current[userId];
|
|
49056
|
+
if (buttonRef) {
|
|
49057
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49058
|
+
setDropdownPosition({
|
|
49059
|
+
top: rect.bottom + 8,
|
|
49060
|
+
// 8px below the button
|
|
49061
|
+
left: rect.right - 192
|
|
49062
|
+
// 192px is the dropdown width (w-48 = 12rem = 192px)
|
|
49063
|
+
});
|
|
49064
|
+
setOpenActionMenuId(userId);
|
|
49065
|
+
}
|
|
49066
|
+
};
|
|
49067
|
+
const handleCloseActionMenu = () => {
|
|
49068
|
+
setOpenActionMenuId(null);
|
|
49069
|
+
setDropdownPosition(null);
|
|
49070
|
+
};
|
|
49071
|
+
React23.useEffect(() => {
|
|
49072
|
+
if (openActionMenuId && actionButtonRefs.current[openActionMenuId]) {
|
|
49073
|
+
const updatePosition = () => {
|
|
49074
|
+
const buttonRef = actionButtonRefs.current[openActionMenuId];
|
|
49075
|
+
if (buttonRef) {
|
|
49076
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49077
|
+
setDropdownPosition({
|
|
49078
|
+
top: rect.bottom + 8,
|
|
49079
|
+
left: rect.right - 192
|
|
49080
|
+
});
|
|
49081
|
+
}
|
|
49082
|
+
};
|
|
49083
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
49084
|
+
window.addEventListener("resize", updatePosition);
|
|
49085
|
+
return () => {
|
|
49086
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
49087
|
+
window.removeEventListener("resize", updatePosition);
|
|
49088
|
+
};
|
|
49089
|
+
}
|
|
49090
|
+
}, [openActionMenuId]);
|
|
49240
49091
|
const filteredAndSortedUsers = React23.useMemo(() => {
|
|
49241
49092
|
let filtered = users;
|
|
49242
49093
|
filtered = filtered.filter((user) => availableRoles.includes(user.role_level));
|
|
@@ -49427,7 +49278,7 @@ var UserManagementTable = ({
|
|
|
49427
49278
|
LineAssignmentDropdown,
|
|
49428
49279
|
{
|
|
49429
49280
|
userId: user.user_id,
|
|
49430
|
-
currentLineIds: user.properties?.
|
|
49281
|
+
currentLineIds: user.properties?.line_ids || [],
|
|
49431
49282
|
availableLines: (
|
|
49432
49283
|
// Filter lines to only show those from the target user's company
|
|
49433
49284
|
(() => {
|
|
@@ -49465,69 +49316,24 @@ var UserManagementTable = ({
|
|
|
49465
49316
|
}
|
|
49466
49317
|
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
49467
49318
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: formatDate(user.created_at) }) }),
|
|
49468
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */ jsxRuntime.
|
|
49469
|
-
|
|
49470
|
-
|
|
49471
|
-
{
|
|
49472
|
-
|
|
49473
|
-
|
|
49474
|
-
|
|
49475
|
-
|
|
49476
|
-
|
|
49477
|
-
|
|
49478
|
-
|
|
49479
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49480
|
-
"div",
|
|
49481
|
-
{
|
|
49482
|
-
className: "fixed inset-0 z-[9998]",
|
|
49483
|
-
onClick: () => setOpenActionMenuId(null)
|
|
49319
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
49320
|
+
"button",
|
|
49321
|
+
{
|
|
49322
|
+
ref: (el) => {
|
|
49323
|
+
actionButtonRefs.current[user.user_id] = el;
|
|
49324
|
+
},
|
|
49325
|
+
onClick: () => {
|
|
49326
|
+
if (openActionMenuId === user.user_id) {
|
|
49327
|
+
handleCloseActionMenu();
|
|
49328
|
+
} else {
|
|
49329
|
+
handleOpenActionMenu(user.user_id);
|
|
49484
49330
|
}
|
|
49485
|
-
|
|
49486
|
-
|
|
49487
|
-
|
|
49488
|
-
|
|
49489
|
-
|
|
49490
|
-
|
|
49491
|
-
onUserClick?.(user);
|
|
49492
|
-
setOpenActionMenuId(null);
|
|
49493
|
-
},
|
|
49494
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49495
|
-
children: [
|
|
49496
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "w-4 h-4" }),
|
|
49497
|
-
"View Details"
|
|
49498
|
-
]
|
|
49499
|
-
}
|
|
49500
|
-
),
|
|
49501
|
-
canChangeRole && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49502
|
-
"button",
|
|
49503
|
-
{
|
|
49504
|
-
onClick: () => handleChangeRole(user),
|
|
49505
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49506
|
-
disabled: isCurrentUser,
|
|
49507
|
-
children: [
|
|
49508
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserCog, { className: "w-4 h-4" }),
|
|
49509
|
-
"Change Role"
|
|
49510
|
-
]
|
|
49511
|
-
}
|
|
49512
|
-
),
|
|
49513
|
-
canRemove && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
49514
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49515
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49516
|
-
"button",
|
|
49517
|
-
{
|
|
49518
|
-
onClick: () => handleRemoveUser(user),
|
|
49519
|
-
className: "w-full px-4 py-2 text-sm text-left text-red-600 hover:bg-red-50 flex items-center gap-2",
|
|
49520
|
-
disabled: isCurrentUser,
|
|
49521
|
-
children: [
|
|
49522
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-4 h-4" }),
|
|
49523
|
-
"Remove User"
|
|
49524
|
-
]
|
|
49525
|
-
}
|
|
49526
|
-
)
|
|
49527
|
-
] })
|
|
49528
|
-
] }) })
|
|
49529
|
-
] })
|
|
49530
|
-
] }) })
|
|
49331
|
+
},
|
|
49332
|
+
className: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
49333
|
+
"aria-label": "User actions",
|
|
49334
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, { className: "w-4 h-4" })
|
|
49335
|
+
}
|
|
49336
|
+
) }) })
|
|
49531
49337
|
]
|
|
49532
49338
|
},
|
|
49533
49339
|
user.user_id
|
|
@@ -49535,6 +49341,75 @@ var UserManagementTable = ({
|
|
|
49535
49341
|
}) })
|
|
49536
49342
|
] }) }) })
|
|
49537
49343
|
] }),
|
|
49344
|
+
openActionMenuId && dropdownPosition && typeof document !== "undefined" && reactDom.createPortal(
|
|
49345
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
49346
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49347
|
+
"div",
|
|
49348
|
+
{
|
|
49349
|
+
className: "fixed inset-0 z-[9998]",
|
|
49350
|
+
onClick: handleCloseActionMenu
|
|
49351
|
+
}
|
|
49352
|
+
),
|
|
49353
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49354
|
+
"div",
|
|
49355
|
+
{
|
|
49356
|
+
className: "fixed w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-[9999]",
|
|
49357
|
+
style: {
|
|
49358
|
+
top: `${dropdownPosition.top}px`,
|
|
49359
|
+
left: `${dropdownPosition.left}px`
|
|
49360
|
+
},
|
|
49361
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-1", children: [
|
|
49362
|
+
(() => {
|
|
49363
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49364
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49365
|
+
const canChangeRole = user && permissions.canChangeRole(user);
|
|
49366
|
+
return canChangeRole && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49367
|
+
"button",
|
|
49368
|
+
{
|
|
49369
|
+
onClick: () => {
|
|
49370
|
+
if (user) {
|
|
49371
|
+
handleChangeRole(user);
|
|
49372
|
+
}
|
|
49373
|
+
},
|
|
49374
|
+
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",
|
|
49375
|
+
disabled: isCurrentUser,
|
|
49376
|
+
children: [
|
|
49377
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserCog, { className: "w-4 h-4" }),
|
|
49378
|
+
"Change Role"
|
|
49379
|
+
]
|
|
49380
|
+
}
|
|
49381
|
+
);
|
|
49382
|
+
})(),
|
|
49383
|
+
(() => {
|
|
49384
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49385
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49386
|
+
const canRemove = user && permissions.canRemoveUser(user);
|
|
49387
|
+
return canRemove && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
49388
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49389
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49390
|
+
"button",
|
|
49391
|
+
{
|
|
49392
|
+
onClick: () => {
|
|
49393
|
+
if (user) {
|
|
49394
|
+
handleRemoveUser(user);
|
|
49395
|
+
}
|
|
49396
|
+
},
|
|
49397
|
+
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",
|
|
49398
|
+
disabled: isCurrentUser,
|
|
49399
|
+
children: [
|
|
49400
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-4 h-4" }),
|
|
49401
|
+
"Delete User"
|
|
49402
|
+
]
|
|
49403
|
+
}
|
|
49404
|
+
)
|
|
49405
|
+
] });
|
|
49406
|
+
})()
|
|
49407
|
+
] })
|
|
49408
|
+
}
|
|
49409
|
+
)
|
|
49410
|
+
] }),
|
|
49411
|
+
document.body
|
|
49412
|
+
),
|
|
49538
49413
|
showChangeRoleDialog && selectedUser && /* @__PURE__ */ jsxRuntime.jsx(
|
|
49539
49414
|
ChangeRoleDialog,
|
|
49540
49415
|
{
|
|
@@ -49648,6 +49523,34 @@ var InviteUserDialog = ({
|
|
|
49648
49523
|
throw new Error(data.error);
|
|
49649
49524
|
}
|
|
49650
49525
|
sonner.toast.success(data?.message || "User added successfully!");
|
|
49526
|
+
try {
|
|
49527
|
+
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
49528
|
+
if (dashboardUrl) {
|
|
49529
|
+
console.log("Sending welcome email to:", email.trim());
|
|
49530
|
+
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
49531
|
+
body: {
|
|
49532
|
+
action: "send-welcome-email",
|
|
49533
|
+
email: email.trim(),
|
|
49534
|
+
company_id: companyId,
|
|
49535
|
+
dashboard_url: dashboardUrl
|
|
49536
|
+
}
|
|
49537
|
+
});
|
|
49538
|
+
if (emailError) {
|
|
49539
|
+
console.error("Failed to send welcome email:", emailError);
|
|
49540
|
+
sonner.toast.warning("User added successfully, but welcome email could not be sent");
|
|
49541
|
+
} else if (emailData?.success) {
|
|
49542
|
+
console.log("Welcome email sent successfully:", emailData);
|
|
49543
|
+
sonner.toast.success("User added and welcome email sent!");
|
|
49544
|
+
} else {
|
|
49545
|
+
console.log("Welcome email response:", emailData);
|
|
49546
|
+
}
|
|
49547
|
+
} else {
|
|
49548
|
+
console.warn("Dashboard URL not available, skipping welcome email");
|
|
49549
|
+
}
|
|
49550
|
+
} catch (emailErr) {
|
|
49551
|
+
console.error("Error sending welcome email:", emailErr);
|
|
49552
|
+
sonner.toast.info("User added successfully (email service unavailable)");
|
|
49553
|
+
}
|
|
49651
49554
|
onInviteSent?.();
|
|
49652
49555
|
onClose();
|
|
49653
49556
|
} catch (err) {
|
|
@@ -50158,15 +50061,12 @@ var TeamManagementView = ({
|
|
|
50158
50061
|
if (!supabase || !user) return;
|
|
50159
50062
|
try {
|
|
50160
50063
|
const userManagementService = createUserManagementService(supabase);
|
|
50161
|
-
await userManagementService.
|
|
50162
|
-
|
|
50163
|
-
deactivated_by: user.id
|
|
50164
|
-
});
|
|
50165
|
-
sonner.toast.success("User removed successfully");
|
|
50064
|
+
const result = await userManagementService.deleteUser(userId);
|
|
50065
|
+
sonner.toast.success(`User ${result.deleted_user_email} permanently deleted`);
|
|
50166
50066
|
loadData();
|
|
50167
50067
|
} catch (err) {
|
|
50168
|
-
console.error("Error
|
|
50169
|
-
sonner.toast.error("Failed to
|
|
50068
|
+
console.error("Error deleting user:", err);
|
|
50069
|
+
sonner.toast.error("Failed to delete user");
|
|
50170
50070
|
}
|
|
50171
50071
|
}, [supabase, user, loadData]);
|
|
50172
50072
|
const handleLineAssignmentUpdate = React23.useCallback(async (userId, lineIds) => {
|
|
@@ -50260,9 +50160,6 @@ var TeamManagementView = ({
|
|
|
50260
50160
|
{
|
|
50261
50161
|
users,
|
|
50262
50162
|
currentUserId: user?.id,
|
|
50263
|
-
onUserClick: (clickedUser) => {
|
|
50264
|
-
console.log("View user details:", clickedUser);
|
|
50265
|
-
},
|
|
50266
50163
|
onRoleChange: handleRoleChange,
|
|
50267
50164
|
onRemoveUser: handleRemoveUser,
|
|
50268
50165
|
onLineAssignmentUpdate: handleLineAssignmentUpdate,
|
|
@@ -51440,7 +51337,7 @@ var S3Service = class {
|
|
|
51440
51337
|
};
|
|
51441
51338
|
|
|
51442
51339
|
// src/lib/api/optifye-agent.ts
|
|
51443
|
-
var OPTIFYE_API_URL = "https://
|
|
51340
|
+
var OPTIFYE_API_URL = process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app";
|
|
51444
51341
|
var OptifyeAgentClient = class {
|
|
51445
51342
|
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
51446
51343
|
this.apiUrl = apiUrl;
|