@optifye/dashboard-core 6.10.27 → 6.10.29

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.mjs CHANGED
@@ -10,7 +10,7 @@ import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
10
10
  import Hls, { Events, ErrorTypes } from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { memo, noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds } from 'motion-utils';
13
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Palette, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Search, Edit2, ArrowRight, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, MessageSquare, Menu, Send, Copy, Settings, LifeBuoy, EyeOff, UserCircle } from 'lucide-react';
13
+ import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Palette, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Search, Edit2, ArrowRight, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, MessageSquare, Menu, Send, Copy, Settings, LifeBuoy, EyeOff, UserCircle, Flame, Crown, Medal } from 'lucide-react';
14
14
  import { toast } from 'sonner';
15
15
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
16
16
  import { Slot } from '@radix-ui/react-slot';
@@ -5049,6 +5049,145 @@ var getAnonClient = () => {
5049
5049
  });
5050
5050
  };
5051
5051
 
5052
+ // src/lib/auth/session.ts
5053
+ var DEFAULT_MIN_VALIDITY_MS = 6e4;
5054
+ var refreshPromises = /* @__PURE__ */ new WeakMap();
5055
+ var isInvalidRefreshTokenError = (error) => {
5056
+ if (!error || typeof error !== "object") return false;
5057
+ const message = error.message?.toLowerCase() || "";
5058
+ if (!message.includes("refresh token")) return false;
5059
+ return message.includes("invalid") || message.includes("not found") || message.includes("revoked");
5060
+ };
5061
+ var isSessionValid = (session, minValidityMs = 0) => {
5062
+ if (!session) return false;
5063
+ if (!session.expires_at) return true;
5064
+ return session.expires_at * 1e3 - Date.now() > minValidityMs;
5065
+ };
5066
+ var buildAuthHeaders = (headers, token) => {
5067
+ const merged = new Headers(headers || {});
5068
+ merged.set("Authorization", `Bearer ${token}`);
5069
+ return merged;
5070
+ };
5071
+ var redirectToLogin = (reason) => {
5072
+ if (typeof window === "undefined") return;
5073
+ if (window.location.pathname === "/login") return;
5074
+ const returnTo = `${window.location.pathname}${window.location.search}${window.location.hash}`;
5075
+ const url = new URL("/login", window.location.origin);
5076
+ if (returnTo) {
5077
+ url.searchParams.set("returnTo", returnTo);
5078
+ }
5079
+ if (reason) {
5080
+ url.searchParams.set("reason", reason);
5081
+ }
5082
+ window.location.assign(url.toString());
5083
+ };
5084
+ var handleAuthRequired = async (supabase, reason) => {
5085
+ try {
5086
+ await supabase.auth.signOut({ scope: "local" });
5087
+ } catch (error) {
5088
+ console.error("[Auth] Error clearing local session:", error);
5089
+ }
5090
+ redirectToLogin(reason);
5091
+ };
5092
+ var refreshSessionSingleFlight = async (supabase) => {
5093
+ const existing = refreshPromises.get(supabase);
5094
+ if (existing) {
5095
+ return existing;
5096
+ }
5097
+ const refreshPromise = (async () => {
5098
+ let refreshError = null;
5099
+ try {
5100
+ const { data, error } = await supabase.auth.refreshSession();
5101
+ if (error) {
5102
+ refreshError = error;
5103
+ } else if (isSessionValid(data.session ?? null, 0)) {
5104
+ return { session: data.session ?? null, error: null, invalidRefreshToken: false };
5105
+ }
5106
+ } catch (error) {
5107
+ refreshError = error;
5108
+ }
5109
+ const {
5110
+ data: { session }
5111
+ } = await supabase.auth.getSession();
5112
+ if (isSessionValid(session, 0)) {
5113
+ return { session, error: refreshError, invalidRefreshToken: false };
5114
+ }
5115
+ const invalidRefreshToken = isInvalidRefreshTokenError(refreshError);
5116
+ if (invalidRefreshToken) {
5117
+ try {
5118
+ await supabase.auth.signOut({ scope: "local" });
5119
+ } catch (error) {
5120
+ console.error("[Auth] Error clearing invalid refresh token session:", error);
5121
+ }
5122
+ }
5123
+ return { session: null, error: refreshError, invalidRefreshToken };
5124
+ })();
5125
+ refreshPromises.set(supabase, refreshPromise);
5126
+ try {
5127
+ return await refreshPromise;
5128
+ } finally {
5129
+ refreshPromises.delete(supabase);
5130
+ }
5131
+ };
5132
+ var getAccessTokenOrRedirect = async (supabase, options = {}) => {
5133
+ const {
5134
+ minValidityMs = DEFAULT_MIN_VALIDITY_MS,
5135
+ forceRefresh = false,
5136
+ redirectOnFailure = true,
5137
+ redirectReason = "session_expired"
5138
+ } = options;
5139
+ if (!forceRefresh) {
5140
+ const {
5141
+ data: { session }
5142
+ } = await supabase.auth.getSession();
5143
+ if (isSessionValid(session, minValidityMs) && session?.access_token) {
5144
+ return session.access_token;
5145
+ }
5146
+ }
5147
+ const refreshResult = await refreshSessionSingleFlight(supabase);
5148
+ if (refreshResult.session?.access_token) {
5149
+ return refreshResult.session.access_token;
5150
+ }
5151
+ if (redirectOnFailure) {
5152
+ if (refreshResult.invalidRefreshToken || !refreshResult.error && !refreshResult.session) {
5153
+ await handleAuthRequired(supabase, redirectReason);
5154
+ }
5155
+ }
5156
+ throw new Error("Authentication required");
5157
+ };
5158
+ var fetchWithSupabaseAuth = async (supabase, input, options = {}) => {
5159
+ const {
5160
+ retryOnUnauthorized = true,
5161
+ minValidityMs,
5162
+ redirectReason,
5163
+ ...fetchOptions
5164
+ } = options;
5165
+ const token = await getAccessTokenOrRedirect(supabase, {
5166
+ minValidityMs,
5167
+ redirectReason
5168
+ });
5169
+ const response = await fetch(input, {
5170
+ ...fetchOptions,
5171
+ headers: buildAuthHeaders(fetchOptions.headers, token)
5172
+ });
5173
+ if (response.status !== 401 || !retryOnUnauthorized) {
5174
+ return response;
5175
+ }
5176
+ const refreshedToken = await getAccessTokenOrRedirect(supabase, {
5177
+ forceRefresh: true,
5178
+ redirectReason
5179
+ });
5180
+ const retryResponse = await fetch(input, {
5181
+ ...fetchOptions,
5182
+ headers: buildAuthHeaders(fetchOptions.headers, refreshedToken)
5183
+ });
5184
+ if (retryResponse.status === 401) {
5185
+ await handleAuthRequired(supabase, redirectReason || "unauthorized");
5186
+ throw new Error("Authentication required");
5187
+ }
5188
+ return retryResponse;
5189
+ };
5190
+
5052
5191
  // src/lib/api/s3-clips-supabase.ts
5053
5192
  var cachedClient = null;
5054
5193
  var cachedConfig = null;
@@ -5074,17 +5213,6 @@ var getSupabaseClient = () => {
5074
5213
  }
5075
5214
  return cachedClient;
5076
5215
  };
5077
- var getAuthToken3 = async () => {
5078
- try {
5079
- const supabase = getSupabaseClient();
5080
- const { data: { session } } = await supabase.auth.getSession();
5081
- console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
5082
- return session?.access_token || null;
5083
- } catch (error) {
5084
- console.error("[S3ClipsSupabase] Error getting auth token:", error);
5085
- return null;
5086
- }
5087
- };
5088
5216
  var S3ClipsSupabaseService = class {
5089
5217
  constructor(config) {
5090
5218
  this.requestCache = /* @__PURE__ */ new Map();
@@ -5112,23 +5240,20 @@ var S3ClipsSupabaseService = class {
5112
5240
  * Fetch with authentication and error handling
5113
5241
  */
5114
5242
  async fetchWithAuth(endpoint, body) {
5115
- const token = await getAuthToken3();
5116
- if (!token) {
5117
- throw new Error("Authentication required");
5118
- }
5243
+ const supabase = getSupabaseClient();
5119
5244
  const apiEndpoint = "/api/clips/supabase";
5120
5245
  const requestBody = {
5121
5246
  ...body,
5122
5247
  action: endpoint.replace("/api/clips/supabase/", "")
5123
5248
  };
5124
5249
  console.log(`[S3ClipsSupabase] Making request to ${apiEndpoint} with action: ${requestBody.action}, body:`, requestBody);
5125
- const response = await fetch(apiEndpoint, {
5250
+ const response = await fetchWithSupabaseAuth(supabase, apiEndpoint, {
5126
5251
  method: "POST",
5127
5252
  headers: {
5128
- "Authorization": `Bearer ${token}`,
5129
5253
  "Content-Type": "application/json"
5130
5254
  },
5131
- body: JSON.stringify(requestBody)
5255
+ body: JSON.stringify(requestBody),
5256
+ redirectReason: "session_expired"
5132
5257
  });
5133
5258
  console.log(`[S3ClipsSupabase] Response status: ${response.status}`);
5134
5259
  if (!response.ok) {
@@ -6923,11 +7048,103 @@ var UserManagementService = class {
6923
7048
  throw error;
6924
7049
  }
6925
7050
  }
7051
+ /**
7052
+ * Update a user's profile (name + photo)
7053
+ * @param input - Update profile input
7054
+ */
7055
+ async updateUserProfile(input) {
7056
+ try {
7057
+ const token = await this.getAuthToken();
7058
+ const backendUrl = this.getBackendUrl();
7059
+ console.log("[UserManagementService] Updating user profile:", input);
7060
+ const response = await fetch(
7061
+ `${backendUrl}/api/users/profile`,
7062
+ {
7063
+ method: "PUT",
7064
+ headers: {
7065
+ "Authorization": `Bearer ${token}`,
7066
+ "Content-Type": "application/json"
7067
+ },
7068
+ body: JSON.stringify(input)
7069
+ }
7070
+ );
7071
+ if (!response.ok) {
7072
+ const errorData = await response.json();
7073
+ throw new Error(`Failed to update user profile: ${errorData.detail || response.statusText}`);
7074
+ }
7075
+ console.log("[UserManagementService] User profile updated successfully");
7076
+ } catch (error) {
7077
+ console.error("[UserManagementService] Error updating user profile:", error);
7078
+ throw error;
7079
+ }
7080
+ }
6926
7081
  };
6927
7082
  var createUserManagementService = (supabase) => {
6928
7083
  return new UserManagementService(supabase);
6929
7084
  };
6930
7085
 
7086
+ // src/lib/services/storageService.ts
7087
+ var AVATAR_BUCKET = "avatars";
7088
+ var createStorageService = (supabase) => ({
7089
+ /**
7090
+ * Upload an avatar image to Supabase Storage
7091
+ * @param userId - The user ID to associate the avatar with
7092
+ * @param file - The file to upload
7093
+ * @returns The public URL of the uploaded avatar
7094
+ */
7095
+ async uploadAvatar(userId, file) {
7096
+ const fileExt = file.name.split(".").pop()?.toLowerCase() || "jpg";
7097
+ const fileName = `${userId}/${Date.now()}.${fileExt}`;
7098
+ const { error: uploadError } = await supabase.storage.from(AVATAR_BUCKET).upload(fileName, file, {
7099
+ cacheControl: "3600",
7100
+ upsert: true
7101
+ });
7102
+ if (uploadError) {
7103
+ console.error("[StorageService] Upload failed:", uploadError);
7104
+ throw uploadError;
7105
+ }
7106
+ const { data: publicUrlData } = supabase.storage.from(AVATAR_BUCKET).getPublicUrl(fileName);
7107
+ if (!publicUrlData?.publicUrl) {
7108
+ throw new Error("Failed to get public URL for uploaded avatar");
7109
+ }
7110
+ return publicUrlData.publicUrl;
7111
+ },
7112
+ /**
7113
+ * Delete an avatar from Supabase Storage
7114
+ * @param filePath - The path to the file in the bucket (e.g., "userId/timestamp.jpg")
7115
+ */
7116
+ async deleteAvatar(filePath) {
7117
+ const { error } = await supabase.storage.from(AVATAR_BUCKET).remove([filePath]);
7118
+ if (error) {
7119
+ console.error("[StorageService] Delete failed:", error);
7120
+ throw error;
7121
+ }
7122
+ },
7123
+ /**
7124
+ * Get the public URL for an avatar
7125
+ * @param filePath - The path to the file in the bucket
7126
+ * @returns The public URL
7127
+ */
7128
+ getAvatarPublicUrl(filePath) {
7129
+ const { data } = supabase.storage.from(AVATAR_BUCKET).getPublicUrl(filePath);
7130
+ return data?.publicUrl || "";
7131
+ },
7132
+ /**
7133
+ * Extract the file path from a full public URL
7134
+ * @param publicUrl - The full public URL
7135
+ * @returns The file path within the bucket, or null if not an avatar URL
7136
+ */
7137
+ extractFilePathFromUrl(publicUrl) {
7138
+ try {
7139
+ const url = new URL(publicUrl);
7140
+ const pathParts = url.pathname.split(`/storage/v1/object/public/${AVATAR_BUCKET}/`);
7141
+ return pathParts.length > 1 ? pathParts[1] : null;
7142
+ } catch {
7143
+ return null;
7144
+ }
7145
+ }
7146
+ });
7147
+
6931
7148
  // src/lib/constants/idleTimeColors.ts
6932
7149
  var IDLE_TIME_REASON_COLORS = {
6933
7150
  "Operator Absent": {
@@ -7375,6 +7592,48 @@ var SessionTracker = class {
7375
7592
  function createSessionTracker(config) {
7376
7593
  return new SessionTracker(config);
7377
7594
  }
7595
+
7596
+ // src/lib/services/awardsService.ts
7597
+ var awardsService = {
7598
+ async getMyAwards(supabase) {
7599
+ const data = await fetchBackendJson(supabase, "/api/awards/me");
7600
+ return data?.awards || [];
7601
+ },
7602
+ async getNotifications(supabase) {
7603
+ const data = await fetchBackendJson(
7604
+ supabase,
7605
+ "/api/awards/notifications"
7606
+ );
7607
+ return data?.notifications || [];
7608
+ },
7609
+ async markNotificationsRead(supabase, notificationIds) {
7610
+ if (notificationIds.length === 0) return 0;
7611
+ const data = await fetchBackendJson(supabase, "/api/awards/notifications/mark-read", {
7612
+ method: "POST",
7613
+ body: JSON.stringify({ notification_ids: notificationIds })
7614
+ });
7615
+ return data?.updated ?? 0;
7616
+ },
7617
+ async getTopPerformer(supabase) {
7618
+ const data = await fetchBackendJson(
7619
+ supabase,
7620
+ "/api/awards/top-performer"
7621
+ );
7622
+ return data?.top_performer ?? null;
7623
+ }
7624
+ };
7625
+
7626
+ // src/lib/services/weeklyTopPerformerService.ts
7627
+ var weeklyTopPerformerService = {
7628
+ async getWeeklyTopPerformer(supabase, companyId) {
7629
+ const query = companyId ? `?company_id=${encodeURIComponent(companyId)}` : "";
7630
+ const data = await fetchBackendJson(
7631
+ supabase,
7632
+ `/api/dashboard/weekly-top-performer${query}`
7633
+ );
7634
+ return data?.top_performer ?? null;
7635
+ }
7636
+ };
7378
7637
  var SupabaseContext = createContext(void 0);
7379
7638
  var SupabaseProvider = ({ client, children }) => {
7380
7639
  _setSupabaseInstance(client);
@@ -7470,6 +7729,15 @@ var useSessionKeepAlive = (options = {}) => {
7470
7729
  };
7471
7730
  }, [enabled, intervalMs, supabase]);
7472
7731
  };
7732
+ var isAuthError = (err) => {
7733
+ if (!err || typeof err !== "object") return false;
7734
+ const message = err.message?.toLowerCase() || "";
7735
+ const status = err.status;
7736
+ if (status === 401 || status === 403) {
7737
+ return true;
7738
+ }
7739
+ return message.includes("jwt expired") || message.includes("invalid token") || message.includes("unauthorized") || message.includes("http 401") || message.includes("status: 401");
7740
+ };
7473
7741
  var AuthContext = createContext({
7474
7742
  session: null,
7475
7743
  user: null,
@@ -7497,7 +7765,7 @@ var AuthProvider = ({ children }) => {
7497
7765
  const isFetchingRef = useRef(false);
7498
7766
  const lastProcessedSessionRef = useRef(null);
7499
7767
  const hasAuthenticatedRef = useRef(false);
7500
- const fetchSession = useCallback(async (supabaseSession, isReauth = false) => {
7768
+ const fetchSession = useCallback(async (supabaseSession, isReauth = false, allowRetry = true) => {
7501
7769
  if (isFetchingRef.current) {
7502
7770
  console.log("[AuthContext] Already fetching, skipping duplicate request");
7503
7771
  return;
@@ -7534,13 +7802,19 @@ var AuthProvider = ({ children }) => {
7534
7802
  });
7535
7803
  } catch (err) {
7536
7804
  console.error("[AuthContext] Failed to fetch session:", err);
7537
- if (err.message?.includes("JWT expired") || err.message?.includes("invalid token") || err.message?.includes("token") || err.message?.includes("401")) {
7538
- console.log("[AuthContext] Token expired or invalid, clearing session and redirecting to login");
7539
- setUser(null);
7540
- setSession(null);
7541
- setError(new Error("Your session has expired. Please log in again."));
7542
- await supabase.auth.signOut();
7543
- router.replace("/login");
7805
+ if (allowRetry && isAuthError(err)) {
7806
+ console.warn("[AuthContext] Auth error while fetching session, attempting refresh...");
7807
+ const refreshResult = await refreshSessionSingleFlight(supabase);
7808
+ if (refreshResult.session?.access_token) {
7809
+ await fetchSession(refreshResult.session, true, false);
7810
+ return;
7811
+ }
7812
+ if (refreshResult.invalidRefreshToken) {
7813
+ console.warn("[AuthContext] Refresh token invalid, redirecting to login");
7814
+ await handleAuthRequired(supabase, "session_expired");
7815
+ return;
7816
+ }
7817
+ setError(new Error("Unable to refresh session. Please try again."));
7544
7818
  return;
7545
7819
  }
7546
7820
  setError(err);
@@ -7559,7 +7833,7 @@ var AuthProvider = ({ children }) => {
7559
7833
  isFetchingRef.current = false;
7560
7834
  console.log("[AuthContext] Loading set to false", { isReauth });
7561
7835
  }
7562
- }, [supabase, router]);
7836
+ }, [supabase]);
7563
7837
  const markFirstLoginCompleted = useCallback(async () => {
7564
7838
  if (!user?.id || !supabase) return false;
7565
7839
  try {
@@ -7610,7 +7884,7 @@ var AuthProvider = ({ children }) => {
7610
7884
  if (!session || !supabase) {
7611
7885
  return;
7612
7886
  }
7613
- const monitorTokenExpiry = () => {
7887
+ const monitorTokenExpiry = async () => {
7614
7888
  const expiresAt = session.expires_at;
7615
7889
  if (!expiresAt) {
7616
7890
  console.warn("[AuthContext] Session has no expiry time");
@@ -7621,19 +7895,31 @@ var AuthProvider = ({ children }) => {
7621
7895
  const timeUntilExpiry = expiryTime - now4;
7622
7896
  const minutesUntilExpiry = Math.floor(timeUntilExpiry / 6e4);
7623
7897
  console.log(`[AuthContext] Token expires in ${minutesUntilExpiry} minutes`);
7624
- if (minutesUntilExpiry < 5 && minutesUntilExpiry > 0) {
7898
+ if (minutesUntilExpiry < 5 && timeUntilExpiry > 0) {
7625
7899
  console.warn("[AuthContext] Token expiring soon, attempting refresh...");
7626
- supabase.auth.refreshSession().then(({ data, error: error2 }) => {
7627
- if (error2) {
7628
- console.error("[AuthContext] Token refresh failed:", error2);
7629
- } else {
7630
- console.log("[AuthContext] Token refreshed successfully");
7631
- }
7632
- });
7900
+ const refreshResult = await refreshSessionSingleFlight(supabase);
7901
+ if (isSessionValid(refreshResult.session, 0)) {
7902
+ setSession(refreshResult.session);
7903
+ console.log("[AuthContext] Token refreshed successfully");
7904
+ return;
7905
+ }
7906
+ if (refreshResult.invalidRefreshToken) {
7907
+ console.error("[AuthContext] Refresh token invalid during proactive refresh");
7908
+ await handleAuthRequired(supabase, "session_expired");
7909
+ }
7633
7910
  }
7634
- if (timeUntilExpiry < 0) {
7635
- console.error("[AuthContext] Token has expired, logging out");
7636
- signOut();
7911
+ if (timeUntilExpiry <= 0) {
7912
+ console.warn("[AuthContext] Token has expired, attempting refresh...");
7913
+ const refreshResult = await refreshSessionSingleFlight(supabase);
7914
+ if (isSessionValid(refreshResult.session, 0)) {
7915
+ setSession(refreshResult.session);
7916
+ console.log("[AuthContext] Token refreshed after expiry");
7917
+ return;
7918
+ }
7919
+ if (refreshResult.invalidRefreshToken) {
7920
+ console.error("[AuthContext] Refresh token invalid after expiry");
7921
+ await handleAuthRequired(supabase, "session_expired");
7922
+ }
7637
7923
  }
7638
7924
  };
7639
7925
  monitorTokenExpiry();
@@ -7641,7 +7927,7 @@ var AuthProvider = ({ children }) => {
7641
7927
  return () => {
7642
7928
  clearInterval(intervalId);
7643
7929
  };
7644
- }, [session, supabase, signOut]);
7930
+ }, [session, supabase]);
7645
7931
  useEffect(() => {
7646
7932
  if (!supabase) {
7647
7933
  console.log("[AuthContext] No Supabase client, skipping auth initialization");
@@ -14784,7 +15070,7 @@ function useAccessControl() {
14784
15070
  const userRole = useMemo(() => {
14785
15071
  if (!user?.role_level) return null;
14786
15072
  const roleLevel = user.role_level;
14787
- if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
15073
+ if (roleLevel === "owner" || roleLevel === "it" || roleLevel === "plant_head" || roleLevel === "supervisor" || roleLevel === "optifye") {
14788
15074
  return roleLevel;
14789
15075
  }
14790
15076
  return "supervisor";
@@ -14836,6 +15122,21 @@ function useAccessControl() {
14836
15122
  "/factory-view",
14837
15123
  "/team-management"
14838
15124
  ],
15125
+ it: [
15126
+ "/",
15127
+ "/leaderboard",
15128
+ "/kpis",
15129
+ "/targets",
15130
+ "/shifts",
15131
+ "/supervisor-management",
15132
+ "/skus",
15133
+ "/help",
15134
+ "/health",
15135
+ "/profile",
15136
+ "/workspace",
15137
+ "/factory-view",
15138
+ "/team-management"
15139
+ ],
14839
15140
  plant_head: [
14840
15141
  "/",
14841
15142
  "/leaderboard",
@@ -14917,7 +15218,7 @@ function useTeamManagementPermissions() {
14917
15218
  canAssignLines: (targetUser) => {
14918
15219
  if (!currentRole) return false;
14919
15220
  if (currentRole === "optifye") return true;
14920
- if (currentRole === "owner" && targetUser.role_level === "supervisor") {
15221
+ if ((currentRole === "owner" || currentRole === "it") && targetUser.role_level === "supervisor") {
14921
15222
  return true;
14922
15223
  }
14923
15224
  if (currentRole === "plant_head" && targetUser.role_level === "supervisor") {
@@ -14931,7 +15232,7 @@ function useTeamManagementPermissions() {
14931
15232
  canAssignFactories: (targetUser) => {
14932
15233
  if (!currentRole) return false;
14933
15234
  if (currentRole === "optifye") return true;
14934
- if (currentRole === "owner" && targetUser.role_level === "plant_head") {
15235
+ if ((currentRole === "owner" || currentRole === "it") && targetUser.role_level === "plant_head") {
14935
15236
  return true;
14936
15237
  }
14937
15238
  return false;
@@ -14942,7 +15243,7 @@ function useTeamManagementPermissions() {
14942
15243
  canChangeRole: (targetUser) => {
14943
15244
  if (!currentRole) return false;
14944
15245
  if (currentRole === "optifye") return true;
14945
- if (currentRole === "owner") {
15246
+ if (currentRole === "owner" || currentRole === "it") {
14946
15247
  return ["plant_head", "supervisor"].includes(targetUser.role_level);
14947
15248
  }
14948
15249
  return false;
@@ -14953,7 +15254,7 @@ function useTeamManagementPermissions() {
14953
15254
  canRemoveUser: (targetUser) => {
14954
15255
  if (!currentRole) return false;
14955
15256
  if (currentRole === "optifye") return true;
14956
- if (currentRole === "owner") {
15257
+ if (currentRole === "owner" || currentRole === "it") {
14957
15258
  return ["plant_head", "supervisor"].includes(targetUser.role_level);
14958
15259
  }
14959
15260
  return false;
@@ -14965,9 +15266,12 @@ function useTeamManagementPermissions() {
14965
15266
  availableRolesToAssign: () => {
14966
15267
  if (!currentRole) return [];
14967
15268
  if (currentRole === "optifye") {
14968
- return ["owner", "plant_head", "supervisor"];
15269
+ return ["owner", "it", "plant_head", "supervisor"];
14969
15270
  }
14970
15271
  if (currentRole === "owner") {
15272
+ return ["it", "plant_head", "supervisor"];
15273
+ }
15274
+ if (currentRole === "it") {
14971
15275
  return ["plant_head", "supervisor"];
14972
15276
  }
14973
15277
  if (currentRole === "plant_head") {
@@ -14982,6 +15286,9 @@ function useTeamManagementPermissions() {
14982
15286
  if (!currentRole) return false;
14983
15287
  if (currentRole === "optifye") return true;
14984
15288
  if (currentRole === "owner") {
15289
+ return ["it", "plant_head", "supervisor"].includes(role);
15290
+ }
15291
+ if (currentRole === "it") {
14985
15292
  return ["plant_head", "supervisor"].includes(role);
14986
15293
  }
14987
15294
  if (currentRole === "plant_head") {
@@ -15005,7 +15312,7 @@ function useTeamManagementPermissions() {
15005
15312
  * Should the assignment column be shown?
15006
15313
  */
15007
15314
  showAssignmentColumn: () => {
15008
- return !!currentRole && ["optifye", "owner", "plant_head"].includes(currentRole);
15315
+ return !!currentRole && ["optifye", "owner", "it", "plant_head"].includes(currentRole);
15009
15316
  }
15010
15317
  };
15011
15318
  }
@@ -15077,7 +15384,7 @@ function useLineSupervisor(lineId) {
15077
15384
  try {
15078
15385
  setIsLoading(true);
15079
15386
  setError(null);
15080
- const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties").eq("role_level", "supervisor");
15387
+ const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
15081
15388
  if (fetchError) {
15082
15389
  console.error("[useLineSupervisor] Query error:", fetchError);
15083
15390
  throw fetchError;
@@ -15099,7 +15406,8 @@ function useLineSupervisor(lineId) {
15099
15406
  return {
15100
15407
  userId: supervisorData.user_id,
15101
15408
  email: supervisorData.email,
15102
- displayName
15409
+ displayName,
15410
+ profilePhotoUrl: supervisorData.profile_photo_url
15103
15411
  };
15104
15412
  });
15105
15413
  setSupervisors(allSupervisors);
@@ -15177,7 +15485,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
15177
15485
  try {
15178
15486
  setIsLoading(true);
15179
15487
  setError(null);
15180
- const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties").eq("role_level", "supervisor");
15488
+ const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
15181
15489
  if (fetchError) {
15182
15490
  throw fetchError;
15183
15491
  }
@@ -15195,7 +15503,8 @@ var useSupervisorsByLineIds = (lineIds, options) => {
15195
15503
  const supervisor = {
15196
15504
  userId: row.user_id,
15197
15505
  email,
15198
- displayName
15506
+ displayName,
15507
+ profilePhotoUrl: row.profile_photo_url
15199
15508
  };
15200
15509
  assignedLineIds.forEach((lineId) => {
15201
15510
  if (typeof lineId !== "string") return;
@@ -15464,7 +15773,7 @@ function useIdleTimeClipClassifications({
15464
15773
  enabled = true,
15465
15774
  limit = DEFAULT_LIMIT
15466
15775
  }) {
15467
- const { session } = useAuth();
15776
+ const supabase = useSupabase();
15468
15777
  const [idleClips, setIdleClips] = useState([]);
15469
15778
  const [clipClassifications, setClipClassifications] = useState({});
15470
15779
  const [isLoading, setIsLoading] = useState(false);
@@ -15484,11 +15793,6 @@ function useIdleTimeClipClassifications({
15484
15793
  setIsLoading(false);
15485
15794
  return;
15486
15795
  }
15487
- if (!session?.access_token) {
15488
- setError("Not authenticated");
15489
- setIsLoading(false);
15490
- return;
15491
- }
15492
15796
  setIsLoading(true);
15493
15797
  setError(null);
15494
15798
  lastRequestKeyRef.current = requestKey;
@@ -15497,11 +15801,10 @@ function useIdleTimeClipClassifications({
15497
15801
  let page = 1;
15498
15802
  let hasMore = true;
15499
15803
  while (hasMore && page <= MAX_PAGES && collected.length < limit) {
15500
- const response = await fetch("/api/clips/supabase", {
15804
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
15501
15805
  method: "POST",
15502
15806
  headers: {
15503
- "Content-Type": "application/json",
15504
- "Authorization": `Bearer ${session.access_token}`
15807
+ "Content-Type": "application/json"
15505
15808
  },
15506
15809
  body: JSON.stringify({
15507
15810
  action: "clip-metadata",
@@ -15511,7 +15814,8 @@ function useIdleTimeClipClassifications({
15511
15814
  category: "idle_time",
15512
15815
  page,
15513
15816
  limit
15514
- })
15817
+ }),
15818
+ redirectReason: "session_expired"
15515
15819
  });
15516
15820
  if (!response.ok) {
15517
15821
  throw new Error(`Idle clip metadata request failed: ${response.status}`);
@@ -15541,7 +15845,7 @@ function useIdleTimeClipClassifications({
15541
15845
  setIsLoading(false);
15542
15846
  }
15543
15847
  }
15544
- }, [enabled, workspaceId, date, shiftId, session?.access_token, requestKey, limit]);
15848
+ }, [enabled, workspaceId, date, shiftId, requestKey, limit, supabase]);
15545
15849
  useEffect(() => {
15546
15850
  fetchIdleClips();
15547
15851
  }, [fetchIdleClips]);
@@ -15553,18 +15857,31 @@ function useIdleTimeClipClassifications({
15553
15857
  [idleClips]
15554
15858
  );
15555
15859
  useEffect(() => {
15556
- if (!enabled || idleClipIds.length === 0 || !session?.access_token) {
15860
+ if (!enabled || idleClipIds.length === 0) {
15557
15861
  return;
15558
15862
  }
15559
- fetchClassifications(idleClipIds, session.access_token).then((classifications) => {
15560
- setClipClassifications((prev) => ({
15561
- ...prev,
15562
- ...classifications
15563
- }));
15564
- }).catch((err) => {
15565
- console.error("[useIdleTimeClipClassifications] Error fetching classifications:", err);
15566
- });
15567
- }, [enabled, idleClipIds, session?.access_token]);
15863
+ let cancelled = false;
15864
+ const fetchIdleClassifications = async () => {
15865
+ try {
15866
+ const token = await getAccessTokenOrRedirect(supabase, { redirectReason: "session_expired" });
15867
+ if (cancelled) return;
15868
+ const classifications = await fetchClassifications(idleClipIds, token);
15869
+ if (cancelled) return;
15870
+ setClipClassifications((prev) => ({
15871
+ ...prev,
15872
+ ...classifications
15873
+ }));
15874
+ } catch (err) {
15875
+ if (!cancelled) {
15876
+ console.error("[useIdleTimeClipClassifications] Error fetching classifications:", err);
15877
+ }
15878
+ }
15879
+ };
15880
+ fetchIdleClassifications();
15881
+ return () => {
15882
+ cancelled = true;
15883
+ };
15884
+ }, [enabled, idleClipIds, supabase]);
15568
15885
  useClassificationRealtimeUpdates({
15569
15886
  clipIds: idleClipIds,
15570
15887
  enabled: enabled && idleClipIds.length > 0,
@@ -15977,6 +16294,70 @@ var aggregateKPIsFromLineMetricsRows = (rows) => {
15977
16294
  };
15978
16295
  };
15979
16296
 
16297
+ // src/lib/utils/awards.ts
16298
+ var toNumber2 = (value) => {
16299
+ if (typeof value === "number" && Number.isFinite(value)) return value;
16300
+ if (typeof value === "string") {
16301
+ const parsed = Number.parseFloat(value);
16302
+ return Number.isFinite(parsed) ? parsed : null;
16303
+ }
16304
+ return null;
16305
+ };
16306
+ var formatAwardMonth = (periodStart) => {
16307
+ if (!periodStart) return "Unknown";
16308
+ const date = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
16309
+ if (Number.isNaN(date.getTime())) return "Unknown";
16310
+ return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
16311
+ };
16312
+ var getAwardTitle = (awardType) => {
16313
+ switch (awardType) {
16314
+ case "top_efficiency":
16315
+ return "Top Performer";
16316
+ case "most_improved":
16317
+ return "Most Improved";
16318
+ default:
16319
+ return "Achievement";
16320
+ }
16321
+ };
16322
+ var getAwardBadgeType = (awardType) => {
16323
+ switch (awardType) {
16324
+ case "top_efficiency":
16325
+ return "gold";
16326
+ case "most_improved":
16327
+ return "silver";
16328
+ default:
16329
+ return "special";
16330
+ }
16331
+ };
16332
+ var getAwardDescription = (award) => {
16333
+ const metrics2 = award.metrics || {};
16334
+ if (award.award_type === "top_efficiency") {
16335
+ const avg = toNumber2(metrics2.avg_efficiency);
16336
+ const lineCount = toNumber2(metrics2.line_count);
16337
+ if (avg !== null) {
16338
+ const lineSuffix = lineCount ? ` across ${lineCount} line${lineCount === 1 ? "" : "s"}` : "";
16339
+ return `Average efficiency ${avg.toFixed(1)}%${lineSuffix}.`;
16340
+ }
16341
+ return "Highest average efficiency across assigned lines.";
16342
+ }
16343
+ if (award.award_type === "most_improved") {
16344
+ const delta = toNumber2(metrics2.delta);
16345
+ if (delta !== null) {
16346
+ const sign = delta >= 0 ? "+" : "";
16347
+ return `Improved by ${sign}${delta.toFixed(1)} points month-over-month.`;
16348
+ }
16349
+ return "Largest efficiency improvement across months.";
16350
+ }
16351
+ return void 0;
16352
+ };
16353
+ var getInitials = (name) => {
16354
+ if (!name) return "TP";
16355
+ const parts = name.trim().split(/\s+/).filter(Boolean);
16356
+ if (parts.length === 0) return "TP";
16357
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
16358
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
16359
+ };
16360
+
15980
16361
  // ../../node_modules/clsx/dist/clsx.mjs
15981
16362
  function r(e) {
15982
16363
  var t, f, n = "";
@@ -18815,6 +19196,59 @@ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
18815
19196
  };
18816
19197
  var throttledReloadDashboard = createThrottledReload(5e3, 3);
18817
19198
 
19199
+ // src/lib/utils/sentryContext.ts
19200
+ function getSentry() {
19201
+ try {
19202
+ return __require("@sentry/nextjs");
19203
+ } catch {
19204
+ return null;
19205
+ }
19206
+ }
19207
+ function setSentryUserContext(user) {
19208
+ const sentry = getSentry();
19209
+ if (!sentry) return;
19210
+ if (user) {
19211
+ sentry.setUser({
19212
+ id: user.id,
19213
+ email: user.email
19214
+ });
19215
+ sentry.setTags({
19216
+ company_id: user.company_id || "unknown",
19217
+ role: user.role || "unknown",
19218
+ role_level: user.role_level || "unknown"
19219
+ });
19220
+ } else {
19221
+ sentry.setUser(null);
19222
+ sentry.setTags({
19223
+ company_id: void 0,
19224
+ role: void 0,
19225
+ role_level: void 0
19226
+ });
19227
+ }
19228
+ }
19229
+ function setSentryWorkspaceContext(config) {
19230
+ const sentry = getSentry();
19231
+ if (!sentry) return;
19232
+ sentry.setTags({
19233
+ workspace_company: config.companyId || "unknown",
19234
+ workspace_factory: config.factoryId || "unknown",
19235
+ factory_name: config.factoryName || "unknown"
19236
+ });
19237
+ }
19238
+ function clearSentryContext() {
19239
+ const sentry = getSentry();
19240
+ if (!sentry) return;
19241
+ sentry.setUser(null);
19242
+ sentry.setTags({
19243
+ company_id: void 0,
19244
+ role: void 0,
19245
+ role_level: void 0,
19246
+ workspace_company: void 0,
19247
+ workspace_factory: void 0,
19248
+ factory_name: void 0
19249
+ });
19250
+ }
19251
+
18818
19252
  // src/lib/utils/index.ts
18819
19253
  var formatIdleTime = (idleTimeInSeconds) => {
18820
19254
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -26612,7 +27046,7 @@ var OptifyeLogoLoader = ({
26612
27046
  message,
26613
27047
  className
26614
27048
  }) => {
26615
- const sizeClasses = {
27049
+ const sizeClasses3 = {
26616
27050
  sm: "w-10 h-10",
26617
27051
  // 40px
26618
27052
  md: "w-16 h-16",
@@ -26630,7 +27064,7 @@ var OptifyeLogoLoader = ({
26630
27064
  /* @__PURE__ */ jsx(
26631
27065
  Logo,
26632
27066
  {
26633
- className: `${sizeClasses[size]} object-contain animate-pulse select-none pointer-events-none`,
27067
+ className: `${sizeClasses3[size]} object-contain animate-pulse select-none pointer-events-none`,
26634
27068
  alt: "Optifye Logo"
26635
27069
  }
26636
27070
  ),
@@ -27146,7 +27580,7 @@ var RoleBadge = ({
27146
27580
  className = ""
27147
27581
  }) => {
27148
27582
  const config = getRoleConfig(role);
27149
- const sizeClasses = {
27583
+ const sizeClasses3 = {
27150
27584
  sm: "px-2 py-0.5 text-xs gap-1",
27151
27585
  md: "px-2.5 py-1 text-sm gap-1.5",
27152
27586
  lg: "px-3 py-1.5 text-base gap-2"
@@ -27161,7 +27595,7 @@ var RoleBadge = ({
27161
27595
  {
27162
27596
  className: cn(
27163
27597
  "inline-flex items-center font-medium rounded-full transition-all duration-200",
27164
- sizeClasses[size],
27598
+ sizeClasses3[size],
27165
27599
  config.bgColor,
27166
27600
  config.textColor,
27167
27601
  config.borderColor,
@@ -27196,6 +27630,15 @@ function getRoleConfig(role) {
27196
27630
  textColor: "text-blue-700",
27197
27631
  borderColor: "border-blue-200"
27198
27632
  };
27633
+ case "it":
27634
+ return {
27635
+ label: "IT",
27636
+ description: "Company-wide access - IT personnel with full company access",
27637
+ icon: Wrench,
27638
+ bgColor: "bg-teal-50",
27639
+ textColor: "text-teal-700",
27640
+ borderColor: "border-teal-200"
27641
+ };
27199
27642
  case "plant_head":
27200
27643
  return {
27201
27644
  label: "Plant Head",
@@ -32838,21 +33281,16 @@ function useWorkspaceCrop(workspaceId) {
32838
33281
  setIsLoading(true);
32839
33282
  setError(null);
32840
33283
  try {
32841
- const { data: { session } } = await supabase.auth.getSession();
32842
- const token = session?.access_token || null;
32843
- if (!token) {
32844
- throw new Error("Authentication required");
32845
- }
32846
- const response = await fetch("/api/clips/supabase", {
33284
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
32847
33285
  method: "POST",
32848
33286
  headers: {
32849
- "Content-Type": "application/json",
32850
- "Authorization": `Bearer ${token}`
33287
+ "Content-Type": "application/json"
32851
33288
  },
32852
33289
  body: JSON.stringify({
32853
33290
  action: "crop",
32854
33291
  workspaceId
32855
- })
33292
+ }),
33293
+ redirectReason: "session_expired"
32856
33294
  });
32857
33295
  if (!response.ok) {
32858
33296
  throw new Error(`Failed to fetch crop: ${response.statusText}`);
@@ -33408,7 +33846,7 @@ var BackButton = ({
33408
33846
  disabled = false,
33409
33847
  "aria-label": ariaLabel
33410
33848
  }) => {
33411
- const sizeClasses = {
33849
+ const sizeClasses3 = {
33412
33850
  sm: {
33413
33851
  container: "gap-1 px-2 py-1.5",
33414
33852
  icon: "w-3.5 h-3.5",
@@ -33425,7 +33863,7 @@ var BackButton = ({
33425
33863
  text: "text-base"
33426
33864
  }
33427
33865
  };
33428
- const currentSize = sizeClasses[size];
33866
+ const currentSize = sizeClasses3[size];
33429
33867
  return /* @__PURE__ */ jsxs(
33430
33868
  "button",
33431
33869
  {
@@ -33471,7 +33909,7 @@ var BackButtonMinimal = ({
33471
33909
  disabled = false,
33472
33910
  "aria-label": ariaLabel
33473
33911
  }) => {
33474
- const sizeClasses = {
33912
+ const sizeClasses3 = {
33475
33913
  sm: {
33476
33914
  icon: "w-3.5 h-3.5",
33477
33915
  text: "text-xs ml-1"
@@ -33485,7 +33923,7 @@ var BackButtonMinimal = ({
33485
33923
  text: "text-base ml-2"
33486
33924
  }
33487
33925
  };
33488
- const currentSize = sizeClasses[size];
33926
+ const currentSize = sizeClasses3[size];
33489
33927
  return /* @__PURE__ */ jsxs(
33490
33928
  "button",
33491
33929
  {
@@ -33764,6 +34202,196 @@ var NewClipsNotification = ({
33764
34202
  }
33765
34203
  );
33766
34204
  };
34205
+ var sizeClasses = {
34206
+ xs: { container: "w-5 h-5", text: "text-[8px]" },
34207
+ sm: { container: "w-6 h-6", text: "text-[10px]" },
34208
+ md: { container: "w-8 h-8", text: "text-xs" },
34209
+ lg: { container: "w-10 h-10", text: "text-sm" },
34210
+ xl: { container: "w-12 h-12", text: "text-base" }
34211
+ };
34212
+ var getInitials2 = (firstName, lastName, name, email) => {
34213
+ if (firstName) {
34214
+ const first = firstName.charAt(0).toUpperCase();
34215
+ const last = lastName ? lastName.charAt(0).toUpperCase() : "";
34216
+ return `${first}${last}` || first;
34217
+ }
34218
+ if (name) {
34219
+ const parts = name.trim().split(/\s+/).filter(Boolean);
34220
+ if (parts.length === 0) return "U";
34221
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
34222
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
34223
+ }
34224
+ if (email) {
34225
+ const localPart = email.split("@")[0];
34226
+ return localPart.slice(0, 2).toUpperCase();
34227
+ }
34228
+ return "U";
34229
+ };
34230
+ var UserAvatar = ({
34231
+ imageUrl,
34232
+ firstName,
34233
+ lastName,
34234
+ name,
34235
+ email,
34236
+ size = "md",
34237
+ className = "",
34238
+ showBorder = true
34239
+ }) => {
34240
+ const [imageError, setImageError] = useState(false);
34241
+ const { container, text } = sizeClasses[size] || sizeClasses.md;
34242
+ const initials = getInitials2(firstName, lastName, name, email);
34243
+ const showImage = imageUrl && !imageError;
34244
+ const borderClass = showBorder ? "border border-gray-200" : "";
34245
+ return /* @__PURE__ */ jsx(
34246
+ "div",
34247
+ {
34248
+ className: `${container} rounded-full overflow-hidden flex-shrink-0 ${borderClass} ${className}`,
34249
+ children: showImage ? /* @__PURE__ */ jsx(
34250
+ "img",
34251
+ {
34252
+ src: imageUrl,
34253
+ alt: name || firstName || email || "User avatar",
34254
+ className: "w-full h-full object-cover",
34255
+ onError: () => setImageError(true)
34256
+ }
34257
+ ) : /* @__PURE__ */ jsx(
34258
+ "div",
34259
+ {
34260
+ className: `w-full h-full flex items-center justify-center bg-gray-100 text-gray-600 font-bold ${text}`,
34261
+ children: initials
34262
+ }
34263
+ )
34264
+ }
34265
+ );
34266
+ };
34267
+ var BUCKET_NAME = "avatars";
34268
+ var MAX_FILE_SIZE = 2 * 1024 * 1024;
34269
+ var ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"];
34270
+ var sizeClasses2 = {
34271
+ md: { container: "w-16 h-16", icon: "w-5 h-5" },
34272
+ lg: { container: "w-24 h-24", icon: "w-6 h-6" }
34273
+ };
34274
+ var AvatarUpload = ({
34275
+ currentImageUrl,
34276
+ firstName,
34277
+ lastName,
34278
+ email,
34279
+ userId,
34280
+ onUploadComplete,
34281
+ onUploadError,
34282
+ onRemove,
34283
+ size = "lg",
34284
+ disabled = false,
34285
+ supabase
34286
+ }) => {
34287
+ const [isUploading, setIsUploading] = useState(false);
34288
+ const [previewUrl, setPreviewUrl] = useState(null);
34289
+ const fileInputRef = useRef(null);
34290
+ const { container, icon } = sizeClasses2[size] || sizeClasses2.lg;
34291
+ const displayUrl = previewUrl || currentImageUrl;
34292
+ const handleFileSelect = async (event) => {
34293
+ const file = event.target.files?.[0];
34294
+ if (!file) return;
34295
+ if (!ALLOWED_TYPES.includes(file.type)) {
34296
+ const error = new Error("Invalid file type. Please upload a JPG, PNG, or WebP image.");
34297
+ onUploadError?.(error);
34298
+ return;
34299
+ }
34300
+ if (file.size > MAX_FILE_SIZE) {
34301
+ const error = new Error("File too large. Maximum size is 2MB.");
34302
+ onUploadError?.(error);
34303
+ return;
34304
+ }
34305
+ const reader = new FileReader();
34306
+ reader.onload = (e) => {
34307
+ setPreviewUrl(e.target?.result);
34308
+ };
34309
+ reader.readAsDataURL(file);
34310
+ setIsUploading(true);
34311
+ try {
34312
+ const fileExt = file.name.split(".").pop()?.toLowerCase() || "jpg";
34313
+ const fileName = `${userId}/${Date.now()}.${fileExt}`;
34314
+ const { error: uploadError } = await supabase.storage.from(BUCKET_NAME).upload(fileName, file, {
34315
+ cacheControl: "3600",
34316
+ upsert: true
34317
+ });
34318
+ if (uploadError) {
34319
+ throw uploadError;
34320
+ }
34321
+ const { data: publicUrlData } = supabase.storage.from(BUCKET_NAME).getPublicUrl(fileName);
34322
+ const publicUrl = publicUrlData?.publicUrl;
34323
+ if (publicUrl) {
34324
+ onUploadComplete(publicUrl);
34325
+ }
34326
+ } catch (error) {
34327
+ console.error("[AvatarUpload] Upload failed:", error);
34328
+ setPreviewUrl(null);
34329
+ onUploadError?.(error instanceof Error ? error : new Error("Upload failed"));
34330
+ } finally {
34331
+ setIsUploading(false);
34332
+ if (fileInputRef.current) {
34333
+ fileInputRef.current.value = "";
34334
+ }
34335
+ }
34336
+ };
34337
+ const handleClick = () => {
34338
+ if (!disabled && !isUploading) {
34339
+ fileInputRef.current?.click();
34340
+ }
34341
+ };
34342
+ const handleRemove = (e) => {
34343
+ e.stopPropagation();
34344
+ setPreviewUrl(null);
34345
+ onRemove?.();
34346
+ };
34347
+ return /* @__PURE__ */ jsxs("div", { className: "relative inline-block", children: [
34348
+ /* @__PURE__ */ jsxs(
34349
+ "button",
34350
+ {
34351
+ type: "button",
34352
+ onClick: handleClick,
34353
+ disabled: disabled || isUploading,
34354
+ className: `${container} rounded-full overflow-hidden relative group cursor-pointer disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`,
34355
+ children: [
34356
+ /* @__PURE__ */ jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsx(
34357
+ UserAvatar,
34358
+ {
34359
+ imageUrl: displayUrl,
34360
+ firstName,
34361
+ lastName,
34362
+ email,
34363
+ size: size === "lg" ? "xl" : "lg",
34364
+ showBorder: false,
34365
+ className: "w-full h-full"
34366
+ }
34367
+ ) }),
34368
+ !isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center rounded-full", children: /* @__PURE__ */ jsx(Camera, { className: `${icon} text-white` }) }),
34369
+ isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/40 flex items-center justify-center rounded-full", children: /* @__PURE__ */ jsx(Loader2, { className: `${icon} text-white animate-spin` }) })
34370
+ ]
34371
+ }
34372
+ ),
34373
+ displayUrl && onRemove && !isUploading && /* @__PURE__ */ jsx(
34374
+ "button",
34375
+ {
34376
+ type: "button",
34377
+ onClick: handleRemove,
34378
+ className: "absolute -top-1 -right-1 w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center hover:bg-red-600 transition-colors shadow-sm",
34379
+ children: /* @__PURE__ */ jsx(X, { className: "w-3 h-3" })
34380
+ }
34381
+ ),
34382
+ /* @__PURE__ */ jsx(
34383
+ "input",
34384
+ {
34385
+ ref: fileInputRef,
34386
+ type: "file",
34387
+ accept: ALLOWED_TYPES.join(","),
34388
+ onChange: handleFileSelect,
34389
+ className: "hidden",
34390
+ disabled: disabled || isUploading
34391
+ }
34392
+ )
34393
+ ] });
34394
+ };
33767
34395
  var parseCycleTime = (value) => {
33768
34396
  if (typeof value === "number" && Number.isFinite(value)) {
33769
34397
  return value;
@@ -33836,6 +34464,8 @@ var FileManagerFilters = ({
33836
34464
  activeFilter,
33837
34465
  currentVideoId,
33838
34466
  counts,
34467
+ isReady = false,
34468
+ prefetchedPercentileCounts,
33839
34469
  onFilterChange,
33840
34470
  onVideoSelect,
33841
34471
  onClipSelect,
@@ -33871,8 +34501,16 @@ var FileManagerFilters = ({
33871
34501
  ...localClipClassifications
33872
34502
  }), [clipClassifications, localClipClassifications]);
33873
34503
  const { state: filterState } = useClipFilter();
33874
- const [percentileCounts, setPercentileCounts] = useState({});
34504
+ const [percentileCounts, setPercentileCounts] = useState({
34505
+ "fast-cycles": null,
34506
+ "slow-cycles": null
34507
+ });
33875
34508
  const [percentileClips, setPercentileClips] = useState({});
34509
+ const percentileCountsKeyRef = useRef(null);
34510
+ const percentilePrefetchRef = useRef({
34511
+ key: null,
34512
+ types: /* @__PURE__ */ new Set()
34513
+ });
33876
34514
  const [loadingPercentile, setLoadingPercentile] = useState(false);
33877
34515
  const resolvedTargetCycleTime = targetCycleTime && targetCycleTime > 0 ? targetCycleTime : null;
33878
34516
  const getRootCauseConfig = (reason) => {
@@ -33924,6 +34562,14 @@ var FileManagerFilters = ({
33924
34562
  }
33925
34563
  return { text: "Fast", className: "bg-green-100 text-green-700" };
33926
34564
  }, [resolvedTargetCycleTime]);
34565
+ const getAuthToken3 = useCallback(async () => {
34566
+ try {
34567
+ return await getAccessTokenOrRedirect(supabase, { redirectReason: "session_expired" });
34568
+ } catch (error) {
34569
+ console.error("[FileManager] Error getting auth token:", error);
34570
+ return null;
34571
+ }
34572
+ }, [supabase]);
33927
34573
  const fetchClipMetadata = useCallback(async (categoryId, page = 1) => {
33928
34574
  if (!workspaceId || !date || shift === void 0) {
33929
34575
  console.warn("[FileManager] Missing required params for clip metadata fetch");
@@ -33932,12 +34578,10 @@ var FileManagerFilters = ({
33932
34578
  const loadingKey = `${categoryId}-${page}`;
33933
34579
  setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
33934
34580
  try {
33935
- const authToken = await getAuthToken4();
33936
- const response = await fetch("/api/clips/supabase", {
34581
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
33937
34582
  method: "POST",
33938
34583
  headers: {
33939
- "Content-Type": "application/json",
33940
- "Authorization": `Bearer ${authToken}`
34584
+ "Content-Type": "application/json"
33941
34585
  },
33942
34586
  body: JSON.stringify({
33943
34587
  action: "clip-metadata",
@@ -33949,7 +34593,8 @@ var FileManagerFilters = ({
33949
34593
  limit: 50,
33950
34594
  snapshotDateTime,
33951
34595
  snapshotClipId
33952
- })
34596
+ }),
34597
+ redirectReason: "session_expired"
33953
34598
  });
33954
34599
  if (!response.ok) {
33955
34600
  throw new Error(`API error: ${response.status}`);
@@ -33959,6 +34604,7 @@ var FileManagerFilters = ({
33959
34604
  ...prev,
33960
34605
  [categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
33961
34606
  }));
34607
+ const authToken = categoryId === "idle_time" ? await getAuthToken3() : null;
33962
34608
  if (categoryId === "idle_time" && authToken) {
33963
34609
  const seededClassifications = {};
33964
34610
  (data.clips || []).forEach((clip) => {
@@ -34004,16 +34650,7 @@ var FileManagerFilters = ({
34004
34650
  return newSet;
34005
34651
  });
34006
34652
  }
34007
- }, [workspaceId, date, shift, mergedClipClassifications, snapshotDateTime, snapshotClipId]);
34008
- const getAuthToken4 = async () => {
34009
- try {
34010
- const { data: { session } } = await supabase.auth.getSession();
34011
- return session?.access_token || null;
34012
- } catch (error) {
34013
- console.error("[FileManager] Error getting auth token:", error);
34014
- return null;
34015
- }
34016
- };
34653
+ }, [workspaceId, date, shift, mergedClipClassifications, snapshotDateTime, snapshotClipId, supabase, getAuthToken3]);
34017
34654
  const fetchPercentileClips = useCallback(async (type) => {
34018
34655
  if (!workspaceId || !date || shift === void 0) {
34019
34656
  console.warn("[FileManager] Missing required params for percentile clips fetch");
@@ -34022,11 +34659,10 @@ var FileManagerFilters = ({
34022
34659
  try {
34023
34660
  const startDate = `${date}T00:00:00Z`;
34024
34661
  const endDate = `${date}T23:59:59Z`;
34025
- const response = await fetch("/api/clips/supabase", {
34662
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
34026
34663
  method: "POST",
34027
34664
  headers: {
34028
- "Content-Type": "application/json",
34029
- "Authorization": `Bearer ${await getAuthToken4()}`
34665
+ "Content-Type": "application/json"
34030
34666
  },
34031
34667
  body: JSON.stringify({
34032
34668
  action: "percentile-clips",
@@ -34038,7 +34674,8 @@ var FileManagerFilters = ({
34038
34674
  limit: 50,
34039
34675
  // The actual percentile action (fast-cycles, slow-cycles, idle-times)
34040
34676
  percentileAction: type
34041
- })
34677
+ }),
34678
+ redirectReason: "session_expired"
34042
34679
  });
34043
34680
  if (!response.ok) {
34044
34681
  throw new Error(`API error: ${response.status}`);
@@ -34048,15 +34685,132 @@ var FileManagerFilters = ({
34048
34685
  ...prev,
34049
34686
  [type]: data.clips || []
34050
34687
  }));
34051
- setPercentileCounts((prev) => ({
34052
- ...prev,
34053
- [type]: data.total || 0
34054
- }));
34688
+ setPercentileCounts((prev) => {
34689
+ if (typeof prev[type] === "number") {
34690
+ return prev;
34691
+ }
34692
+ return {
34693
+ ...prev,
34694
+ [type]: data.total || 0
34695
+ };
34696
+ });
34055
34697
  console.log(`[FileManager] Loaded ${data.clips?.length || 0} ${type} clips (percentile: ${filterState.percentile}%)`);
34056
34698
  } catch (error) {
34057
34699
  console.error(`[FileManager] Error fetching ${type} clips:`, error);
34058
34700
  }
34701
+ }, [workspaceId, date, shift, filterState.percentile, supabase]);
34702
+ const percentileCountsKey = useMemo(() => {
34703
+ if (!workspaceId || !date || shift === void 0) {
34704
+ return null;
34705
+ }
34706
+ return `${workspaceId}:${date}:${shift}:${filterState.percentile}`;
34059
34707
  }, [workspaceId, date, shift, filterState.percentile]);
34708
+ useEffect(() => {
34709
+ if (!percentileCountsKey) {
34710
+ percentileCountsKeyRef.current = null;
34711
+ percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
34712
+ setPercentileCounts({
34713
+ "fast-cycles": null,
34714
+ "slow-cycles": null
34715
+ });
34716
+ setPercentileClips({});
34717
+ return;
34718
+ }
34719
+ if (percentileCountsKeyRef.current === percentileCountsKey) {
34720
+ return;
34721
+ }
34722
+ percentileCountsKeyRef.current = percentileCountsKey;
34723
+ percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
34724
+ setPercentileCounts({
34725
+ "fast-cycles": null,
34726
+ "slow-cycles": null
34727
+ });
34728
+ setPercentileClips({});
34729
+ }, [percentileCountsKey]);
34730
+ useEffect(() => {
34731
+ if (!prefetchedPercentileCounts || !percentileCountsKey) {
34732
+ return;
34733
+ }
34734
+ if (prefetchedPercentileCounts.key !== percentileCountsKey) {
34735
+ return;
34736
+ }
34737
+ if (prefetchedPercentileCounts.percentile !== filterState.percentile) {
34738
+ return;
34739
+ }
34740
+ setPercentileCounts((prev) => ({
34741
+ ...prev,
34742
+ "fast-cycles": typeof prefetchedPercentileCounts.counts["fast-cycles"] === "number" ? prefetchedPercentileCounts.counts["fast-cycles"] : prev["fast-cycles"],
34743
+ "slow-cycles": typeof prefetchedPercentileCounts.counts["slow-cycles"] === "number" ? prefetchedPercentileCounts.counts["slow-cycles"] : prev["slow-cycles"]
34744
+ }));
34745
+ }, [prefetchedPercentileCounts, percentileCountsKey, filterState.percentile]);
34746
+ const fetchPercentileCounts = useCallback(async (options) => {
34747
+ if (!workspaceId || !date || shift === void 0) {
34748
+ console.warn("[FileManager] Missing required params for percentile counts fetch");
34749
+ return;
34750
+ }
34751
+ const requestKey = `${workspaceId}:${date}:${shift}:${filterState.percentile}`;
34752
+ if (percentileCountsKeyRef.current === requestKey && typeof percentileCounts["fast-cycles"] === "number" && typeof percentileCounts["slow-cycles"] === "number") {
34753
+ return;
34754
+ }
34755
+ try {
34756
+ const startDate = `${date}T00:00:00Z`;
34757
+ const endDate = `${date}T23:59:59Z`;
34758
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
34759
+ method: "POST",
34760
+ headers: {
34761
+ "Content-Type": "application/json"
34762
+ },
34763
+ body: JSON.stringify({
34764
+ action: "percentile-counts",
34765
+ workspaceId,
34766
+ startDate,
34767
+ endDate,
34768
+ percentile: filterState.percentile,
34769
+ shiftId: shift
34770
+ }),
34771
+ redirectReason: "session_expired"
34772
+ });
34773
+ if (!response.ok) {
34774
+ throw new Error(`API error: ${response.status}`);
34775
+ }
34776
+ const data = await response.json();
34777
+ const fastCycles = data?.counts?.["fast-cycles"];
34778
+ const slowCycles = data?.counts?.["slow-cycles"];
34779
+ setPercentileCounts((prev) => ({
34780
+ ...prev,
34781
+ "fast-cycles": typeof fastCycles === "number" ? fastCycles : prev["fast-cycles"],
34782
+ "slow-cycles": typeof slowCycles === "number" ? slowCycles : prev["slow-cycles"]
34783
+ }));
34784
+ if (options?.prefetchClips) {
34785
+ if (percentilePrefetchRef.current.key !== requestKey) {
34786
+ percentilePrefetchRef.current = { key: requestKey, types: /* @__PURE__ */ new Set() };
34787
+ }
34788
+ if (typeof fastCycles === "number" && fastCycles > 0 && (percentileClips["fast-cycles"] || []).length === 0 && !percentilePrefetchRef.current.types.has("fast-cycles")) {
34789
+ percentilePrefetchRef.current.types.add("fast-cycles");
34790
+ fetchPercentileClips("fast-cycles");
34791
+ }
34792
+ if (typeof slowCycles === "number" && slowCycles > 0 && (percentileClips["slow-cycles"] || []).length === 0 && !percentilePrefetchRef.current.types.has("slow-cycles")) {
34793
+ percentilePrefetchRef.current.types.add("slow-cycles");
34794
+ fetchPercentileClips("slow-cycles");
34795
+ }
34796
+ }
34797
+ } catch (error) {
34798
+ console.error("[FileManager] Error fetching percentile counts:", error);
34799
+ }
34800
+ }, [workspaceId, date, shift, filterState.percentile, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
34801
+ useEffect(() => {
34802
+ if (!isReady || !percentileCountsKey) {
34803
+ return;
34804
+ }
34805
+ const schedule = () => {
34806
+ fetchPercentileCounts({ prefetchClips: true });
34807
+ };
34808
+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
34809
+ window.requestIdleCallback(schedule, { timeout: 1e3 });
34810
+ } else {
34811
+ setTimeout(schedule, 0);
34812
+ }
34813
+ }, [isReady, percentileCountsKey, fetchPercentileCounts]);
34060
34814
  const shouldShowCategory = useCallback((categoryId) => {
34061
34815
  switch (categoryId) {
34062
34816
  case "fast-cycles":
@@ -34242,6 +34996,8 @@ var FileManagerFilters = ({
34242
34996
  });
34243
34997
  const filteredFastCycles = (percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
34244
34998
  const filteredSlowCycles = (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
34999
+ const fastCount = typeof percentileCounts["fast-cycles"] === "number" ? percentileCounts["fast-cycles"] : null;
35000
+ const slowCount = typeof percentileCounts["slow-cycles"] === "number" ? percentileCounts["slow-cycles"] : null;
34245
35001
  const percentileCategories = [
34246
35002
  {
34247
35003
  id: "fast-cycles",
@@ -34249,7 +35005,7 @@ var FileManagerFilters = ({
34249
35005
  subtitle: "Top 10% fastest performance",
34250
35006
  description: "Top 10% fastest performance",
34251
35007
  type: "percentile-category",
34252
- count: isTimeFilterActive ? filteredFastCycles.length : percentileCounts["fast-cycles"] || 0,
35008
+ count: isTimeFilterActive ? fastCount === null && filteredFastCycles.length === 0 ? null : filteredFastCycles.length : fastCount,
34253
35009
  icon: getPercentileIcon("fast-cycles", expandedNodes.has("fast-cycles"), { text: "text-green-600" }),
34254
35010
  color: "green",
34255
35011
  percentileType: "fast-cycles",
@@ -34281,7 +35037,7 @@ var FileManagerFilters = ({
34281
35037
  subtitle: "Bottom 10% slowest performance",
34282
35038
  description: "Bottom 10% slowest performance",
34283
35039
  type: "percentile-category",
34284
- count: isTimeFilterActive ? filteredSlowCycles.length : percentileCounts["slow-cycles"] || 0,
35040
+ count: isTimeFilterActive ? slowCount === null && filteredSlowCycles.length === 0 ? null : filteredSlowCycles.length : slowCount,
34285
35041
  icon: getPercentileIcon("slow-cycles", expandedNodes.has("slow-cycles"), { text: "text-red-600" }),
34286
35042
  color: "red",
34287
35043
  percentileType: "slow-cycles",
@@ -34344,7 +35100,8 @@ var FileManagerFilters = ({
34344
35100
  const orderedIds = ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
34345
35101
  orderedIds.forEach((orderedId) => {
34346
35102
  const percentileCategory = percentileCategories.find((cat) => cat.id === orderedId);
34347
- if (percentileCategory && percentileCategory.count > 0 && shouldShowCategory(orderedId)) {
35103
+ const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" && percentileCategory.count > 0 : false;
35104
+ if (percentileCategory && shouldIncludePercentile && shouldShowCategory(orderedId)) {
34348
35105
  tree.push(percentileCategory);
34349
35106
  }
34350
35107
  const regularCategory = regularCategoryNodes.find((cat) => cat.id === orderedId);
@@ -34358,7 +35115,8 @@ var FileManagerFilters = ({
34358
35115
  }
34359
35116
  });
34360
35117
  percentileCategories.forEach((category) => {
34361
- if (!orderedIds.includes(category.id) && category.count > 0 && shouldShowCategory(category.id)) {
35118
+ const shouldIncludePercentile = typeof category.count === "number" && category.count > 0;
35119
+ if (!orderedIds.includes(category.id) && shouldIncludePercentile && shouldShowCategory(category.id)) {
34362
35120
  tree.push(category);
34363
35121
  }
34364
35122
  });
@@ -34435,7 +35193,9 @@ var FileManagerFilters = ({
34435
35193
  } else if (node.type === "video") {
34436
35194
  if (onClipSelect && node.categoryId !== void 0 && node.clipId !== void 0) {
34437
35195
  console.log(`[FileManager] Selecting clip: category=${node.categoryId}, clipId=${node.clipId}, position=${node.clipPosition}`);
34438
- onClipSelect(node.categoryId, node.clipId, node.clipPosition);
35196
+ const categoryClips = node.categoryId ? clipMetadata[node.categoryId] : void 0;
35197
+ const selectionContext = categoryClips?.length ? { clips: categoryClips, total: counts?.[node.categoryId] } : void 0;
35198
+ onClipSelect(node.categoryId, node.clipId, node.clipPosition, selectionContext);
34439
35199
  } else {
34440
35200
  const videoIndex = videos.findIndex((v) => v.id === node.id);
34441
35201
  if (videoIndex !== -1) {
@@ -34448,7 +35208,8 @@ var FileManagerFilters = ({
34448
35208
  const isExpanded = expandedNodes.has(node.id);
34449
35209
  const isActive = activeFilter === node.id;
34450
35210
  const isCurrentVideo = currentVideoId === node.id;
34451
- const hasChildren = (node.count || 0) > 0;
35211
+ const isCountUnknown = node.type === "percentile-category" && node.count === null;
35212
+ const hasChildren = isCountUnknown || (node.count || 0) > 0;
34452
35213
  const colorClasses = node.color ? getColorClasses(node.color) : null;
34453
35214
  return /* @__PURE__ */ jsxs("div", { className: "select-none animate-in", children: [
34454
35215
  /* @__PURE__ */ jsxs(
@@ -34509,7 +35270,7 @@ var FileManagerFilters = ({
34509
35270
  })()
34510
35271
  ) })
34511
35272
  ] }),
34512
- node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count }) })
35273
+ node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count === null ? "\u2014" : node.count }) })
34513
35274
  ] })
34514
35275
  ]
34515
35276
  }
@@ -35230,12 +35991,12 @@ var BottlenecksContent = ({
35230
35991
  workspaceMetrics: workspaceMetricsOverride,
35231
35992
  customCounterContent,
35232
35993
  triageMode = false,
35233
- ticketId
35994
+ ticketId,
35995
+ prefetchedPercentileCounts
35234
35996
  }) => {
35235
35997
  console.log("\u{1F3AB} [BottlenecksContent] Rendered with ticketId:", ticketId || "NONE", "workspaceId:", workspaceId, "date:", date, "shift:", shift);
35236
35998
  const dashboardConfig = useDashboardConfig();
35237
35999
  const supabase = useSupabase();
35238
- const { session } = useAuth();
35239
36000
  const timezone = useAppTimezone();
35240
36001
  const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineId);
35241
36002
  const { effectiveShift, effectiveDate } = useMemo(() => {
@@ -35659,10 +36420,9 @@ var BottlenecksContent = ({
35659
36420
  await loadFirstVideoForCategory(activeFilter);
35660
36421
  }
35661
36422
  }, [acknowledgeNewClips, refreshCounts, activeFilter, mergedCounts, loadFirstVideoForCategory, invalidateMetadataCache]);
35662
- const getAuthToken4 = useCallback(async () => {
36423
+ const getAuthToken3 = useCallback(async () => {
35663
36424
  try {
35664
- const { data: { session: session2 } } = await supabase.auth.getSession();
35665
- return session2?.access_token || null;
36425
+ return await getAccessTokenOrRedirect(supabase, { redirectReason: "session_expired" });
35666
36426
  } catch (error2) {
35667
36427
  console.error("[BottlenecksContent] Error getting auth token:", error2);
35668
36428
  return null;
@@ -35673,17 +36433,11 @@ var BottlenecksContent = ({
35673
36433
  const fetchTriageClips = async () => {
35674
36434
  setIsLoadingTriageClips(true);
35675
36435
  try {
35676
- const token = await getAuthToken4();
35677
- if (!token) {
35678
- console.error("[BottlenecksContent] No auth token available");
35679
- return;
35680
- }
35681
36436
  const fetchCategoryMetadata = async (categoryId) => {
35682
- const response = await fetch("/api/clips/supabase", {
36437
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
35683
36438
  method: "POST",
35684
36439
  headers: {
35685
- "Content-Type": "application/json",
35686
- "Authorization": `Bearer ${token}`
36440
+ "Content-Type": "application/json"
35687
36441
  },
35688
36442
  body: JSON.stringify({
35689
36443
  action: "metadata",
@@ -35694,7 +36448,8 @@ var BottlenecksContent = ({
35694
36448
  page: 1,
35695
36449
  limit: 100
35696
36450
  // Last 15 minutes
35697
- })
36451
+ }),
36452
+ redirectReason: "session_expired"
35698
36453
  });
35699
36454
  if (!response.ok) {
35700
36455
  console.error(`Failed to fetch ${categoryId} metadata`);
@@ -35721,28 +36476,42 @@ var BottlenecksContent = ({
35721
36476
  }
35722
36477
  };
35723
36478
  fetchTriageClips();
35724
- }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, getAuthToken4, isEffectiveShiftReady]);
36479
+ }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady]);
35725
36480
  useEffect(() => {
35726
- if (!triageMode || triageClips.length === 0 || !session?.access_token) {
35727
- return;
35728
- }
35729
- const idleClipIds = triageClips.filter((c) => c.categoryId === "idle_time").map((c) => c.id || c.clipId).filter(Boolean);
35730
- const limitedIdleClipIds = idleClipIds.slice(0, 20);
35731
- if (limitedIdleClipIds.length === 0) {
35732
- console.log("[BottlenecksContent] No idle_time clips to fetch classifications for (triage mode)");
36481
+ if (!triageMode || triageClips.length === 0) {
35733
36482
  return;
35734
36483
  }
35735
- console.log(`[BottlenecksContent] Fetching classifications for ${limitedIdleClipIds.length} idle_time clips (triage mode)`);
35736
- fetchClassifications(limitedIdleClipIds, session.access_token).then((classifications) => {
35737
- console.log("[BottlenecksContent] Received classifications (triage mode):", classifications);
35738
- setClipClassifications((prev) => ({
35739
- ...prev,
35740
- ...classifications
35741
- }));
35742
- }).catch((error2) => {
35743
- console.error("[BottlenecksContent] Error fetching classifications (triage mode):", error2);
35744
- });
35745
- }, [triageClips, triageMode, session?.access_token]);
36484
+ let cancelled = false;
36485
+ const fetchIdleClassifications = async () => {
36486
+ const idleClipIds = triageClips.filter((c) => c.categoryId === "idle_time").map((c) => c.id || c.clipId).filter(Boolean);
36487
+ const limitedIdleClipIds = idleClipIds.slice(0, 20);
36488
+ if (limitedIdleClipIds.length === 0) {
36489
+ console.log("[BottlenecksContent] No idle_time clips to fetch classifications for (triage mode)");
36490
+ return;
36491
+ }
36492
+ const token = await getAuthToken3();
36493
+ if (!token || cancelled) {
36494
+ return;
36495
+ }
36496
+ console.log(`[BottlenecksContent] Fetching classifications for ${limitedIdleClipIds.length} idle_time clips (triage mode)`);
36497
+ fetchClassifications(limitedIdleClipIds, token).then((classifications) => {
36498
+ if (cancelled) return;
36499
+ console.log("[BottlenecksContent] Received classifications (triage mode):", classifications);
36500
+ setClipClassifications((prev) => ({
36501
+ ...prev,
36502
+ ...classifications
36503
+ }));
36504
+ }).catch((error2) => {
36505
+ if (!cancelled) {
36506
+ console.error("[BottlenecksContent] Error fetching classifications (triage mode):", error2);
36507
+ }
36508
+ });
36509
+ };
36510
+ fetchIdleClassifications();
36511
+ return () => {
36512
+ cancelled = true;
36513
+ };
36514
+ }, [triageClips, triageMode, getAuthToken3]);
35746
36515
  useEffect(() => {
35747
36516
  if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
35748
36517
  if (firstClip && firstClip.type === activeFilter) {
@@ -35761,6 +36530,25 @@ var BottlenecksContent = ({
35761
36530
  const isPercentileCategory = useCallback((categoryId) => {
35762
36531
  return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
35763
36532
  }, []);
36533
+ const getMetadataCacheKey = useCallback((categoryId) => {
36534
+ return `${categoryId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}`;
36535
+ }, [effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId]);
36536
+ const applyMetadataSnapshot = useCallback((categoryId, clips, total) => {
36537
+ if (!clips || clips.length === 0) {
36538
+ return;
36539
+ }
36540
+ const cacheKey = getMetadataCacheKey(categoryId);
36541
+ setMetadataCache((prev) => ({
36542
+ ...prev,
36543
+ [cacheKey]: clips
36544
+ }));
36545
+ categoryMetadataRef.current = clips;
36546
+ setCategoryMetadata(clips);
36547
+ if (!isPercentileCategory(categoryId) && typeof total === "number") {
36548
+ currentTotalRef.current = total;
36549
+ setCurrentTotal(total);
36550
+ }
36551
+ }, [getMetadataCacheKey, isPercentileCategory]);
35764
36552
  const getClipTypesForPercentileCategory = useCallback((categoryId) => {
35765
36553
  switch (categoryId) {
35766
36554
  case "fast-cycles":
@@ -35818,7 +36606,7 @@ var BottlenecksContent = ({
35818
36606
  return;
35819
36607
  }
35820
36608
  const resolvedDate = effectiveDateString;
35821
- const cacheKey = `${categoryId}-${resolvedDate}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}`;
36609
+ const cacheKey = getMetadataCacheKey(categoryId);
35822
36610
  const cachedMetadata = !forceRefresh ? metadataCache[cacheKey] : void 0;
35823
36611
  try {
35824
36612
  if (cachedMetadata) {
@@ -35845,19 +36633,13 @@ var BottlenecksContent = ({
35845
36633
  return;
35846
36634
  }
35847
36635
  console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}${forceRefresh ? " (force refresh)" : ""}`);
35848
- const authToken = await getAuthToken4();
35849
- if (!authToken) {
35850
- console.error("Authentication required for metadata loading");
35851
- return;
35852
- }
35853
36636
  let response;
35854
36637
  if (isPercentileCategory(categoryId)) {
35855
36638
  const percentileType = categoryId === "fast-cycles" ? "fast-cycles" : categoryId === "slow-cycles" ? "slow-cycles" : "idle-times";
35856
- response = await fetch("/api/clips/supabase", {
36639
+ response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
35857
36640
  method: "POST",
35858
36641
  headers: {
35859
- "Content-Type": "application/json",
35860
- "Authorization": `Bearer ${authToken}`
36642
+ "Content-Type": "application/json"
35861
36643
  },
35862
36644
  body: JSON.stringify({
35863
36645
  action: "percentile-clips",
@@ -35868,14 +36650,14 @@ var BottlenecksContent = ({
35868
36650
  percentile: 10,
35869
36651
  shiftId: effectiveShiftId,
35870
36652
  limit: 100
35871
- })
36653
+ }),
36654
+ redirectReason: "session_expired"
35872
36655
  });
35873
36656
  } else {
35874
- response = await fetch("/api/clips/supabase", {
36657
+ response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
35875
36658
  method: "POST",
35876
36659
  headers: {
35877
- "Content-Type": "application/json",
35878
- "Authorization": `Bearer ${authToken}`
36660
+ "Content-Type": "application/json"
35879
36661
  },
35880
36662
  body: JSON.stringify({
35881
36663
  action: "clip-metadata",
@@ -35887,7 +36669,8 @@ var BottlenecksContent = ({
35887
36669
  limit: 100,
35888
36670
  snapshotDateTime,
35889
36671
  snapshotClipId
35890
- })
36672
+ }),
36673
+ redirectReason: "session_expired"
35891
36674
  });
35892
36675
  }
35893
36676
  if (!response.ok) {
@@ -35966,7 +36749,7 @@ var BottlenecksContent = ({
35966
36749
  } finally {
35967
36750
  setIsCategoryLoading(false);
35968
36751
  }
35969
- }, [workspaceId, effectiveDateString, effectiveShiftId, isPercentileCategory, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId]);
36752
+ }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, supabase]);
35970
36753
  useEffect(() => {
35971
36754
  if (previousFilterRef.current !== activeFilter) {
35972
36755
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -36014,7 +36797,7 @@ var BottlenecksContent = ({
36014
36797
  }
36015
36798
  loadCategoryMetadata(activeFilter, false);
36016
36799
  }, [currentClipId, activeFilter, isCategoryLoading, loadCategoryMetadata]);
36017
- const loadAndPlayClipById = useCallback(async (clipId, categoryId, position) => {
36800
+ const loadAndPlayClipById = useCallback(async (clipId, categoryId, position, metadataContext) => {
36018
36801
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
36019
36802
  console.log(`[BottlenecksContent] Loading clip by ID: ${clipId}, category=${categoryId}, position=${position}`);
36020
36803
  setIsTransitioning(true);
@@ -36036,7 +36819,12 @@ var BottlenecksContent = ({
36036
36819
  updateActiveFilter(categoryId);
36037
36820
  }
36038
36821
  try {
36039
- const metadataPromise = loadCategoryMetadata(categoryId, false).catch((err) => {
36822
+ const fallbackMetadata = metadataContext?.clips;
36823
+ const fallbackTotal = metadataContext?.total;
36824
+ if (fallbackMetadata && fallbackMetadata.length > 0) {
36825
+ applyMetadataSnapshot(categoryId, fallbackMetadata, fallbackTotal);
36826
+ }
36827
+ const metadataPromise = fallbackMetadata?.length ? Promise.resolve() : loadCategoryMetadata(categoryId, false).catch((err) => {
36040
36828
  console.warn(`[BottlenecksContent] Metadata fetch error (non-blocking):`, err);
36041
36829
  return null;
36042
36830
  });
@@ -36051,18 +36839,42 @@ var BottlenecksContent = ({
36051
36839
  }
36052
36840
  await metadataPromise;
36053
36841
  let metadataArray = categoryMetadataRef.current;
36054
- const clipExistsInMetadata = metadataArray.some((clip) => clip.clipId === clipId);
36055
- if (metadataArray.length === 0 || !clipExistsInMetadata) {
36056
- console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
36057
- await loadCategoryMetadata(categoryId, false, true);
36058
- metadataArray = categoryMetadataRef.current;
36842
+ const fallbackHasClip = fallbackMetadata?.some((clip) => clip.clipId === clipId);
36843
+ if ((metadataArray.length === 0 || !metadataArray.some((clip) => clip.clipId === clipId)) && fallbackHasClip) {
36844
+ applyMetadataSnapshot(categoryId, fallbackMetadata, fallbackTotal);
36845
+ metadataArray = fallbackMetadata;
36846
+ }
36847
+ if (metadataArray.length === 0 || !metadataArray.some((clip) => clip.clipId === clipId)) {
36848
+ if (!fallbackHasClip) {
36849
+ console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
36850
+ await loadCategoryMetadata(categoryId, false, true);
36851
+ metadataArray = categoryMetadataRef.current;
36852
+ }
36059
36853
  }
36060
36854
  if (metadataArray.length === 0) {
36061
- throw new Error(`No metadata available for category ${categoryId}`);
36855
+ console.warn(`[BottlenecksContent] No metadata available for category ${categoryId} after refresh`);
36856
+ if (!isPercentileCategory(categoryId)) {
36857
+ const resolvedPosition = typeof position === "number" ? position : 1;
36858
+ currentPositionRef.current = resolvedPosition;
36859
+ setCurrentPosition(resolvedPosition);
36860
+ const total = fallbackTotal ?? mergedCounts[categoryId] ?? 0;
36861
+ currentTotalRef.current = total;
36862
+ setCurrentTotal(total);
36863
+ }
36864
+ return;
36062
36865
  }
36063
36866
  const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
36064
36867
  if (clickedClipIndex === -1) {
36065
- throw new Error(`Clip ${clipId} not found after metadata refresh`);
36868
+ console.warn(`[BottlenecksContent] Clip ${clipId} not found after metadata refresh`);
36869
+ if (!isPercentileCategory(categoryId)) {
36870
+ const resolvedPosition = typeof position === "number" ? position : 1;
36871
+ currentPositionRef.current = resolvedPosition;
36872
+ setCurrentPosition(resolvedPosition);
36873
+ const total = fallbackTotal ?? mergedCounts[categoryId] ?? metadataArray.length;
36874
+ currentTotalRef.current = total;
36875
+ setCurrentTotal(total);
36876
+ }
36877
+ return;
36066
36878
  }
36067
36879
  setCurrentMetadataIndex(clickedClipIndex);
36068
36880
  currentMetadataIndexRef.current = clickedClipIndex;
@@ -36087,7 +36899,7 @@ var BottlenecksContent = ({
36087
36899
  clearLoadingState();
36088
36900
  }
36089
36901
  }
36090
- }, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, loadCategoryMetadata, mergedCounts, isPercentileCategory]);
36902
+ }, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, loadCategoryMetadata, applyMetadataSnapshot, mergedCounts, isPercentileCategory]);
36091
36903
  useCallback(async (categoryId, clipIndex) => {
36092
36904
  console.warn("[BottlenecksContent] loadAndPlayClip is deprecated, use loadAndPlayClipById instead");
36093
36905
  if (!workspaceId || !s3ClipsService || !isMountedRef.current || !isEffectiveShiftReady) return;
@@ -36361,22 +37173,36 @@ var BottlenecksContent = ({
36361
37173
  }, [])
36362
37174
  });
36363
37175
  useEffect(() => {
36364
- if (triageMode || !currentVideo || currentVideo.type !== "idle_time" || !session?.access_token) {
37176
+ if (triageMode || !currentVideo || currentVideo.type !== "idle_time") {
36365
37177
  return;
36366
37178
  }
36367
37179
  const clipId = currentVideo.id;
36368
37180
  if (!clipId || clipClassifications[clipId]) {
36369
37181
  return;
36370
37182
  }
36371
- fetchClassifications([clipId], session.access_token).then((classifications) => {
36372
- setClipClassifications((prev) => ({
36373
- ...prev,
36374
- ...classifications
36375
- }));
36376
- }).catch((error2) => {
36377
- console.error("[BottlenecksContent] Error fetching classification (current clip):", error2);
36378
- });
36379
- }, [triageMode, currentVideo, session?.access_token, clipClassifications]);
37183
+ let cancelled = false;
37184
+ const fetchCurrentClassification = async () => {
37185
+ const token = await getAuthToken3();
37186
+ if (!token || cancelled) {
37187
+ return;
37188
+ }
37189
+ fetchClassifications([clipId], token).then((classifications) => {
37190
+ if (cancelled) return;
37191
+ setClipClassifications((prev) => ({
37192
+ ...prev,
37193
+ ...classifications
37194
+ }));
37195
+ }).catch((error2) => {
37196
+ if (!cancelled) {
37197
+ console.error("[BottlenecksContent] Error fetching classification (current clip):", error2);
37198
+ }
37199
+ });
37200
+ };
37201
+ fetchCurrentClassification();
37202
+ return () => {
37203
+ cancelled = true;
37204
+ };
37205
+ }, [triageMode, currentVideo, clipClassifications, getAuthToken3]);
36380
37206
  useEffect(() => {
36381
37207
  if (!s3ClipsService || !currentVideo) {
36382
37208
  return;
@@ -37129,6 +37955,8 @@ var BottlenecksContent = ({
37129
37955
  activeFilter,
37130
37956
  currentVideoId: currentVideo?.id,
37131
37957
  counts: mergedCounts,
37958
+ isReady: hasInitialLoad,
37959
+ prefetchedPercentileCounts: prefetchedPercentileCounts || void 0,
37132
37960
  workspaceId,
37133
37961
  date: effectiveDateString,
37134
37962
  shift: effectiveShiftId,
@@ -37159,9 +37987,9 @@ var BottlenecksContent = ({
37159
37987
  }
37160
37988
  }
37161
37989
  },
37162
- onClipSelect: (categoryId, clipId, position) => {
37990
+ onClipSelect: (categoryId, clipId, position, context) => {
37163
37991
  console.log(`[BottlenecksContent] Clip selected: ${categoryId}, clipId ${clipId}, position=${position}`);
37164
- loadAndPlayClipById(clipId, categoryId, position);
37992
+ loadAndPlayClipById(clipId, categoryId, position, context);
37165
37993
  const category = categoriesToShow.find((cat) => cat.type === categoryId);
37166
37994
  if (category) {
37167
37995
  trackCoreEvent(`${category.label} Clip Selected`, {
@@ -38767,7 +39595,7 @@ var HealthStatusIndicator = ({
38767
39595
  };
38768
39596
  const config = getStatusConfig();
38769
39597
  config.icon;
38770
- const sizeClasses = {
39598
+ const sizeClasses3 = {
38771
39599
  sm: {
38772
39600
  dot: "h-2 w-2",
38773
39601
  icon: "h-3 w-3",
@@ -38787,7 +39615,7 @@ var HealthStatusIndicator = ({
38787
39615
  spacing: "gap-2"
38788
39616
  }
38789
39617
  };
38790
- const currentSize = sizeClasses[size];
39618
+ const currentSize = sizeClasses3[size];
38791
39619
  const containerClasses = clsx(
38792
39620
  "flex items-center",
38793
39621
  currentSize.spacing,
@@ -38878,7 +39706,7 @@ var AxelOrb = ({
38878
39706
  size = "md",
38879
39707
  animate = false
38880
39708
  }) => {
38881
- const sizeClasses = {
39709
+ const sizeClasses3 = {
38882
39710
  sm: "w-8 h-8",
38883
39711
  md: "w-10 h-10",
38884
39712
  lg: "w-12 h-12",
@@ -38888,7 +39716,7 @@ var AxelOrb = ({
38888
39716
  return /* @__PURE__ */ jsx(
38889
39717
  "div",
38890
39718
  {
38891
- className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
39719
+ className: `${sizeClasses3[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
38892
39720
  style: {
38893
39721
  background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
38894
39722
  boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
@@ -39677,8 +40505,7 @@ var LineMonthlyHistory = ({
39677
40505
  data: idleReasonsChartData,
39678
40506
  isLoading: idleReasonsLoading,
39679
40507
  error: idleReasonsError
39680
- },
39681
- chartKey
40508
+ }
39682
40509
  ) })
39683
40510
  ] }),
39684
40511
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg sm:rounded-xl shadow-sm border border-gray-100 p-2 sm:p-3 lg:p-4 min-h-[160px] sm:min-h-[180px] lg:min-h-[220px] flex flex-col", children: [
@@ -41680,8 +42507,7 @@ var WorkspaceMonthlyHistory = ({
41680
42507
  data: idleReasonsChartData,
41681
42508
  isLoading: idleReasonsLoading,
41682
42509
  error: idleReasonsError
41683
- },
41684
- chartKey
42510
+ }
41685
42511
  ) })
41686
42512
  ] })
41687
42513
  ] }),
@@ -43016,7 +43842,7 @@ var KPIHeader = ({
43016
43842
  actionsClassName,
43017
43843
  size = "md"
43018
43844
  }) => {
43019
- const sizeClasses = {
43845
+ const sizeClasses3 = {
43020
43846
  sm: {
43021
43847
  container: "mb-2",
43022
43848
  title: "text-lg font-semibold",
@@ -43038,7 +43864,7 @@ var KPIHeader = ({
43038
43864
  {
43039
43865
  className: clsx(
43040
43866
  "flex justify-between items-start",
43041
- sizeClasses[size].container,
43867
+ sizeClasses3[size].container,
43042
43868
  className
43043
43869
  ),
43044
43870
  children: [
@@ -43047,7 +43873,7 @@ var KPIHeader = ({
43047
43873
  "h2",
43048
43874
  {
43049
43875
  className: clsx(
43050
- sizeClasses[size].title,
43876
+ sizeClasses3[size].title,
43051
43877
  "text-gray-900 dark:text-gray-100",
43052
43878
  titleClassName
43053
43879
  ),
@@ -43058,7 +43884,7 @@ var KPIHeader = ({
43058
43884
  "p",
43059
43885
  {
43060
43886
  className: clsx(
43061
- sizeClasses[size].subtitle,
43887
+ sizeClasses3[size].subtitle,
43062
43888
  "text-gray-500 dark:text-gray-400 mt-1",
43063
43889
  subtitleClassName
43064
43890
  ),
@@ -45325,6 +46151,482 @@ var DashboardLayout = ({
45325
46151
  ] })
45326
46152
  ] });
45327
46153
  };
46154
+ var getBadgeStyles = (type) => {
46155
+ switch (type) {
46156
+ case "gold":
46157
+ return {
46158
+ bg: "bg-gradient-to-br from-yellow-200 via-yellow-400 to-yellow-600",
46159
+ shadow: "shadow-yellow-500/40",
46160
+ text: "text-yellow-900",
46161
+ iconColor: "text-yellow-900",
46162
+ ring: "ring-yellow-400/30",
46163
+ glow: "bg-yellow-400",
46164
+ aura: "from-yellow-400/20 via-yellow-500/40 to-yellow-600/20"
46165
+ };
46166
+ case "silver":
46167
+ return {
46168
+ bg: "bg-gradient-to-br from-gray-100 via-gray-300 to-gray-400",
46169
+ shadow: "shadow-gray-400/40",
46170
+ text: "text-gray-800",
46171
+ iconColor: "text-gray-700",
46172
+ ring: "ring-gray-300/30",
46173
+ glow: "bg-gray-300",
46174
+ aura: "from-gray-300/20 via-gray-400/40 to-gray-500/20"
46175
+ };
46176
+ case "bronze":
46177
+ return {
46178
+ bg: "bg-gradient-to-br from-orange-200 via-orange-400 to-orange-700",
46179
+ shadow: "shadow-orange-500/40",
46180
+ text: "text-orange-900",
46181
+ iconColor: "text-orange-900",
46182
+ ring: "ring-orange-400/30",
46183
+ glow: "bg-orange-400",
46184
+ aura: "from-orange-400/20 via-orange-500/40 to-orange-600/20"
46185
+ };
46186
+ case "diamond":
46187
+ return {
46188
+ bg: "bg-gradient-to-br from-cyan-100 via-cyan-300 to-blue-500",
46189
+ shadow: "shadow-cyan-400/40",
46190
+ text: "text-blue-900",
46191
+ iconColor: "text-blue-800",
46192
+ ring: "ring-cyan-300/30",
46193
+ glow: "bg-cyan-400",
46194
+ aura: "from-cyan-300/20 via-blue-400/40 to-blue-600/20"
46195
+ };
46196
+ case "platinum":
46197
+ return {
46198
+ bg: "bg-gradient-to-br from-slate-200 via-slate-300 to-slate-400",
46199
+ shadow: "shadow-slate-400/40",
46200
+ text: "text-slate-800",
46201
+ iconColor: "text-slate-700",
46202
+ ring: "ring-slate-300/30",
46203
+ glow: "bg-slate-300",
46204
+ aura: "from-slate-300/20 via-slate-400/40 to-slate-500/20"
46205
+ };
46206
+ case "special":
46207
+ default:
46208
+ return {
46209
+ bg: "bg-gradient-to-br from-purple-400 via-fuchsia-500 to-pink-600",
46210
+ shadow: "shadow-purple-500/40",
46211
+ text: "text-white",
46212
+ iconColor: "text-white",
46213
+ ring: "ring-purple-400/30",
46214
+ glow: "bg-purple-400",
46215
+ aura: "from-purple-400/20 via-fuchsia-500/40 to-pink-600/20"
46216
+ };
46217
+ }
46218
+ };
46219
+ var getDefaultIcon = (type) => {
46220
+ switch (type) {
46221
+ case "gold":
46222
+ return Trophy;
46223
+ case "silver":
46224
+ return Medal;
46225
+ case "bronze":
46226
+ return Star;
46227
+ case "diamond":
46228
+ return Crown;
46229
+ case "platinum":
46230
+ return ShieldCheck;
46231
+ case "special":
46232
+ return Flame;
46233
+ default:
46234
+ return Trophy;
46235
+ }
46236
+ };
46237
+ var AwardBadge = ({
46238
+ type,
46239
+ title,
46240
+ date,
46241
+ icon: CustomIcon,
46242
+ className,
46243
+ description,
46244
+ floating = true
46245
+ }) => {
46246
+ const styles2 = getBadgeStyles(type);
46247
+ const Icon2 = CustomIcon || getDefaultIcon(type);
46248
+ const randomDelay = React25__default.useMemo(() => Math.random() * 2, []);
46249
+ const floatingAnimation = {
46250
+ animate: {
46251
+ y: [0, -10, 0],
46252
+ transition: {
46253
+ duration: 3 + Math.random() * 2,
46254
+ repeat: Infinity,
46255
+ ease: "easeInOut",
46256
+ delay: randomDelay
46257
+ }
46258
+ }
46259
+ };
46260
+ return /* @__PURE__ */ jsxs(
46261
+ motion.div,
46262
+ {
46263
+ whileHover: "hover",
46264
+ className: cn(
46265
+ "relative flex flex-col items-center p-8 rounded-2xl border border-gray-100 bg-white shadow-sm transition-all duration-500 text-center group cursor-pointer",
46266
+ className
46267
+ ),
46268
+ variants: {
46269
+ hover: {
46270
+ borderColor: "transparent",
46271
+ boxShadow: `0 0 25px 2px var(--award-glow-color)`,
46272
+ transition: { duration: 0.3 }
46273
+ }
46274
+ },
46275
+ style: {
46276
+ // @ts-ignore - custom property for the glow color
46277
+ "--award-glow-color": type === "gold" ? "rgba(250, 204, 21, 0.4)" : type === "silver" ? "rgba(156, 163, 175, 0.4)" : type === "bronze" ? "rgba(251, 146, 60, 0.4)" : type === "diamond" ? "rgba(34, 211, 238, 0.4)" : type === "platinum" ? "rgba(148, 163, 184, 0.4)" : "rgba(192, 38, 211, 0.4)"
46278
+ },
46279
+ children: [
46280
+ /* @__PURE__ */ jsx(
46281
+ motion.div,
46282
+ {
46283
+ variants: {
46284
+ hover: {
46285
+ opacity: 1,
46286
+ scale: 1.1,
46287
+ transition: { duration: 0.4, ease: "easeOut" }
46288
+ }
46289
+ },
46290
+ className: cn(
46291
+ "absolute -inset-2 blur-3xl opacity-0 transition-all duration-700 rounded-[2.5rem] bg-gradient-to-br -z-20",
46292
+ styles2.aura
46293
+ )
46294
+ }
46295
+ ),
46296
+ /* @__PURE__ */ jsx(
46297
+ motion.div,
46298
+ {
46299
+ variants: {
46300
+ hover: { opacity: 0.2, scale: 1.5 }
46301
+ },
46302
+ className: cn(
46303
+ "absolute top-1/4 left-1/2 -translate-x-1/2 blur-[40px] opacity-0 transition-all duration-700 rounded-full w-32 h-32 -z-10",
46304
+ styles2.glow
46305
+ )
46306
+ }
46307
+ ),
46308
+ /* @__PURE__ */ jsxs("div", { className: "relative mb-6 h-28 flex items-center justify-center", children: [
46309
+ /* @__PURE__ */ jsxs(
46310
+ motion.div,
46311
+ {
46312
+ variants: floating ? floatingAnimation : void 0,
46313
+ animate: floating ? "animate" : void 0,
46314
+ whileHover: {
46315
+ y: -15,
46316
+ scale: 1.1,
46317
+ transition: { duration: 0.3, ease: "easeOut" }
46318
+ },
46319
+ className: cn(
46320
+ "relative w-24 h-24 rounded-full flex items-center justify-center shadow-lg ring-4 transition-all duration-500",
46321
+ styles2.bg,
46322
+ styles2.shadow,
46323
+ styles2.ring
46324
+ ),
46325
+ children: [
46326
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-gradient-to-tr from-white/40 via-transparent to-transparent pointer-events-none" }),
46327
+ /* @__PURE__ */ jsx(Icon2, { className: cn("w-12 h-12 drop-shadow-md", styles2.iconColor) })
46328
+ ]
46329
+ }
46330
+ ),
46331
+ /* @__PURE__ */ jsx(
46332
+ motion.div,
46333
+ {
46334
+ variants: {
46335
+ animate: {
46336
+ scale: [1, 0.8, 1],
46337
+ opacity: [0.2, 0.1, 0.2]
46338
+ },
46339
+ hover: {
46340
+ scale: 0.6,
46341
+ opacity: 0.05
46342
+ }
46343
+ },
46344
+ animate: "animate",
46345
+ transition: {
46346
+ duration: 3 + Math.random() * 2,
46347
+ repeat: Infinity,
46348
+ ease: "easeInOut",
46349
+ delay: randomDelay
46350
+ },
46351
+ className: "absolute -bottom-1 left-1/2 -translate-x-1/2 w-12 h-1.5 bg-black/10 blur-sm rounded-[100%]"
46352
+ }
46353
+ )
46354
+ ] }),
46355
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1 z-10", children: [
46356
+ /* @__PURE__ */ jsx("h3", { className: "font-bold text-gray-900 text-lg leading-tight transition-colors", children: title }),
46357
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-bold text-gray-400 uppercase tracking-widest", children: date }),
46358
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-3 line-clamp-2 max-w-[200px]", children: description })
46359
+ ] }),
46360
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/0 via-white/0 to-white/5 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" })
46361
+ ]
46362
+ }
46363
+ );
46364
+ };
46365
+ var Button2 = (props) => {
46366
+ const { Button: RegisteredButton } = useRegistry();
46367
+ return /* @__PURE__ */ jsx(RegisteredButton, { ...props });
46368
+ };
46369
+ var ConfettiPiece = ({ index }) => {
46370
+ const colors = ["#fbbf24", "#3b82f6", "#8b5cf6", "#ec4899", "#10b981"];
46371
+ const color2 = colors[index % colors.length];
46372
+ return /* @__PURE__ */ jsx(
46373
+ motion.div,
46374
+ {
46375
+ initial: {
46376
+ x: 0,
46377
+ y: 0,
46378
+ rotate: 0,
46379
+ scale: 0
46380
+ },
46381
+ animate: {
46382
+ x: (Math.random() - 0.5) * 800,
46383
+ y: (Math.random() - 0.5) * 800 - 100,
46384
+ rotate: Math.random() * 360,
46385
+ scale: [0, 1, 0.5, 0],
46386
+ transition: {
46387
+ duration: 2.5 + Math.random() * 1.5,
46388
+ ease: [0.23, 1, 0.32, 1]
46389
+ }
46390
+ },
46391
+ className: "absolute w-2 h-2 rounded-full z-50 opacity-60",
46392
+ style: { backgroundColor: color2 }
46393
+ }
46394
+ );
46395
+ };
46396
+ var AwardOpeningModal = ({ isOpen, onClose, award }) => {
46397
+ const [phase, setPhase] = useState("locked");
46398
+ const [showConfetti, setShowConfetti] = useState(false);
46399
+ useEffect(() => {
46400
+ if (isOpen) {
46401
+ setPhase("locked");
46402
+ setShowConfetti(false);
46403
+ }
46404
+ }, [isOpen]);
46405
+ const handleReveal = () => {
46406
+ setPhase("opening");
46407
+ setTimeout(() => {
46408
+ setPhase("revealed");
46409
+ setShowConfetti(true);
46410
+ }, 1e3);
46411
+ };
46412
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [
46413
+ /* @__PURE__ */ jsx(
46414
+ motion.div,
46415
+ {
46416
+ initial: { opacity: 0 },
46417
+ animate: { opacity: 1 },
46418
+ exit: { opacity: 0 },
46419
+ className: "absolute inset-0 bg-slate-950/90 backdrop-blur-xl",
46420
+ onClick: phase === "revealed" ? onClose : void 0
46421
+ }
46422
+ ),
46423
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-lg flex flex-col items-center", children: [
46424
+ showConfetti && Array.from({ length: 100 }).map((_, i) => /* @__PURE__ */ jsx(ConfettiPiece, { index: i }, i)),
46425
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: phase === "locked" || phase === "opening" ? /* @__PURE__ */ jsx(
46426
+ motion.div,
46427
+ {
46428
+ initial: { opacity: 0, y: 20 },
46429
+ animate: { opacity: 1, y: 0 },
46430
+ exit: { opacity: 0, scale: 1.1, filter: "blur(10px)" },
46431
+ className: "flex flex-col items-center",
46432
+ children: /* @__PURE__ */ jsxs(
46433
+ "div",
46434
+ {
46435
+ className: `w-64 h-80 rounded-3xl border border-white/10 bg-white/5 backdrop-blur-sm flex flex-col items-center justify-center cursor-pointer transition-all duration-500 hover:bg-white/10 ${phase === "opening" ? "scale-110 ring-4 ring-blue-500/20" : ""}`,
46436
+ onClick: phase === "locked" ? handleReveal : void 0,
46437
+ children: [
46438
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx("div", { className: "w-20 h-20 rounded-full border-2 border-white/20 flex items-center justify-center", children: /* @__PURE__ */ jsx(
46439
+ motion.div,
46440
+ {
46441
+ animate: phase === "opening" ? { rotate: 360 } : { rotate: 0 },
46442
+ transition: { duration: 1, repeat: Infinity, ease: "linear" },
46443
+ children: /* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-full bg-gradient-to-tr from-blue-500 to-cyan-400 opacity-50 blur-sm" })
46444
+ }
46445
+ ) }) }),
46446
+ /* @__PURE__ */ jsx("p", { className: "mt-8 text-white font-semibold tracking-[0.2em] text-xs uppercase opacity-60", children: phase === "opening" ? "Unlocking Achievement" : "Click to Unlock" })
46447
+ ]
46448
+ }
46449
+ )
46450
+ },
46451
+ "locked"
46452
+ ) : /* @__PURE__ */ jsxs(
46453
+ motion.div,
46454
+ {
46455
+ initial: { scale: 0.9, opacity: 0 },
46456
+ animate: { scale: 1, opacity: 1 },
46457
+ transition: { type: "spring", damping: 20, stiffness: 100 },
46458
+ className: "flex flex-col items-center",
46459
+ children: [
46460
+ /* @__PURE__ */ jsxs(
46461
+ motion.div,
46462
+ {
46463
+ initial: { y: 10, opacity: 0 },
46464
+ animate: { y: 0, opacity: 1 },
46465
+ transition: { delay: 0.3 },
46466
+ className: "mb-10 text-center space-y-2",
46467
+ children: [
46468
+ /* @__PURE__ */ jsx("h2", { className: "text-white text-3xl font-bold tracking-tight", children: "Congratulations" }),
46469
+ /* @__PURE__ */ jsx("p", { className: "text-blue-400 font-medium text-sm tracking-widest uppercase", children: "New Achievement Unlocked" })
46470
+ ]
46471
+ }
46472
+ ),
46473
+ /* @__PURE__ */ jsx("div", { className: "relative group", children: /* @__PURE__ */ jsx(
46474
+ motion.div,
46475
+ {
46476
+ initial: { scale: 0.8, opacity: 0 },
46477
+ animate: { scale: 1, opacity: 1 },
46478
+ transition: { delay: 0.2, type: "spring" },
46479
+ children: /* @__PURE__ */ jsx(
46480
+ AwardBadge,
46481
+ {
46482
+ ...award,
46483
+ className: "w-80 h-auto shadow-[0_32px_64px_-12px_rgba(0,0,0,0.5)] border-white/10",
46484
+ floating: true
46485
+ }
46486
+ )
46487
+ }
46488
+ ) }),
46489
+ /* @__PURE__ */ jsx(
46490
+ motion.div,
46491
+ {
46492
+ initial: { opacity: 0, y: 10 },
46493
+ animate: { opacity: 1, y: 0 },
46494
+ transition: { delay: 0.8 },
46495
+ className: "mt-12",
46496
+ children: /* @__PURE__ */ jsx(
46497
+ Button2,
46498
+ {
46499
+ size: "lg",
46500
+ onClick: onClose,
46501
+ className: "bg-white text-slate-950 hover:bg-slate-100 px-10 py-3 text-sm font-bold rounded-full transition-all hover:scale-105 active:scale-95",
46502
+ children: "Continue"
46503
+ }
46504
+ )
46505
+ }
46506
+ )
46507
+ ]
46508
+ },
46509
+ "revealed"
46510
+ ) })
46511
+ ] })
46512
+ ] }) });
46513
+ };
46514
+ var AwardCongratsToast = ({ notification, onDismiss }) => {
46515
+ useEffect(() => {
46516
+ const timer = setTimeout(onDismiss, 8e3);
46517
+ return () => clearTimeout(timer);
46518
+ }, [onDismiss]);
46519
+ const award = notification.awards;
46520
+ const winnerName = award?.recipient_name || "a teammate";
46521
+ const awardTitle = getAwardTitle(award.award_type);
46522
+ const monthLabel = formatAwardMonth(award.period_start);
46523
+ const initials = getInitials(winnerName);
46524
+ return /* @__PURE__ */ jsx(
46525
+ motion.div,
46526
+ {
46527
+ initial: { opacity: 0, y: 20, scale: 0.95 },
46528
+ animate: { opacity: 1, y: 0, scale: 1 },
46529
+ exit: { opacity: 0, y: 10, scale: 0.98 },
46530
+ transition: { duration: 0.25, ease: "easeOut" },
46531
+ className: "fixed bottom-6 right-6 z-50 w-[320px] rounded-xl border border-amber-200 bg-white shadow-lg",
46532
+ role: "alert",
46533
+ "aria-live": "polite",
46534
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 p-4", children: [
46535
+ /* @__PURE__ */ jsxs("div", { className: "relative flex h-12 w-12 items-center justify-center rounded-full bg-amber-100 text-amber-600 font-semibold", children: [
46536
+ initials,
46537
+ /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full bg-white shadow-sm border border-amber-100", children: /* @__PURE__ */ jsx(Trophy, { className: "h-3.5 w-3.5 text-amber-500 fill-amber-500" }) })
46538
+ ] }),
46539
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
46540
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-semibold text-gray-900", children: [
46541
+ "Congrats to ",
46542
+ winnerName
46543
+ ] }),
46544
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-600 mt-1", children: [
46545
+ awardTitle,
46546
+ " \u2022 ",
46547
+ monthLabel
46548
+ ] })
46549
+ ] }),
46550
+ /* @__PURE__ */ jsx(
46551
+ "button",
46552
+ {
46553
+ onClick: onDismiss,
46554
+ className: "text-gray-400 hover:text-gray-600 transition-colors",
46555
+ "aria-label": "Dismiss notification",
46556
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
46557
+ }
46558
+ )
46559
+ ] })
46560
+ }
46561
+ );
46562
+ };
46563
+ var buildAwardProps = (award) => ({
46564
+ type: getAwardBadgeType(award.award_type),
46565
+ title: getAwardTitle(award.award_type),
46566
+ date: formatAwardMonth(award.period_start),
46567
+ description: getAwardDescription(award)
46568
+ });
46569
+ var AwardNotificationManager = () => {
46570
+ const supabase = useSupabase();
46571
+ const { user } = useAuth();
46572
+ const [queue, setQueue] = useState([]);
46573
+ const [activeNotification, setActiveNotification] = useState(null);
46574
+ const lastUserIdRef = useRef(null);
46575
+ useEffect(() => {
46576
+ if (!user) {
46577
+ lastUserIdRef.current = null;
46578
+ return;
46579
+ }
46580
+ if (lastUserIdRef.current === user.id) return;
46581
+ lastUserIdRef.current = user.id;
46582
+ const loadNotifications = async () => {
46583
+ try {
46584
+ const notifications = await awardsService.getNotifications(supabase);
46585
+ setQueue(notifications);
46586
+ } catch (error) {
46587
+ console.error("[AwardNotificationManager] Failed to load notifications:", error);
46588
+ }
46589
+ };
46590
+ loadNotifications();
46591
+ }, [user, supabase]);
46592
+ useEffect(() => {
46593
+ if (!activeNotification && queue.length > 0) {
46594
+ setActiveNotification(queue[0]);
46595
+ }
46596
+ }, [queue, activeNotification]);
46597
+ const dismissActive = async () => {
46598
+ if (!activeNotification) return;
46599
+ try {
46600
+ await awardsService.markNotificationsRead(supabase, [activeNotification.id]);
46601
+ } catch (error) {
46602
+ console.error("[AwardNotificationManager] Failed to mark notification read:", error);
46603
+ } finally {
46604
+ setQueue((prev) => prev.filter((item) => item.id !== activeNotification.id));
46605
+ setActiveNotification(null);
46606
+ }
46607
+ };
46608
+ const awardProps = useMemo(
46609
+ () => activeNotification?.notification_type === "recipient" ? buildAwardProps(activeNotification.awards) : null,
46610
+ [activeNotification]
46611
+ );
46612
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
46613
+ activeNotification?.notification_type === "recipient" && awardProps && /* @__PURE__ */ jsx(
46614
+ AwardOpeningModal,
46615
+ {
46616
+ isOpen: true,
46617
+ onClose: dismissActive,
46618
+ award: awardProps
46619
+ }
46620
+ ),
46621
+ /* @__PURE__ */ jsx(AnimatePresence, { children: activeNotification?.notification_type === "congrats" && /* @__PURE__ */ jsx(
46622
+ AwardCongratsToast,
46623
+ {
46624
+ notification: activeNotification,
46625
+ onDismiss: dismissActive
46626
+ }
46627
+ ) })
46628
+ ] });
46629
+ };
45328
46630
  var MainLayoutInner = ({
45329
46631
  children,
45330
46632
  className = "",
@@ -45354,7 +46656,8 @@ var MainLayoutInner = ({
45354
46656
  onMobileMenuClose
45355
46657
  }
45356
46658
  ),
45357
- /* @__PURE__ */ jsx("main", { className: "md:ml-20 bg-gray-50 min-h-screen transition-all duration-300", children: /* @__PURE__ */ jsx("div", { className: "h-full", children }) })
46659
+ /* @__PURE__ */ jsx("main", { className: "md:ml-20 bg-gray-50 min-h-screen transition-all duration-300", children: /* @__PURE__ */ jsx("div", { className: "h-full", children }) }),
46660
+ /* @__PURE__ */ jsx(AwardNotificationManager, {})
45358
46661
  ] });
45359
46662
  };
45360
46663
  var MainLayout = ({
@@ -46908,8 +48211,9 @@ var InviteUserDialog = ({
46908
48211
  const [error, setError] = useState(null);
46909
48212
  const [isLinesDropdownOpen, setIsLinesDropdownOpen] = useState(false);
46910
48213
  const [isFactoriesDropdownOpen, setIsFactoriesDropdownOpen] = useState(false);
46911
- const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "optifye";
46912
- const canInviteSupervisor = ["owner", "plant_head", "optifye"].includes(user?.role_level || "");
48214
+ const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
48215
+ const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
48216
+ const canInviteSupervisor = ["owner", "it", "plant_head", "optifye"].includes(user?.role_level || "");
46913
48217
  useEffect(() => {
46914
48218
  if (!isOpen) {
46915
48219
  setEmail("");
@@ -47138,6 +48442,33 @@ var InviteUserDialog = ({
47138
48442
  /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
47139
48443
  ] }),
47140
48444
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
48445
+ canInviteIT && /* @__PURE__ */ jsxs(
48446
+ "label",
48447
+ {
48448
+ className: cn(
48449
+ "flex items-start gap-3 p-4 border-2 rounded-lg cursor-pointer transition-all duration-200",
48450
+ selectedRole === "it" ? "border-teal-500 bg-teal-50" : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
48451
+ ),
48452
+ children: [
48453
+ /* @__PURE__ */ jsx(
48454
+ "input",
48455
+ {
48456
+ type: "radio",
48457
+ name: "role",
48458
+ value: "it",
48459
+ checked: selectedRole === "it",
48460
+ onChange: (e) => setSelectedRole(e.target.value),
48461
+ className: "mt-1",
48462
+ disabled: isSubmitting
48463
+ }
48464
+ ),
48465
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
48466
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mb-1", children: /* @__PURE__ */ jsx(RoleBadge_default, { role: "it", size: "sm" }) }),
48467
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Company-wide access. IT personnel with full company access." })
48468
+ ] })
48469
+ ]
48470
+ }
48471
+ ),
47141
48472
  canInvitePlantHead && /* @__PURE__ */ jsxs(
47142
48473
  "label",
47143
48474
  {
@@ -47362,6 +48693,7 @@ var getRoleLabel = (role) => {
47362
48693
  const labels = {
47363
48694
  "supervisor": "Supervisor",
47364
48695
  "plant_head": "Plant Head",
48696
+ "it": "IT",
47365
48697
  "owner": "Owner",
47366
48698
  "optifye": "Optifye Admin"
47367
48699
  };
@@ -47637,27 +48969,41 @@ var ConfirmRemoveUserDialog = ({
47637
48969
  ] })
47638
48970
  ] }) });
47639
48971
  };
47640
- var EditUserNameDialog = ({
48972
+ var EditUserProfileDialog = ({
47641
48973
  isOpen,
47642
48974
  onClose,
47643
48975
  onSave,
47644
48976
  user
47645
48977
  }) => {
48978
+ const supabase = useSupabase();
47646
48979
  const [firstName, setFirstName] = useState("");
47647
48980
  const [lastName, setLastName] = useState("");
48981
+ const [profilePhotoUrl, setProfilePhotoUrl] = useState(null);
47648
48982
  const [isSubmitting, setIsSubmitting] = useState(false);
47649
48983
  const [error, setError] = useState(null);
47650
48984
  useEffect(() => {
47651
48985
  if (isOpen && user) {
47652
48986
  setFirstName(user.first_name || "");
47653
48987
  setLastName(user.last_name || "");
48988
+ setProfilePhotoUrl(user.profile_photo_url || null);
47654
48989
  setError(null);
47655
48990
  } else if (!isOpen) {
47656
48991
  setFirstName("");
47657
48992
  setLastName("");
48993
+ setProfilePhotoUrl(null);
47658
48994
  setError(null);
47659
48995
  }
47660
48996
  }, [isOpen, user]);
48997
+ const handlePhotoUploadComplete = (url) => {
48998
+ setProfilePhotoUrl(url);
48999
+ toast.success("Photo uploaded successfully");
49000
+ };
49001
+ const handlePhotoUploadError = (err) => {
49002
+ toast.error(err.message || "Failed to upload photo");
49003
+ };
49004
+ const handleRemovePhoto = () => {
49005
+ setProfilePhotoUrl(null);
49006
+ };
47661
49007
  const handleSubmit = async (e) => {
47662
49008
  e.preventDefault();
47663
49009
  setError(null);
@@ -47674,14 +49020,15 @@ var EditUserNameDialog = ({
47674
49020
  await onSave(
47675
49021
  user.user_id,
47676
49022
  firstName.trim(),
47677
- lastName.trim() || void 0
49023
+ lastName.trim() || void 0,
49024
+ profilePhotoUrl
47678
49025
  );
47679
- toast.success("User name updated successfully");
49026
+ toast.success("Profile updated successfully");
47680
49027
  onClose();
47681
49028
  } catch (err) {
47682
- console.error("Error updating user name:", err);
47683
- setError(err.message || "Failed to update user name");
47684
- toast.error(err.message || "Failed to update user name");
49029
+ console.error("Error updating user profile:", err);
49030
+ setError(err.message || "Failed to update profile");
49031
+ toast.error(err.message || "Failed to update profile");
47685
49032
  } finally {
47686
49033
  setIsSubmitting(false);
47687
49034
  }
@@ -47703,7 +49050,7 @@ var EditUserNameDialog = ({
47703
49050
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
47704
49051
  /* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(User, { className: "w-5 h-5 text-blue-600" }) }),
47705
49052
  /* @__PURE__ */ jsxs("div", { children: [
47706
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900", children: "Edit User Name" }),
49053
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900", children: "Edit Profile" }),
47707
49054
  /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: user.email })
47708
49055
  ] })
47709
49056
  ] }),
@@ -47723,6 +49070,25 @@ var EditUserNameDialog = ({
47723
49070
  /* @__PURE__ */ jsx(AlertCircle, { className: "w-4 h-4 flex-shrink-0 mt-0.5" }),
47724
49071
  /* @__PURE__ */ jsx("span", { children: error })
47725
49072
  ] }),
49073
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center mb-6", children: [
49074
+ /* @__PURE__ */ jsx(
49075
+ AvatarUpload,
49076
+ {
49077
+ currentImageUrl: profilePhotoUrl,
49078
+ firstName: firstName || user.first_name,
49079
+ lastName: lastName || user.last_name,
49080
+ email: user.email,
49081
+ userId: user.user_id,
49082
+ onUploadComplete: handlePhotoUploadComplete,
49083
+ onUploadError: handlePhotoUploadError,
49084
+ onRemove: handleRemovePhoto,
49085
+ size: "lg",
49086
+ disabled: isSubmitting,
49087
+ supabase
49088
+ }
49089
+ ),
49090
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-2", children: "Click to upload photo" })
49091
+ ] }),
47726
49092
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
47727
49093
  /* @__PURE__ */ jsxs("div", { children: [
47728
49094
  /* @__PURE__ */ jsxs("label", { htmlFor: "editFirstName", className: "block text-sm font-medium text-gray-700 mb-2", children: [
@@ -47744,8 +49110,7 @@ var EditUserNameDialog = ({
47744
49110
  ),
47745
49111
  placeholder: "John",
47746
49112
  disabled: isSubmitting,
47747
- required: true,
47748
- autoFocus: true
49113
+ required: true
47749
49114
  }
47750
49115
  )
47751
49116
  ] }),
@@ -48709,7 +50074,7 @@ var UserManagementTable = ({
48709
50074
  onRemoveUser,
48710
50075
  onLineAssignmentUpdate,
48711
50076
  onFactoryAssignmentUpdate,
48712
- onNameUpdate,
50077
+ onProfileUpdate,
48713
50078
  availableLines = [],
48714
50079
  availableFactories = [],
48715
50080
  currentUserId,
@@ -48722,8 +50087,9 @@ var UserManagementTable = ({
48722
50087
  const availableRoles = useMemo(() => {
48723
50088
  const currentRole = currentUser?.role_level;
48724
50089
  const roleHierarchy = {
48725
- "optifye": ["optifye", "owner", "plant_head", "supervisor"],
48726
- "owner": ["owner", "plant_head", "supervisor"],
50090
+ "optifye": ["optifye", "owner", "it", "plant_head", "supervisor"],
50091
+ "owner": ["owner", "it", "plant_head", "supervisor"],
50092
+ "it": ["it", "plant_head", "supervisor"],
48727
50093
  "plant_head": ["plant_head", "supervisor"],
48728
50094
  "supervisor": ["supervisor"]
48729
50095
  };
@@ -48814,7 +50180,7 @@ var UserManagementTable = ({
48814
50180
  }
48815
50181
  };
48816
50182
  const formatAssignments = (user) => {
48817
- if (user.role_level === "owner") return "Company-wide";
50183
+ if (user.role_level === "owner" || user.role_level === "it") return "Company-wide";
48818
50184
  if (user.role_level === "optifye") return "All companies";
48819
50185
  if (user.role_level === "plant_head" && user.assigned_factories) {
48820
50186
  return user.assigned_factories.join(", ") || "No factories assigned";
@@ -48848,14 +50214,14 @@ var UserManagementTable = ({
48848
50214
  setShowRemoveUserDialog(false);
48849
50215
  setSelectedUser(null);
48850
50216
  };
48851
- const handleEditName = (user) => {
50217
+ const handleEditProfile = (user) => {
48852
50218
  setSelectedUser(user);
48853
50219
  setShowEditNameDialog(true);
48854
50220
  setOpenActionMenuId(null);
48855
50221
  };
48856
- const handleNameUpdateConfirm = async (userId, firstName, lastName) => {
48857
- if (onNameUpdate) {
48858
- await onNameUpdate(userId, firstName, lastName);
50222
+ const handleProfileUpdateConfirm = async (userId, firstName, lastName, profilePhotoUrl) => {
50223
+ if (onProfileUpdate) {
50224
+ await onProfileUpdate(userId, firstName, lastName, profilePhotoUrl);
48859
50225
  }
48860
50226
  setShowEditNameDialog(false);
48861
50227
  setSelectedUser(null);
@@ -48889,6 +50255,7 @@ var UserManagementTable = ({
48889
50255
  /* @__PURE__ */ jsx("option", { value: "all", children: "All Roles" }),
48890
50256
  availableRoles.includes("optifye") && /* @__PURE__ */ jsx("option", { value: "optifye", children: "Optifye" }),
48891
50257
  availableRoles.includes("owner") && /* @__PURE__ */ jsx("option", { value: "owner", children: "Owner" }),
50258
+ availableRoles.includes("it") && /* @__PURE__ */ jsx("option", { value: "it", children: "IT" }),
48892
50259
  availableRoles.includes("plant_head") && /* @__PURE__ */ jsx("option", { value: "plant_head", children: "Plant Head" }),
48893
50260
  availableRoles.includes("supervisor") && /* @__PURE__ */ jsx("option", { value: "supervisor", children: "Supervisor" })
48894
50261
  ]
@@ -48954,7 +50321,18 @@ var UserManagementTable = ({
48954
50321
  ),
48955
50322
  children: [
48956
50323
  /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
48957
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-10 h-10 bg-gradient-to-br from-blue-100 to-indigo-100 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-blue-700", children: user.first_name ? user.first_name.charAt(0).toUpperCase() : user.email.charAt(0).toUpperCase() }) }),
50324
+ /* @__PURE__ */ jsx(
50325
+ UserAvatar,
50326
+ {
50327
+ imageUrl: user.profile_photo_url,
50328
+ firstName: user.first_name,
50329
+ lastName: user.last_name,
50330
+ email: user.email,
50331
+ size: "lg",
50332
+ showBorder: false,
50333
+ className: "flex-shrink-0 bg-gradient-to-br from-blue-100 to-indigo-100"
50334
+ }
50335
+ ),
48958
50336
  /* @__PURE__ */ jsxs("div", { children: [
48959
50337
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
48960
50338
  user.first_name ? /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-900", children: [
@@ -49090,22 +50468,22 @@ var UserManagementTable = ({
49090
50468
  })(),
49091
50469
  (() => {
49092
50470
  const user = users.find((u) => u.user_id === openActionMenuId);
49093
- const canEditName = user && onNameUpdate && permissions.canChangeRole(user);
50471
+ const canEditProfile = user && onProfileUpdate && permissions.canChangeRole(user);
49094
50472
  const canShowUsage = showUsageStats && user && (user.role_level === "plant_head" || user.role_level === "supervisor");
49095
- return canEditName && /* @__PURE__ */ jsxs(Fragment, { children: [
50473
+ return canEditProfile && /* @__PURE__ */ jsxs(Fragment, { children: [
49096
50474
  canShowUsage && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
49097
50475
  /* @__PURE__ */ jsxs(
49098
50476
  "button",
49099
50477
  {
49100
50478
  onClick: () => {
49101
50479
  if (user) {
49102
- handleEditName(user);
50480
+ handleEditProfile(user);
49103
50481
  }
49104
50482
  },
49105
50483
  className: "w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100 flex items-center gap-2",
49106
50484
  children: [
49107
50485
  /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" }),
49108
- "Edit Name"
50486
+ "Edit Profile"
49109
50487
  ]
49110
50488
  }
49111
50489
  )
@@ -49116,9 +50494,9 @@ var UserManagementTable = ({
49116
50494
  const isCurrentUser = user?.user_id === currentUserId;
49117
50495
  const canChangeRole = user && permissions.canChangeRole(user);
49118
50496
  const canShowUsage = showUsageStats && user && (user.role_level === "plant_head" || user.role_level === "supervisor");
49119
- const hasEditNameAbove = !!onNameUpdate && canChangeRole;
50497
+ const hasEditProfileAbove = !!onProfileUpdate && canChangeRole;
49120
50498
  return canChangeRole && /* @__PURE__ */ jsxs(Fragment, { children: [
49121
- canShowUsage && !hasEditNameAbove && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
50499
+ canShowUsage && !hasEditProfileAbove && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 my-1" }),
49122
50500
  /* @__PURE__ */ jsxs(
49123
50501
  "button",
49124
50502
  {
@@ -49191,14 +50569,14 @@ var UserManagementTable = ({
49191
50569
  }
49192
50570
  ),
49193
50571
  showEditNameDialog && selectedUser && /* @__PURE__ */ jsx(
49194
- EditUserNameDialog,
50572
+ EditUserProfileDialog,
49195
50573
  {
49196
50574
  isOpen: showEditNameDialog,
49197
50575
  onClose: () => {
49198
50576
  setShowEditNameDialog(false);
49199
50577
  setSelectedUser(null);
49200
50578
  },
49201
- onSave: handleNameUpdateConfirm,
50579
+ onSave: handleProfileUpdateConfirm,
49202
50580
  user: selectedUser
49203
50581
  }
49204
50582
  ),
@@ -49417,7 +50795,7 @@ function formatRoleDisplay(role) {
49417
50795
  }
49418
50796
  }
49419
50797
  function getAssignmentText(user) {
49420
- if (user.role_level === "owner") return "Company-wide";
50798
+ if (user.role_level === "owner" || user.role_level === "it") return "Company-wide";
49421
50799
  if (user.role_level === "optifye") return "All companies";
49422
50800
  if (user.role_level === "plant_head" && user.assigned_factories) {
49423
50801
  return user.assigned_factories.join(", ") || "No factories assigned";
@@ -50452,8 +51830,11 @@ function HomeView({
50452
51830
  const fallbackLineId = visibleLineIds[0] || defaultLineId;
50453
51831
  const defaultHomeLineId = fallbackLineId;
50454
51832
  const availableLineIds = useMemo(() => {
51833
+ if (visibleLineIds.length > 1) {
51834
+ return [...visibleLineIds, factoryViewId];
51835
+ }
50455
51836
  return visibleLineIds;
50456
- }, [visibleLineIds]);
51837
+ }, [visibleLineIds, factoryViewId]);
50457
51838
  const LINE_FILTER_STORAGE_KEY = "optifye_home_line_filter";
50458
51839
  const [selectedLineId, setSelectedLineId] = useState(() => {
50459
51840
  if (typeof window === "undefined") {
@@ -50462,7 +51843,7 @@ function HomeView({
50462
51843
  try {
50463
51844
  const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
50464
51845
  if (savedLineId) {
50465
- if (savedLineId !== factoryViewId && availableLineIds.includes(savedLineId)) {
51846
+ if (availableLineIds.includes(savedLineId)) {
50466
51847
  return savedLineId;
50467
51848
  }
50468
51849
  }
@@ -52788,6 +54169,8 @@ var KPIDetailView = ({
52788
54169
  };
52789
54170
  var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
52790
54171
  var KPIDetailView_default = KPIDetailViewWithDisplayNames;
54172
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
54173
+ var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
52791
54174
  var LineCard = ({
52792
54175
  line,
52793
54176
  kpis,
@@ -52795,7 +54178,8 @@ var LineCard = ({
52795
54178
  error,
52796
54179
  onClick,
52797
54180
  supervisorEnabled = false,
52798
- supervisorName
54181
+ supervisorName,
54182
+ supervisors
52799
54183
  }) => {
52800
54184
  const isOnTrack = React25__default.useMemo(() => {
52801
54185
  if (!kpis) return null;
@@ -52813,10 +54197,20 @@ var LineCard = ({
52813
54197
  /* @__PURE__ */ jsx("div", { className: "mb-4 sm:mb-5 md:mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start gap-2 sm:gap-3", children: [
52814
54198
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
52815
54199
  /* @__PURE__ */ jsx("h3", { className: "text-lg sm:text-xl font-semibold text-gray-900 break-words", children: line.line_name }),
52816
- supervisorEnabled && /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600 mt-1", children: [
54200
+ supervisorEnabled && /* @__PURE__ */ jsx("div", { className: "mt-1.5", children: supervisors && supervisors.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: supervisors.map((supervisor) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 bg-gray-50 rounded-full pl-0.5 pr-2.5 py-0.5 border border-gray-100 hover:border-gray-200 transition-all duration-300 ease-out group/supervisor hover:scale-150 hover:z-50 hover:shadow-lg relative", children: [
54201
+ /* @__PURE__ */ jsx("div", { className: "w-6 h-6 rounded-full overflow-hidden bg-white border border-gray-100 shadow-sm flex-shrink-0", children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsx(
54202
+ "img",
54203
+ {
54204
+ src: supervisor.profilePhotoUrl,
54205
+ alt: supervisor.displayName,
54206
+ className: "w-full h-full object-cover"
54207
+ }
54208
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[9px] font-bold text-gray-500 uppercase", children: getInitials(supervisor.displayName) }) }),
54209
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700 truncate max-w-[120px]", children: supervisor.displayName })
54210
+ ] }, supervisor.userId)) }) : /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600", children: [
52817
54211
  "Supervisor: ",
52818
54212
  supervisorName || "Unassigned"
52819
- ] })
54213
+ ] }) })
52820
54214
  ] }),
52821
54215
  kpis && isOnTrack !== null && /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
52822
54216
  /* @__PURE__ */ jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
@@ -52903,9 +54297,22 @@ var KPIsOverviewView = ({
52903
54297
  const [lines, setLines] = useState([]);
52904
54298
  const [loading, setLoading] = useState(true);
52905
54299
  const [error, setError] = useState(null);
54300
+ const [topPerformer, setTopPerformer] = useState({
54301
+ name: "Top Performer",
54302
+ role: "Sup.",
54303
+ unit: "Line",
54304
+ periodLabel: "Last Week",
54305
+ efficiency: null,
54306
+ imageUrl: null,
54307
+ initials: "TP"
54308
+ });
54309
+ const [topPerformerLoading, setTopPerformerLoading] = useState(true);
54310
+ const [topPerformerImageError, setTopPerformerImageError] = useState(false);
52906
54311
  const supabase = useSupabase();
52907
54312
  const dashboardConfig = useDashboardConfig();
52908
54313
  const entityConfig = useEntityConfig();
54314
+ const configCompanyId = (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0) || dashboardConfig.customConfig?.companyId || dashboardConfig.customConfig?.company_id;
54315
+ const resolvedCompanyId = resolveCompanyId(companyId, entityConfig.companyId, configCompanyId);
52909
54316
  const navigation = useNavigation(navigate);
52910
54317
  const dateTimeConfig = useDateTimeConfig();
52911
54318
  const mobileMenuContext = useMobileMenu();
@@ -52917,7 +54324,6 @@ var KPIsOverviewView = ({
52917
54324
  const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
52918
54325
  const factoryViewId = entityConfig.factoryViewId || "factory";
52919
54326
  const {
52920
- workspaceMetrics,
52921
54327
  lineMetrics,
52922
54328
  isLoading: metricsLoading,
52923
54329
  error: metricsError
@@ -52934,85 +54340,33 @@ var KPIsOverviewView = ({
52934
54340
  return map;
52935
54341
  }, [lineMetrics]);
52936
54342
  const visibleLineIds = React25__default.useMemo(() => lines.map((l) => l.id), [lines]);
52937
- const { supervisorNamesByLineId } = useSupervisorsByLineIds(visibleLineIds, {
54343
+ const { supervisorNamesByLineId, supervisorsByLineId } = useSupervisorsByLineIds(visibleLineIds, {
52938
54344
  enabled: supervisorEnabled && visibleLineIds.length > 0
52939
54345
  });
52940
54346
  useEffect(() => {
52941
- if (!lineMetrics || !workspaceMetrics || lines.length === 0) {
52942
- return;
52943
- }
52944
- const toNumber2 = (value) => {
52945
- if (typeof value === "number" && Number.isFinite(value)) return value;
52946
- if (typeof value === "string" && value.trim() !== "") {
52947
- const parsed = Number(value);
52948
- return Number.isFinite(parsed) ? parsed : 0;
54347
+ let isMounted = true;
54348
+ const loadTopPerformer = async () => {
54349
+ try {
54350
+ const record = await weeklyTopPerformerService.getWeeklyTopPerformer(
54351
+ supabase,
54352
+ resolvedCompanyId
54353
+ );
54354
+ if (!isMounted) return;
54355
+ if (record) {
54356
+ setTopPerformer(buildTopPerformerDisplay(record));
54357
+ setTopPerformerImageError(false);
54358
+ }
54359
+ } catch (err) {
54360
+ console.error("[KPIsOverviewView] Failed to load top performer:", err);
54361
+ } finally {
54362
+ if (isMounted) setTopPerformerLoading(false);
52949
54363
  }
52950
- return 0;
52951
54364
  };
52952
- const lineById = new Map(lines.map((line) => [line.id, line]));
52953
- const workspacesByKey = /* @__PURE__ */ new Map();
52954
- workspaceMetrics.forEach((workspace) => {
52955
- if (!workspace.line_id || !workspace.date || workspace.shift_id === void 0) {
52956
- return;
52957
- }
52958
- const key = `${workspace.line_id}:${workspace.date}:${workspace.shift_id}`;
52959
- const bucket = workspacesByKey.get(key) || [];
52960
- bucket.push(workspace);
52961
- workspacesByKey.set(key, bucket);
52962
- });
52963
- lineMetrics.forEach((row) => {
52964
- const lineId = row?.line_id;
52965
- const shiftId = row?.shift_id;
52966
- const date = row?.date;
52967
- if (!lineId || !date || shiftId === void 0) {
52968
- return;
52969
- }
52970
- const line = lineById.get(lineId);
52971
- if (!line) {
52972
- return;
52973
- }
52974
- const key = `${lineId}:${date}:${shiftId}`;
52975
- const lineWorkspaces = workspacesByKey.get(key) || [];
52976
- const poorestPerformingWorkspaces = [...lineWorkspaces].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 5).map((ws) => ({
52977
- workspace_name: ws.workspace_name,
52978
- efficiency: ws.efficiency || 0,
52979
- action_count: ws.action_count || 0,
52980
- action_threshold: ws.action_threshold || 0
52981
- }));
52982
- const lineInfo = {
52983
- line_id: lineId,
52984
- line_name: line.line_name,
52985
- company_id: line.company_id || "",
52986
- company_name: line.company_name || "",
52987
- factory_id: line.factory_id || "",
52988
- factory_name: line.factory_name || "",
52989
- shift_id: shiftId,
52990
- date,
52991
- metrics: {
52992
- avg_efficiency: toNumber2(row.avg_efficiency),
52993
- avg_cycle_time: toNumber2(row.avg_cycle_time),
52994
- current_output: toNumber2(row.current_output),
52995
- ideal_output: toNumber2(row.ideal_output) || toNumber2(row.line_threshold),
52996
- total_workspaces: toNumber2(row.total_workspaces),
52997
- underperforming_workspaces: toNumber2(row.underperforming_workspaces),
52998
- underperforming_workspace_names: row.underperforming_workspace_names || [],
52999
- underperforming_workspace_uuids: row.underperforming_workspace_uuids || [],
53000
- output_array: row.output_array || [],
53001
- output_hourly: row.output_hourly || void 0,
53002
- line_threshold: toNumber2(row.line_threshold),
53003
- threshold_pph: toNumber2(row.threshold_pph),
53004
- shift_start: row.shift_start || void 0,
53005
- shift_end: row.shift_end || void 0,
53006
- last_updated: row.last_updated || (/* @__PURE__ */ new Date()).toISOString(),
53007
- poorest_performing_workspaces: poorestPerformingWorkspaces
53008
- }
53009
- };
53010
- lineDetailStore.setSnapshot(lineId, date, shiftId, {
53011
- lineInfo,
53012
- workspaces: lineWorkspaces
53013
- });
53014
- });
53015
- }, [lineMetrics, workspaceMetrics, lines]);
54365
+ loadTopPerformer();
54366
+ return () => {
54367
+ isMounted = false;
54368
+ };
54369
+ }, [supabase, resolvedCompanyId]);
53016
54370
  useEffect(() => {
53017
54371
  trackCorePageView("KPIs Overview");
53018
54372
  }, []);
@@ -53048,6 +54402,29 @@ var KPIsOverviewView = ({
53048
54402
  };
53049
54403
  fetchLines();
53050
54404
  }, [supabase, dashboardConfig, lineIds]);
54405
+ const formatTopPerformerWeek = (periodStart, periodEnd) => {
54406
+ if (!periodStart || !periodEnd) return "Last Week";
54407
+ const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
54408
+ const endDate = /* @__PURE__ */ new Date(`${periodEnd}T00:00:00`);
54409
+ if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) return "Last Week";
54410
+ const startLabel = startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" });
54411
+ const endLabel = endDate.toLocaleDateString("en-US", { month: "short", day: "numeric" });
54412
+ return `${startLabel} - ${endLabel}`;
54413
+ };
54414
+ const buildTopPerformerDisplay = (record) => {
54415
+ const name = record.recipient_name || "Supervisor";
54416
+ const efficiencyValue = typeof record.avg_efficiency === "number" ? record.avg_efficiency : Number.parseFloat(String(record.avg_efficiency));
54417
+ const efficiency = Number.isFinite(efficiencyValue) ? efficiencyValue : null;
54418
+ return {
54419
+ name,
54420
+ role: "Sup.",
54421
+ unit: record.unit || "Line",
54422
+ periodLabel: formatTopPerformerWeek(record.period_start, record.period_end),
54423
+ efficiency,
54424
+ imageUrl: record.profile_photo_url || null,
54425
+ initials: getInitials(name)
54426
+ };
54427
+ };
53051
54428
  const handleLineClick = (line, kpis) => {
53052
54429
  const trackProps = {
53053
54430
  line_id: line.id,
@@ -53086,6 +54463,9 @@ var KPIsOverviewView = ({
53086
54463
  };
53087
54464
  const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
53088
54465
  const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
54466
+ const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
54467
+ const hasTopPerformerEfficiency = typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
54468
+ const topPerformerEfficiencyLabel = topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
53089
54469
  const getShiftIcon = (shiftId) => {
53090
54470
  const shiftNameLower = shiftName.toLowerCase();
53091
54471
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
@@ -53194,18 +54574,51 @@ var KPIsOverviewView = ({
53194
54574
  ] }) }),
53195
54575
  /* @__PURE__ */ jsx("div", { className: "w-12" })
53196
54576
  ] }),
53197
- /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-center gap-2", children: [
54577
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-2", children: [
53198
54578
  /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700", children: formatLocalDate2(/* @__PURE__ */ new Date()) }) }),
53199
54579
  /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
53200
54580
  /* @__PURE__ */ jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(currentShiftDetails.shiftId) }),
53201
54581
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
53202
54582
  ] }),
53203
54583
  /* @__PURE__ */ jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsx(ISTTimer_default, {}) }) })
54584
+ ] }),
54585
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 bg-white shadow-sm hover:shadow-md transition-all duration-300 ease-out hover:scale-[1.02] relative rounded-full pl-1.5 pr-4 py-1.5 flex items-center justify-between", children: [
54586
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
54587
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
54588
+ /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full border border-gray-100 overflow-hidden bg-gray-50", children: showTopPerformerImage ? /* @__PURE__ */ jsx(
54589
+ "img",
54590
+ {
54591
+ src: topPerformer.imageUrl || "",
54592
+ alt: "Top Performer",
54593
+ className: "w-full h-full object-cover",
54594
+ onError: () => setTopPerformerImageError(true)
54595
+ }
54596
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[11px] font-semibold text-gray-600", children: topPerformer.initials }) }),
54597
+ /* @__PURE__ */ jsx("div", { className: "absolute -top-1 -right-1 bg-white rounded-full p-1 shadow-sm border border-gray-100", children: /* @__PURE__ */ jsx(Trophy, { className: "w-3 h-3 text-amber-500 fill-amber-500" }) })
54598
+ ] }),
54599
+ /* @__PURE__ */ jsxs("div", { children: [
54600
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 leading-none mb-0.5", children: [
54601
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-amber-600 uppercase tracking-wide", children: "Top Performer" }),
54602
+ /* @__PURE__ */ jsx("span", { className: "w-0.5 h-0.5 bg-gray-300 rounded-full" }),
54603
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-gray-500", children: topPerformerLoading ? "Loading" : topPerformer.periodLabel })
54604
+ ] }),
54605
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 leading-none", children: [
54606
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-900", children: topPerformer.unit }),
54607
+ /* @__PURE__ */ jsx("span", { className: "w-px h-2.5 bg-gray-200" }),
54608
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-gray-500", children: [
54609
+ topPerformer.role,
54610
+ " ",
54611
+ topPerformer.name
54612
+ ] })
54613
+ ] })
54614
+ ] })
54615
+ ] }),
54616
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-gray-700", children: topPerformerEfficiencyLabel })
53204
54617
  ] })
53205
54618
  ] }),
53206
54619
  /* @__PURE__ */ jsxs("div", { className: "hidden sm:block", children: [
53207
- /* @__PURE__ */ jsxs("div", { className: "flex items-center relative", children: [
53208
- /* @__PURE__ */ jsx("div", { className: "absolute left-0", children: /* @__PURE__ */ jsx(
54620
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center relative mb-1", children: [
54621
+ /* @__PURE__ */ jsx("div", { className: "absolute left-0 z-10", children: /* @__PURE__ */ jsx(
53209
54622
  BackButtonMinimal,
53210
54623
  {
53211
54624
  onClick: handleBackClick,
@@ -53217,9 +54630,35 @@ var KPIsOverviewView = ({
53217
54630
  /* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
53218
54631
  /* @__PURE__ */ jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Shop-floor overview" }),
53219
54632
  /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
53220
- ] }) })
54633
+ ] }) }),
54634
+ /* @__PURE__ */ jsxs("div", { className: "absolute right-0 z-10 flex items-center gap-1.5 sm:gap-2 lg:gap-3 cursor-default group pr-2 sm:pr-4 xl:pr-6", children: [
54635
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10", children: [
54636
+ /* @__PURE__ */ jsx("div", { className: "w-7 h-7 sm:w-8 sm:h-8 md:w-9 md:h-9 lg:w-10 lg:h-10 xl:w-12 xl:h-12 rounded-full border border-gray-100 overflow-hidden shadow-sm transition-all duration-300", children: showTopPerformerImage ? /* @__PURE__ */ jsx(
54637
+ "img",
54638
+ {
54639
+ src: topPerformer.imageUrl || "",
54640
+ alt: "Top Performer",
54641
+ className: "w-full h-full object-cover transform transition-transform duration-500 group-hover:scale-105",
54642
+ onError: () => setTopPerformerImageError(true)
54643
+ }
54644
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[10px] sm:text-xs lg:text-sm font-semibold text-gray-600", children: topPerformer.initials }) }),
54645
+ /* @__PURE__ */ jsx("div", { className: "absolute -bottom-0.5 sm:-bottom-1 -right-0.5 sm:-right-1 bg-white rounded-full p-0.5 sm:p-1 shadow-sm border border-gray-100 flex items-center justify-center ring-2 ring-white", children: /* @__PURE__ */ jsx(Trophy, { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 lg:w-3 lg:h-3 xl:w-3.5 xl:h-3.5 text-amber-500 fill-amber-500 transition-all duration-300" }) })
54646
+ ] }),
54647
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center z-10 gap-0.5 min-w-0", children: [
54648
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-1.5 lg:gap-2 leading-none", children: [
54649
+ /* @__PURE__ */ jsx("span", { className: "text-[8px] sm:text-[9px] lg:text-[10px] font-bold text-amber-600 uppercase tracking-wide sm:tracking-widest transition-all duration-300 truncate", children: "Top Performer" }),
54650
+ /* @__PURE__ */ jsx("span", { className: "w-0.5 h-0.5 bg-gray-300 rounded-full flex-shrink-0" }),
54651
+ /* @__PURE__ */ jsx("span", { className: "text-[8px] sm:text-[9px] lg:text-[10px] font-medium text-gray-500 transition-all duration-300 truncate", children: topPerformerLoading ? "Loading" : topPerformer.periodLabel })
54652
+ ] }),
54653
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 sm:gap-1.5 lg:gap-2 xl:gap-3 mt-0.5 transition-all duration-300 min-w-0", children: [
54654
+ /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm lg:text-base font-semibold text-gray-900 tracking-tight transition-all duration-300 truncate", children: topPerformer.unit }),
54655
+ /* @__PURE__ */ jsx("span", { className: "w-px h-2.5 sm:h-3 lg:h-3.5 bg-gray-200 flex-shrink-0" }),
54656
+ /* @__PURE__ */ jsx("span", { className: "text-[9px] sm:text-[10px] lg:text-xs font-bold text-gray-700 transition-all duration-300 truncate whitespace-nowrap", children: hasTopPerformerEfficiency ? `${topPerformer.efficiency?.toFixed(1)}% Eff.` : "TBD" })
54657
+ ] })
54658
+ ] })
54659
+ ] })
53221
54660
  ] }),
53222
- /* @__PURE__ */ jsx("div", { className: "mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
54661
+ /* @__PURE__ */ jsx("div", { className: "mt-2 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
53223
54662
  /* @__PURE__ */ jsx("div", { className: "text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsx(ISTTimer_default, {}) }),
53224
54663
  /* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-blue-300" }),
53225
54664
  /* @__PURE__ */ jsx("span", { className: "text-sm md:text-base font-medium text-blue-600", children: formatLocalDate2(/* @__PURE__ */ new Date()) }),
@@ -53243,7 +54682,8 @@ var KPIsOverviewView = ({
53243
54682
  error: metricsError,
53244
54683
  onClick: (kpis) => handleLineClick(line, kpis),
53245
54684
  supervisorEnabled,
53246
- supervisorName: supervisorNamesByLineId.get(line.id) || null
54685
+ supervisorName: supervisorNamesByLineId.get(line.id) || null,
54686
+ supervisors: supervisorsByLineId?.get(line.id)
53247
54687
  },
53248
54688
  line.id
53249
54689
  )) }) })
@@ -54208,10 +55648,98 @@ function LoginView({
54208
55648
  );
54209
55649
  }
54210
55650
  var LoginView_default = LoginView;
54211
- var Button2 = (props) => {
54212
- const { Button: RegisteredButton } = useRegistry();
54213
- return /* @__PURE__ */ jsx(RegisteredButton, { ...props });
55651
+ var AwardsSection = ({ className = "", variant = "card" }) => {
55652
+ const supabase = useSupabase();
55653
+ const { user } = useAuth();
55654
+ const [awards, setAwards] = useState([]);
55655
+ const [loading, setLoading] = useState(true);
55656
+ const [error, setError] = useState(null);
55657
+ const buildAwardDisplay = useMemo(
55658
+ () => (award) => ({
55659
+ id: award.id,
55660
+ type: getAwardBadgeType(award.award_type),
55661
+ title: getAwardTitle(award.award_type),
55662
+ date: formatAwardMonth(award.period_start),
55663
+ description: getAwardDescription(award)
55664
+ }),
55665
+ []
55666
+ );
55667
+ useEffect(() => {
55668
+ let isMounted = true;
55669
+ const loadAwards = async () => {
55670
+ if (!user) {
55671
+ if (isMounted) setLoading(false);
55672
+ return;
55673
+ }
55674
+ setLoading(true);
55675
+ setError(null);
55676
+ try {
55677
+ const data = await awardsService.getMyAwards(supabase);
55678
+ if (!isMounted) return;
55679
+ setAwards(data.map(buildAwardDisplay));
55680
+ } catch (err) {
55681
+ if (!isMounted) return;
55682
+ console.error("[AwardsSection] Failed to load awards:", err);
55683
+ setError("Failed to load awards");
55684
+ } finally {
55685
+ if (isMounted) setLoading(false);
55686
+ }
55687
+ };
55688
+ loadAwards();
55689
+ return () => {
55690
+ isMounted = false;
55691
+ };
55692
+ }, [user, supabase, buildAwardDisplay]);
55693
+ const containerClasses = variant === "card" ? `bg-white/50 backdrop-blur-sm border border-gray-200/60 rounded-lg shadow-sm overflow-hidden ${className}` : `${className}`;
55694
+ const headerClasses = variant === "card" ? "p-6 border-b border-gray-200/60 flex items-center justify-between" : "hidden";
55695
+ const contentClasses = variant === "card" ? "p-6 bg-gradient-to-b from-white to-gray-50/50" : "py-8";
55696
+ return /* @__PURE__ */ jsxs("div", { className: containerClasses, children: [
55697
+ /* @__PURE__ */ jsx("div", { className: headerClasses, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
55698
+ /* @__PURE__ */ jsx("div", { className: "p-2 bg-gradient-to-br from-yellow-100 to-amber-100 rounded-lg border border-yellow-200 shadow-sm", children: /* @__PURE__ */ jsx(Award, { className: "h-5 w-5 text-amber-600" }) }),
55699
+ /* @__PURE__ */ jsxs("div", { children: [
55700
+ /* @__PURE__ */ jsxs("h2", { className: "text-xl font-semibold text-gray-900 flex items-center gap-2", children: [
55701
+ "Achievements & Awards",
55702
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-amber-400" })
55703
+ ] }),
55704
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Recognition for your outstanding contributions" })
55705
+ ] })
55706
+ ] }) }),
55707
+ /* @__PURE__ */ jsxs("div", { className: contentClasses, children: [
55708
+ (loading || error) && /* @__PURE__ */ jsx("div", { className: "mb-6 text-center text-sm text-gray-500", children: loading ? "Loading awards..." : error }),
55709
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-12", children: [
55710
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, mode: "popLayout", children: awards.map((award, index) => /* @__PURE__ */ jsx(
55711
+ motion.div,
55712
+ {
55713
+ layout: true,
55714
+ initial: { opacity: 0, scale: 0.95 },
55715
+ animate: { opacity: 1, scale: 1 },
55716
+ transition: {
55717
+ layout: { type: "spring", damping: 25, stiffness: 120 },
55718
+ opacity: { duration: 0.4 },
55719
+ scale: { type: "spring", damping: 15 }
55720
+ },
55721
+ className: "perspective-1000",
55722
+ children: /* @__PURE__ */ jsx(AwardBadge, { ...award, className: "w-full max-w-none", floating: true })
55723
+ },
55724
+ award.id || `${award.title}-${award.date}-${index}`
55725
+ )) }),
55726
+ !loading && awards.length === 0 && /* @__PURE__ */ jsxs(
55727
+ motion.div,
55728
+ {
55729
+ layout: true,
55730
+ className: "flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-slate-200 p-6 text-slate-400 hover:border-slate-300 hover:text-slate-500 transition-all cursor-pointer group h-full min-h-[250px] hover:bg-white/40",
55731
+ children: [
55732
+ /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-full bg-slate-50/50 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform shadow-inner", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-6 w-6" }) }),
55733
+ /* @__PURE__ */ jsx("p", { className: "font-bold text-xs tracking-widest uppercase", children: "No Awards Yet" }),
55734
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-center mt-1 font-medium text-slate-400", children: "Keep up the great work!" })
55735
+ ]
55736
+ }
55737
+ )
55738
+ ] })
55739
+ ] })
55740
+ ] });
54214
55741
  };
55742
+ var AwardsSection_default = AwardsSection;
54215
55743
  var ProfileView = () => {
54216
55744
  const { user, signOut, loading: authLoading } = useAuth();
54217
55745
  const supabase = useSupabase();
@@ -54219,7 +55747,7 @@ var ProfileView = () => {
54219
55747
  const { authConfig } = useDashboardConfig();
54220
55748
  const mobileMenuContext = useMobileMenu();
54221
55749
  useHideMobileHeader(!!mobileMenuContext);
54222
- const [activeTab, setActiveTab] = useState("profile");
55750
+ const [activeTab, setActiveTab] = useState("awards");
54223
55751
  const [profileData, setProfileData] = useState({
54224
55752
  email: user?.email || ""
54225
55753
  });
@@ -54318,86 +55846,105 @@ var ProfileView = () => {
54318
55846
  return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
54319
55847
  /* @__PURE__ */ jsx(AlertCircle, { className: "h-12 w-12 text-red-500 mx-auto mb-4" }),
54320
55848
  /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900 mb-2", children: "Authentication Required" }),
54321
- /* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-4", children: "Please sign in to access your profile." }),
54322
55849
  /* @__PURE__ */ jsx(Button2, { variant: "outline", onClick: () => navigation.navigate("/login"), children: "Sign In" })
54323
55850
  ] }) });
54324
55851
  }
54325
- const tabs = [
54326
- { id: "profile", label: "Profile", icon: User },
54327
- { id: "sessions", label: "Sessions", icon: Clock }
54328
- ];
54329
- return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50", children: /* @__PURE__ */ jsxs("div", { className: "max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-6", children: [
54330
- /* @__PURE__ */ jsxs("div", { className: "mb-6 sm:mb-8", children: [
54331
- /* @__PURE__ */ jsx("div", { className: "sm:hidden pb-3 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
54332
- mobileMenuContext && /* @__PURE__ */ jsx(
54333
- HamburgerButton,
54334
- {
54335
- onClick: mobileMenuContext.onMobileMenuOpen,
54336
- className: "flex-shrink-0 -ml-1"
54337
- }
54338
- ),
54339
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
54340
- /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Account Settings" }),
54341
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: "Manage your account" })
54342
- ] }),
54343
- /* @__PURE__ */ jsx("div", { className: "w-12" })
54344
- ] }) }),
54345
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
54346
- /* @__PURE__ */ jsxs("div", { children: [
54347
- /* @__PURE__ */ jsx("h1", { className: "text-3xl font-semibold text-gray-900", children: "Account Settings" }),
54348
- /* @__PURE__ */ jsx("p", { className: "text-gray-600 mt-2", children: "Manage your account settings and preferences" })
55852
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
55853
+ /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 bg-white border-b flex-shrink-0", children: [
55854
+ /* @__PURE__ */ jsxs("div", { className: "px-3 sm:px-4 md:px-6 py-2 sm:py-3", children: [
55855
+ /* @__PURE__ */ jsxs("div", { className: "sm:hidden flex items-center justify-between", children: [
55856
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: mobileMenuContext && /* @__PURE__ */ jsx(
55857
+ HamburgerButton,
55858
+ {
55859
+ onClick: mobileMenuContext.onMobileMenuOpen,
55860
+ className: "flex-shrink-0 -ml-1"
55861
+ }
55862
+ ) }),
55863
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "My Profile" }) }),
55864
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded-full border border-gray-200 overflow-hidden bg-white shadow-sm", children: profileData.avatar_url ? /* @__PURE__ */ jsx("img", { src: profileData.avatar_url, alt: "Profile", className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-blue-50", children: /* @__PURE__ */ jsx(User, { className: "w-4 h-4 text-blue-500" }) }) }) })
54349
55865
  ] }),
54350
- /* @__PURE__ */ jsxs(
54351
- Button2,
54352
- {
54353
- variant: "outline",
54354
- onClick: () => navigation.goToDashboard(),
54355
- className: "flex items-center gap-2",
54356
- children: [
54357
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
54358
- "Close"
54359
- ]
54360
- }
54361
- )
54362
- ] }) })
54363
- ] }),
54364
- error && /* @__PURE__ */ jsx("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
54365
- /* @__PURE__ */ jsx(AlertCircle, { className: "h-5 w-5 text-red-500 mr-2" }),
54366
- /* @__PURE__ */ jsx("span", { className: "text-red-700", children: error })
54367
- ] }) }),
54368
- success && /* @__PURE__ */ jsx("div", { className: "mb-6 p-4 bg-green-50 border border-green-200 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
54369
- /* @__PURE__ */ jsx(Check, { className: "h-5 w-5 text-green-500 mr-2" }),
54370
- /* @__PURE__ */ jsx("span", { className: "text-green-700", children: success })
54371
- ] }) }),
54372
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-8", children: [
54373
- /* @__PURE__ */ jsx("div", { className: "lg:w-64 flex-shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "bg-white/50 backdrop-blur-sm border border-gray-200/60 rounded-lg shadow-sm", children: [
54374
- /* @__PURE__ */ jsx("div", { className: "p-6 border-b border-gray-200/60", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
54375
- /* @__PURE__ */ jsx("div", { className: "w-12 h-12 bg-blue-50 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx(User, { className: "h-6 w-6 text-blue-600" }) }),
54376
- /* @__PURE__ */ jsxs("div", { children: [
54377
- /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900", children: profileData.full_name || "User" }),
54378
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: profileData.email })
54379
- ] })
55866
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between relative", children: [
55867
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(
55868
+ BackButtonMinimal,
55869
+ {
55870
+ onClick: () => navigation.goToDashboard(),
55871
+ text: "Back",
55872
+ size: "default",
55873
+ className: "bg-transparent border-transparent hover:bg-gray-100 pl-0"
55874
+ }
55875
+ ) }),
55876
+ /* @__PURE__ */ jsx("h1", { className: "absolute left-1/2 -translate-x-1/2 text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900", children: "My Profile" }),
55877
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full border border-gray-200 overflow-hidden bg-white shadow-sm ring-2 ring-gray-50 transition-transform hover:scale-105", children: profileData.avatar_url ? /* @__PURE__ */ jsx("img", { src: profileData.avatar_url, alt: "Profile", className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-blue-50", children: /* @__PURE__ */ jsx(User, { className: "w-5 h-5 text-blue-500" }) }) }) })
55878
+ ] }) })
55879
+ ] }),
55880
+ /* @__PURE__ */ jsxs("div", { className: "flex mt-2 px-3 sm:px-4 md:px-6", children: [
55881
+ /* @__PURE__ */ jsx("div", { className: "sm:hidden w-full pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex bg-gray-100 rounded-lg p-0.5", children: [
55882
+ /* @__PURE__ */ jsxs(
55883
+ "button",
55884
+ {
55885
+ onClick: () => setActiveTab("awards"),
55886
+ className: `flex-1 px-2 py-1.5 text-xs font-medium rounded-md transition-all duration-200 flex items-center justify-center gap-1.5 ${activeTab === "awards" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600"}`,
55887
+ children: [
55888
+ /* @__PURE__ */ jsx(Award, { className: "w-3.5 h-3.5" }),
55889
+ "Awards"
55890
+ ]
55891
+ }
55892
+ ),
55893
+ /* @__PURE__ */ jsxs(
55894
+ "button",
55895
+ {
55896
+ onClick: () => setActiveTab("details"),
55897
+ className: `flex-1 px-2 py-1.5 text-xs font-medium rounded-md transition-all duration-200 flex items-center justify-center gap-1.5 ${activeTab === "details" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600"}`,
55898
+ children: [
55899
+ /* @__PURE__ */ jsx(User, { className: "w-3.5 h-3.5" }),
55900
+ "Details"
55901
+ ]
55902
+ }
55903
+ )
54380
55904
  ] }) }),
54381
- /* @__PURE__ */ jsx("nav", { className: "p-2", children: tabs.map((tab) => {
54382
- const Icon2 = tab.icon;
54383
- return /* @__PURE__ */ jsxs(
55905
+ /* @__PURE__ */ jsxs("div", { className: "hidden sm:flex items-center gap-2 mb-3 -ml-3", children: [
55906
+ /* @__PURE__ */ jsxs(
54384
55907
  "button",
54385
55908
  {
54386
- onClick: () => setActiveTab(tab.id),
54387
- className: `w-full flex items-center space-x-3 px-4 py-3 text-left rounded-lg transition-colors ${activeTab === tab.id ? "bg-blue-50 text-blue-600" : "text-gray-700 hover:bg-gray-50"}`,
55909
+ onClick: () => setActiveTab("awards"),
55910
+ className: `px-3 py-1.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-2 ${activeTab === "awards" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
54388
55911
  children: [
54389
- /* @__PURE__ */ jsx(Icon2, { className: "h-5 w-5" }),
54390
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: tab.label })
55912
+ /* @__PURE__ */ jsx(Award, { className: "w-4 h-4" }),
55913
+ "Awards"
54391
55914
  ]
54392
- },
54393
- tab.id
54394
- );
54395
- }) })
54396
- ] }) }),
54397
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs("div", { className: "bg-white/50 backdrop-blur-sm border border-gray-200/60 rounded-lg shadow-sm", children: [
54398
- activeTab === "profile" && /* @__PURE__ */ jsxs("div", { className: "p-6", children: [
54399
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
54400
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900", children: "Profile Information" }),
55915
+ }
55916
+ ),
55917
+ /* @__PURE__ */ jsxs(
55918
+ "button",
55919
+ {
55920
+ onClick: () => setActiveTab("details"),
55921
+ className: `px-3 py-1.5 text-sm font-medium rounded-lg transition-colors flex items-center gap-2 ${activeTab === "details" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
55922
+ children: [
55923
+ /* @__PURE__ */ jsx(User, { className: "w-4 h-4" }),
55924
+ "Details"
55925
+ ]
55926
+ }
55927
+ )
55928
+ ] })
55929
+ ] })
55930
+ ] }),
55931
+ /* @__PURE__ */ jsxs("main", { className: "flex-1 p-4 sm:p-6 lg:p-8 max-w-7xl mx-auto w-full", children: [
55932
+ error && /* @__PURE__ */ jsxs("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg flex items-center text-red-700", children: [
55933
+ /* @__PURE__ */ jsx(AlertCircle, { className: "h-5 w-5 mr-2" }),
55934
+ error
55935
+ ] }),
55936
+ success && /* @__PURE__ */ jsxs("div", { className: "mb-6 p-4 bg-green-50 border border-green-200 rounded-lg flex items-center text-green-700", children: [
55937
+ /* @__PURE__ */ jsx(Check, { className: "h-5 w-5 mr-2" }),
55938
+ success
55939
+ ] }),
55940
+ activeTab === "awards" && /* @__PURE__ */ jsx("div", { className: "animate-in fade-in slide-in-from-bottom-4 duration-500", children: /* @__PURE__ */ jsx(AwardsSection_default, { variant: "clean" }) }),
55941
+ activeTab === "details" && /* @__PURE__ */ jsxs("div", { className: "space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-4xl mx-auto", children: [
55942
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden", children: [
55943
+ /* @__PURE__ */ jsxs("div", { className: "p-6 border-b border-gray-100 flex items-center justify-between", children: [
55944
+ /* @__PURE__ */ jsxs("div", { children: [
55945
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Personal Information" }),
55946
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Update your personal details here" })
55947
+ ] }),
54401
55948
  /* @__PURE__ */ jsx(
54402
55949
  Button2,
54403
55950
  {
@@ -54406,15 +55953,15 @@ var ProfileView = () => {
54406
55953
  className: "flex items-center gap-2",
54407
55954
  children: isEditing ? /* @__PURE__ */ jsxs(Fragment, { children: [
54408
55955
  /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
54409
- "Cancel"
55956
+ " Cancel"
54410
55957
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
54411
55958
  /* @__PURE__ */ jsx(Edit2, { className: "h-4 w-4" }),
54412
- "Edit"
55959
+ " Edit"
54413
55960
  ] })
54414
55961
  }
54415
55962
  )
54416
55963
  ] }),
54417
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [
55964
+ /* @__PURE__ */ jsxs("div", { className: "p-6 grid grid-cols-1 md:grid-cols-2 gap-6", children: [
54418
55965
  /* @__PURE__ */ jsxs("div", { children: [
54419
55966
  /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Full Name" }),
54420
55967
  isEditing ? /* @__PURE__ */ jsx(
@@ -54423,94 +55970,75 @@ var ProfileView = () => {
54423
55970
  type: "text",
54424
55971
  value: profileData.full_name || "",
54425
55972
  onChange: (e) => setProfileData((prev) => ({ ...prev, full_name: e.target.value })),
54426
- className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
55973
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
54427
55974
  }
54428
- ) : /* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.full_name || "Not set" })
55975
+ ) : /* @__PURE__ */ jsx("p", { className: "text-gray-900 font-medium", children: profileData.full_name || "Not set" })
54429
55976
  ] }),
54430
55977
  /* @__PURE__ */ jsxs("div", { children: [
54431
- /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Email" }),
55978
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Email Address" }),
54432
55979
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
54433
55980
  /* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.email }),
54434
55981
  profileData.email_verified && /* @__PURE__ */ jsx(UserCheck, { className: "h-4 w-4 text-green-500" })
54435
55982
  ] })
54436
55983
  ] })
54437
55984
  ] }),
54438
- isEditing && /* @__PURE__ */ jsx("div", { className: "mt-6 flex gap-3", children: /* @__PURE__ */ jsx(Button2, { variant: "outline", onClick: handleSaveProfile, disabled: loading, children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
55985
+ isEditing && /* @__PURE__ */ jsx("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-100 flex justify-end", children: /* @__PURE__ */ jsx(Button2, { onClick: handleSaveProfile, disabled: loading, children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
54439
55986
  /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "sm", className: "mr-2" }),
54440
- "Saving..."
55987
+ " Saving..."
54441
55988
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
54442
55989
  /* @__PURE__ */ jsx(Save, { className: "h-4 w-4 mr-2" }),
54443
- "Save Changes"
55990
+ " Save Changes"
54444
55991
  ] }) }) })
54445
55992
  ] }),
54446
- activeTab === "sessions" && /* @__PURE__ */ jsxs("div", { className: "p-6", children: [
54447
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900 mb-6", children: "Active Sessions" }),
54448
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsx("div", { className: "border border-gray-200/60 rounded-lg p-4 bg-white/30", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
54449
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
54450
- /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-green-500 rounded-full" }),
54451
- /* @__PURE__ */ jsxs("div", { children: [
54452
- /* @__PURE__ */ jsx("p", { className: "font-medium text-gray-900", children: "Current Session" }),
54453
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500", children: [
54454
- navigator.userAgent.includes("Chrome") ? "Chrome" : navigator.userAgent.includes("Firefox") ? "Firefox" : navigator.userAgent.includes("Safari") ? "Safari" : "Unknown Browser",
54455
- "\u2022 Last active now"
55993
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden", children: [
55994
+ /* @__PURE__ */ jsxs("div", { className: "p-6 border-b border-gray-100", children: [
55995
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Sessions & Security" }),
55996
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Manage your active sessions and data" })
55997
+ ] }),
55998
+ /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-6", children: [
55999
+ /* @__PURE__ */ jsxs("div", { className: "bg-green-50 border border-green-100 rounded-lg p-4 flex items-center justify-between", children: [
56000
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
56001
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-green-500 rounded-full animate-pulse" }),
56002
+ /* @__PURE__ */ jsxs("div", { children: [
56003
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-gray-900", children: "Current Session" }),
56004
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 mt-0.5", children: [
56005
+ "Active now \u2022 ",
56006
+ navigator.userAgent.includes("Mac") ? "Mac OS" : "Windows"
56007
+ ] })
54456
56008
  ] })
54457
- ] })
56009
+ ] }),
56010
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-green-700 bg-green-100 px-2 py-1 rounded-full", children: "Active" })
54458
56011
  ] }),
54459
- /* @__PURE__ */ jsx("span", { className: "text-sm text-green-600 font-medium", children: "Active" })
54460
- ] }) }) }),
54461
- /* @__PURE__ */ jsxs("div", { className: "mt-8 pt-6 border-t border-gray-200/60", children: [
54462
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 mb-4", children: "Data & Privacy" }),
54463
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
54464
- /* @__PURE__ */ jsxs(
54465
- Button2,
54466
- {
54467
- variant: "outline",
54468
- onClick: exportData,
54469
- className: "flex items-center gap-2",
54470
- children: [
54471
- /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
54472
- "Export Your Data"
54473
- ]
54474
- }
54475
- ),
54476
- /* @__PURE__ */ jsxs(
54477
- Button2,
54478
- {
54479
- variant: "outline",
54480
- className: "flex items-center gap-2 text-red-600 hover:text-red-700 border-red-200 hover:border-red-300",
54481
- onClick: () => {
54482
- if (confirm("Are you sure you want to delete your account? This action cannot be undone.")) {
54483
- console.log("Account deletion requested");
54484
- }
54485
- },
54486
- children: [
54487
- /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }),
54488
- "Delete Account"
54489
- ]
54490
- }
54491
- )
56012
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-100 pt-6", children: [
56013
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-900 mb-4", children: "Data Management" }),
56014
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-4", children: [
56015
+ /* @__PURE__ */ jsxs(Button2, { variant: "outline", onClick: exportData, className: "flex items-center gap-2", children: [
56016
+ /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
56017
+ " Export Data"
56018
+ ] }),
56019
+ /* @__PURE__ */ jsxs(Button2, { variant: "outline", onClick: handleSignOut, className: "flex items-center gap-2 text-gray-700", children: [
56020
+ /* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" }),
56021
+ " Sign Out"
56022
+ ] }),
56023
+ /* @__PURE__ */ jsxs(
56024
+ Button2,
56025
+ {
56026
+ variant: "ghost",
56027
+ className: "flex items-center gap-2 text-red-600 hover:text-red-700 hover:bg-red-50 ml-auto",
56028
+ onClick: () => confirm("Delete account?") && console.log("Delete"),
56029
+ children: [
56030
+ /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }),
56031
+ " Delete Account"
56032
+ ]
56033
+ }
56034
+ )
56035
+ ] })
54492
56036
  ] })
54493
56037
  ] })
54494
- ] }),
54495
- /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200/60 p-6", children: /* @__PURE__ */ jsx(
54496
- Button2,
54497
- {
54498
- variant: "outline",
54499
- onClick: handleSignOut,
54500
- disabled: loading,
54501
- className: "flex items-center gap-2 text-red-600 hover:text-red-700 border-red-200 hover:border-red-300",
54502
- children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
54503
- /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "sm", className: "mr-2" }),
54504
- "Signing out..."
54505
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
54506
- /* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" }),
54507
- "Sign Out"
54508
- ] })
54509
- }
54510
- ) })
54511
- ] }) })
56038
+ ] })
56039
+ ] })
54512
56040
  ] })
54513
- ] }) });
56041
+ ] });
54514
56042
  };
54515
56043
  var ProfileView_default = ProfileView;
54516
56044
  var calculateShiftHours = (startTime, endTime, breaks = []) => {
@@ -57093,6 +58621,14 @@ var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
57093
58621
  }
57094
58622
  return "overview";
57095
58623
  };
58624
+ var getInitials3 = (name) => {
58625
+ if (!name) return "??";
58626
+ const parts = name.trim().split(" ");
58627
+ if (parts.length >= 2) {
58628
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
58629
+ }
58630
+ return name.substring(0, 2).toUpperCase();
58631
+ };
57096
58632
  var chartCardVariants = {
57097
58633
  initial: { opacity: 0, y: 10 },
57098
58634
  animate: {
@@ -57174,7 +58710,7 @@ var WorkspaceDetailView = ({
57174
58710
  const prewarmedClipsRef = useRef(/* @__PURE__ */ new Set());
57175
58711
  const prewarmInFlightRef = useRef(/* @__PURE__ */ new Set());
57176
58712
  const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
57177
- dashboardConfig?.supervisorConfig?.enabled || false;
58713
+ const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
57178
58714
  const effectiveLineId = lineId || selectedLineId;
57179
58715
  const { shiftConfig, isLoading: isShiftConfigLoading, isFromDatabase: isShiftConfigFromDatabase } = useDynamicShiftConfig(effectiveLineId);
57180
58716
  const currentShiftDetails = useMemo(() => {
@@ -57225,6 +58761,116 @@ var WorkspaceDetailView = ({
57225
58761
  buildIndex: true,
57226
58762
  subscriberId: "workspace-detail-view"
57227
58763
  });
58764
+ const [prefetchedPercentileCounts, setPrefetchedPercentileCounts] = useState(null);
58765
+ const percentileCountsAbortRef = useRef(null);
58766
+ const percentileCountsInFlightRef = useRef(/* @__PURE__ */ new Set());
58767
+ const { percentileDate, percentileShiftId } = useMemo(() => {
58768
+ if (shift !== void 0 && shift !== null && date) {
58769
+ const shiftNumber = Number(shift);
58770
+ return {
58771
+ percentileDate: date,
58772
+ percentileShiftId: Number.isFinite(shiftNumber) ? shiftNumber : null
58773
+ };
58774
+ }
58775
+ if ((shift === void 0 || shift === null) && isShiftConfigLoading) {
58776
+ return { percentileDate: null, percentileShiftId: null };
58777
+ }
58778
+ const currentShift = getCurrentShift(timezone, shiftConfig);
58779
+ if (shift !== void 0 && shift !== null) {
58780
+ const shiftNumber = Number(shift);
58781
+ return {
58782
+ percentileDate: date || currentShift.date,
58783
+ percentileShiftId: Number.isFinite(shiftNumber) ? shiftNumber : null
58784
+ };
58785
+ }
58786
+ return {
58787
+ percentileDate: date || currentShift.date,
58788
+ percentileShiftId: currentShift.shiftId
58789
+ };
58790
+ }, [shift, date, timezone, shiftConfig, isShiftConfigLoading]);
58791
+ const percentileCountsKey = useMemo(() => {
58792
+ if (!workspaceId || !percentileDate || percentileShiftId === null || percentileShiftId === void 0) {
58793
+ return null;
58794
+ }
58795
+ return `${workspaceId}:${percentileDate}:${percentileShiftId.toString()}:10`;
58796
+ }, [workspaceId, percentileDate, percentileShiftId]);
58797
+ useEffect(() => {
58798
+ if (!percentileCountsKey) {
58799
+ setPrefetchedPercentileCounts(null);
58800
+ return;
58801
+ }
58802
+ if (prefetchedPercentileCounts && prefetchedPercentileCounts.key !== percentileCountsKey) {
58803
+ setPrefetchedPercentileCounts(null);
58804
+ }
58805
+ }, [percentileCountsKey, prefetchedPercentileCounts]);
58806
+ useEffect(() => {
58807
+ if (!percentileCountsKey || !percentileDate || percentileShiftId === null || percentileShiftId === void 0) {
58808
+ return;
58809
+ }
58810
+ if (prefetchedPercentileCounts?.key === percentileCountsKey) {
58811
+ return;
58812
+ }
58813
+ if (percentileCountsInFlightRef.current.has(percentileCountsKey)) {
58814
+ return;
58815
+ }
58816
+ percentileCountsInFlightRef.current.add(percentileCountsKey);
58817
+ const controller = new AbortController();
58818
+ percentileCountsAbortRef.current?.abort();
58819
+ percentileCountsAbortRef.current = controller;
58820
+ const fetchCounts = async () => {
58821
+ try {
58822
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
58823
+ method: "POST",
58824
+ headers: {
58825
+ "Content-Type": "application/json"
58826
+ },
58827
+ body: JSON.stringify({
58828
+ action: "percentile-counts",
58829
+ workspaceId,
58830
+ startDate: `${percentileDate}T00:00:00Z`,
58831
+ endDate: `${percentileDate}T23:59:59Z`,
58832
+ percentile: 10,
58833
+ shiftId: percentileShiftId
58834
+ }),
58835
+ signal: controller.signal,
58836
+ redirectReason: "session_expired"
58837
+ });
58838
+ if (!response.ok) {
58839
+ throw new Error(`API error: ${response.status}`);
58840
+ }
58841
+ const data = await response.json();
58842
+ const fastCycles = data?.counts?.["fast-cycles"];
58843
+ const slowCycles = data?.counts?.["slow-cycles"];
58844
+ setPrefetchedPercentileCounts({
58845
+ key: percentileCountsKey,
58846
+ percentile: 10,
58847
+ counts: {
58848
+ "fast-cycles": typeof fastCycles === "number" ? fastCycles : 0,
58849
+ "slow-cycles": typeof slowCycles === "number" ? slowCycles : 0
58850
+ }
58851
+ });
58852
+ } catch (error2) {
58853
+ if (error2.name === "AbortError") {
58854
+ return;
58855
+ }
58856
+ console.error("[WorkspaceDetailView] Error prefetching percentile counts:", error2);
58857
+ } finally {
58858
+ percentileCountsInFlightRef.current.delete(percentileCountsKey);
58859
+ }
58860
+ };
58861
+ if (typeof window !== "undefined" && "requestIdleCallback" in window) {
58862
+ window.requestIdleCallback(() => {
58863
+ fetchCounts();
58864
+ }, { timeout: 1e3 });
58865
+ } else {
58866
+ setTimeout(() => {
58867
+ fetchCounts();
58868
+ }, 0);
58869
+ }
58870
+ return () => {
58871
+ controller.abort();
58872
+ };
58873
+ }, [percentileCountsKey, percentileDate, percentileShiftId, workspaceId, supabase, prefetchedPercentileCounts]);
57228
58874
  const {
57229
58875
  metrics: historicMetrics,
57230
58876
  isLoading: historicLoading,
@@ -57373,7 +59019,7 @@ var WorkspaceDetailView = ({
57373
59019
  timezone,
57374
59020
  workspace
57375
59021
  ]);
57376
- const { supervisorName } = useLineSupervisor(workspace?.line_id || lineId);
59022
+ const { supervisorName, supervisors } = useLineSupervisor(workspace?.line_id || lineId);
57377
59023
  useEffect(() => {
57378
59024
  if (onTabChange) {
57379
59025
  onTabChange(activeTab);
@@ -57430,7 +59076,7 @@ var WorkspaceDetailView = ({
57430
59076
  enabled: idleTimeReasonsEnabled,
57431
59077
  keepPreviousData: false
57432
59078
  });
57433
- const idleTimeChartKey = useMemo(() => {
59079
+ useMemo(() => {
57434
59080
  return `${idleTimeReasonsDate || "date"}_${idleTimeReasonsShiftId ?? "shift"}`;
57435
59081
  }, [idleTimeReasonsDate, idleTimeReasonsShiftId]);
57436
59082
  const idleTimeData = useMemo(() => ({
@@ -57696,15 +59342,28 @@ var WorkspaceDetailView = ({
57696
59342
  "aria-label": "Navigate back to previous page"
57697
59343
  }
57698
59344
  ) }),
57699
- /* @__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: [
57700
- /* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
57701
- /* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
57702
- isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
57703
- /* @__PURE__ */ jsx("span", { className: clsx(
57704
- "relative inline-flex rounded-full h-2.5 w-2.5",
57705
- isLive ? "bg-green-500" : "bg-red-500"
57706
- ) })
57707
- ] })
59345
+ /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 max-w-[calc(100%-200px)]", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
59346
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
59347
+ /* @__PURE__ */ jsx("h1", { className: "text-lg md:text-xl lg:text-2xl xl:text-3xl font-semibold text-gray-900 truncate", children: formattedWorkspaceName }),
59348
+ /* @__PURE__ */ jsxs("div", { className: "relative flex h-2.5 w-2.5", children: [
59349
+ isLive && /* @__PURE__ */ jsx("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
59350
+ /* @__PURE__ */ jsx("span", { className: clsx(
59351
+ "relative inline-flex rounded-full h-2.5 w-2.5",
59352
+ isLive ? "bg-green-500" : "bg-red-500"
59353
+ ) })
59354
+ ] })
59355
+ ] }),
59356
+ supervisorEnabled && supervisors && supervisors.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 justify-center", children: supervisors.map((supervisor) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 bg-gray-50 rounded-full pl-0.5 pr-2.5 py-0.5 border border-gray-100 hover:border-gray-200 transition-all duration-300 ease-out group/supervisor hover:scale-150 hover:z-50 hover:shadow-lg relative", children: [
59357
+ /* @__PURE__ */ jsx("div", { className: "w-6 h-6 rounded-full overflow-hidden bg-white border border-gray-100 shadow-sm flex-shrink-0", children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsx(
59358
+ "img",
59359
+ {
59360
+ src: supervisor.profilePhotoUrl,
59361
+ alt: supervisor.displayName,
59362
+ className: "w-full h-full object-cover"
59363
+ }
59364
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 text-[9px] font-bold text-gray-500 uppercase", children: getInitials3(supervisor.displayName) }) }),
59365
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700 truncate max-w-[120px]", children: supervisor.displayName })
59366
+ ] }, supervisor.userId)) })
57708
59367
  ] }) }),
57709
59368
  activeTab !== "monthly_history" && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-0 flex flex-col items-end gap-1", children: workspaceHealth && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500", children: [
57710
59369
  "Last update: ",
@@ -57990,8 +59649,7 @@ var WorkspaceDetailView = ({
57990
59649
  data: idleTimeData.chartData,
57991
59650
  isLoading: idleTimeData.isLoading,
57992
59651
  error: idleTimeData.error
57993
- },
57994
- idleTimeChartKey
59652
+ }
57995
59653
  ) })
57996
59654
  ]
57997
59655
  }
@@ -58125,8 +59783,7 @@ var WorkspaceDetailView = ({
58125
59783
  data: idleTimeData.chartData,
58126
59784
  isLoading: idleTimeData.isLoading,
58127
59785
  error: idleTimeData.error
58128
- },
58129
- idleTimeChartKey
59786
+ }
58130
59787
  ) })
58131
59788
  ]
58132
59789
  }
@@ -58237,6 +59894,7 @@ var WorkspaceDetailView = ({
58237
59894
  shift,
58238
59895
  totalOutput: workspace?.total_actions,
58239
59896
  workspaceMetrics: detailedWorkspaceMetrics || void 0,
59897
+ prefetchedPercentileCounts,
58240
59898
  className: "h-[calc(100vh-10rem)]"
58241
59899
  }
58242
59900
  ) })
@@ -59486,13 +61144,14 @@ var TeamManagementView = ({
59486
61144
  const [stats, setStats] = useState({
59487
61145
  totalUsers: 0,
59488
61146
  owners: 0,
61147
+ it: 0,
59489
61148
  plantHeads: 0,
59490
61149
  supervisors: 0,
59491
61150
  optifye: 0
59492
61151
  });
59493
61152
  const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
59494
- const canAddUsers = user?.role_level === "owner" || user?.role_level === "optifye";
59495
- const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "optifye";
61153
+ const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
61154
+ const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
59496
61155
  const companyIdForUsage = entityConfig?.companyId || user?.properties?.company_id;
59497
61156
  const usageDateRange = useMemo(() => {
59498
61157
  const today = /* @__PURE__ */ new Date();
@@ -59656,6 +61315,7 @@ var TeamManagementView = ({
59656
61315
  setStats({
59657
61316
  totalUsers: userStats.total,
59658
61317
  owners: userStats.owners,
61318
+ it: userStats.it,
59659
61319
  plantHeads: userStats.plant_heads,
59660
61320
  supervisors: userStats.supervisors,
59661
61321
  optifye: userStats.optifye
@@ -59669,6 +61329,7 @@ var TeamManagementView = ({
59669
61329
  setStats({
59670
61330
  totalUsers: userStats.total,
59671
61331
  owners: userStats.owners,
61332
+ it: userStats.it,
59672
61333
  plantHeads: userStats.plant_heads,
59673
61334
  supervisors: userStats.supervisors,
59674
61335
  optifye: 0
@@ -59753,19 +61414,20 @@ var TeamManagementView = ({
59753
61414
  toast.error("Failed to update factory assignments");
59754
61415
  }
59755
61416
  }, [supabase, user, loadData]);
59756
- const handleNameUpdate = useCallback(async (userId, firstName, lastName) => {
61417
+ const handleProfileUpdate = useCallback(async (userId, firstName, lastName, profilePhotoUrl) => {
59757
61418
  if (!supabase || !user) return;
59758
61419
  try {
59759
61420
  const userManagementService = createUserManagementService(supabase);
59760
- await userManagementService.updateUserName({
61421
+ await userManagementService.updateUserProfile({
59761
61422
  user_id: userId,
59762
61423
  first_name: firstName,
59763
61424
  last_name: lastName,
61425
+ profile_photo_url: profilePhotoUrl,
59764
61426
  updated_by: user.id
59765
61427
  });
59766
61428
  loadData();
59767
61429
  } catch (err) {
59768
- console.error("Error updating user name:", err);
61430
+ console.error("Error updating user profile:", err);
59769
61431
  throw err;
59770
61432
  }
59771
61433
  }, [supabase, user, loadData]);
@@ -59876,7 +61538,7 @@ var TeamManagementView = ({
59876
61538
  onRemoveUser: handleRemoveUser,
59877
61539
  onLineAssignmentUpdate: handleLineAssignmentUpdate,
59878
61540
  onFactoryAssignmentUpdate: handleFactoryAssignmentUpdate,
59879
- onNameUpdate: handleNameUpdate,
61541
+ onProfileUpdate: handleProfileUpdate,
59880
61542
  availableLines,
59881
61543
  availableFactories,
59882
61544
  avgDailyUsage: avgDailyUsageMap,
@@ -61457,13 +63119,14 @@ var ImprovementCenterView = () => {
61457
63119
  const supervisors = await userService2.getCompanyUsers(companyId, "supervisor");
61458
63120
  if (cancelled) return;
61459
63121
  const members = supervisors.map((user2) => {
61460
- const rawName = user2.properties?.full_name || user2.email?.split("@")[0] || "Unknown";
63122
+ const rawName = user2.first_name ? `${user2.first_name}${user2.last_name ? " " + user2.last_name : ""}` : user2.properties?.full_name || user2.email?.split("@")[0] || "Unknown";
61461
63123
  return {
61462
63124
  id: user2.user_id,
61463
63125
  name: rawName,
61464
63126
  role: user2.role_level,
61465
63127
  initials: buildInitials(rawName),
61466
- email: user2.email
63128
+ email: user2.email,
63129
+ profilePhotoUrl: user2.profile_photo_url
61467
63130
  };
61468
63131
  });
61469
63132
  members.sort((a, b) => a.name.localeCompare(b.name));
@@ -61804,7 +63467,16 @@ var ImprovementCenterView = () => {
61804
63467
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: rec.assigned_user_ids.map((uid) => {
61805
63468
  const user2 = teamMembersById.get(uid);
61806
63469
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
61807
- /* @__PURE__ */ jsx("div", { className: "w-6 h-6 rounded-full bg-gray-100 text-gray-600 flex items-center justify-center text-[10px] font-bold border border-gray-200", children: user2?.initials || "??" }),
63470
+ /* @__PURE__ */ jsx(
63471
+ UserAvatar,
63472
+ {
63473
+ imageUrl: user2?.profilePhotoUrl,
63474
+ name: user2?.name,
63475
+ email: user2?.email,
63476
+ size: "sm",
63477
+ className: "border border-gray-200"
63478
+ }
63479
+ ),
61808
63480
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-600", children: user2?.name || "Unknown" })
61809
63481
  ] }, uid);
61810
63482
  }) })
@@ -64399,4 +66071,4 @@ var streamProxyConfig = {
64399
66071
  }
64400
66072
  };
64401
66073
 
64402
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
66074
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, setSentryUserContext, setSentryWorkspaceContext, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };