@optifye/dashboard-core 6.9.2 → 6.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +0 -4
- package/dist/index.d.mts +43 -15
- package/dist/index.d.ts +43 -15
- package/dist/index.js +375 -609
- package/dist/index.mjs +375 -609
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2058,6 +2058,59 @@ var workspaceService = {
|
|
|
2058
2058
|
console.error("Error updating shift configuration:", error);
|
|
2059
2059
|
throw error;
|
|
2060
2060
|
}
|
|
2061
|
+
},
|
|
2062
|
+
/**
|
|
2063
|
+
* Fetch bulk targets data for multiple lines and shifts in a single API call
|
|
2064
|
+
* This replaces 120+ individual API calls with 1 optimized call
|
|
2065
|
+
*
|
|
2066
|
+
* @param params - Parameters for the bulk fetch
|
|
2067
|
+
* @returns Promise with complete targets data for all lines and shifts
|
|
2068
|
+
*/
|
|
2069
|
+
async fetchBulkTargets(params) {
|
|
2070
|
+
try {
|
|
2071
|
+
const token = await getAuthToken();
|
|
2072
|
+
const apiUrl = getBackendUrl();
|
|
2073
|
+
const {
|
|
2074
|
+
companyId,
|
|
2075
|
+
lineIds,
|
|
2076
|
+
date,
|
|
2077
|
+
shifts = [0, 1],
|
|
2078
|
+
includeSkus = true,
|
|
2079
|
+
includeActions = true
|
|
2080
|
+
} = params;
|
|
2081
|
+
const queryParams = new URLSearchParams({
|
|
2082
|
+
company_id: companyId,
|
|
2083
|
+
line_ids: lineIds.join(","),
|
|
2084
|
+
date,
|
|
2085
|
+
shifts: shifts.join(","),
|
|
2086
|
+
include_skus: includeSkus.toString(),
|
|
2087
|
+
include_actions: includeActions.toString()
|
|
2088
|
+
});
|
|
2089
|
+
const response = await fetch(
|
|
2090
|
+
`${apiUrl}/api/targets/bulk-load?${queryParams}`,
|
|
2091
|
+
{
|
|
2092
|
+
method: "GET",
|
|
2093
|
+
headers: {
|
|
2094
|
+
"Authorization": `Bearer ${token}`,
|
|
2095
|
+
"Content-Type": "application/json"
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
);
|
|
2099
|
+
if (!response.ok) {
|
|
2100
|
+
const errorText = await response.text();
|
|
2101
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
2102
|
+
}
|
|
2103
|
+
const data = await response.json();
|
|
2104
|
+
console.log("[fetchBulkTargets] Success:", {
|
|
2105
|
+
lineCount: data.metadata?.line_count,
|
|
2106
|
+
totalWorkspaces: data.metadata?.total_workspaces,
|
|
2107
|
+
timestamp: data.metadata?.timestamp
|
|
2108
|
+
});
|
|
2109
|
+
return data;
|
|
2110
|
+
} catch (error) {
|
|
2111
|
+
console.error("Error fetching bulk targets:", error);
|
|
2112
|
+
throw error;
|
|
2113
|
+
}
|
|
2061
2114
|
}
|
|
2062
2115
|
};
|
|
2063
2116
|
var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
@@ -5792,60 +5845,32 @@ var UserManagementService = class {
|
|
|
5792
5845
|
}
|
|
5793
5846
|
}
|
|
5794
5847
|
/**
|
|
5795
|
-
*
|
|
5796
|
-
* @param
|
|
5848
|
+
* Permanently delete a user
|
|
5849
|
+
* @param userId - The ID of the user to delete
|
|
5850
|
+
* @returns Promise with deletion response
|
|
5797
5851
|
*/
|
|
5798
|
-
async
|
|
5852
|
+
async deleteUser(userId) {
|
|
5799
5853
|
try {
|
|
5800
5854
|
const token = await this.getAuthToken();
|
|
5801
5855
|
const backendUrl = this.getBackendUrl();
|
|
5802
5856
|
const response = await fetch(
|
|
5803
|
-
`${backendUrl}/api/users
|
|
5804
|
-
{
|
|
5805
|
-
method: "POST",
|
|
5806
|
-
headers: {
|
|
5807
|
-
"Authorization": `Bearer ${token}`,
|
|
5808
|
-
"Content-Type": "application/json"
|
|
5809
|
-
},
|
|
5810
|
-
body: JSON.stringify(input)
|
|
5811
|
-
}
|
|
5812
|
-
);
|
|
5813
|
-
if (!response.ok) {
|
|
5814
|
-
const errorData = await response.json();
|
|
5815
|
-
throw new Error(`Failed to deactivate user: ${errorData.detail || response.statusText}`);
|
|
5816
|
-
}
|
|
5817
|
-
console.log("[UserManagementService] User deactivated successfully");
|
|
5818
|
-
} catch (error) {
|
|
5819
|
-
console.error("[UserManagementService] Error deactivating user:", error);
|
|
5820
|
-
throw error;
|
|
5821
|
-
}
|
|
5822
|
-
}
|
|
5823
|
-
/**
|
|
5824
|
-
* Reactivate a user
|
|
5825
|
-
* @param input - Reactivation input
|
|
5826
|
-
*/
|
|
5827
|
-
async reactivateUser(input) {
|
|
5828
|
-
try {
|
|
5829
|
-
const token = await this.getAuthToken();
|
|
5830
|
-
const backendUrl = this.getBackendUrl();
|
|
5831
|
-
const response = await fetch(
|
|
5832
|
-
`${backendUrl}/api/users/reactivate`,
|
|
5857
|
+
`${backendUrl}/api/users/${userId}`,
|
|
5833
5858
|
{
|
|
5834
|
-
method: "
|
|
5859
|
+
method: "DELETE",
|
|
5835
5860
|
headers: {
|
|
5836
|
-
"Authorization": `Bearer ${token}
|
|
5837
|
-
|
|
5838
|
-
},
|
|
5839
|
-
body: JSON.stringify(input)
|
|
5861
|
+
"Authorization": `Bearer ${token}`
|
|
5862
|
+
}
|
|
5840
5863
|
}
|
|
5841
5864
|
);
|
|
5842
5865
|
if (!response.ok) {
|
|
5843
5866
|
const errorData = await response.json();
|
|
5844
|
-
throw new Error(`Failed to
|
|
5867
|
+
throw new Error(`Failed to delete user: ${errorData.detail || response.statusText}`);
|
|
5845
5868
|
}
|
|
5846
|
-
|
|
5869
|
+
const result = await response.json();
|
|
5870
|
+
console.log("[UserManagementService] User permanently deleted:", result);
|
|
5871
|
+
return result;
|
|
5847
5872
|
} catch (error) {
|
|
5848
|
-
console.error("[UserManagementService] Error
|
|
5873
|
+
console.error("[UserManagementService] Error deleting user:", error);
|
|
5849
5874
|
throw error;
|
|
5850
5875
|
}
|
|
5851
5876
|
}
|
|
@@ -7658,7 +7683,7 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
|
|
|
7658
7683
|
refetch: fetchLeaderboard
|
|
7659
7684
|
};
|
|
7660
7685
|
};
|
|
7661
|
-
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
7686
|
+
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
|
|
7662
7687
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
7663
7688
|
const entityConfig = useEntityConfig();
|
|
7664
7689
|
const databaseConfig = useDatabaseConfig();
|
|
@@ -7703,7 +7728,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7703
7728
|
try {
|
|
7704
7729
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7705
7730
|
const operationalDate = getOperationalDate(defaultTimezone);
|
|
7706
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7731
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7707
7732
|
if (targetLineIds.length === 0 && currentLineIdToUse === (entityConfig.factoryViewId || "factory")) {
|
|
7708
7733
|
throw new Error("Factory view selected, but no lines are configured in entityConfig.");
|
|
7709
7734
|
}
|
|
@@ -7829,7 +7854,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7829
7854
|
}
|
|
7830
7855
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
7831
7856
|
const operationalDateForSubscription = getOperationalDate(defaultTimezone);
|
|
7832
|
-
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7857
|
+
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
7833
7858
|
if (targetLineIds.length === 0) return;
|
|
7834
7859
|
const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
7835
7860
|
const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
@@ -7867,7 +7892,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
7867
7892
|
entityConfig?.companyId,
|
|
7868
7893
|
entityConfig?.factoryViewId,
|
|
7869
7894
|
defaultTimezone,
|
|
7870
|
-
lineId
|
|
7895
|
+
lineId,
|
|
7896
|
+
userAccessibleLineIds
|
|
7871
7897
|
]);
|
|
7872
7898
|
return {
|
|
7873
7899
|
workspaceMetrics: metrics2?.workspaceMetrics || [],
|
|
@@ -9857,7 +9883,8 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9857
9883
|
}
|
|
9858
9884
|
setError(null);
|
|
9859
9885
|
try {
|
|
9860
|
-
const
|
|
9886
|
+
const allConfiguredLineIds = getConfiguredLineIds(entityConfig);
|
|
9887
|
+
const configuredLineIds = options?.allowedLineIds ? allConfiguredLineIds.filter((id3) => options.allowedLineIds.includes(id3)) : allConfiguredLineIds;
|
|
9861
9888
|
let enabledWorkspaceIds = [];
|
|
9862
9889
|
for (const lineId of configuredLineIds) {
|
|
9863
9890
|
const workspaces2 = await workspaceService.getWorkspaces(lineId);
|
|
@@ -9920,7 +9947,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
9920
9947
|
setLoading(false);
|
|
9921
9948
|
isFetchingRef.current = false;
|
|
9922
9949
|
}
|
|
9923
|
-
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
|
|
9950
|
+
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId, entityConfig, options?.allowedLineIds]);
|
|
9924
9951
|
useEffect(() => {
|
|
9925
9952
|
if (!initialized) {
|
|
9926
9953
|
fetchWorkspaceMetrics();
|
|
@@ -24305,6 +24332,7 @@ var VideoCard = React23__default.memo(({
|
|
|
24305
24332
|
useRAF = true,
|
|
24306
24333
|
className = "",
|
|
24307
24334
|
compact = false,
|
|
24335
|
+
displayName,
|
|
24308
24336
|
onMouseEnter,
|
|
24309
24337
|
onMouseLeave
|
|
24310
24338
|
}) => {
|
|
@@ -24326,7 +24354,7 @@ var VideoCard = React23__default.memo(({
|
|
|
24326
24354
|
onFatalError: onFatalError ?? (() => throttledReloadDashboard())
|
|
24327
24355
|
});
|
|
24328
24356
|
}
|
|
24329
|
-
const
|
|
24357
|
+
const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24330
24358
|
const getEfficiencyOverlayColor = (efficiency) => {
|
|
24331
24359
|
if (efficiency >= 80) {
|
|
24332
24360
|
return "bg-[#00D654]/25";
|
|
@@ -24419,7 +24447,7 @@ var VideoCard = React23__default.memo(({
|
|
|
24419
24447
|
/* @__PURE__ */ jsxs("div", { className: `absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 p-1.5 sm:${compact ? "p-1" : "p-1.5"} flex justify-between items-center z-10`, children: [
|
|
24420
24448
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
24421
24449
|
/* @__PURE__ */ jsx(Camera, { size: compact ? 10 : 12, className: "text-white" }),
|
|
24422
|
-
/* @__PURE__ */ jsx("p", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"} font-medium tracking-wide`, children:
|
|
24450
|
+
/* @__PURE__ */ jsx("p", { className: `text-white text-[11px] sm:${compact ? "text-[10px]" : "text-xs"} font-medium tracking-wide`, children: workspaceDisplayName })
|
|
24423
24451
|
] }),
|
|
24424
24452
|
/* @__PURE__ */ jsxs("div", { className: `flex items-center ${compact ? "gap-1" : "gap-1.5"}`, children: [
|
|
24425
24453
|
trendInfo && /* @__PURE__ */ jsx(
|
|
@@ -24456,6 +24484,7 @@ var VideoGridView = React23__default.memo(({
|
|
|
24456
24484
|
selectedLine,
|
|
24457
24485
|
className = "",
|
|
24458
24486
|
videoSources = {},
|
|
24487
|
+
displayNames = {},
|
|
24459
24488
|
onWorkspaceHover,
|
|
24460
24489
|
onWorkspaceHoverEnd
|
|
24461
24490
|
}) => {
|
|
@@ -24670,6 +24699,7 @@ var VideoGridView = React23__default.memo(({
|
|
|
24670
24699
|
isVeryLowEfficiency,
|
|
24671
24700
|
cropping: workspaceCropping,
|
|
24672
24701
|
canvasFps: canvasConfig?.fps,
|
|
24702
|
+
displayName: displayNames[workspace.workspace_name] || workspace.workspace_name,
|
|
24673
24703
|
useRAF: canvasConfig?.useRAF,
|
|
24674
24704
|
compact: !selectedLine,
|
|
24675
24705
|
onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(workspaceId) : void 0,
|
|
@@ -24687,6 +24717,7 @@ VideoGridView.displayName = "VideoGridView";
|
|
|
24687
24717
|
var MapGridView = React23__default.memo(({
|
|
24688
24718
|
workspaces,
|
|
24689
24719
|
className = "",
|
|
24720
|
+
displayNames = {},
|
|
24690
24721
|
onWorkspaceHover,
|
|
24691
24722
|
onWorkspaceHoverEnd
|
|
24692
24723
|
}) => {
|
|
@@ -24717,10 +24748,10 @@ var MapGridView = React23__default.memo(({
|
|
|
24717
24748
|
efficiency: workspace.efficiency,
|
|
24718
24749
|
action_count: workspace.action_count
|
|
24719
24750
|
});
|
|
24720
|
-
const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24751
|
+
const displayName = displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24721
24752
|
const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id);
|
|
24722
24753
|
router.push(`/workspace/${workspaceId}${navParams}`);
|
|
24723
|
-
}, [router]);
|
|
24754
|
+
}, [router, displayNames]);
|
|
24724
24755
|
const activePositions = useMemo(() => {
|
|
24725
24756
|
return workspacePositions.filter((pos) => {
|
|
24726
24757
|
const wsKey = pos.id.toUpperCase();
|
|
@@ -24746,7 +24777,7 @@ var MapGridView = React23__default.memo(({
|
|
|
24746
24777
|
if (!workspace) return null;
|
|
24747
24778
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
24748
24779
|
getPerformanceColor(workspace.efficiency);
|
|
24749
|
-
const
|
|
24780
|
+
const workspaceDisplayName = displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24750
24781
|
return /* @__PURE__ */ jsx(
|
|
24751
24782
|
motion.div,
|
|
24752
24783
|
{
|
|
@@ -24772,7 +24803,7 @@ var MapGridView = React23__default.memo(({
|
|
|
24772
24803
|
${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : "w-36 h-28"}
|
|
24773
24804
|
`,
|
|
24774
24805
|
children: [
|
|
24775
|
-
/* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 px-2 py-1.5 border-b border-gray-200", children: /* @__PURE__ */ jsx("div", { className: "text-xs font-bold text-gray-800 truncate text-center leading-tight", children:
|
|
24806
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 px-2 py-1.5 border-b border-gray-200", children: /* @__PURE__ */ jsx("div", { className: "text-xs font-bold text-gray-800 truncate text-center leading-tight", children: workspaceDisplayName }) }),
|
|
24776
24807
|
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 px-2 py-2", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center", children: /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
|
|
24777
24808
|
Math.round(workspace.efficiency),
|
|
24778
24809
|
"%"
|
|
@@ -34800,6 +34831,7 @@ var WorkspaceGrid = React23__default.memo(({
|
|
|
34800
34831
|
line2Uuid = "line-2",
|
|
34801
34832
|
className = "",
|
|
34802
34833
|
videoSources = {},
|
|
34834
|
+
displayNames = {},
|
|
34803
34835
|
onWorkspaceHover,
|
|
34804
34836
|
onWorkspaceHoverEnd
|
|
34805
34837
|
}) => {
|
|
@@ -34865,6 +34897,7 @@ var WorkspaceGrid = React23__default.memo(({
|
|
|
34865
34897
|
{
|
|
34866
34898
|
workspaces,
|
|
34867
34899
|
videoSources,
|
|
34900
|
+
displayNames,
|
|
34868
34901
|
onWorkspaceHover,
|
|
34869
34902
|
onWorkspaceHoverEnd
|
|
34870
34903
|
}
|
|
@@ -34883,6 +34916,7 @@ var WorkspaceGrid = React23__default.memo(({
|
|
|
34883
34916
|
MapGridViewComponent,
|
|
34884
34917
|
{
|
|
34885
34918
|
workspaces,
|
|
34919
|
+
displayNames,
|
|
34886
34920
|
onWorkspaceHover,
|
|
34887
34921
|
onWorkspaceHoverEnd
|
|
34888
34922
|
}
|
|
@@ -41419,7 +41453,6 @@ function HomeView({
|
|
|
41419
41453
|
factoryName = "Simba Beer - Line 1"
|
|
41420
41454
|
}) {
|
|
41421
41455
|
const [isHydrated, setIsHydrated] = useState(false);
|
|
41422
|
-
const availableLineIds = useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
|
|
41423
41456
|
const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
|
|
41424
41457
|
const [isChangingFilter, setIsChangingFilter] = useState(false);
|
|
41425
41458
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
@@ -41429,6 +41462,21 @@ function HomeView({
|
|
|
41429
41462
|
const entityConfig = useEntityConfig();
|
|
41430
41463
|
const supabaseClient = useSupabaseClient();
|
|
41431
41464
|
const { user } = useAuth();
|
|
41465
|
+
const isSupervisor = user?.role_level === "supervisor";
|
|
41466
|
+
const hasMultipleLines = allLineIds.length > 1;
|
|
41467
|
+
const availableLineIds = useMemo(() => {
|
|
41468
|
+
if (isSupervisor && !hasMultipleLines) {
|
|
41469
|
+
return allLineIds;
|
|
41470
|
+
}
|
|
41471
|
+
return [factoryViewId, ...allLineIds];
|
|
41472
|
+
}, [factoryViewId, allLineIds, isSupervisor, hasMultipleLines]);
|
|
41473
|
+
useEffect(() => {
|
|
41474
|
+
if (user) {
|
|
41475
|
+
if (isSupervisor && allLineIds.length > 0) {
|
|
41476
|
+
setSelectedLineId(allLineIds[0]);
|
|
41477
|
+
}
|
|
41478
|
+
}
|
|
41479
|
+
}, [user, isSupervisor, allLineIds]);
|
|
41432
41480
|
const userCompanyId = useMemo(() => {
|
|
41433
41481
|
return user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
41434
41482
|
}, [user, entityConfig.companyId]);
|
|
@@ -41463,7 +41511,7 @@ function HomeView({
|
|
|
41463
41511
|
displayNames: workspaceDisplayNames,
|
|
41464
41512
|
loading: displayNamesLoading,
|
|
41465
41513
|
error: displayNamesError
|
|
41466
|
-
} = useWorkspaceDisplayNames(void 0
|
|
41514
|
+
} = useWorkspaceDisplayNames(selectedLineId, void 0);
|
|
41467
41515
|
useCallback(() => {
|
|
41468
41516
|
console.log("Refetching KPIs after line metrics update");
|
|
41469
41517
|
}, []);
|
|
@@ -41489,7 +41537,9 @@ function HomeView({
|
|
|
41489
41537
|
refetch: refetchMetrics
|
|
41490
41538
|
} = useDashboardMetrics({
|
|
41491
41539
|
lineId: selectedLineId,
|
|
41492
|
-
onLineMetricsUpdate
|
|
41540
|
+
onLineMetricsUpdate,
|
|
41541
|
+
userAccessibleLineIds: allLineIds
|
|
41542
|
+
// Pass user's accessible lines for supervisor filtering
|
|
41493
41543
|
});
|
|
41494
41544
|
const {
|
|
41495
41545
|
activeBreaks: allActiveBreaks,
|
|
@@ -41825,7 +41875,7 @@ function HomeView({
|
|
|
41825
41875
|
/* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
|
|
41826
41876
|
] });
|
|
41827
41877
|
}, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
|
|
41828
|
-
const isInitialLoading = !isHydrated || !displayNamesInitialized && displayNamesLoading;
|
|
41878
|
+
const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized && displayNamesLoading;
|
|
41829
41879
|
const isDataLoading = metricsLoading || kpisLoading;
|
|
41830
41880
|
if (isInitialLoading) {
|
|
41831
41881
|
return /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading Dashboard..." });
|
|
@@ -41870,6 +41920,7 @@ function HomeView({
|
|
|
41870
41920
|
lineNames,
|
|
41871
41921
|
factoryView: factoryViewId,
|
|
41872
41922
|
videoSources,
|
|
41923
|
+
displayNames: workspaceDisplayNames,
|
|
41873
41924
|
className: "h-full",
|
|
41874
41925
|
onWorkspaceHover: handleWorkspaceHover,
|
|
41875
41926
|
onWorkspaceHoverEnd: handleWorkspaceHoverEnd
|
|
@@ -41897,6 +41948,7 @@ function HomeView({
|
|
|
41897
41948
|
lineNames,
|
|
41898
41949
|
factoryView: factoryViewId,
|
|
41899
41950
|
videoSources,
|
|
41951
|
+
displayNames: workspaceDisplayNames,
|
|
41900
41952
|
className: "h-full",
|
|
41901
41953
|
onWorkspaceHover: handleWorkspaceHover,
|
|
41902
41954
|
onWorkspaceHoverEnd: handleWorkspaceHoverEnd
|
|
@@ -43548,7 +43600,8 @@ var LeaderboardDetailView = memo(({
|
|
|
43548
43600
|
line1Id = "",
|
|
43549
43601
|
line2Id = "",
|
|
43550
43602
|
lineNames = {},
|
|
43551
|
-
className = ""
|
|
43603
|
+
className = "",
|
|
43604
|
+
userAccessibleLineIds
|
|
43552
43605
|
}) => {
|
|
43553
43606
|
const navigation = useNavigation();
|
|
43554
43607
|
const entityConfig = useEntityConfig();
|
|
@@ -43588,7 +43641,9 @@ var LeaderboardDetailView = memo(({
|
|
|
43588
43641
|
refreshWorkspaces
|
|
43589
43642
|
} = useAllWorkspaceMetrics({
|
|
43590
43643
|
initialDate: date,
|
|
43591
|
-
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
|
|
43644
|
+
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
|
|
43645
|
+
allowedLineIds: userAccessibleLineIds
|
|
43646
|
+
// Filter to user's accessible lines only
|
|
43592
43647
|
});
|
|
43593
43648
|
const getShiftName = useCallback((shiftId2) => {
|
|
43594
43649
|
if (shiftId2 === void 0) return "Day";
|
|
@@ -44944,12 +44999,17 @@ var ACTION_NAMES = {
|
|
|
44944
44999
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
44945
45000
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
44946
45001
|
const pph = 3600 / cycleTime;
|
|
44947
|
-
return
|
|
45002
|
+
return Math.round(pph);
|
|
44948
45003
|
};
|
|
44949
45004
|
var calculateDayOutput = (pph, shiftHours, breaks = []) => {
|
|
44950
45005
|
if (pph === "") return "";
|
|
44951
45006
|
return Math.round(pph * shiftHours);
|
|
44952
45007
|
};
|
|
45008
|
+
var calculateDayOutputFromCycleTime = (cycleTime, shiftHours) => {
|
|
45009
|
+
if (cycleTime === "" || cycleTime === 0 || shiftHours === 0) return "";
|
|
45010
|
+
const dayOutput = 3600 / cycleTime * shiftHours;
|
|
45011
|
+
return Math.round(dayOutput);
|
|
45012
|
+
};
|
|
44953
45013
|
var formatWorkspaceName = (name, lineId) => {
|
|
44954
45014
|
return getWorkspaceDisplayName(name, lineId);
|
|
44955
45015
|
};
|
|
@@ -44961,20 +45021,6 @@ var getStoredLineState2 = (lineId) => {
|
|
|
44961
45021
|
return false;
|
|
44962
45022
|
}
|
|
44963
45023
|
};
|
|
44964
|
-
var calculateShiftHours2 = (startTime, endTime, breaks = []) => {
|
|
44965
|
-
if (!startTime || !endTime) return 8;
|
|
44966
|
-
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
44967
|
-
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
44968
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
44969
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
44970
|
-
if (endMinutes < startMinutes) {
|
|
44971
|
-
endMinutes += 24 * 60;
|
|
44972
|
-
}
|
|
44973
|
-
const safeBreaks = Array.isArray(breaks) ? breaks : [];
|
|
44974
|
-
const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
|
|
44975
|
-
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
44976
|
-
return Number(hoursDiff.toFixed(1));
|
|
44977
|
-
};
|
|
44978
45024
|
var BulkConfigureModal = ({
|
|
44979
45025
|
isOpen,
|
|
44980
45026
|
onClose,
|
|
@@ -46182,121 +46228,6 @@ var TargetsView = ({
|
|
|
46182
46228
|
const dashboardConfig = useDashboardConfig();
|
|
46183
46229
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
46184
46230
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
46185
|
-
const loadOperatingHours = useCallback(async (lineId, shiftId) => {
|
|
46186
|
-
try {
|
|
46187
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
46188
|
-
if (!session?.access_token) {
|
|
46189
|
-
console.error("No authentication token available");
|
|
46190
|
-
return {
|
|
46191
|
-
startTime: "08:00",
|
|
46192
|
-
// Default values
|
|
46193
|
-
endTime: "19:00",
|
|
46194
|
-
breaks: []
|
|
46195
|
-
};
|
|
46196
|
-
}
|
|
46197
|
-
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL;
|
|
46198
|
-
if (!backendUrl) {
|
|
46199
|
-
console.error("Backend URL is not configured");
|
|
46200
|
-
return {
|
|
46201
|
-
startTime: "08:00",
|
|
46202
|
-
// Default values
|
|
46203
|
-
endTime: "19:00",
|
|
46204
|
-
breaks: []
|
|
46205
|
-
};
|
|
46206
|
-
}
|
|
46207
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}/operating-hours?shift_id=${shiftId}`, {
|
|
46208
|
-
headers: {
|
|
46209
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46210
|
-
"Content-Type": "application/json"
|
|
46211
|
-
}
|
|
46212
|
-
});
|
|
46213
|
-
if (!response.ok) {
|
|
46214
|
-
console.log(`No operating hours found for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46215
|
-
return {
|
|
46216
|
-
startTime: "08:00",
|
|
46217
|
-
// Default values
|
|
46218
|
-
endTime: "19:00",
|
|
46219
|
-
breaks: []
|
|
46220
|
-
};
|
|
46221
|
-
}
|
|
46222
|
-
const data = await response.json();
|
|
46223
|
-
let breaks = [];
|
|
46224
|
-
if (data?.breaks) {
|
|
46225
|
-
if (Array.isArray(data.breaks)) {
|
|
46226
|
-
breaks = data.breaks.map((breakItem) => {
|
|
46227
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46228
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46229
|
-
const calculateDuration = (start, end) => {
|
|
46230
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46231
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46232
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46233
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46234
|
-
if (endMinutes < startMinutes) {
|
|
46235
|
-
endMinutes += 24 * 60;
|
|
46236
|
-
}
|
|
46237
|
-
return endMinutes - startMinutes;
|
|
46238
|
-
};
|
|
46239
|
-
return {
|
|
46240
|
-
startTime,
|
|
46241
|
-
endTime,
|
|
46242
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46243
|
-
};
|
|
46244
|
-
});
|
|
46245
|
-
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
46246
|
-
breaks = data.breaks.breaks.map((breakItem) => {
|
|
46247
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
46248
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
46249
|
-
const calculateDuration = (start, end) => {
|
|
46250
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
46251
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
46252
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
46253
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
46254
|
-
if (endMinutes < startMinutes) {
|
|
46255
|
-
endMinutes += 24 * 60;
|
|
46256
|
-
}
|
|
46257
|
-
return endMinutes - startMinutes;
|
|
46258
|
-
};
|
|
46259
|
-
return {
|
|
46260
|
-
startTime,
|
|
46261
|
-
endTime,
|
|
46262
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
46263
|
-
};
|
|
46264
|
-
});
|
|
46265
|
-
} else if (typeof data.breaks === "string") {
|
|
46266
|
-
try {
|
|
46267
|
-
const parsedBreaks = JSON.parse(data.breaks);
|
|
46268
|
-
if (Array.isArray(parsedBreaks)) {
|
|
46269
|
-
breaks = parsedBreaks.map((breakItem) => ({
|
|
46270
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46271
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46272
|
-
duration: breakItem.duration || 0
|
|
46273
|
-
}));
|
|
46274
|
-
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
46275
|
-
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
46276
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
46277
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
46278
|
-
duration: breakItem.duration || 0
|
|
46279
|
-
}));
|
|
46280
|
-
}
|
|
46281
|
-
} catch (e) {
|
|
46282
|
-
console.error("Error parsing breaks data:", e);
|
|
46283
|
-
}
|
|
46284
|
-
}
|
|
46285
|
-
}
|
|
46286
|
-
return {
|
|
46287
|
-
startTime: data?.start_time || "08:00",
|
|
46288
|
-
endTime: data?.end_time || "19:00",
|
|
46289
|
-
breaks
|
|
46290
|
-
};
|
|
46291
|
-
} catch (e) {
|
|
46292
|
-
console.error("Exception when loading operating hours:", e);
|
|
46293
|
-
return {
|
|
46294
|
-
startTime: "08:00",
|
|
46295
|
-
endTime: "19:00",
|
|
46296
|
-
breaks: []
|
|
46297
|
-
};
|
|
46298
|
-
}
|
|
46299
|
-
}, [supabase]);
|
|
46300
46231
|
useEffect(() => {
|
|
46301
46232
|
console.log("[TargetsView] Component mounted/re-rendered", {
|
|
46302
46233
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -46308,221 +46239,108 @@ var TargetsView = ({
|
|
|
46308
46239
|
};
|
|
46309
46240
|
}, []);
|
|
46310
46241
|
useEffect(() => {
|
|
46311
|
-
let timeoutId;
|
|
46312
|
-
let retryCount = 0;
|
|
46313
|
-
const MAX_RETRIES2 = 2;
|
|
46314
|
-
const LOADING_TIMEOUT = 15e3;
|
|
46315
46242
|
const fetchInitialData = async () => {
|
|
46316
|
-
if (lineIds.length === 0) return;
|
|
46317
|
-
console.log("[TargetsView] Starting fetchInitialData");
|
|
46243
|
+
if (lineIds.length === 0 || !companyId) return;
|
|
46244
|
+
console.log("[TargetsView] Starting optimized fetchInitialData with bulk endpoint");
|
|
46318
46245
|
setIsLoading(true);
|
|
46319
|
-
timeoutId = setTimeout(() => {
|
|
46320
|
-
console.error("Loading timeout reached");
|
|
46321
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46322
|
-
retryCount++;
|
|
46323
|
-
console.log(`Retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46324
|
-
toast.warning("Loading is taking longer than expected. Retrying...");
|
|
46325
|
-
fetchInitialData();
|
|
46326
|
-
} else {
|
|
46327
|
-
setIsLoading(false);
|
|
46328
|
-
toast.error("Failed to load data. Please refresh the page.");
|
|
46329
|
-
}
|
|
46330
|
-
}, LOADING_TIMEOUT);
|
|
46331
46246
|
try {
|
|
46332
|
-
const
|
|
46333
|
-
|
|
46334
|
-
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
|
|
46247
|
+
const currentDate = getOperationalDate(timezone);
|
|
46248
|
+
const bulkResponse = await workspaceService.fetchBulkTargets({
|
|
46249
|
+
companyId,
|
|
46250
|
+
lineIds,
|
|
46251
|
+
date: currentDate,
|
|
46252
|
+
shifts: [0, 1],
|
|
46253
|
+
// Fetch both shifts at once
|
|
46254
|
+
includeSkus: skuEnabled,
|
|
46255
|
+
includeActions: true
|
|
46256
|
+
});
|
|
46257
|
+
if (!bulkResponse.success) {
|
|
46258
|
+
throw new Error("Failed to fetch bulk targets data");
|
|
46339
46259
|
}
|
|
46340
|
-
const
|
|
46341
|
-
|
|
46342
|
-
|
|
46343
|
-
try {
|
|
46344
|
-
const response = await fetch(`${backendUrl}/api/lines/${lineId}`, {
|
|
46345
|
-
headers: {
|
|
46346
|
-
"Authorization": `Bearer ${session.access_token}`,
|
|
46347
|
-
"Content-Type": "application/json"
|
|
46348
|
-
}
|
|
46349
|
-
});
|
|
46350
|
-
if (!response.ok) {
|
|
46351
|
-
console.error(`Error fetching line data for ${lineId}:`, response.statusText);
|
|
46352
|
-
return { lineId, factoryId: void 0 };
|
|
46353
|
-
}
|
|
46354
|
-
const lineData = await response.json();
|
|
46355
|
-
return { lineId, factoryId: lineData.factory_id };
|
|
46356
|
-
} catch (err) {
|
|
46357
|
-
console.error(`Exception fetching line data for ${lineId}:`, err);
|
|
46358
|
-
return { lineId, factoryId: void 0 };
|
|
46359
|
-
}
|
|
46360
|
-
})),
|
|
46361
|
-
// Fetch action IDs
|
|
46362
|
-
actionService.getActionsByName(
|
|
46363
|
-
[ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
|
|
46364
|
-
companyId
|
|
46365
|
-
)
|
|
46366
|
-
]);
|
|
46367
|
-
const factoryResults = linesResponse;
|
|
46368
|
-
const assemblyAction = actions.find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46369
|
-
const packagingAction = actions.find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46260
|
+
const { data } = bulkResponse;
|
|
46261
|
+
const assemblyAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
|
|
46262
|
+
const packagingAction = Object.values(data.actions).find((a) => a.action_name === ACTION_NAMES.PACKAGING);
|
|
46370
46263
|
if (!assemblyAction || !packagingAction) {
|
|
46371
|
-
throw new Error("Could not find required actions");
|
|
46264
|
+
throw new Error("Could not find required actions in bulk response");
|
|
46372
46265
|
}
|
|
46373
46266
|
const actionIdsData = {
|
|
46374
46267
|
assembly: assemblyAction.id,
|
|
46375
46268
|
packaging: packagingAction.id
|
|
46376
46269
|
};
|
|
46377
46270
|
setActionIds(actionIdsData);
|
|
46378
|
-
const
|
|
46379
|
-
|
|
46380
|
-
|
|
46381
|
-
|
|
46382
|
-
|
|
46383
|
-
|
|
46384
|
-
|
|
46385
|
-
|
|
46386
|
-
if (!updatedLineWorkspaces[lineId]?.factoryId) {
|
|
46387
|
-
console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
|
|
46388
|
-
continue;
|
|
46389
|
-
}
|
|
46390
|
-
try {
|
|
46391
|
-
const workspacesData = await workspaceService.getWorkspaces(lineId);
|
|
46392
|
-
const enabledWorkspaces = workspacesData.filter((ws) => ws.enable === true);
|
|
46393
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46394
|
-
lineId,
|
|
46395
|
-
currentDate,
|
|
46396
|
-
0
|
|
46397
|
-
// Always use day shift for initial load
|
|
46398
|
-
);
|
|
46399
|
-
const operatingHoursData = await loadOperatingHours(lineId, 0);
|
|
46400
|
-
if (operatingHoursData) {
|
|
46401
|
-
updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
|
|
46402
|
-
updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
|
|
46403
|
-
updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
|
|
46404
|
-
updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
|
|
46405
|
-
operatingHoursData.startTime,
|
|
46406
|
-
operatingHoursData.endTime,
|
|
46407
|
-
operatingHoursData.breaks
|
|
46408
|
-
);
|
|
46271
|
+
const newAllShiftsData = { 0: {}, 1: {} };
|
|
46272
|
+
const newDbValues = { 0: {}, 1: {} };
|
|
46273
|
+
Object.entries(data.lines).forEach(([lineId, lineData]) => {
|
|
46274
|
+
[0, 1].forEach((shiftId) => {
|
|
46275
|
+
const shiftData = lineData.shifts[shiftId.toString()];
|
|
46276
|
+
if (!shiftData) {
|
|
46277
|
+
console.warn(`No shift ${shiftId} data for line ${lineId}`);
|
|
46278
|
+
return;
|
|
46409
46279
|
}
|
|
46280
|
+
const operatingHours = shiftData.operating_hours || {
|
|
46281
|
+
start_time: "08:00",
|
|
46282
|
+
end_time: "19:00",
|
|
46283
|
+
breaks: [],
|
|
46284
|
+
total_hours: 11
|
|
46285
|
+
};
|
|
46286
|
+
newAllShiftsData[shiftId][lineId] = {
|
|
46287
|
+
productId: "",
|
|
46288
|
+
shiftStartTime: operatingHours.start_time,
|
|
46289
|
+
shiftEndTime: operatingHours.end_time,
|
|
46290
|
+
breaks: operatingHours.breaks,
|
|
46291
|
+
shiftHours: operatingHours.total_hours,
|
|
46292
|
+
workspaces: [],
|
|
46293
|
+
factoryId: lineData.line_info.factory_id
|
|
46294
|
+
};
|
|
46295
|
+
newDbValues[shiftId][lineId] = {};
|
|
46296
|
+
const enabledWorkspaces = lineData.workspaces.filter((ws) => ws.enable === true);
|
|
46410
46297
|
const mappedWorkspaces = enabledWorkspaces.map((ws) => {
|
|
46411
|
-
const threshold =
|
|
46412
|
-
if (!dbValues[0][lineId]) {
|
|
46413
|
-
dbValues[0][lineId] = {};
|
|
46414
|
-
}
|
|
46298
|
+
const threshold = shiftData.thresholds.find((t) => t.workspace_id === ws.id);
|
|
46415
46299
|
if (threshold) {
|
|
46416
|
-
|
|
46417
|
-
targetPPH: threshold.pph_threshold,
|
|
46418
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46419
|
-
targetDayOutput: threshold.total_day_output
|
|
46300
|
+
newDbValues[shiftId][lineId][ws.id] = {
|
|
46301
|
+
targetPPH: threshold.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46302
|
+
targetCycleTime: threshold.ideal_cycle_time ?? "",
|
|
46303
|
+
targetDayOutput: threshold.total_day_output ?? ""
|
|
46420
46304
|
};
|
|
46421
46305
|
}
|
|
46306
|
+
let actionType = "assembly";
|
|
46307
|
+
let actionId = actionIdsData.assembly;
|
|
46308
|
+
if (ws.action_id === packagingAction.id || ws.action_type === "packaging") {
|
|
46309
|
+
actionType = "packaging";
|
|
46310
|
+
actionId = packagingAction.id;
|
|
46311
|
+
} else if (ws.action_id === assemblyAction.id || ws.action_type === "assembly") {
|
|
46312
|
+
actionType = "assembly";
|
|
46313
|
+
actionId = assemblyAction.id;
|
|
46314
|
+
}
|
|
46422
46315
|
return {
|
|
46423
46316
|
id: ws.id,
|
|
46424
46317
|
name: ws.workspace_id,
|
|
46425
|
-
targetPPH: threshold?.pph_threshold
|
|
46426
|
-
targetCycleTime: threshold?.ideal_cycle_time ??
|
|
46427
|
-
targetDayOutput: threshold?.total_day_output ??
|
|
46428
|
-
actionType
|
|
46429
|
-
actionId
|
|
46318
|
+
targetPPH: threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : "",
|
|
46319
|
+
targetCycleTime: threshold?.ideal_cycle_time ?? "",
|
|
46320
|
+
targetDayOutput: threshold?.total_day_output ?? "",
|
|
46321
|
+
actionType,
|
|
46322
|
+
actionId
|
|
46430
46323
|
};
|
|
46431
46324
|
}).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
|
|
46432
|
-
|
|
46433
|
-
}
|
|
46434
|
-
|
|
46435
|
-
|
|
46436
|
-
|
|
46437
|
-
|
|
46438
|
-
|
|
46325
|
+
newAllShiftsData[shiftId][lineId].workspaces = mappedWorkspaces;
|
|
46326
|
+
});
|
|
46327
|
+
});
|
|
46328
|
+
setAllShiftsData(newAllShiftsData);
|
|
46329
|
+
setDbValues(newDbValues);
|
|
46330
|
+
console.log("[TargetsView] Successfully loaded all data with bulk endpoint:", {
|
|
46331
|
+
lineCount: bulkResponse.metadata.line_count,
|
|
46332
|
+
totalWorkspaces: bulkResponse.metadata.total_workspaces,
|
|
46333
|
+
loadTime: "Fast!"
|
|
46334
|
+
});
|
|
46439
46335
|
} catch (error) {
|
|
46440
|
-
console.error("Error fetching
|
|
46441
|
-
|
|
46442
|
-
if (retryCount < MAX_RETRIES2) {
|
|
46443
|
-
retryCount++;
|
|
46444
|
-
console.log(`Error occurred, retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
|
|
46445
|
-
toast.warning("Error loading data. Retrying...");
|
|
46446
|
-
setTimeout(() => fetchInitialData(), 1e3);
|
|
46447
|
-
} else {
|
|
46448
|
-
toast.error("Failed to load initial data");
|
|
46449
|
-
setIsLoading(false);
|
|
46450
|
-
}
|
|
46336
|
+
console.error("[TargetsView] Error fetching bulk data:", error);
|
|
46337
|
+
toast.error("Failed to load targets data. Please refresh the page.");
|
|
46451
46338
|
} finally {
|
|
46452
|
-
|
|
46453
|
-
if (retryCount === 0 || retryCount >= MAX_RETRIES2) {
|
|
46454
|
-
setIsLoading(false);
|
|
46455
|
-
}
|
|
46339
|
+
setIsLoading(false);
|
|
46456
46340
|
}
|
|
46457
46341
|
};
|
|
46458
46342
|
fetchInitialData();
|
|
46459
|
-
|
|
46460
|
-
clearTimeout(timeoutId);
|
|
46461
|
-
};
|
|
46462
|
-
}, [lineIds, companyId, loadOperatingHours]);
|
|
46463
|
-
const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
|
|
46464
|
-
if (!supabase) return;
|
|
46465
|
-
const currentDate = getOperationalDate(timezone);
|
|
46466
|
-
const newAllShiftsData = {
|
|
46467
|
-
0: JSON.parse(JSON.stringify(currentWorkspaces)),
|
|
46468
|
-
// Deep clone for day shift
|
|
46469
|
-
1: JSON.parse(JSON.stringify(currentWorkspaces))
|
|
46470
|
-
// Deep clone for night shift
|
|
46471
|
-
};
|
|
46472
|
-
const newDbValues = { 0: {}, 1: {} };
|
|
46473
|
-
for (const shiftId of [0, 1]) {
|
|
46474
|
-
for (const lineId of lineIds) {
|
|
46475
|
-
try {
|
|
46476
|
-
const operatingHoursData = await loadOperatingHours(lineId, shiftId);
|
|
46477
|
-
if (!operatingHoursData) {
|
|
46478
|
-
console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
46479
|
-
continue;
|
|
46480
|
-
}
|
|
46481
|
-
const { startTime, endTime, breaks } = operatingHoursData;
|
|
46482
|
-
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
46483
|
-
const actionThresholds = await workspaceService.getActionThresholds(
|
|
46484
|
-
lineId,
|
|
46485
|
-
currentDate,
|
|
46486
|
-
shiftId
|
|
46487
|
-
);
|
|
46488
|
-
if (!newDbValues[shiftId][lineId]) {
|
|
46489
|
-
newDbValues[shiftId][lineId] = {};
|
|
46490
|
-
}
|
|
46491
|
-
const existingLine = newAllShiftsData[shiftId][lineId];
|
|
46492
|
-
if (existingLine) {
|
|
46493
|
-
newAllShiftsData[shiftId][lineId] = {
|
|
46494
|
-
...existingLine,
|
|
46495
|
-
shiftStartTime: startTime,
|
|
46496
|
-
shiftEndTime: endTime,
|
|
46497
|
-
breaks,
|
|
46498
|
-
shiftHours: Number(shiftHours),
|
|
46499
|
-
workspaces: existingLine.workspaces.map((ws) => {
|
|
46500
|
-
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
46501
|
-
if (threshold) {
|
|
46502
|
-
newDbValues[shiftId][lineId][ws.id] = {
|
|
46503
|
-
targetPPH: threshold.pph_threshold,
|
|
46504
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46505
|
-
targetDayOutput: threshold.total_day_output
|
|
46506
|
-
};
|
|
46507
|
-
return {
|
|
46508
|
-
...ws,
|
|
46509
|
-
targetPPH: threshold.pph_threshold,
|
|
46510
|
-
targetCycleTime: threshold.ideal_cycle_time,
|
|
46511
|
-
targetDayOutput: threshold.total_day_output
|
|
46512
|
-
};
|
|
46513
|
-
}
|
|
46514
|
-
return ws;
|
|
46515
|
-
})
|
|
46516
|
-
};
|
|
46517
|
-
}
|
|
46518
|
-
} catch (error) {
|
|
46519
|
-
console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
|
|
46520
|
-
}
|
|
46521
|
-
}
|
|
46522
|
-
}
|
|
46523
|
-
setAllShiftsData(newAllShiftsData);
|
|
46524
|
-
setDbValues(newDbValues);
|
|
46525
|
-
}, [supabase, lineIds, loadOperatingHours]);
|
|
46343
|
+
}, [lineIds, companyId, timezone, skuEnabled]);
|
|
46526
46344
|
const toggleLineDropdown = useCallback((lineId) => {
|
|
46527
46345
|
setDropdownStates((prev) => {
|
|
46528
46346
|
const newIsOpen = !prev[lineId];
|
|
@@ -46595,7 +46413,7 @@ var TargetsView = ({
|
|
|
46595
46413
|
if (value !== "") {
|
|
46596
46414
|
const pph = calculatePPH(value, prev[lineId].breaks, shiftHours);
|
|
46597
46415
|
updates.targetPPH = pph;
|
|
46598
|
-
updates.targetDayOutput =
|
|
46416
|
+
updates.targetDayOutput = calculateDayOutputFromCycleTime(value, shiftHours);
|
|
46599
46417
|
} else {
|
|
46600
46418
|
updates.targetPPH = "";
|
|
46601
46419
|
updates.targetDayOutput = "";
|
|
@@ -46713,7 +46531,8 @@ var TargetsView = ({
|
|
|
46713
46531
|
action_id: ws.actionId,
|
|
46714
46532
|
workspace_id: ws.id,
|
|
46715
46533
|
date: currentDate,
|
|
46716
|
-
pph_threshold: Number(ws.targetPPH)
|
|
46534
|
+
pph_threshold: ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0,
|
|
46535
|
+
// Round to whole number
|
|
46717
46536
|
ideal_cycle_time: Number(ws.targetCycleTime) || 0,
|
|
46718
46537
|
total_day_output: Number(ws.targetDayOutput) || 0,
|
|
46719
46538
|
action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
|
|
@@ -46732,7 +46551,8 @@ var TargetsView = ({
|
|
|
46732
46551
|
shift_id: selectedShift,
|
|
46733
46552
|
product_code: lineDataToSave.productId,
|
|
46734
46553
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
46735
|
-
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH)
|
|
46554
|
+
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
46555
|
+
// Round each PPH value
|
|
46736
46556
|
...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
|
|
46737
46557
|
};
|
|
46738
46558
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
@@ -48484,94 +48304,14 @@ var SupervisorManagementView = ({
|
|
|
48484
48304
|
] });
|
|
48485
48305
|
};
|
|
48486
48306
|
var SupervisorManagementView_default = SupervisorManagementView;
|
|
48487
|
-
var
|
|
48488
|
-
|
|
48489
|
-
|
|
48490
|
-
|
|
48491
|
-
|
|
48492
|
-
|
|
48493
|
-
|
|
48494
|
-
|
|
48495
|
-
],
|
|
48496
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48497
|
-
},
|
|
48498
|
-
supervisor_to_owner: {
|
|
48499
|
-
title: "Supervisor \u2192 Owner",
|
|
48500
|
-
warnings: [
|
|
48501
|
-
"Will gain full company access",
|
|
48502
|
-
"Will lose line-specific restrictions",
|
|
48503
|
-
"Will be able to manage all users",
|
|
48504
|
-
"Will see all company data"
|
|
48505
|
-
],
|
|
48506
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48507
|
-
},
|
|
48508
|
-
plant_head_to_supervisor: {
|
|
48509
|
-
title: "Plant Head \u2192 Supervisor",
|
|
48510
|
-
warnings: [
|
|
48511
|
-
"Will lose factory access",
|
|
48512
|
-
"Will lose ability to manage others",
|
|
48513
|
-
"Will need line assignment",
|
|
48514
|
-
"Will only see assigned line data"
|
|
48515
|
-
],
|
|
48516
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48517
|
-
},
|
|
48518
|
-
plant_head_to_owner: {
|
|
48519
|
-
title: "Plant Head \u2192 Owner",
|
|
48520
|
-
warnings: [
|
|
48521
|
-
"Will gain full company access",
|
|
48522
|
-
"Will lose factory-specific restrictions",
|
|
48523
|
-
"Will be able to manage all users",
|
|
48524
|
-
"Will see all company data"
|
|
48525
|
-
],
|
|
48526
|
-
colorClass: "text-blue-700 bg-blue-50 border-blue-200"
|
|
48527
|
-
},
|
|
48528
|
-
owner_to_plant_head: {
|
|
48529
|
-
title: "Owner \u2192 Plant Head",
|
|
48530
|
-
warnings: [
|
|
48531
|
-
"Will lose company-wide access",
|
|
48532
|
-
"Will need factory assignment",
|
|
48533
|
-
"Will only manage supervisors",
|
|
48534
|
-
"Will only see factory data"
|
|
48535
|
-
],
|
|
48536
|
-
colorClass: "text-orange-700 bg-orange-50 border-orange-200"
|
|
48537
|
-
},
|
|
48538
|
-
owner_to_supervisor: {
|
|
48539
|
-
title: "Owner \u2192 Supervisor",
|
|
48540
|
-
warnings: [
|
|
48541
|
-
"Will lose company-wide access",
|
|
48542
|
-
"Will lose ability to manage others",
|
|
48543
|
-
"Will need line assignment",
|
|
48544
|
-
"Will only see assigned line data"
|
|
48545
|
-
],
|
|
48546
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48547
|
-
},
|
|
48548
|
-
to_optifye: {
|
|
48549
|
-
title: "Promoting to Optifye",
|
|
48550
|
-
warnings: [
|
|
48551
|
-
"Will gain access to ALL companies",
|
|
48552
|
-
"Will be able to manage all users globally",
|
|
48553
|
-
"Will see all data across the platform",
|
|
48554
|
-
"This is the highest privilege level"
|
|
48555
|
-
],
|
|
48556
|
-
colorClass: "text-purple-700 bg-purple-50 border-purple-200"
|
|
48557
|
-
},
|
|
48558
|
-
from_optifye: {
|
|
48559
|
-
title: "Demoting from Optifye",
|
|
48560
|
-
warnings: [
|
|
48561
|
-
"Will lose access to other companies",
|
|
48562
|
-
"Will only see assigned company data",
|
|
48563
|
-
"Will lose global management capabilities",
|
|
48564
|
-
"Will need appropriate assignments"
|
|
48565
|
-
],
|
|
48566
|
-
colorClass: "text-red-700 bg-red-50 border-red-200"
|
|
48567
|
-
}
|
|
48568
|
-
};
|
|
48569
|
-
var getRoleChangeKey = (currentRole, newRole) => {
|
|
48570
|
-
if (currentRole === newRole) return null;
|
|
48571
|
-
if (newRole === "optifye") return "to_optifye";
|
|
48572
|
-
if (currentRole === "optifye") return "from_optifye";
|
|
48573
|
-
const key = `${currentRole}_to_${newRole}`;
|
|
48574
|
-
return roleChangeImpacts[key] ? key : null;
|
|
48307
|
+
var getRoleLabel = (role) => {
|
|
48308
|
+
const labels = {
|
|
48309
|
+
"supervisor": "Supervisor",
|
|
48310
|
+
"plant_head": "Plant Head",
|
|
48311
|
+
"owner": "Owner",
|
|
48312
|
+
"optifye": "Optifye Admin"
|
|
48313
|
+
};
|
|
48314
|
+
return labels[role] || role;
|
|
48575
48315
|
};
|
|
48576
48316
|
var ChangeRoleDialog = ({
|
|
48577
48317
|
user,
|
|
@@ -48584,8 +48324,6 @@ var ChangeRoleDialog = ({
|
|
|
48584
48324
|
const [confirmed, setConfirmed] = useState(false);
|
|
48585
48325
|
const [isChanging, setIsChanging] = useState(false);
|
|
48586
48326
|
const roleChanged = selectedRole !== user.role_level;
|
|
48587
|
-
const changeKey = roleChanged ? getRoleChangeKey(user.role_level, selectedRole) : null;
|
|
48588
|
-
const impactInfo = changeKey ? roleChangeImpacts[changeKey] : null;
|
|
48589
48327
|
const handleNext = () => {
|
|
48590
48328
|
if (roleChanged) {
|
|
48591
48329
|
setStep("confirm");
|
|
@@ -48605,7 +48343,7 @@ var ChangeRoleDialog = ({
|
|
|
48605
48343
|
setStep("select");
|
|
48606
48344
|
setConfirmed(false);
|
|
48607
48345
|
};
|
|
48608
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-
|
|
48346
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48609
48347
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48610
48348
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48611
48349
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(UserCog, { className: "w-5 h-5 text-blue-600" }) }),
|
|
@@ -48615,20 +48353,14 @@ var ChangeRoleDialog = ({
|
|
|
48615
48353
|
"button",
|
|
48616
48354
|
{
|
|
48617
48355
|
onClick: onClose,
|
|
48618
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48356
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48619
48357
|
disabled: isChanging,
|
|
48358
|
+
"aria-label": "Close dialog",
|
|
48620
48359
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48621
48360
|
}
|
|
48622
48361
|
)
|
|
48623
48362
|
] }),
|
|
48624
|
-
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-
|
|
48625
|
-
roleChanged && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4 bg-amber-50 border border-amber-200 rounded-lg", children: [
|
|
48626
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "w-5 h-5 text-amber-600 mt-0.5 flex-shrink-0" }),
|
|
48627
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
48628
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-amber-900 text-sm", children: "Changing roles affects access" }),
|
|
48629
|
-
/* @__PURE__ */ jsx("p", { className: "text-amber-700 text-sm mt-1", children: "Review the impact carefully before confirming." })
|
|
48630
|
-
] })
|
|
48631
|
-
] }),
|
|
48363
|
+
/* @__PURE__ */ jsx("div", { className: "p-6 space-y-5", children: step === "select" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
48632
48364
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48633
48365
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48634
48366
|
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-gray-50 rounded-lg", children: [
|
|
@@ -48639,7 +48371,7 @@ var ChangeRoleDialog = ({
|
|
|
48639
48371
|
] })
|
|
48640
48372
|
] })
|
|
48641
48373
|
] }),
|
|
48642
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48374
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48643
48375
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "Select new role" }),
|
|
48644
48376
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: availableRoles.map((role) => /* @__PURE__ */ jsxs(
|
|
48645
48377
|
"label",
|
|
@@ -48663,27 +48395,24 @@ var ChangeRoleDialog = ({
|
|
|
48663
48395
|
role
|
|
48664
48396
|
)) })
|
|
48665
48397
|
] }),
|
|
48666
|
-
|
|
48667
|
-
|
|
48668
|
-
/* @__PURE__ */ jsx("
|
|
48669
|
-
|
|
48670
|
-
|
|
48671
|
-
|
|
48672
|
-
] })
|
|
48398
|
+
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: [
|
|
48399
|
+
"Changing from ",
|
|
48400
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(user.role_level) }),
|
|
48401
|
+
" to ",
|
|
48402
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: getRoleLabel(selectedRole) }),
|
|
48403
|
+
" will modify user permissions."
|
|
48404
|
+
] }) })
|
|
48673
48405
|
] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
48674
48406
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-3 p-4 bg-gray-50 rounded-lg", children: [
|
|
48675
48407
|
/* @__PURE__ */ jsx(RoleBadge, { role: user.role_level }),
|
|
48676
48408
|
/* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5 text-gray-400" }),
|
|
48677
48409
|
/* @__PURE__ */ jsx(RoleBadge, { role: selectedRole })
|
|
48678
48410
|
] }),
|
|
48679
|
-
|
|
48680
|
-
/* @__PURE__ */ jsx("p", { className: "
|
|
48681
|
-
/* @__PURE__ */ jsx("
|
|
48682
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5", children: "\u2022" }),
|
|
48683
|
-
/* @__PURE__ */ jsx("span", { children: warning6 })
|
|
48684
|
-
] }, idx)) })
|
|
48411
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48412
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "User" }),
|
|
48413
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
48685
48414
|
] }),
|
|
48686
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-
|
|
48415
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48687
48416
|
/* @__PURE__ */ jsx(
|
|
48688
48417
|
"input",
|
|
48689
48418
|
{
|
|
@@ -48694,7 +48423,7 @@ var ChangeRoleDialog = ({
|
|
|
48694
48423
|
className: "mt-0.5 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
48695
48424
|
}
|
|
48696
48425
|
),
|
|
48697
|
-
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-role-change", className: "text-sm text-gray-700 cursor-pointer", children: "I
|
|
48426
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-role-change", className: "text-sm text-gray-700 cursor-pointer", children: "I confirm this role change and understand the permissions will be updated" })
|
|
48698
48427
|
] })
|
|
48699
48428
|
] }) }) }),
|
|
48700
48429
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: step === "select" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -48737,7 +48466,7 @@ var ChangeRoleDialog = ({
|
|
|
48737
48466
|
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
48738
48467
|
] }),
|
|
48739
48468
|
"Changing..."
|
|
48740
|
-
] }) : "Change
|
|
48469
|
+
] }) : "Confirm Change"
|
|
48741
48470
|
}
|
|
48742
48471
|
)
|
|
48743
48472
|
] }) })
|
|
@@ -48760,32 +48489,34 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48760
48489
|
setIsRemoving(false);
|
|
48761
48490
|
}
|
|
48762
48491
|
};
|
|
48763
|
-
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-
|
|
48492
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full mx-4 transform transition-all", children: [
|
|
48764
48493
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
48765
48494
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
48766
48495
|
/* @__PURE__ */ jsx("div", { className: "p-2 bg-red-100 rounded-lg", children: /* @__PURE__ */ jsx(Trash2, { className: "w-5 h-5 text-red-600" }) }),
|
|
48767
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "
|
|
48496
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Permanently Delete User" })
|
|
48768
48497
|
] }),
|
|
48769
48498
|
/* @__PURE__ */ jsx(
|
|
48770
48499
|
"button",
|
|
48771
48500
|
{
|
|
48772
48501
|
onClick: onCancel,
|
|
48773
|
-
className: "text-gray-400 hover:text-gray-600 transition-colors",
|
|
48502
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
|
|
48503
|
+
disabled: isRemoving,
|
|
48504
|
+
"aria-label": "Close dialog",
|
|
48774
48505
|
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
48775
48506
|
}
|
|
48776
48507
|
)
|
|
48777
48508
|
] }),
|
|
48778
|
-
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-
|
|
48779
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-
|
|
48780
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-
|
|
48781
|
-
/* @__PURE__ */ jsxs("
|
|
48782
|
-
/* @__PURE__ */ jsx("
|
|
48783
|
-
|
|
48509
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-5", children: [
|
|
48510
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 bg-red-50 border border-red-200 rounded-lg", children: [
|
|
48511
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4 text-red-600 mt-0.5 flex-shrink-0" }),
|
|
48512
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-red-900", children: [
|
|
48513
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "This action cannot be undone." }),
|
|
48514
|
+
" This will permanently delete the user and all related data from the system."
|
|
48784
48515
|
] })
|
|
48785
48516
|
] }),
|
|
48786
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
48787
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to
|
|
48788
|
-
/* @__PURE__ */ jsxs("div", { className: "p-
|
|
48517
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
48518
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "You are about to permanently delete:" }),
|
|
48519
|
+
/* @__PURE__ */ jsxs("div", { className: "p-3 bg-gray-50 rounded-lg space-y-2.5", children: [
|
|
48789
48520
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
48790
48521
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: "Email" }),
|
|
48791
48522
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: user.email })
|
|
@@ -48810,28 +48541,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48810
48541
|
] })
|
|
48811
48542
|
] })
|
|
48812
48543
|
] }),
|
|
48813
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
48814
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700", children: "This will:" }),
|
|
48815
|
-
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm text-gray-600", children: [
|
|
48816
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48817
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48818
|
-
/* @__PURE__ */ jsx("span", { children: "Revoke all access immediately" })
|
|
48819
|
-
] }),
|
|
48820
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48821
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48822
|
-
/* @__PURE__ */ jsx("span", { children: "Remove all line and factory assignments" })
|
|
48823
|
-
] }),
|
|
48824
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48825
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48826
|
-
/* @__PURE__ */ jsx("span", { children: "Log the user out of all sessions" })
|
|
48827
|
-
] }),
|
|
48828
|
-
/* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
|
|
48829
|
-
/* @__PURE__ */ jsx("span", { className: "text-red-500 mt-0.5", children: "\u2022" }),
|
|
48830
|
-
/* @__PURE__ */ jsx("span", { children: "User will not be able to access the dashboard" })
|
|
48831
|
-
] })
|
|
48832
|
-
] })
|
|
48833
|
-
] }),
|
|
48834
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4 border border-gray-200 rounded-lg", children: [
|
|
48544
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-3 border border-gray-200 rounded-lg bg-gray-50", children: [
|
|
48835
48545
|
/* @__PURE__ */ jsx(
|
|
48836
48546
|
"input",
|
|
48837
48547
|
{
|
|
@@ -48842,7 +48552,7 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48842
48552
|
className: "mt-0.5 h-4 w-4 text-red-600 border-gray-300 rounded focus:ring-red-500"
|
|
48843
48553
|
}
|
|
48844
48554
|
),
|
|
48845
|
-
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-remove", className: "text-sm text-gray-700 cursor-pointer", children: "I understand this
|
|
48555
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "confirm-remove", className: "text-sm text-gray-700 cursor-pointer", children: "I understand this will permanently delete the user and all related data, and this action cannot be undone" })
|
|
48846
48556
|
] })
|
|
48847
48557
|
] }),
|
|
48848
48558
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 p-6 border-t border-gray-200 bg-gray-50", children: [
|
|
@@ -48866,8 +48576,8 @@ var ConfirmRemoveUserDialog = ({
|
|
|
48866
48576
|
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
48867
48577
|
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
48868
48578
|
] }),
|
|
48869
|
-
"
|
|
48870
|
-
] }) : "
|
|
48579
|
+
"Deleting..."
|
|
48580
|
+
] }) : "Delete User"
|
|
48871
48581
|
}
|
|
48872
48582
|
)
|
|
48873
48583
|
] })
|
|
@@ -49290,7 +49000,6 @@ var FactoryAssignmentDropdown = ({
|
|
|
49290
49000
|
var UserManagementTable = ({
|
|
49291
49001
|
users,
|
|
49292
49002
|
isLoading = false,
|
|
49293
|
-
onUserClick,
|
|
49294
49003
|
onRoleChange,
|
|
49295
49004
|
onRemoveUser,
|
|
49296
49005
|
onLineAssignmentUpdate,
|
|
@@ -49316,9 +49025,48 @@ var UserManagementTable = ({
|
|
|
49316
49025
|
const [sortField, setSortField] = useState("email");
|
|
49317
49026
|
const [sortDirection, setSortDirection] = useState("asc");
|
|
49318
49027
|
const [openActionMenuId, setOpenActionMenuId] = useState(null);
|
|
49028
|
+
const [dropdownPosition, setDropdownPosition] = useState(null);
|
|
49029
|
+
const actionButtonRefs = useRef({});
|
|
49319
49030
|
const [selectedUser, setSelectedUser] = useState(null);
|
|
49320
49031
|
const [showChangeRoleDialog, setShowChangeRoleDialog] = useState(false);
|
|
49321
49032
|
const [showRemoveUserDialog, setShowRemoveUserDialog] = useState(false);
|
|
49033
|
+
const handleOpenActionMenu = (userId) => {
|
|
49034
|
+
const buttonRef = actionButtonRefs.current[userId];
|
|
49035
|
+
if (buttonRef) {
|
|
49036
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49037
|
+
setDropdownPosition({
|
|
49038
|
+
top: rect.bottom + 8,
|
|
49039
|
+
// 8px below the button
|
|
49040
|
+
left: rect.right - 192
|
|
49041
|
+
// 192px is the dropdown width (w-48 = 12rem = 192px)
|
|
49042
|
+
});
|
|
49043
|
+
setOpenActionMenuId(userId);
|
|
49044
|
+
}
|
|
49045
|
+
};
|
|
49046
|
+
const handleCloseActionMenu = () => {
|
|
49047
|
+
setOpenActionMenuId(null);
|
|
49048
|
+
setDropdownPosition(null);
|
|
49049
|
+
};
|
|
49050
|
+
useEffect(() => {
|
|
49051
|
+
if (openActionMenuId && actionButtonRefs.current[openActionMenuId]) {
|
|
49052
|
+
const updatePosition = () => {
|
|
49053
|
+
const buttonRef = actionButtonRefs.current[openActionMenuId];
|
|
49054
|
+
if (buttonRef) {
|
|
49055
|
+
const rect = buttonRef.getBoundingClientRect();
|
|
49056
|
+
setDropdownPosition({
|
|
49057
|
+
top: rect.bottom + 8,
|
|
49058
|
+
left: rect.right - 192
|
|
49059
|
+
});
|
|
49060
|
+
}
|
|
49061
|
+
};
|
|
49062
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
49063
|
+
window.addEventListener("resize", updatePosition);
|
|
49064
|
+
return () => {
|
|
49065
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
49066
|
+
window.removeEventListener("resize", updatePosition);
|
|
49067
|
+
};
|
|
49068
|
+
}
|
|
49069
|
+
}, [openActionMenuId]);
|
|
49322
49070
|
const filteredAndSortedUsers = useMemo(() => {
|
|
49323
49071
|
let filtered = users;
|
|
49324
49072
|
filtered = filtered.filter((user) => availableRoles.includes(user.role_level));
|
|
@@ -49509,7 +49257,7 @@ var UserManagementTable = ({
|
|
|
49509
49257
|
LineAssignmentDropdown,
|
|
49510
49258
|
{
|
|
49511
49259
|
userId: user.user_id,
|
|
49512
|
-
currentLineIds: user.properties?.
|
|
49260
|
+
currentLineIds: user.properties?.line_ids || [],
|
|
49513
49261
|
availableLines: (
|
|
49514
49262
|
// Filter lines to only show those from the target user's company
|
|
49515
49263
|
(() => {
|
|
@@ -49547,69 +49295,24 @@ var UserManagementTable = ({
|
|
|
49547
49295
|
}
|
|
49548
49296
|
) : /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-900", children: formatAssignments(user) }) }),
|
|
49549
49297
|
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: formatDate(user.created_at) }) }),
|
|
49550
|
-
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */
|
|
49551
|
-
|
|
49552
|
-
|
|
49553
|
-
{
|
|
49554
|
-
|
|
49555
|
-
|
|
49556
|
-
|
|
49557
|
-
|
|
49558
|
-
|
|
49559
|
-
|
|
49560
|
-
|
|
49561
|
-
/* @__PURE__ */ jsx(
|
|
49562
|
-
"div",
|
|
49563
|
-
{
|
|
49564
|
-
className: "fixed inset-0 z-[9998]",
|
|
49565
|
-
onClick: () => setOpenActionMenuId(null)
|
|
49298
|
+
/* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right", children: hasActions && /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
|
|
49299
|
+
"button",
|
|
49300
|
+
{
|
|
49301
|
+
ref: (el) => {
|
|
49302
|
+
actionButtonRefs.current[user.user_id] = el;
|
|
49303
|
+
},
|
|
49304
|
+
onClick: () => {
|
|
49305
|
+
if (openActionMenuId === user.user_id) {
|
|
49306
|
+
handleCloseActionMenu();
|
|
49307
|
+
} else {
|
|
49308
|
+
handleOpenActionMenu(user.user_id);
|
|
49566
49309
|
}
|
|
49567
|
-
|
|
49568
|
-
|
|
49569
|
-
|
|
49570
|
-
|
|
49571
|
-
|
|
49572
|
-
|
|
49573
|
-
onUserClick?.(user);
|
|
49574
|
-
setOpenActionMenuId(null);
|
|
49575
|
-
},
|
|
49576
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49577
|
-
children: [
|
|
49578
|
-
/* @__PURE__ */ jsx(Eye, { className: "w-4 h-4" }),
|
|
49579
|
-
"View Details"
|
|
49580
|
-
]
|
|
49581
|
-
}
|
|
49582
|
-
),
|
|
49583
|
-
canChangeRole && /* @__PURE__ */ jsxs(
|
|
49584
|
-
"button",
|
|
49585
|
-
{
|
|
49586
|
-
onClick: () => handleChangeRole(user),
|
|
49587
|
-
className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
|
|
49588
|
-
disabled: isCurrentUser,
|
|
49589
|
-
children: [
|
|
49590
|
-
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
49591
|
-
"Change Role"
|
|
49592
|
-
]
|
|
49593
|
-
}
|
|
49594
|
-
),
|
|
49595
|
-
canRemove && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49596
|
-
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49597
|
-
/* @__PURE__ */ jsxs(
|
|
49598
|
-
"button",
|
|
49599
|
-
{
|
|
49600
|
-
onClick: () => handleRemoveUser(user),
|
|
49601
|
-
className: "w-full px-4 py-2 text-sm text-left text-red-600 hover:bg-red-50 flex items-center gap-2",
|
|
49602
|
-
disabled: isCurrentUser,
|
|
49603
|
-
children: [
|
|
49604
|
-
/* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
|
|
49605
|
-
"Remove User"
|
|
49606
|
-
]
|
|
49607
|
-
}
|
|
49608
|
-
)
|
|
49609
|
-
] })
|
|
49610
|
-
] }) })
|
|
49611
|
-
] })
|
|
49612
|
-
] }) })
|
|
49310
|
+
},
|
|
49311
|
+
className: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
49312
|
+
"aria-label": "User actions",
|
|
49313
|
+
children: /* @__PURE__ */ jsx(MoreVertical, { className: "w-4 h-4" })
|
|
49314
|
+
}
|
|
49315
|
+
) }) })
|
|
49613
49316
|
]
|
|
49614
49317
|
},
|
|
49615
49318
|
user.user_id
|
|
@@ -49617,6 +49320,75 @@ var UserManagementTable = ({
|
|
|
49617
49320
|
}) })
|
|
49618
49321
|
] }) }) })
|
|
49619
49322
|
] }),
|
|
49323
|
+
openActionMenuId && dropdownPosition && typeof document !== "undefined" && createPortal(
|
|
49324
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49325
|
+
/* @__PURE__ */ jsx(
|
|
49326
|
+
"div",
|
|
49327
|
+
{
|
|
49328
|
+
className: "fixed inset-0 z-[9998]",
|
|
49329
|
+
onClick: handleCloseActionMenu
|
|
49330
|
+
}
|
|
49331
|
+
),
|
|
49332
|
+
/* @__PURE__ */ jsx(
|
|
49333
|
+
"div",
|
|
49334
|
+
{
|
|
49335
|
+
className: "fixed w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-[9999]",
|
|
49336
|
+
style: {
|
|
49337
|
+
top: `${dropdownPosition.top}px`,
|
|
49338
|
+
left: `${dropdownPosition.left}px`
|
|
49339
|
+
},
|
|
49340
|
+
children: /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
|
|
49341
|
+
(() => {
|
|
49342
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49343
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49344
|
+
const canChangeRole = user && permissions.canChangeRole(user);
|
|
49345
|
+
return canChangeRole && /* @__PURE__ */ jsxs(
|
|
49346
|
+
"button",
|
|
49347
|
+
{
|
|
49348
|
+
onClick: () => {
|
|
49349
|
+
if (user) {
|
|
49350
|
+
handleChangeRole(user);
|
|
49351
|
+
}
|
|
49352
|
+
},
|
|
49353
|
+
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",
|
|
49354
|
+
disabled: isCurrentUser,
|
|
49355
|
+
children: [
|
|
49356
|
+
/* @__PURE__ */ jsx(UserCog, { className: "w-4 h-4" }),
|
|
49357
|
+
"Change Role"
|
|
49358
|
+
]
|
|
49359
|
+
}
|
|
49360
|
+
);
|
|
49361
|
+
})(),
|
|
49362
|
+
(() => {
|
|
49363
|
+
const user = users.find((u) => u.user_id === openActionMenuId);
|
|
49364
|
+
const isCurrentUser = user?.user_id === currentUserId;
|
|
49365
|
+
const canRemove = user && permissions.canRemoveUser(user);
|
|
49366
|
+
return canRemove && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49367
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
|
|
49368
|
+
/* @__PURE__ */ jsxs(
|
|
49369
|
+
"button",
|
|
49370
|
+
{
|
|
49371
|
+
onClick: () => {
|
|
49372
|
+
if (user) {
|
|
49373
|
+
handleRemoveUser(user);
|
|
49374
|
+
}
|
|
49375
|
+
},
|
|
49376
|
+
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",
|
|
49377
|
+
disabled: isCurrentUser,
|
|
49378
|
+
children: [
|
|
49379
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }),
|
|
49380
|
+
"Delete User"
|
|
49381
|
+
]
|
|
49382
|
+
}
|
|
49383
|
+
)
|
|
49384
|
+
] });
|
|
49385
|
+
})()
|
|
49386
|
+
] })
|
|
49387
|
+
}
|
|
49388
|
+
)
|
|
49389
|
+
] }),
|
|
49390
|
+
document.body
|
|
49391
|
+
),
|
|
49620
49392
|
showChangeRoleDialog && selectedUser && /* @__PURE__ */ jsx(
|
|
49621
49393
|
ChangeRoleDialog,
|
|
49622
49394
|
{
|
|
@@ -50268,15 +50040,12 @@ var TeamManagementView = ({
|
|
|
50268
50040
|
if (!supabase || !user) return;
|
|
50269
50041
|
try {
|
|
50270
50042
|
const userManagementService = createUserManagementService(supabase);
|
|
50271
|
-
await userManagementService.
|
|
50272
|
-
|
|
50273
|
-
deactivated_by: user.id
|
|
50274
|
-
});
|
|
50275
|
-
toast.success("User removed successfully");
|
|
50043
|
+
const result = await userManagementService.deleteUser(userId);
|
|
50044
|
+
toast.success(`User ${result.deleted_user_email} permanently deleted`);
|
|
50276
50045
|
loadData();
|
|
50277
50046
|
} catch (err) {
|
|
50278
|
-
console.error("Error
|
|
50279
|
-
toast.error("Failed to
|
|
50047
|
+
console.error("Error deleting user:", err);
|
|
50048
|
+
toast.error("Failed to delete user");
|
|
50280
50049
|
}
|
|
50281
50050
|
}, [supabase, user, loadData]);
|
|
50282
50051
|
const handleLineAssignmentUpdate = useCallback(async (userId, lineIds) => {
|
|
@@ -50370,9 +50139,6 @@ var TeamManagementView = ({
|
|
|
50370
50139
|
{
|
|
50371
50140
|
users,
|
|
50372
50141
|
currentUserId: user?.id,
|
|
50373
|
-
onUserClick: (clickedUser) => {
|
|
50374
|
-
console.log("View user details:", clickedUser);
|
|
50375
|
-
},
|
|
50376
50142
|
onRoleChange: handleRoleChange,
|
|
50377
50143
|
onRemoveUser: handleRemoveUser,
|
|
50378
50144
|
onLineAssignmentUpdate: handleLineAssignmentUpdate,
|