@optifye/dashboard-core 6.9.1 → 6.9.2
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 -4
- package/dist/index.d.mts +12 -10
- package/dist/index.d.ts +12 -10
- package/dist/index.js +217 -77
- package/dist/index.mjs +217 -77
- package/package.json +1 -1
package/dist/index.css
CHANGED
|
@@ -2454,10 +2454,6 @@ body {
|
|
|
2454
2454
|
--tw-bg-opacity: 1;
|
|
2455
2455
|
background-color: rgb(254 226 226 / var(--tw-bg-opacity, 1));
|
|
2456
2456
|
}
|
|
2457
|
-
.bg-red-400 {
|
|
2458
|
-
--tw-bg-opacity: 1;
|
|
2459
|
-
background-color: rgb(248 113 113 / var(--tw-bg-opacity, 1));
|
|
2460
|
-
}
|
|
2461
2457
|
.bg-red-400\/50 {
|
|
2462
2458
|
background-color: rgb(248 113 113 / 0.5);
|
|
2463
2459
|
}
|
|
@@ -5631,6 +5627,10 @@ input[type=range]:active::-moz-range-thumb {
|
|
|
5631
5627
|
padding-top: 0.75rem;
|
|
5632
5628
|
padding-bottom: 0.75rem;
|
|
5633
5629
|
}
|
|
5630
|
+
.lg\:py-3\.5 {
|
|
5631
|
+
padding-top: 0.875rem;
|
|
5632
|
+
padding-bottom: 0.875rem;
|
|
5633
|
+
}
|
|
5634
5634
|
.lg\:text-2xl {
|
|
5635
5635
|
font-size: 1.5rem;
|
|
5636
5636
|
line-height: 2rem;
|
package/dist/index.d.mts
CHANGED
|
@@ -3607,13 +3607,13 @@ interface UseWorkspaceHealthStatusReturn {
|
|
|
3607
3607
|
*
|
|
3608
3608
|
* @description This hook subscribes to the workspace_health_status table and monitors
|
|
3609
3609
|
* the last_heartbeat column for real-time updates. It automatically formats the time
|
|
3610
|
-
*
|
|
3610
|
+
* with whole numbers (e.g., "Less than a minute ago", "4 minutes ago") and updates periodically.
|
|
3611
3611
|
*
|
|
3612
3612
|
* @param {string} workspaceId - The workspace UUID to monitor
|
|
3613
3613
|
*
|
|
3614
3614
|
* @returns {UseWorkspaceHealthStatusReturn} Object containing:
|
|
3615
3615
|
* - lastHeartbeat: ISO timestamp string or null
|
|
3616
|
-
* - timeSinceUpdate: Formatted relative time string (e.g., "
|
|
3616
|
+
* - timeSinceUpdate: Formatted relative time string (e.g., "Less than a minute ago", "4 minutes ago")
|
|
3617
3617
|
* - isHealthy: Boolean health status flag
|
|
3618
3618
|
* - healthData: Full health status record from database
|
|
3619
3619
|
* - loading: Loading state
|
|
@@ -3622,7 +3622,7 @@ interface UseWorkspaceHealthStatusReturn {
|
|
|
3622
3622
|
*
|
|
3623
3623
|
* @example
|
|
3624
3624
|
* const { lastHeartbeat, timeSinceUpdate, isHealthy } = useWorkspaceHealthStatus('workspace-123');
|
|
3625
|
-
* // timeSinceUpdate: "
|
|
3625
|
+
* // timeSinceUpdate: "Less than a minute ago" or "4 minutes ago"
|
|
3626
3626
|
*/
|
|
3627
3627
|
declare const useWorkspaceHealthStatus: (workspaceId: string) => UseWorkspaceHealthStatusReturn;
|
|
3628
3628
|
|
|
@@ -3708,17 +3708,18 @@ interface UseSessionKeepAliveOptions {
|
|
|
3708
3708
|
*/
|
|
3709
3709
|
declare const useSessionKeepAlive: (options?: UseSessionKeepAliveOptions) => void;
|
|
3710
3710
|
|
|
3711
|
-
type UserRole = 'owner' | 'plant_head' | 'supervisor';
|
|
3711
|
+
type UserRole = 'owner' | 'plant_head' | 'supervisor' | 'optifye';
|
|
3712
3712
|
interface AccessControlReturn {
|
|
3713
3713
|
userRole: UserRole | null;
|
|
3714
3714
|
hasAccess: (path: string) => boolean;
|
|
3715
3715
|
accessiblePages: string[];
|
|
3716
3716
|
isPageVisible: (path: string) => boolean;
|
|
3717
3717
|
canAccessPage: (path: string) => boolean;
|
|
3718
|
+
assignedLineIds: string[];
|
|
3719
|
+
assignedFactoryIds: string[];
|
|
3718
3720
|
}
|
|
3719
3721
|
/**
|
|
3720
3722
|
* Hook to manage role-based access control
|
|
3721
|
-
* TEMPORARILY DISABLED: All users have access to all pages
|
|
3722
3723
|
*
|
|
3723
3724
|
* @returns {AccessControlReturn} Access control utilities and state
|
|
3724
3725
|
*/
|
|
@@ -3914,6 +3915,7 @@ interface LineSupervisor {
|
|
|
3914
3915
|
interface UseLineSupervisorReturn {
|
|
3915
3916
|
supervisorName: string | null;
|
|
3916
3917
|
supervisor: LineSupervisor | null;
|
|
3918
|
+
supervisors: LineSupervisor[];
|
|
3917
3919
|
isLoading: boolean;
|
|
3918
3920
|
error: Error | null;
|
|
3919
3921
|
}
|
|
@@ -4919,15 +4921,15 @@ declare const getCompanyMetricsTableName: (companyId: string | undefined, prefix
|
|
|
4919
4921
|
declare const getMetricsTablePrefix: (companyId?: string) => string;
|
|
4920
4922
|
|
|
4921
4923
|
/**
|
|
4922
|
-
* Format a timestamp as relative time with
|
|
4923
|
-
* Shows
|
|
4924
|
+
* Format a timestamp as relative time with whole number precision
|
|
4925
|
+
* Shows minutes, hours, or days ago without real-time seconds
|
|
4924
4926
|
*
|
|
4925
4927
|
* @param timestamp - Date object or ISO string timestamp
|
|
4926
|
-
* @returns Formatted relative time string (e.g., "
|
|
4928
|
+
* @returns Formatted relative time string (e.g., "Less than a minute ago", "4 minutes ago", "2 hours ago", "3 days ago")
|
|
4927
4929
|
*
|
|
4928
4930
|
* @example
|
|
4929
|
-
* formatRelativeTime(new Date()) // "
|
|
4930
|
-
* formatRelativeTime("2024-01-01T12:00:00Z") // "
|
|
4931
|
+
* formatRelativeTime(new Date()) // "Less than a minute ago"
|
|
4932
|
+
* formatRelativeTime("2024-01-01T12:00:00Z") // "4 minutes ago"
|
|
4931
4933
|
*/
|
|
4932
4934
|
declare function formatRelativeTime(timestamp: string | Date | null | undefined): string;
|
|
4933
4935
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -3607,13 +3607,13 @@ interface UseWorkspaceHealthStatusReturn {
|
|
|
3607
3607
|
*
|
|
3608
3608
|
* @description This hook subscribes to the workspace_health_status table and monitors
|
|
3609
3609
|
* the last_heartbeat column for real-time updates. It automatically formats the time
|
|
3610
|
-
*
|
|
3610
|
+
* with whole numbers (e.g., "Less than a minute ago", "4 minutes ago") and updates periodically.
|
|
3611
3611
|
*
|
|
3612
3612
|
* @param {string} workspaceId - The workspace UUID to monitor
|
|
3613
3613
|
*
|
|
3614
3614
|
* @returns {UseWorkspaceHealthStatusReturn} Object containing:
|
|
3615
3615
|
* - lastHeartbeat: ISO timestamp string or null
|
|
3616
|
-
* - timeSinceUpdate: Formatted relative time string (e.g., "
|
|
3616
|
+
* - timeSinceUpdate: Formatted relative time string (e.g., "Less than a minute ago", "4 minutes ago")
|
|
3617
3617
|
* - isHealthy: Boolean health status flag
|
|
3618
3618
|
* - healthData: Full health status record from database
|
|
3619
3619
|
* - loading: Loading state
|
|
@@ -3622,7 +3622,7 @@ interface UseWorkspaceHealthStatusReturn {
|
|
|
3622
3622
|
*
|
|
3623
3623
|
* @example
|
|
3624
3624
|
* const { lastHeartbeat, timeSinceUpdate, isHealthy } = useWorkspaceHealthStatus('workspace-123');
|
|
3625
|
-
* // timeSinceUpdate: "
|
|
3625
|
+
* // timeSinceUpdate: "Less than a minute ago" or "4 minutes ago"
|
|
3626
3626
|
*/
|
|
3627
3627
|
declare const useWorkspaceHealthStatus: (workspaceId: string) => UseWorkspaceHealthStatusReturn;
|
|
3628
3628
|
|
|
@@ -3708,17 +3708,18 @@ interface UseSessionKeepAliveOptions {
|
|
|
3708
3708
|
*/
|
|
3709
3709
|
declare const useSessionKeepAlive: (options?: UseSessionKeepAliveOptions) => void;
|
|
3710
3710
|
|
|
3711
|
-
type UserRole = 'owner' | 'plant_head' | 'supervisor';
|
|
3711
|
+
type UserRole = 'owner' | 'plant_head' | 'supervisor' | 'optifye';
|
|
3712
3712
|
interface AccessControlReturn {
|
|
3713
3713
|
userRole: UserRole | null;
|
|
3714
3714
|
hasAccess: (path: string) => boolean;
|
|
3715
3715
|
accessiblePages: string[];
|
|
3716
3716
|
isPageVisible: (path: string) => boolean;
|
|
3717
3717
|
canAccessPage: (path: string) => boolean;
|
|
3718
|
+
assignedLineIds: string[];
|
|
3719
|
+
assignedFactoryIds: string[];
|
|
3718
3720
|
}
|
|
3719
3721
|
/**
|
|
3720
3722
|
* Hook to manage role-based access control
|
|
3721
|
-
* TEMPORARILY DISABLED: All users have access to all pages
|
|
3722
3723
|
*
|
|
3723
3724
|
* @returns {AccessControlReturn} Access control utilities and state
|
|
3724
3725
|
*/
|
|
@@ -3914,6 +3915,7 @@ interface LineSupervisor {
|
|
|
3914
3915
|
interface UseLineSupervisorReturn {
|
|
3915
3916
|
supervisorName: string | null;
|
|
3916
3917
|
supervisor: LineSupervisor | null;
|
|
3918
|
+
supervisors: LineSupervisor[];
|
|
3917
3919
|
isLoading: boolean;
|
|
3918
3920
|
error: Error | null;
|
|
3919
3921
|
}
|
|
@@ -4919,15 +4921,15 @@ declare const getCompanyMetricsTableName: (companyId: string | undefined, prefix
|
|
|
4919
4921
|
declare const getMetricsTablePrefix: (companyId?: string) => string;
|
|
4920
4922
|
|
|
4921
4923
|
/**
|
|
4922
|
-
* Format a timestamp as relative time with
|
|
4923
|
-
* Shows
|
|
4924
|
+
* Format a timestamp as relative time with whole number precision
|
|
4925
|
+
* Shows minutes, hours, or days ago without real-time seconds
|
|
4924
4926
|
*
|
|
4925
4927
|
* @param timestamp - Date object or ISO string timestamp
|
|
4926
|
-
* @returns Formatted relative time string (e.g., "
|
|
4928
|
+
* @returns Formatted relative time string (e.g., "Less than a minute ago", "4 minutes ago", "2 hours ago", "3 days ago")
|
|
4927
4929
|
*
|
|
4928
4930
|
* @example
|
|
4929
|
-
* formatRelativeTime(new Date()) // "
|
|
4930
|
-
* formatRelativeTime("2024-01-01T12:00:00Z") // "
|
|
4931
|
+
* formatRelativeTime(new Date()) // "Less than a minute ago"
|
|
4932
|
+
* formatRelativeTime("2024-01-01T12:00:00Z") // "4 minutes ago"
|
|
4931
4933
|
*/
|
|
4932
4934
|
declare function formatRelativeTime(timestamp: string | Date | null | undefined): string;
|
|
4933
4935
|
/**
|
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
|
|
@@ -3246,7 +3246,7 @@ var SSEChatClient = class {
|
|
|
3246
3246
|
user_id: userId,
|
|
3247
3247
|
context
|
|
3248
3248
|
});
|
|
3249
|
-
const agnoApiUrl = this.baseUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
3249
|
+
const agnoApiUrl = this.baseUrl || (process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app");
|
|
3250
3250
|
const endpoint = `${agnoApiUrl}/api/v2/chat`;
|
|
3251
3251
|
console.log("[SSEClient] Posting directly to AGNO:", endpoint);
|
|
3252
3252
|
const response = await fetch(endpoint, {
|
|
@@ -10739,23 +10739,18 @@ function formatRelativeTime(timestamp) {
|
|
|
10739
10739
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10740
10740
|
const diffDays = Math.floor(diffHours / 24);
|
|
10741
10741
|
if (diffSeconds < 60) {
|
|
10742
|
-
return
|
|
10742
|
+
return "Less than a minute ago";
|
|
10743
10743
|
}
|
|
10744
10744
|
if (diffMinutes < 60) {
|
|
10745
|
-
const
|
|
10746
|
-
|
|
10747
|
-
return `${diffMinutes}m ago`;
|
|
10748
|
-
}
|
|
10749
|
-
return `${diffMinutes}m ${remainingSeconds}s ago`;
|
|
10745
|
+
const minuteLabel = diffMinutes === 1 ? "minute" : "minutes";
|
|
10746
|
+
return `${diffMinutes} ${minuteLabel} ago`;
|
|
10750
10747
|
}
|
|
10751
10748
|
if (diffHours < 24) {
|
|
10752
|
-
const
|
|
10753
|
-
|
|
10754
|
-
return `${diffHours}h ago`;
|
|
10755
|
-
}
|
|
10756
|
-
return `${diffHours}h ${remainingMinutes}m ago`;
|
|
10749
|
+
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
10750
|
+
return `${diffHours} ${hourLabel} ago`;
|
|
10757
10751
|
}
|
|
10758
|
-
|
|
10752
|
+
const dayLabel = diffDays === 1 ? "day" : "days";
|
|
10753
|
+
return `${diffDays} ${dayLabel} ago`;
|
|
10759
10754
|
} catch (error) {
|
|
10760
10755
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
10761
10756
|
return "Unknown";
|
|
@@ -10771,8 +10766,8 @@ function getNextUpdateInterval(timestamp) {
|
|
|
10771
10766
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
10772
10767
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
10773
10768
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10774
|
-
if (diffSeconds < 60) return
|
|
10775
|
-
if (diffMinutes < 60) return
|
|
10769
|
+
if (diffSeconds < 60) return 1e4;
|
|
10770
|
+
if (diffMinutes < 60) return 6e4;
|
|
10776
10771
|
if (diffHours < 24) return 6e4;
|
|
10777
10772
|
return 36e5;
|
|
10778
10773
|
} catch (error) {
|
|
@@ -10949,50 +10944,129 @@ function useAccessControl() {
|
|
|
10949
10944
|
const userRole = React23.useMemo(() => {
|
|
10950
10945
|
if (!user?.role_level) return null;
|
|
10951
10946
|
const roleLevel = user.role_level;
|
|
10952
|
-
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
10947
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
|
|
10953
10948
|
return roleLevel;
|
|
10954
10949
|
}
|
|
10955
10950
|
return "supervisor";
|
|
10956
10951
|
}, [user?.role_level]);
|
|
10957
|
-
const
|
|
10958
|
-
|
|
10959
|
-
"
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
"
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10952
|
+
const assignedLineIds = React23.useMemo(() => {
|
|
10953
|
+
if (!user) return [];
|
|
10954
|
+
if (user.role_level === "supervisor") {
|
|
10955
|
+
const lines = user.properties?.line_id || user.properties?.line_ids || [];
|
|
10956
|
+
return Array.isArray(lines) ? lines : [];
|
|
10957
|
+
}
|
|
10958
|
+
return [];
|
|
10959
|
+
}, [user]);
|
|
10960
|
+
const assignedFactoryIds = React23.useMemo(() => {
|
|
10961
|
+
if (!user) return [];
|
|
10962
|
+
if (user.role_level === "plant_head") {
|
|
10963
|
+
const factories = user.properties?.factory_id || user.properties?.factory_ids || [];
|
|
10964
|
+
return Array.isArray(factories) ? factories : [];
|
|
10965
|
+
}
|
|
10966
|
+
return [];
|
|
10967
|
+
}, [user]);
|
|
10968
|
+
const roleAccessMap = {
|
|
10969
|
+
optifye: [
|
|
10970
|
+
"/",
|
|
10971
|
+
"/leaderboard",
|
|
10972
|
+
"/kpis",
|
|
10973
|
+
"/targets",
|
|
10974
|
+
"/shifts",
|
|
10975
|
+
"/supervisor-management",
|
|
10976
|
+
"/skus",
|
|
10977
|
+
"/ai-agent",
|
|
10978
|
+
"/help",
|
|
10979
|
+
"/health",
|
|
10980
|
+
"/profile",
|
|
10981
|
+
"/workspace",
|
|
10982
|
+
"/factory-view",
|
|
10983
|
+
"/team-management"
|
|
10984
|
+
],
|
|
10985
|
+
owner: [
|
|
10986
|
+
"/",
|
|
10987
|
+
"/leaderboard",
|
|
10988
|
+
"/kpis",
|
|
10989
|
+
"/targets",
|
|
10990
|
+
"/shifts",
|
|
10991
|
+
"/supervisor-management",
|
|
10992
|
+
"/skus",
|
|
10993
|
+
"/ai-agent",
|
|
10994
|
+
"/help",
|
|
10995
|
+
"/health",
|
|
10996
|
+
"/profile",
|
|
10997
|
+
"/workspace",
|
|
10998
|
+
"/factory-view",
|
|
10999
|
+
"/team-management"
|
|
11000
|
+
],
|
|
11001
|
+
plant_head: [
|
|
11002
|
+
"/",
|
|
11003
|
+
"/leaderboard",
|
|
11004
|
+
"/kpis",
|
|
11005
|
+
"/targets",
|
|
11006
|
+
"/shifts",
|
|
11007
|
+
"/supervisor-management",
|
|
11008
|
+
"/skus",
|
|
11009
|
+
"/ai-agent",
|
|
11010
|
+
"/help",
|
|
11011
|
+
"/health",
|
|
11012
|
+
"/profile",
|
|
11013
|
+
"/workspace",
|
|
11014
|
+
"/factory-view",
|
|
11015
|
+
"/team-management"
|
|
11016
|
+
],
|
|
11017
|
+
supervisor: [
|
|
11018
|
+
"/",
|
|
11019
|
+
"/leaderboard",
|
|
11020
|
+
"/kpis",
|
|
11021
|
+
"/targets",
|
|
11022
|
+
"/shifts",
|
|
11023
|
+
"/skus",
|
|
11024
|
+
"/ai-agent",
|
|
11025
|
+
"/help",
|
|
11026
|
+
"/health",
|
|
11027
|
+
"/profile",
|
|
11028
|
+
"/workspace"
|
|
11029
|
+
]
|
|
11030
|
+
};
|
|
10972
11031
|
const accessiblePages = React23.useMemo(() => {
|
|
10973
|
-
return
|
|
10974
|
-
|
|
11032
|
+
if (!userRole) return [];
|
|
11033
|
+
return roleAccessMap[userRole] || [];
|
|
11034
|
+
}, [userRole]);
|
|
10975
11035
|
const hasAccess = React23.useMemo(() => {
|
|
10976
11036
|
return (path) => {
|
|
10977
|
-
return
|
|
11037
|
+
if (!userRole) return false;
|
|
11038
|
+
const basePath = path.split("?")[0].split("/").slice(0, 2).join("/");
|
|
11039
|
+
const hasBaseAccess = accessiblePages.includes(basePath) || accessiblePages.includes("/");
|
|
11040
|
+
if (userRole === "supervisor" && assignedLineIds.length > 0) {
|
|
11041
|
+
if (path.includes("/kpis/") || path.includes("/workspace/")) {
|
|
11042
|
+
const lineIdMatch = path.match(/\/kpis\/([^/?]+)/);
|
|
11043
|
+
if (lineIdMatch) {
|
|
11044
|
+
const pathLineId = lineIdMatch[1];
|
|
11045
|
+
return assignedLineIds.includes(pathLineId);
|
|
11046
|
+
}
|
|
11047
|
+
}
|
|
11048
|
+
}
|
|
11049
|
+
return hasBaseAccess;
|
|
10978
11050
|
};
|
|
10979
|
-
}, []);
|
|
11051
|
+
}, [userRole, accessiblePages, assignedLineIds]);
|
|
10980
11052
|
const isPageVisible = React23.useMemo(() => {
|
|
10981
11053
|
return (path) => {
|
|
10982
|
-
return
|
|
11054
|
+
return hasAccess(path);
|
|
10983
11055
|
};
|
|
10984
|
-
}, []);
|
|
11056
|
+
}, [hasAccess]);
|
|
10985
11057
|
const canAccessPage = React23.useMemo(() => {
|
|
10986
11058
|
return (path) => {
|
|
10987
|
-
return
|
|
11059
|
+
return hasAccess(path);
|
|
10988
11060
|
};
|
|
10989
|
-
}, []);
|
|
11061
|
+
}, [hasAccess]);
|
|
10990
11062
|
return {
|
|
10991
11063
|
userRole,
|
|
10992
11064
|
hasAccess,
|
|
10993
11065
|
accessiblePages,
|
|
10994
11066
|
isPageVisible,
|
|
10995
|
-
canAccessPage
|
|
11067
|
+
canAccessPage,
|
|
11068
|
+
assignedLineIds,
|
|
11069
|
+
assignedFactoryIds
|
|
10996
11070
|
};
|
|
10997
11071
|
}
|
|
10998
11072
|
|
|
@@ -11156,45 +11230,51 @@ function useHasLineAccess(lineId, configLineIds) {
|
|
|
11156
11230
|
function useLineSupervisor(lineId) {
|
|
11157
11231
|
const supabase = useSupabase();
|
|
11158
11232
|
const [supervisor, setSupervisor] = React23.useState(null);
|
|
11233
|
+
const [supervisors, setSupervisors] = React23.useState([]);
|
|
11159
11234
|
const [isLoading, setIsLoading] = React23.useState(true);
|
|
11160
11235
|
const [error, setError] = React23.useState(null);
|
|
11161
|
-
console.log("[useLineSupervisor] Hook initialized for lineId:", lineId);
|
|
11162
11236
|
const fetchSupervisor = React23.useCallback(async () => {
|
|
11163
11237
|
if (!lineId || !supabase) {
|
|
11164
|
-
console.log("[useLineSupervisor] Skipping fetch - lineId or supabase missing:", { lineId, hasSupabase: !!supabase });
|
|
11165
11238
|
setIsLoading(false);
|
|
11166
11239
|
return;
|
|
11167
11240
|
}
|
|
11168
11241
|
try {
|
|
11169
11242
|
setIsLoading(true);
|
|
11170
11243
|
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 });
|
|
11244
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor");
|
|
11174
11245
|
if (fetchError) {
|
|
11175
11246
|
console.error("[useLineSupervisor] Query error:", fetchError);
|
|
11176
11247
|
throw fetchError;
|
|
11177
11248
|
}
|
|
11178
11249
|
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
|
|
11250
|
+
const supervisorsForLine = data.filter((supervisorData) => {
|
|
11251
|
+
const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
|
|
11252
|
+
const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
|
|
11253
|
+
return isAssigned;
|
|
11190
11254
|
});
|
|
11255
|
+
if (supervisorsForLine.length > 0) {
|
|
11256
|
+
const allSupervisors = supervisorsForLine.map((supervisorData) => {
|
|
11257
|
+
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11258
|
+
return {
|
|
11259
|
+
userId: supervisorData.user_id,
|
|
11260
|
+
email: supervisorData.email,
|
|
11261
|
+
displayName
|
|
11262
|
+
};
|
|
11263
|
+
});
|
|
11264
|
+
setSupervisors(allSupervisors);
|
|
11265
|
+
setSupervisor(allSupervisors[0]);
|
|
11266
|
+
} else {
|
|
11267
|
+
setSupervisors([]);
|
|
11268
|
+
setSupervisor(null);
|
|
11269
|
+
}
|
|
11191
11270
|
} else {
|
|
11192
|
-
|
|
11271
|
+
setSupervisors([]);
|
|
11193
11272
|
setSupervisor(null);
|
|
11194
11273
|
}
|
|
11195
11274
|
} catch (err) {
|
|
11196
|
-
console.error("[useLineSupervisor] Error fetching
|
|
11197
|
-
setError(err instanceof Error ? err : new Error("Failed to fetch
|
|
11275
|
+
console.error("[useLineSupervisor] Error fetching supervisors:", err);
|
|
11276
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
11277
|
+
setSupervisors([]);
|
|
11198
11278
|
setSupervisor(null);
|
|
11199
11279
|
} finally {
|
|
11200
11280
|
setIsLoading(false);
|
|
@@ -11219,7 +11299,6 @@ function useLineSupervisor(lineId) {
|
|
|
11219
11299
|
filter: `role_level=eq.supervisor`
|
|
11220
11300
|
},
|
|
11221
11301
|
(payload) => {
|
|
11222
|
-
console.log("[useLineSupervisor] Real-time update received:", payload);
|
|
11223
11302
|
fetchSupervisor();
|
|
11224
11303
|
}
|
|
11225
11304
|
).subscribe();
|
|
@@ -11231,9 +11310,11 @@ function useLineSupervisor(lineId) {
|
|
|
11231
11310
|
}
|
|
11232
11311
|
};
|
|
11233
11312
|
}, [lineId, supabase, fetchSupervisor]);
|
|
11313
|
+
const supervisorName = supervisors.length > 0 ? supervisors.map((s) => s.displayName).join(", ") : null;
|
|
11234
11314
|
return {
|
|
11235
|
-
supervisorName
|
|
11315
|
+
supervisorName,
|
|
11236
11316
|
supervisor,
|
|
11317
|
+
supervisors,
|
|
11237
11318
|
isLoading,
|
|
11238
11319
|
error
|
|
11239
11320
|
};
|
|
@@ -34104,14 +34185,16 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
34104
34185
|
};
|
|
34105
34186
|
var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
34106
34187
|
const [isGenerating, setIsGenerating] = React23.useState(false);
|
|
34188
|
+
const entityConfig = useEntityConfig();
|
|
34107
34189
|
const generatePDF = async () => {
|
|
34108
34190
|
setIsGenerating(true);
|
|
34109
34191
|
try {
|
|
34192
|
+
const lineName = workspace.line_name || getLineDisplayName(entityConfig, workspace.line_id);
|
|
34110
34193
|
trackCoreEvent("Workspace PDF Export Clicked", {
|
|
34111
34194
|
workspace_id: workspace.workspace_id,
|
|
34112
34195
|
line_id: workspace.line_id,
|
|
34113
34196
|
workspace_name: workspace.workspace_name,
|
|
34114
|
-
line_name:
|
|
34197
|
+
line_name: lineName
|
|
34115
34198
|
});
|
|
34116
34199
|
const doc = new jsPDF.jsPDF();
|
|
34117
34200
|
doc.setFontSize(14);
|
|
@@ -34132,7 +34215,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34132
34215
|
doc.setFontSize(32);
|
|
34133
34216
|
doc.setFont("helvetica", "bold");
|
|
34134
34217
|
doc.setTextColor(0, 0, 0);
|
|
34135
|
-
doc.text(
|
|
34218
|
+
doc.text(lineName, 20, 40);
|
|
34136
34219
|
doc.setFontSize(22);
|
|
34137
34220
|
doc.setFont("helvetica", "normal");
|
|
34138
34221
|
doc.setTextColor(40, 40, 40);
|
|
@@ -38832,7 +38915,7 @@ var AIAgentView = () => {
|
|
|
38832
38915
|
const renderedContentCache = React23.useRef(/* @__PURE__ */ new Map());
|
|
38833
38916
|
const { createThread, mutate: mutateThreads } = useThreads();
|
|
38834
38917
|
const { messages, addMessage, setMessages } = useMessages(activeThreadId);
|
|
38835
|
-
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://
|
|
38918
|
+
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
38836
38919
|
const sseClient = React23.useMemo(() => {
|
|
38837
38920
|
console.log("[AIAgentView] Using AGNO API URL:", agnoApiUrl);
|
|
38838
38921
|
return new SSEChatClient(agnoApiUrl);
|
|
@@ -46938,6 +47021,7 @@ var WorkspaceDetailView = ({
|
|
|
46938
47021
|
const [showIdleTime, setShowIdleTime] = React23.useState(false);
|
|
46939
47022
|
const dashboardConfig = useDashboardConfig();
|
|
46940
47023
|
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
47024
|
+
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
46941
47025
|
const {
|
|
46942
47026
|
workspace: workspaceHealth,
|
|
46943
47027
|
loading: healthLoading,
|
|
@@ -46946,6 +47030,36 @@ var WorkspaceDetailView = ({
|
|
|
46946
47030
|
enableRealtime: true,
|
|
46947
47031
|
refreshInterval: 3e4
|
|
46948
47032
|
});
|
|
47033
|
+
const {
|
|
47034
|
+
isHealthy: isWorkspaceHealthy,
|
|
47035
|
+
timeSinceUpdate,
|
|
47036
|
+
lastHeartbeat,
|
|
47037
|
+
loading: healthStatusLoading,
|
|
47038
|
+
error: healthStatusError
|
|
47039
|
+
} = useWorkspaceHealthStatus(workspaceId);
|
|
47040
|
+
const isLive = React23.useMemo(() => {
|
|
47041
|
+
if (!lastHeartbeat) return false;
|
|
47042
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
47043
|
+
const heartbeat = new Date(lastHeartbeat);
|
|
47044
|
+
const minutesSince = (now2.getTime() - heartbeat.getTime()) / 6e4;
|
|
47045
|
+
return minutesSince < 5;
|
|
47046
|
+
}, [lastHeartbeat]);
|
|
47047
|
+
React23.useEffect(() => {
|
|
47048
|
+
console.log("[WorkspaceDetailView] Workspace Health Status:", {
|
|
47049
|
+
workspaceId,
|
|
47050
|
+
// Old workspace_health table
|
|
47051
|
+
oldStatus: workspaceHealth?.status,
|
|
47052
|
+
oldLastHeartbeat: workspaceHealth?.last_heartbeat,
|
|
47053
|
+
oldTimeSinceLastUpdate: workspaceHealth?.timeSinceLastUpdate,
|
|
47054
|
+
// New workspace_health_status table
|
|
47055
|
+
isHealthy: isWorkspaceHealthy,
|
|
47056
|
+
isLive,
|
|
47057
|
+
lastHeartbeat,
|
|
47058
|
+
timeSinceUpdate,
|
|
47059
|
+
loading: healthLoading || healthStatusLoading,
|
|
47060
|
+
error: healthError || healthStatusError
|
|
47061
|
+
});
|
|
47062
|
+
}, [workspaceId, workspaceHealth, isWorkspaceHealthy, isLive, lastHeartbeat, timeSinceUpdate, healthLoading, healthStatusLoading, healthError, healthStatusError]);
|
|
46949
47063
|
const {
|
|
46950
47064
|
status: prefetchStatus,
|
|
46951
47065
|
data: prefetchData,
|
|
@@ -46982,6 +47096,7 @@ var WorkspaceDetailView = ({
|
|
|
46982
47096
|
const workspace = isHistoricView ? historicMetrics : liveMetrics;
|
|
46983
47097
|
const loading = isHistoricView ? historicLoading : liveLoading;
|
|
46984
47098
|
const error = isHistoricView ? historicError : liveError;
|
|
47099
|
+
const { supervisorName } = useLineSupervisor(workspace?.line_id || lineId);
|
|
46985
47100
|
React23.useEffect(() => {
|
|
46986
47101
|
if (onTabChange) {
|
|
46987
47102
|
onTabChange(activeTab);
|
|
@@ -47204,7 +47319,7 @@ var WorkspaceDetailView = ({
|
|
|
47204
47319
|
initial: { opacity: 1 },
|
|
47205
47320
|
animate: { opacity: 1 },
|
|
47206
47321
|
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-
|
|
47322
|
+
/* @__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
47323
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
47209
47324
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47210
47325
|
"button",
|
|
@@ -47218,15 +47333,15 @@ var WorkspaceDetailView = ({
|
|
|
47218
47333
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
|
|
47219
47334
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
47220
47335
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base font-semibold text-gray-900 truncate max-w-[220px]", children: formattedWorkspaceName }),
|
|
47221
|
-
|
|
47222
|
-
|
|
47336
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-2 w-2", children: [
|
|
47337
|
+
isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47223
47338
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
47224
47339
|
"relative inline-flex rounded-full h-2 w-2",
|
|
47225
|
-
|
|
47340
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47226
47341
|
) })
|
|
47227
47342
|
] }) })
|
|
47228
47343
|
] }),
|
|
47229
|
-
activeTab !== "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
|
|
47344
|
+
activeTab !== "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-0.5 mt-1", children: [
|
|
47230
47345
|
workspaceHealth && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-gray-500", children: workspaceHealth.timeSinceLastUpdate }),
|
|
47231
47346
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47232
47347
|
WorkspaceHealthStatusBadge,
|
|
@@ -47253,14 +47368,11 @@ var WorkspaceDetailView = ({
|
|
|
47253
47368
|
) }),
|
|
47254
47369
|
/* @__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
47370
|
/* @__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
|
-
) }),
|
|
47371
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
|
|
47372
|
+
isLive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47261
47373
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: clsx(
|
|
47262
47374
|
"relative inline-flex rounded-full h-2.5 w-2.5",
|
|
47263
|
-
|
|
47375
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47264
47376
|
) })
|
|
47265
47377
|
] })
|
|
47266
47378
|
] }) }),
|
|
@@ -49648,6 +49760,34 @@ var InviteUserDialog = ({
|
|
|
49648
49760
|
throw new Error(data.error);
|
|
49649
49761
|
}
|
|
49650
49762
|
sonner.toast.success(data?.message || "User added successfully!");
|
|
49763
|
+
try {
|
|
49764
|
+
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
49765
|
+
if (dashboardUrl) {
|
|
49766
|
+
console.log("Sending welcome email to:", email.trim());
|
|
49767
|
+
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
49768
|
+
body: {
|
|
49769
|
+
action: "send-welcome-email",
|
|
49770
|
+
email: email.trim(),
|
|
49771
|
+
company_id: companyId,
|
|
49772
|
+
dashboard_url: dashboardUrl
|
|
49773
|
+
}
|
|
49774
|
+
});
|
|
49775
|
+
if (emailError) {
|
|
49776
|
+
console.error("Failed to send welcome email:", emailError);
|
|
49777
|
+
sonner.toast.warning("User added successfully, but welcome email could not be sent");
|
|
49778
|
+
} else if (emailData?.success) {
|
|
49779
|
+
console.log("Welcome email sent successfully:", emailData);
|
|
49780
|
+
sonner.toast.success("User added and welcome email sent!");
|
|
49781
|
+
} else {
|
|
49782
|
+
console.log("Welcome email response:", emailData);
|
|
49783
|
+
}
|
|
49784
|
+
} else {
|
|
49785
|
+
console.warn("Dashboard URL not available, skipping welcome email");
|
|
49786
|
+
}
|
|
49787
|
+
} catch (emailErr) {
|
|
49788
|
+
console.error("Error sending welcome email:", emailErr);
|
|
49789
|
+
sonner.toast.info("User added successfully (email service unavailable)");
|
|
49790
|
+
}
|
|
49651
49791
|
onInviteSent?.();
|
|
49652
49792
|
onClose();
|
|
49653
49793
|
} catch (err) {
|
|
@@ -51440,7 +51580,7 @@ var S3Service = class {
|
|
|
51440
51580
|
};
|
|
51441
51581
|
|
|
51442
51582
|
// src/lib/api/optifye-agent.ts
|
|
51443
|
-
var OPTIFYE_API_URL = "https://
|
|
51583
|
+
var OPTIFYE_API_URL = process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app";
|
|
51444
51584
|
var OptifyeAgentClient = class {
|
|
51445
51585
|
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
51446
51586
|
this.apiUrl = apiUrl;
|
package/dist/index.mjs
CHANGED
|
@@ -198,7 +198,7 @@ var DEFAULT_DATE_TIME_CONFIG = {
|
|
|
198
198
|
};
|
|
199
199
|
var DEFAULT_ENDPOINTS_CONFIG = {
|
|
200
200
|
whatsapp: "/api/send-whatsapp-direct",
|
|
201
|
-
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://
|
|
201
|
+
agnoApiUrl: process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app",
|
|
202
202
|
// Default AGNO API URL
|
|
203
203
|
// Use environment variable for Slack webhook URL for privacy/security
|
|
204
204
|
// Note: SLACK_WEBHOOK_URL is server-side only, NEXT_PUBLIC_SLACK_WEBHOOK_URL works client-side but is less secure
|
|
@@ -3216,7 +3216,7 @@ var SSEChatClient = class {
|
|
|
3216
3216
|
user_id: userId,
|
|
3217
3217
|
context
|
|
3218
3218
|
});
|
|
3219
|
-
const agnoApiUrl = this.baseUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
3219
|
+
const agnoApiUrl = this.baseUrl || (process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app");
|
|
3220
3220
|
const endpoint = `${agnoApiUrl}/api/v2/chat`;
|
|
3221
3221
|
console.log("[SSEClient] Posting directly to AGNO:", endpoint);
|
|
3222
3222
|
const response = await fetch(endpoint, {
|
|
@@ -10709,23 +10709,18 @@ function formatRelativeTime(timestamp) {
|
|
|
10709
10709
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10710
10710
|
const diffDays = Math.floor(diffHours / 24);
|
|
10711
10711
|
if (diffSeconds < 60) {
|
|
10712
|
-
return
|
|
10712
|
+
return "Less than a minute ago";
|
|
10713
10713
|
}
|
|
10714
10714
|
if (diffMinutes < 60) {
|
|
10715
|
-
const
|
|
10716
|
-
|
|
10717
|
-
return `${diffMinutes}m ago`;
|
|
10718
|
-
}
|
|
10719
|
-
return `${diffMinutes}m ${remainingSeconds}s ago`;
|
|
10715
|
+
const minuteLabel = diffMinutes === 1 ? "minute" : "minutes";
|
|
10716
|
+
return `${diffMinutes} ${minuteLabel} ago`;
|
|
10720
10717
|
}
|
|
10721
10718
|
if (diffHours < 24) {
|
|
10722
|
-
const
|
|
10723
|
-
|
|
10724
|
-
return `${diffHours}h ago`;
|
|
10725
|
-
}
|
|
10726
|
-
return `${diffHours}h ${remainingMinutes}m ago`;
|
|
10719
|
+
const hourLabel = diffHours === 1 ? "hour" : "hours";
|
|
10720
|
+
return `${diffHours} ${hourLabel} ago`;
|
|
10727
10721
|
}
|
|
10728
|
-
|
|
10722
|
+
const dayLabel = diffDays === 1 ? "day" : "days";
|
|
10723
|
+
return `${diffDays} ${dayLabel} ago`;
|
|
10729
10724
|
} catch (error) {
|
|
10730
10725
|
console.error("[formatRelativeTime] Error formatting timestamp:", error);
|
|
10731
10726
|
return "Unknown";
|
|
@@ -10741,8 +10736,8 @@ function getNextUpdateInterval(timestamp) {
|
|
|
10741
10736
|
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
10742
10737
|
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
10743
10738
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
10744
|
-
if (diffSeconds < 60) return
|
|
10745
|
-
if (diffMinutes < 60) return
|
|
10739
|
+
if (diffSeconds < 60) return 1e4;
|
|
10740
|
+
if (diffMinutes < 60) return 6e4;
|
|
10746
10741
|
if (diffHours < 24) return 6e4;
|
|
10747
10742
|
return 36e5;
|
|
10748
10743
|
} catch (error) {
|
|
@@ -10919,50 +10914,129 @@ function useAccessControl() {
|
|
|
10919
10914
|
const userRole = useMemo(() => {
|
|
10920
10915
|
if (!user?.role_level) return null;
|
|
10921
10916
|
const roleLevel = user.role_level;
|
|
10922
|
-
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
10917
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
|
|
10923
10918
|
return roleLevel;
|
|
10924
10919
|
}
|
|
10925
10920
|
return "supervisor";
|
|
10926
10921
|
}, [user?.role_level]);
|
|
10927
|
-
const
|
|
10928
|
-
|
|
10929
|
-
"
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
"
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10922
|
+
const assignedLineIds = useMemo(() => {
|
|
10923
|
+
if (!user) return [];
|
|
10924
|
+
if (user.role_level === "supervisor") {
|
|
10925
|
+
const lines = user.properties?.line_id || user.properties?.line_ids || [];
|
|
10926
|
+
return Array.isArray(lines) ? lines : [];
|
|
10927
|
+
}
|
|
10928
|
+
return [];
|
|
10929
|
+
}, [user]);
|
|
10930
|
+
const assignedFactoryIds = useMemo(() => {
|
|
10931
|
+
if (!user) return [];
|
|
10932
|
+
if (user.role_level === "plant_head") {
|
|
10933
|
+
const factories = user.properties?.factory_id || user.properties?.factory_ids || [];
|
|
10934
|
+
return Array.isArray(factories) ? factories : [];
|
|
10935
|
+
}
|
|
10936
|
+
return [];
|
|
10937
|
+
}, [user]);
|
|
10938
|
+
const roleAccessMap = {
|
|
10939
|
+
optifye: [
|
|
10940
|
+
"/",
|
|
10941
|
+
"/leaderboard",
|
|
10942
|
+
"/kpis",
|
|
10943
|
+
"/targets",
|
|
10944
|
+
"/shifts",
|
|
10945
|
+
"/supervisor-management",
|
|
10946
|
+
"/skus",
|
|
10947
|
+
"/ai-agent",
|
|
10948
|
+
"/help",
|
|
10949
|
+
"/health",
|
|
10950
|
+
"/profile",
|
|
10951
|
+
"/workspace",
|
|
10952
|
+
"/factory-view",
|
|
10953
|
+
"/team-management"
|
|
10954
|
+
],
|
|
10955
|
+
owner: [
|
|
10956
|
+
"/",
|
|
10957
|
+
"/leaderboard",
|
|
10958
|
+
"/kpis",
|
|
10959
|
+
"/targets",
|
|
10960
|
+
"/shifts",
|
|
10961
|
+
"/supervisor-management",
|
|
10962
|
+
"/skus",
|
|
10963
|
+
"/ai-agent",
|
|
10964
|
+
"/help",
|
|
10965
|
+
"/health",
|
|
10966
|
+
"/profile",
|
|
10967
|
+
"/workspace",
|
|
10968
|
+
"/factory-view",
|
|
10969
|
+
"/team-management"
|
|
10970
|
+
],
|
|
10971
|
+
plant_head: [
|
|
10972
|
+
"/",
|
|
10973
|
+
"/leaderboard",
|
|
10974
|
+
"/kpis",
|
|
10975
|
+
"/targets",
|
|
10976
|
+
"/shifts",
|
|
10977
|
+
"/supervisor-management",
|
|
10978
|
+
"/skus",
|
|
10979
|
+
"/ai-agent",
|
|
10980
|
+
"/help",
|
|
10981
|
+
"/health",
|
|
10982
|
+
"/profile",
|
|
10983
|
+
"/workspace",
|
|
10984
|
+
"/factory-view",
|
|
10985
|
+
"/team-management"
|
|
10986
|
+
],
|
|
10987
|
+
supervisor: [
|
|
10988
|
+
"/",
|
|
10989
|
+
"/leaderboard",
|
|
10990
|
+
"/kpis",
|
|
10991
|
+
"/targets",
|
|
10992
|
+
"/shifts",
|
|
10993
|
+
"/skus",
|
|
10994
|
+
"/ai-agent",
|
|
10995
|
+
"/help",
|
|
10996
|
+
"/health",
|
|
10997
|
+
"/profile",
|
|
10998
|
+
"/workspace"
|
|
10999
|
+
]
|
|
11000
|
+
};
|
|
10942
11001
|
const accessiblePages = useMemo(() => {
|
|
10943
|
-
return
|
|
10944
|
-
|
|
11002
|
+
if (!userRole) return [];
|
|
11003
|
+
return roleAccessMap[userRole] || [];
|
|
11004
|
+
}, [userRole]);
|
|
10945
11005
|
const hasAccess = useMemo(() => {
|
|
10946
11006
|
return (path) => {
|
|
10947
|
-
return
|
|
11007
|
+
if (!userRole) return false;
|
|
11008
|
+
const basePath = path.split("?")[0].split("/").slice(0, 2).join("/");
|
|
11009
|
+
const hasBaseAccess = accessiblePages.includes(basePath) || accessiblePages.includes("/");
|
|
11010
|
+
if (userRole === "supervisor" && assignedLineIds.length > 0) {
|
|
11011
|
+
if (path.includes("/kpis/") || path.includes("/workspace/")) {
|
|
11012
|
+
const lineIdMatch = path.match(/\/kpis\/([^/?]+)/);
|
|
11013
|
+
if (lineIdMatch) {
|
|
11014
|
+
const pathLineId = lineIdMatch[1];
|
|
11015
|
+
return assignedLineIds.includes(pathLineId);
|
|
11016
|
+
}
|
|
11017
|
+
}
|
|
11018
|
+
}
|
|
11019
|
+
return hasBaseAccess;
|
|
10948
11020
|
};
|
|
10949
|
-
}, []);
|
|
11021
|
+
}, [userRole, accessiblePages, assignedLineIds]);
|
|
10950
11022
|
const isPageVisible = useMemo(() => {
|
|
10951
11023
|
return (path) => {
|
|
10952
|
-
return
|
|
11024
|
+
return hasAccess(path);
|
|
10953
11025
|
};
|
|
10954
|
-
}, []);
|
|
11026
|
+
}, [hasAccess]);
|
|
10955
11027
|
const canAccessPage = useMemo(() => {
|
|
10956
11028
|
return (path) => {
|
|
10957
|
-
return
|
|
11029
|
+
return hasAccess(path);
|
|
10958
11030
|
};
|
|
10959
|
-
}, []);
|
|
11031
|
+
}, [hasAccess]);
|
|
10960
11032
|
return {
|
|
10961
11033
|
userRole,
|
|
10962
11034
|
hasAccess,
|
|
10963
11035
|
accessiblePages,
|
|
10964
11036
|
isPageVisible,
|
|
10965
|
-
canAccessPage
|
|
11037
|
+
canAccessPage,
|
|
11038
|
+
assignedLineIds,
|
|
11039
|
+
assignedFactoryIds
|
|
10966
11040
|
};
|
|
10967
11041
|
}
|
|
10968
11042
|
|
|
@@ -11126,45 +11200,51 @@ function useHasLineAccess(lineId, configLineIds) {
|
|
|
11126
11200
|
function useLineSupervisor(lineId) {
|
|
11127
11201
|
const supabase = useSupabase();
|
|
11128
11202
|
const [supervisor, setSupervisor] = useState(null);
|
|
11203
|
+
const [supervisors, setSupervisors] = useState([]);
|
|
11129
11204
|
const [isLoading, setIsLoading] = useState(true);
|
|
11130
11205
|
const [error, setError] = useState(null);
|
|
11131
|
-
console.log("[useLineSupervisor] Hook initialized for lineId:", lineId);
|
|
11132
11206
|
const fetchSupervisor = useCallback(async () => {
|
|
11133
11207
|
if (!lineId || !supabase) {
|
|
11134
|
-
console.log("[useLineSupervisor] Skipping fetch - lineId or supabase missing:", { lineId, hasSupabase: !!supabase });
|
|
11135
11208
|
setIsLoading(false);
|
|
11136
11209
|
return;
|
|
11137
11210
|
}
|
|
11138
11211
|
try {
|
|
11139
11212
|
setIsLoading(true);
|
|
11140
11213
|
setError(null);
|
|
11141
|
-
|
|
11142
|
-
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor").filter("properties->line_id", "@>", `["${lineId}"]`).limit(1);
|
|
11143
|
-
console.log("[useLineSupervisor] Query result:", { data, error: fetchError, lineId });
|
|
11214
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, properties").eq("role_level", "supervisor");
|
|
11144
11215
|
if (fetchError) {
|
|
11145
11216
|
console.error("[useLineSupervisor] Query error:", fetchError);
|
|
11146
11217
|
throw fetchError;
|
|
11147
11218
|
}
|
|
11148
11219
|
if (data && data.length > 0) {
|
|
11149
|
-
const
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
line_id_in_properties: supervisorData.properties?.line_id
|
|
11154
|
-
});
|
|
11155
|
-
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11156
|
-
setSupervisor({
|
|
11157
|
-
userId: supervisorData.user_id,
|
|
11158
|
-
email: supervisorData.email,
|
|
11159
|
-
displayName
|
|
11220
|
+
const supervisorsForLine = data.filter((supervisorData) => {
|
|
11221
|
+
const lineIds = supervisorData.properties?.line_id || supervisorData.properties?.line_ids || [];
|
|
11222
|
+
const isAssigned = Array.isArray(lineIds) && lineIds.includes(lineId);
|
|
11223
|
+
return isAssigned;
|
|
11160
11224
|
});
|
|
11225
|
+
if (supervisorsForLine.length > 0) {
|
|
11226
|
+
const allSupervisors = supervisorsForLine.map((supervisorData) => {
|
|
11227
|
+
const displayName = supervisorData.email.split("@")[0] || supervisorData.email;
|
|
11228
|
+
return {
|
|
11229
|
+
userId: supervisorData.user_id,
|
|
11230
|
+
email: supervisorData.email,
|
|
11231
|
+
displayName
|
|
11232
|
+
};
|
|
11233
|
+
});
|
|
11234
|
+
setSupervisors(allSupervisors);
|
|
11235
|
+
setSupervisor(allSupervisors[0]);
|
|
11236
|
+
} else {
|
|
11237
|
+
setSupervisors([]);
|
|
11238
|
+
setSupervisor(null);
|
|
11239
|
+
}
|
|
11161
11240
|
} else {
|
|
11162
|
-
|
|
11241
|
+
setSupervisors([]);
|
|
11163
11242
|
setSupervisor(null);
|
|
11164
11243
|
}
|
|
11165
11244
|
} catch (err) {
|
|
11166
|
-
console.error("[useLineSupervisor] Error fetching
|
|
11167
|
-
setError(err instanceof Error ? err : new Error("Failed to fetch
|
|
11245
|
+
console.error("[useLineSupervisor] Error fetching supervisors:", err);
|
|
11246
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch supervisors"));
|
|
11247
|
+
setSupervisors([]);
|
|
11168
11248
|
setSupervisor(null);
|
|
11169
11249
|
} finally {
|
|
11170
11250
|
setIsLoading(false);
|
|
@@ -11189,7 +11269,6 @@ function useLineSupervisor(lineId) {
|
|
|
11189
11269
|
filter: `role_level=eq.supervisor`
|
|
11190
11270
|
},
|
|
11191
11271
|
(payload) => {
|
|
11192
|
-
console.log("[useLineSupervisor] Real-time update received:", payload);
|
|
11193
11272
|
fetchSupervisor();
|
|
11194
11273
|
}
|
|
11195
11274
|
).subscribe();
|
|
@@ -11201,9 +11280,11 @@ function useLineSupervisor(lineId) {
|
|
|
11201
11280
|
}
|
|
11202
11281
|
};
|
|
11203
11282
|
}, [lineId, supabase, fetchSupervisor]);
|
|
11283
|
+
const supervisorName = supervisors.length > 0 ? supervisors.map((s) => s.displayName).join(", ") : null;
|
|
11204
11284
|
return {
|
|
11205
|
-
supervisorName
|
|
11285
|
+
supervisorName,
|
|
11206
11286
|
supervisor,
|
|
11287
|
+
supervisors,
|
|
11207
11288
|
isLoading,
|
|
11208
11289
|
error
|
|
11209
11290
|
};
|
|
@@ -34074,14 +34155,16 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
34074
34155
|
};
|
|
34075
34156
|
var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
34076
34157
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
34158
|
+
const entityConfig = useEntityConfig();
|
|
34077
34159
|
const generatePDF = async () => {
|
|
34078
34160
|
setIsGenerating(true);
|
|
34079
34161
|
try {
|
|
34162
|
+
const lineName = workspace.line_name || getLineDisplayName(entityConfig, workspace.line_id);
|
|
34080
34163
|
trackCoreEvent("Workspace PDF Export Clicked", {
|
|
34081
34164
|
workspace_id: workspace.workspace_id,
|
|
34082
34165
|
line_id: workspace.line_id,
|
|
34083
34166
|
workspace_name: workspace.workspace_name,
|
|
34084
|
-
line_name:
|
|
34167
|
+
line_name: lineName
|
|
34085
34168
|
});
|
|
34086
34169
|
const doc = new jsPDF$1();
|
|
34087
34170
|
doc.setFontSize(14);
|
|
@@ -34102,7 +34185,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
34102
34185
|
doc.setFontSize(32);
|
|
34103
34186
|
doc.setFont("helvetica", "bold");
|
|
34104
34187
|
doc.setTextColor(0, 0, 0);
|
|
34105
|
-
doc.text(
|
|
34188
|
+
doc.text(lineName, 20, 40);
|
|
34106
34189
|
doc.setFontSize(22);
|
|
34107
34190
|
doc.setFont("helvetica", "normal");
|
|
34108
34191
|
doc.setTextColor(40, 40, 40);
|
|
@@ -38802,7 +38885,7 @@ var AIAgentView = () => {
|
|
|
38802
38885
|
const renderedContentCache = useRef(/* @__PURE__ */ new Map());
|
|
38803
38886
|
const { createThread, mutate: mutateThreads } = useThreads();
|
|
38804
38887
|
const { messages, addMessage, setMessages } = useMessages(activeThreadId);
|
|
38805
|
-
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://
|
|
38888
|
+
const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://fastapi-production-111f9.up.railway.app";
|
|
38806
38889
|
const sseClient = useMemo(() => {
|
|
38807
38890
|
console.log("[AIAgentView] Using AGNO API URL:", agnoApiUrl);
|
|
38808
38891
|
return new SSEChatClient(agnoApiUrl);
|
|
@@ -46908,6 +46991,7 @@ var WorkspaceDetailView = ({
|
|
|
46908
46991
|
const [showIdleTime, setShowIdleTime] = useState(false);
|
|
46909
46992
|
const dashboardConfig = useDashboardConfig();
|
|
46910
46993
|
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
46994
|
+
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
46911
46995
|
const {
|
|
46912
46996
|
workspace: workspaceHealth,
|
|
46913
46997
|
loading: healthLoading,
|
|
@@ -46916,6 +47000,36 @@ var WorkspaceDetailView = ({
|
|
|
46916
47000
|
enableRealtime: true,
|
|
46917
47001
|
refreshInterval: 3e4
|
|
46918
47002
|
});
|
|
47003
|
+
const {
|
|
47004
|
+
isHealthy: isWorkspaceHealthy,
|
|
47005
|
+
timeSinceUpdate,
|
|
47006
|
+
lastHeartbeat,
|
|
47007
|
+
loading: healthStatusLoading,
|
|
47008
|
+
error: healthStatusError
|
|
47009
|
+
} = useWorkspaceHealthStatus(workspaceId);
|
|
47010
|
+
const isLive = useMemo(() => {
|
|
47011
|
+
if (!lastHeartbeat) return false;
|
|
47012
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
47013
|
+
const heartbeat = new Date(lastHeartbeat);
|
|
47014
|
+
const minutesSince = (now2.getTime() - heartbeat.getTime()) / 6e4;
|
|
47015
|
+
return minutesSince < 5;
|
|
47016
|
+
}, [lastHeartbeat]);
|
|
47017
|
+
useEffect(() => {
|
|
47018
|
+
console.log("[WorkspaceDetailView] Workspace Health Status:", {
|
|
47019
|
+
workspaceId,
|
|
47020
|
+
// Old workspace_health table
|
|
47021
|
+
oldStatus: workspaceHealth?.status,
|
|
47022
|
+
oldLastHeartbeat: workspaceHealth?.last_heartbeat,
|
|
47023
|
+
oldTimeSinceLastUpdate: workspaceHealth?.timeSinceLastUpdate,
|
|
47024
|
+
// New workspace_health_status table
|
|
47025
|
+
isHealthy: isWorkspaceHealthy,
|
|
47026
|
+
isLive,
|
|
47027
|
+
lastHeartbeat,
|
|
47028
|
+
timeSinceUpdate,
|
|
47029
|
+
loading: healthLoading || healthStatusLoading,
|
|
47030
|
+
error: healthError || healthStatusError
|
|
47031
|
+
});
|
|
47032
|
+
}, [workspaceId, workspaceHealth, isWorkspaceHealthy, isLive, lastHeartbeat, timeSinceUpdate, healthLoading, healthStatusLoading, healthError, healthStatusError]);
|
|
46919
47033
|
const {
|
|
46920
47034
|
status: prefetchStatus,
|
|
46921
47035
|
data: prefetchData,
|
|
@@ -46952,6 +47066,7 @@ var WorkspaceDetailView = ({
|
|
|
46952
47066
|
const workspace = isHistoricView ? historicMetrics : liveMetrics;
|
|
46953
47067
|
const loading = isHistoricView ? historicLoading : liveLoading;
|
|
46954
47068
|
const error = isHistoricView ? historicError : liveError;
|
|
47069
|
+
const { supervisorName } = useLineSupervisor(workspace?.line_id || lineId);
|
|
46955
47070
|
useEffect(() => {
|
|
46956
47071
|
if (onTabChange) {
|
|
46957
47072
|
onTabChange(activeTab);
|
|
@@ -47174,7 +47289,7 @@ var WorkspaceDetailView = ({
|
|
|
47174
47289
|
initial: { opacity: 1 },
|
|
47175
47290
|
animate: { opacity: 1 },
|
|
47176
47291
|
children: /* @__PURE__ */ jsxs("div", { className: "min-h-screen w-full flex flex-col bg-slate-50", children: [
|
|
47177
|
-
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-
|
|
47292
|
+
/* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 px-3 sm:px-4 md:px-5 lg:px-6 py-3 sm:py-3 lg:py-3.5 flex flex-col shadow-sm bg-white", children: [
|
|
47178
47293
|
/* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
47179
47294
|
/* @__PURE__ */ jsx(
|
|
47180
47295
|
"button",
|
|
@@ -47188,15 +47303,15 @@ var WorkspaceDetailView = ({
|
|
|
47188
47303
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
|
|
47189
47304
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
47190
47305
|
/* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900 truncate max-w-[220px]", children: formattedWorkspaceName }),
|
|
47191
|
-
|
|
47192
|
-
|
|
47306
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-2 w-2", children: [
|
|
47307
|
+
isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47193
47308
|
/* @__PURE__ */ jsx("span", { className: clsx(
|
|
47194
47309
|
"relative inline-flex rounded-full h-2 w-2",
|
|
47195
|
-
|
|
47310
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47196
47311
|
) })
|
|
47197
47312
|
] }) })
|
|
47198
47313
|
] }),
|
|
47199
|
-
activeTab !== "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [
|
|
47314
|
+
activeTab !== "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5 mt-1", children: [
|
|
47200
47315
|
workspaceHealth && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-500", children: workspaceHealth.timeSinceLastUpdate }),
|
|
47201
47316
|
/* @__PURE__ */ jsx(
|
|
47202
47317
|
WorkspaceHealthStatusBadge,
|
|
@@ -47223,14 +47338,11 @@ var WorkspaceDetailView = ({
|
|
|
47223
47338
|
) }),
|
|
47224
47339
|
/* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
47225
47340
|
/* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
|
|
47226
|
-
|
|
47227
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
47228
|
-
"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
|
|
47229
|
-
workspaceHealth.status === "healthy" ? "bg-green-400" : "bg-red-400"
|
|
47230
|
-
) }),
|
|
47341
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
|
|
47342
|
+
isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
47231
47343
|
/* @__PURE__ */ jsx("span", { className: clsx(
|
|
47232
47344
|
"relative inline-flex rounded-full h-2.5 w-2.5",
|
|
47233
|
-
|
|
47345
|
+
isLive ? "bg-green-500" : "bg-red-500"
|
|
47234
47346
|
) })
|
|
47235
47347
|
] })
|
|
47236
47348
|
] }) }),
|
|
@@ -49618,6 +49730,34 @@ var InviteUserDialog = ({
|
|
|
49618
49730
|
throw new Error(data.error);
|
|
49619
49731
|
}
|
|
49620
49732
|
toast.success(data?.message || "User added successfully!");
|
|
49733
|
+
try {
|
|
49734
|
+
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
49735
|
+
if (dashboardUrl) {
|
|
49736
|
+
console.log("Sending welcome email to:", email.trim());
|
|
49737
|
+
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
49738
|
+
body: {
|
|
49739
|
+
action: "send-welcome-email",
|
|
49740
|
+
email: email.trim(),
|
|
49741
|
+
company_id: companyId,
|
|
49742
|
+
dashboard_url: dashboardUrl
|
|
49743
|
+
}
|
|
49744
|
+
});
|
|
49745
|
+
if (emailError) {
|
|
49746
|
+
console.error("Failed to send welcome email:", emailError);
|
|
49747
|
+
toast.warning("User added successfully, but welcome email could not be sent");
|
|
49748
|
+
} else if (emailData?.success) {
|
|
49749
|
+
console.log("Welcome email sent successfully:", emailData);
|
|
49750
|
+
toast.success("User added and welcome email sent!");
|
|
49751
|
+
} else {
|
|
49752
|
+
console.log("Welcome email response:", emailData);
|
|
49753
|
+
}
|
|
49754
|
+
} else {
|
|
49755
|
+
console.warn("Dashboard URL not available, skipping welcome email");
|
|
49756
|
+
}
|
|
49757
|
+
} catch (emailErr) {
|
|
49758
|
+
console.error("Error sending welcome email:", emailErr);
|
|
49759
|
+
toast.info("User added successfully (email service unavailable)");
|
|
49760
|
+
}
|
|
49621
49761
|
onInviteSent?.();
|
|
49622
49762
|
onClose();
|
|
49623
49763
|
} catch (err) {
|
|
@@ -51410,7 +51550,7 @@ var S3Service = class {
|
|
|
51410
51550
|
};
|
|
51411
51551
|
|
|
51412
51552
|
// src/lib/api/optifye-agent.ts
|
|
51413
|
-
var OPTIFYE_API_URL = "https://
|
|
51553
|
+
var OPTIFYE_API_URL = process.env.NEXT_PUBLIC_AGNO_URL || "https://fastapi-production-111f9.up.railway.app";
|
|
51414
51554
|
var OptifyeAgentClient = class {
|
|
51415
51555
|
constructor(apiUrl = OPTIFYE_API_URL) {
|
|
51416
51556
|
this.apiUrl = apiUrl;
|