@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.css +506 -11
- package/dist/index.d.mts +236 -20
- package/dist/index.d.ts +236 -20
- package/dist/index.js +2175 -490
- package/dist/index.mjs +2164 -492
- package/package.json +1 -1
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
|
|
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
|
|
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 (
|
|
7538
|
-
console.
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
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
|
|
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 &&
|
|
7898
|
+
if (minutesUntilExpiry < 5 && timeUntilExpiry > 0) {
|
|
7625
7899
|
console.warn("[AuthContext] Token expiring soon, attempting refresh...");
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
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
|
|
7635
|
-
console.
|
|
7636
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
15860
|
+
if (!enabled || idleClipIds.length === 0) {
|
|
15557
15861
|
return;
|
|
15558
15862
|
}
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
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
|
|
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: `${
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
34053
|
-
|
|
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 :
|
|
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 :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
36423
|
+
const getAuthToken3 = useCallback(async () => {
|
|
35663
36424
|
try {
|
|
35664
|
-
|
|
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
|
|
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,
|
|
36479
|
+
}, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady]);
|
|
35725
36480
|
useEffect(() => {
|
|
35726
|
-
if (!triageMode || triageClips.length === 0
|
|
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
|
-
|
|
35736
|
-
|
|
35737
|
-
|
|
35738
|
-
|
|
35739
|
-
|
|
35740
|
-
|
|
35741
|
-
|
|
35742
|
-
|
|
35743
|
-
|
|
35744
|
-
|
|
35745
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
36055
|
-
if (metadataArray.length === 0 || !
|
|
36056
|
-
|
|
36057
|
-
|
|
36058
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
36372
|
-
|
|
36373
|
-
|
|
36374
|
-
|
|
36375
|
-
|
|
36376
|
-
|
|
36377
|
-
|
|
36378
|
-
|
|
36379
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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: `${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
46912
|
-
const
|
|
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
|
|
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("
|
|
49026
|
+
toast.success("Profile updated successfully");
|
|
47680
49027
|
onClose();
|
|
47681
49028
|
} catch (err) {
|
|
47682
|
-
console.error("Error updating user
|
|
47683
|
-
setError(err.message || "Failed to update
|
|
47684
|
-
toast.error(err.message || "Failed to update
|
|
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
|
|
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
|
-
|
|
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
|
|
50217
|
+
const handleEditProfile = (user) => {
|
|
48852
50218
|
setSelectedUser(user);
|
|
48853
50219
|
setShowEditNameDialog(true);
|
|
48854
50220
|
setOpenActionMenuId(null);
|
|
48855
50221
|
};
|
|
48856
|
-
const
|
|
48857
|
-
if (
|
|
48858
|
-
await
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
50497
|
+
const hasEditProfileAbove = !!onProfileUpdate && canChangeRole;
|
|
49120
50498
|
return canChangeRole && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49121
|
-
canShowUsage && !
|
|
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
|
-
|
|
50572
|
+
EditUserProfileDialog,
|
|
49195
50573
|
{
|
|
49196
50574
|
isOpen: showEditNameDialog,
|
|
49197
50575
|
onClose: () => {
|
|
49198
50576
|
setShowEditNameDialog(false);
|
|
49199
50577
|
setSelectedUser(null);
|
|
49200
50578
|
},
|
|
49201
|
-
onSave:
|
|
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 (
|
|
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("
|
|
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
|
-
|
|
52942
|
-
|
|
52943
|
-
|
|
52944
|
-
|
|
52945
|
-
|
|
52946
|
-
|
|
52947
|
-
|
|
52948
|
-
|
|
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
|
-
|
|
52953
|
-
|
|
52954
|
-
|
|
52955
|
-
|
|
52956
|
-
|
|
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-
|
|
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
|
|
54212
|
-
const
|
|
54213
|
-
|
|
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("
|
|
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
|
-
|
|
54326
|
-
|
|
54327
|
-
|
|
54328
|
-
|
|
54329
|
-
|
|
54330
|
-
|
|
54331
|
-
|
|
54332
|
-
|
|
54333
|
-
|
|
54334
|
-
|
|
54335
|
-
|
|
54336
|
-
|
|
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
|
-
|
|
54352
|
-
|
|
54353
|
-
|
|
54354
|
-
|
|
54355
|
-
|
|
54356
|
-
|
|
54357
|
-
|
|
54358
|
-
|
|
54359
|
-
|
|
54360
|
-
}
|
|
54361
|
-
|
|
54362
|
-
|
|
54363
|
-
|
|
54364
|
-
|
|
54365
|
-
|
|
54366
|
-
|
|
54367
|
-
|
|
54368
|
-
|
|
54369
|
-
|
|
54370
|
-
|
|
54371
|
-
|
|
54372
|
-
|
|
54373
|
-
|
|
54374
|
-
|
|
54375
|
-
|
|
54376
|
-
|
|
54377
|
-
|
|
54378
|
-
|
|
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__ */
|
|
54382
|
-
|
|
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(
|
|
54387
|
-
className: `
|
|
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(
|
|
54390
|
-
|
|
55912
|
+
/* @__PURE__ */ jsx(Award, { className: "w-4 h-4" }),
|
|
55913
|
+
"Awards"
|
|
54391
55914
|
]
|
|
54392
|
-
}
|
|
54393
|
-
|
|
54394
|
-
|
|
54395
|
-
|
|
54396
|
-
|
|
54397
|
-
|
|
54398
|
-
|
|
54399
|
-
|
|
54400
|
-
|
|
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: "
|
|
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
|
-
|
|
54447
|
-
/* @__PURE__ */
|
|
54448
|
-
|
|
54449
|
-
/* @__PURE__ */
|
|
54450
|
-
|
|
54451
|
-
|
|
54452
|
-
|
|
54453
|
-
|
|
54454
|
-
|
|
54455
|
-
|
|
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__ */
|
|
54460
|
-
|
|
54461
|
-
|
|
54462
|
-
|
|
54463
|
-
|
|
54464
|
-
|
|
54465
|
-
|
|
54466
|
-
{
|
|
54467
|
-
|
|
54468
|
-
|
|
54469
|
-
|
|
54470
|
-
|
|
54471
|
-
|
|
54472
|
-
|
|
54473
|
-
|
|
54474
|
-
|
|
54475
|
-
|
|
54476
|
-
|
|
54477
|
-
|
|
54478
|
-
|
|
54479
|
-
|
|
54480
|
-
|
|
54481
|
-
|
|
54482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
57700
|
-
/* @__PURE__ */
|
|
57701
|
-
|
|
57702
|
-
|
|
57703
|
-
|
|
57704
|
-
|
|
57705
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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(
|
|
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 };
|