@optifye/dashboard-core 6.5.12 → 6.6.0
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 +376 -30
- package/dist/index.d.mts +562 -30
- package/dist/index.d.ts +562 -30
- package/dist/index.js +3192 -405
- package/dist/index.mjs +3173 -409
- package/global.css +58 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React19 from 'react';
|
|
2
|
-
import React19__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect,
|
|
2
|
+
import React19__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, forwardRef, useImperativeHandle, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect, Fragment as Fragment$1, createElement, Component } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { useRouter } from 'next/router';
|
|
5
5
|
import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
|
|
@@ -13,15 +13,16 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
|
|
|
13
13
|
import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
|
|
14
14
|
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
|
|
15
15
|
import { Slot } from '@radix-ui/react-slot';
|
|
16
|
-
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, Activity, Sun, Moon, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, UserCheck, LogOut, Package, TrendingUp, TrendingDown, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
|
|
16
|
+
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, ArrowLeft, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, Activity, Sun, Moon, MousePointer, ArrowRight, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, UserCheck, LogOut, Package, TrendingUp, TrendingDown, Building2, FolderOpen, Folder, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
|
|
17
17
|
import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
|
|
18
|
-
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
18
|
+
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
19
19
|
import { CheckIcon } from '@heroicons/react/24/solid';
|
|
20
20
|
import html2canvas from 'html2canvas';
|
|
21
21
|
import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
|
|
22
22
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
23
23
|
import videojs from 'video.js';
|
|
24
24
|
import 'video.js/dist/video-js.css';
|
|
25
|
+
import { createPortal } from 'react-dom';
|
|
25
26
|
import { toast } from 'sonner';
|
|
26
27
|
import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3';
|
|
27
28
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
@@ -4807,8 +4808,347 @@ var S3ClipsService = class {
|
|
|
4807
4808
|
return this.apiClient.getStats();
|
|
4808
4809
|
}
|
|
4809
4810
|
};
|
|
4810
|
-
|
|
4811
|
-
|
|
4811
|
+
var getSupabaseClient2 = () => {
|
|
4812
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
4813
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
4814
|
+
if (!url || !key) {
|
|
4815
|
+
throw new Error("Supabase configuration missing");
|
|
4816
|
+
}
|
|
4817
|
+
return createClient(url, key);
|
|
4818
|
+
};
|
|
4819
|
+
var getAuthToken2 = async () => {
|
|
4820
|
+
try {
|
|
4821
|
+
const supabase = getSupabaseClient2();
|
|
4822
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
4823
|
+
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4824
|
+
return session?.access_token || null;
|
|
4825
|
+
} catch (error) {
|
|
4826
|
+
console.error("[S3ClipsSupabase] Error getting auth token:", error);
|
|
4827
|
+
return null;
|
|
4828
|
+
}
|
|
4829
|
+
};
|
|
4830
|
+
var S3ClipsSupabaseService = class {
|
|
4831
|
+
constructor(config) {
|
|
4832
|
+
this.requestCache = /* @__PURE__ */ new Map();
|
|
4833
|
+
// Flags for compatibility
|
|
4834
|
+
this.isIndexBuilding = false;
|
|
4835
|
+
this.isPrefetching = false;
|
|
4836
|
+
this.currentMetadataFetches = 0;
|
|
4837
|
+
this.MAX_CONCURRENT_METADATA = 3;
|
|
4838
|
+
this.config = config;
|
|
4839
|
+
if (!config.s3Config) {
|
|
4840
|
+
throw new Error("S3 configuration is required");
|
|
4841
|
+
}
|
|
4842
|
+
const processing = config.s3Config.processing || {};
|
|
4843
|
+
this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
|
|
4844
|
+
this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
|
|
4845
|
+
this.concurrencyLimit = processing.concurrencyLimit || 10;
|
|
4846
|
+
this.maxInitialFetch = processing.maxInitialFetch || 60;
|
|
4847
|
+
console.log("[S3ClipsSupabase] \u2705 Initialized with Supabase backend - Direct database queries!");
|
|
4848
|
+
}
|
|
4849
|
+
/**
|
|
4850
|
+
* Fetch with authentication and error handling
|
|
4851
|
+
*/
|
|
4852
|
+
async fetchWithAuth(endpoint, body) {
|
|
4853
|
+
const token = await getAuthToken2();
|
|
4854
|
+
if (!token) {
|
|
4855
|
+
throw new Error("Authentication required");
|
|
4856
|
+
}
|
|
4857
|
+
const apiEndpoint = "/api/clips/supabase";
|
|
4858
|
+
const requestBody = {
|
|
4859
|
+
...body,
|
|
4860
|
+
action: endpoint.replace("/api/clips/supabase/", "")
|
|
4861
|
+
};
|
|
4862
|
+
console.log(`[S3ClipsSupabase] Making request to ${apiEndpoint} with action: ${requestBody.action}, body:`, requestBody);
|
|
4863
|
+
const response = await fetch(apiEndpoint, {
|
|
4864
|
+
method: "POST",
|
|
4865
|
+
headers: {
|
|
4866
|
+
"Authorization": `Bearer ${token}`,
|
|
4867
|
+
"Content-Type": "application/json"
|
|
4868
|
+
},
|
|
4869
|
+
body: JSON.stringify(requestBody)
|
|
4870
|
+
});
|
|
4871
|
+
console.log(`[S3ClipsSupabase] Response status: ${response.status}`);
|
|
4872
|
+
if (!response.ok) {
|
|
4873
|
+
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
4874
|
+
console.error(`[S3ClipsSupabase] API error:`, error);
|
|
4875
|
+
throw new Error(error.error || `API error: ${response.status}`);
|
|
4876
|
+
}
|
|
4877
|
+
const data = await response.json();
|
|
4878
|
+
if (requestBody.action === "by-index" || requestBody.action === "batch") {
|
|
4879
|
+
console.log(`[S3ClipsSupabase] API Response for ${requestBody.action}:`, {
|
|
4880
|
+
action: requestBody.action,
|
|
4881
|
+
hasData: !!data,
|
|
4882
|
+
dataKeys: Object.keys(data || {}),
|
|
4883
|
+
video: requestBody.action === "by-index" ? data?.video : void 0,
|
|
4884
|
+
videosCount: requestBody.action === "batch" ? data?.videos?.length : void 0
|
|
4885
|
+
});
|
|
4886
|
+
} else if (requestBody.action === "clip-types") {
|
|
4887
|
+
console.log(`[S3ClipsSupabase] API Response for clip-types:`, {
|
|
4888
|
+
action: requestBody.action,
|
|
4889
|
+
hasData: !!data,
|
|
4890
|
+
dataKeys: Object.keys(data || {}),
|
|
4891
|
+
clipTypesCount: data?.clipTypes?.length,
|
|
4892
|
+
clipTypes: data?.clipTypes
|
|
4893
|
+
});
|
|
4894
|
+
} else if (requestBody.action === "count") {
|
|
4895
|
+
console.log(`[S3ClipsSupabase] API Response for count:`, {
|
|
4896
|
+
action: requestBody.action,
|
|
4897
|
+
hasData: !!data,
|
|
4898
|
+
counts: data?.counts
|
|
4899
|
+
});
|
|
4900
|
+
}
|
|
4901
|
+
return data;
|
|
4902
|
+
}
|
|
4903
|
+
/**
|
|
4904
|
+
* Deduplicate requests to prevent multiple API calls
|
|
4905
|
+
*/
|
|
4906
|
+
async deduplicate(key, factory) {
|
|
4907
|
+
if (this.requestCache.has(key)) {
|
|
4908
|
+
console.log(`[S3ClipsSupabase] Deduplicating request: ${key}`);
|
|
4909
|
+
return this.requestCache.get(key);
|
|
4910
|
+
}
|
|
4911
|
+
const promise = factory().finally(() => {
|
|
4912
|
+
this.requestCache.delete(key);
|
|
4913
|
+
});
|
|
4914
|
+
this.requestCache.set(key, promise);
|
|
4915
|
+
return promise;
|
|
4916
|
+
}
|
|
4917
|
+
/**
|
|
4918
|
+
* Lists clips using Supabase API
|
|
4919
|
+
*/
|
|
4920
|
+
async listS3Clips(params) {
|
|
4921
|
+
const { workspaceId, date, shiftId } = params;
|
|
4922
|
+
if (!isValidShiftId(shiftId)) {
|
|
4923
|
+
console.error(`[S3ClipsSupabase] Invalid shift ID: ${shiftId}`);
|
|
4924
|
+
return [];
|
|
4925
|
+
}
|
|
4926
|
+
console.log(`[S3ClipsSupabase] Listing clips via Supabase for workspace: ${workspaceId}`);
|
|
4927
|
+
try {
|
|
4928
|
+
const response = await this.fetchWithAuth("list", {
|
|
4929
|
+
workspaceId,
|
|
4930
|
+
date,
|
|
4931
|
+
shift: shiftId,
|
|
4932
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
4933
|
+
});
|
|
4934
|
+
return response.clips.map((clip) => clip.originalUri || `clips:${clip.id}`);
|
|
4935
|
+
} catch (error) {
|
|
4936
|
+
console.error("[S3ClipsSupabase] Error listing clips:", error);
|
|
4937
|
+
return [];
|
|
4938
|
+
}
|
|
4939
|
+
}
|
|
4940
|
+
/**
|
|
4941
|
+
* Get metadata cycle time
|
|
4942
|
+
*/
|
|
4943
|
+
async getMetadataCycleTime(clipId) {
|
|
4944
|
+
const id3 = clipId.startsWith("clips:") ? clipId.substring(6) : clipId;
|
|
4945
|
+
try {
|
|
4946
|
+
console.log(`[S3ClipsSupabase] Metadata cycle time requested for clip: ${id3}`);
|
|
4947
|
+
return null;
|
|
4948
|
+
} catch (error) {
|
|
4949
|
+
console.error("[S3ClipsSupabase] Error fetching metadata cycle time:", error);
|
|
4950
|
+
return null;
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
/**
|
|
4954
|
+
* Control prefetch mode
|
|
4955
|
+
*/
|
|
4956
|
+
setPrefetchMode(enabled) {
|
|
4957
|
+
this.isPrefetching = enabled;
|
|
4958
|
+
console.log(`[S3ClipsSupabase] Prefetch mode ${enabled ? "enabled" : "disabled"}`);
|
|
4959
|
+
}
|
|
4960
|
+
/**
|
|
4961
|
+
* Get full metadata
|
|
4962
|
+
*/
|
|
4963
|
+
async getFullMetadata(clipId) {
|
|
4964
|
+
if (this.isIndexBuilding || this.isPrefetching) {
|
|
4965
|
+
console.warn("[S3ClipsSupabase] Skipping metadata - operation in progress");
|
|
4966
|
+
return null;
|
|
4967
|
+
}
|
|
4968
|
+
return null;
|
|
4969
|
+
}
|
|
4970
|
+
/**
|
|
4971
|
+
* Get clip counts with optional video index
|
|
4972
|
+
*/
|
|
4973
|
+
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false) {
|
|
4974
|
+
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4975
|
+
return this.deduplicate(cacheKey, async () => {
|
|
4976
|
+
console.log(`[S3ClipsSupabase] Fetching clip counts from Supabase for:`, {
|
|
4977
|
+
workspaceId,
|
|
4978
|
+
date,
|
|
4979
|
+
shift: shiftId
|
|
4980
|
+
});
|
|
4981
|
+
const response = await this.fetchWithAuth("count", {
|
|
4982
|
+
workspaceId,
|
|
4983
|
+
date,
|
|
4984
|
+
shift: shiftId.toString()
|
|
4985
|
+
});
|
|
4986
|
+
console.log(`[S3ClipsSupabase] Count API response:`, response);
|
|
4987
|
+
const counts = response.counts || {};
|
|
4988
|
+
console.log(`[S3ClipsSupabase] Extracted counts:`, counts);
|
|
4989
|
+
if (buildIndex) {
|
|
4990
|
+
const videoIndex = {
|
|
4991
|
+
byCategory: /* @__PURE__ */ new Map(),
|
|
4992
|
+
allVideos: [],
|
|
4993
|
+
counts,
|
|
4994
|
+
workspaceId,
|
|
4995
|
+
date,
|
|
4996
|
+
shiftId: shiftId.toString(),
|
|
4997
|
+
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4998
|
+
_debugId: `supabase_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
4999
|
+
};
|
|
5000
|
+
if (buildIndex) {
|
|
5001
|
+
const categories = Object.keys(counts).filter((k) => k !== "total");
|
|
5002
|
+
for (const category of categories) {
|
|
5003
|
+
if (counts[category] > 0) {
|
|
5004
|
+
const categoryResponse = await this.fetchWithAuth("list", {
|
|
5005
|
+
workspaceId,
|
|
5006
|
+
date,
|
|
5007
|
+
shift: shiftId.toString(),
|
|
5008
|
+
category,
|
|
5009
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5010
|
+
});
|
|
5011
|
+
const entries = categoryResponse.clips.map((clip) => ({
|
|
5012
|
+
uri: clip.originalUri || `clips:${clip.id}`,
|
|
5013
|
+
category,
|
|
5014
|
+
timestamp: clip.timestamp,
|
|
5015
|
+
videoId: clip.id,
|
|
5016
|
+
workspaceId,
|
|
5017
|
+
date,
|
|
5018
|
+
shiftId: shiftId.toString()
|
|
5019
|
+
}));
|
|
5020
|
+
videoIndex.byCategory.set(category, entries);
|
|
5021
|
+
videoIndex.allVideos.push(...entries);
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
return {
|
|
5026
|
+
counts,
|
|
5027
|
+
videoIndex
|
|
5028
|
+
};
|
|
5029
|
+
}
|
|
5030
|
+
return counts;
|
|
5031
|
+
});
|
|
5032
|
+
}
|
|
5033
|
+
/**
|
|
5034
|
+
* Get clip counts (simplified version)
|
|
5035
|
+
*/
|
|
5036
|
+
async getClipCounts(workspaceId, date, shiftId) {
|
|
5037
|
+
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false);
|
|
5038
|
+
if (typeof result === "object" && "counts" in result) {
|
|
5039
|
+
return result.counts;
|
|
5040
|
+
}
|
|
5041
|
+
return result;
|
|
5042
|
+
}
|
|
5043
|
+
/**
|
|
5044
|
+
* Get clip by index
|
|
5045
|
+
*/
|
|
5046
|
+
async getClipByIndex(workspaceId, date, shiftId, category, index) {
|
|
5047
|
+
const cacheKey = `clip:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
5048
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5049
|
+
console.log(`[S3ClipsSupabase] Fetching clip by index from Supabase for category: ${category}, index: ${index}`);
|
|
5050
|
+
const response = await this.fetchWithAuth("by-index", {
|
|
5051
|
+
workspaceId,
|
|
5052
|
+
date,
|
|
5053
|
+
shift: shiftId.toString(),
|
|
5054
|
+
category,
|
|
5055
|
+
index,
|
|
5056
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5057
|
+
});
|
|
5058
|
+
const video = response.video;
|
|
5059
|
+
console.log("[S3ClipsSupabase] getClipByIndex response:", {
|
|
5060
|
+
hasVideo: !!video,
|
|
5061
|
+
hasSrc: !!video?.src,
|
|
5062
|
+
srcType: typeof video?.src,
|
|
5063
|
+
srcPreview: video?.src ? video.src.substring(0, 50) : "no src",
|
|
5064
|
+
isProxyUrl: video?.src?.includes("/api/clips/stream/")
|
|
5065
|
+
});
|
|
5066
|
+
return video || null;
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
/**
|
|
5070
|
+
* Get first clip for all categories
|
|
5071
|
+
*/
|
|
5072
|
+
async getFirstClipsForAllCategories(workspaceId, date, shiftId) {
|
|
5073
|
+
console.log(`[S3ClipsSupabase] Getting first clips for all categories`);
|
|
5074
|
+
const counts = await this.getClipCounts(workspaceId, date, shiftId);
|
|
5075
|
+
const categories = Object.keys(counts).filter((k) => k !== "total" && counts[k] > 0);
|
|
5076
|
+
const promises = categories.map(async (category) => {
|
|
5077
|
+
const clip = await this.getClipByIndex(workspaceId, date, shiftId, category, 0);
|
|
5078
|
+
return { category, clip };
|
|
5079
|
+
});
|
|
5080
|
+
const results = await Promise.all(promises);
|
|
5081
|
+
const firstClips = {};
|
|
5082
|
+
for (const { category, clip } of results) {
|
|
5083
|
+
firstClips[category] = clip;
|
|
5084
|
+
}
|
|
5085
|
+
return firstClips;
|
|
5086
|
+
}
|
|
5087
|
+
/**
|
|
5088
|
+
* Get first clip for a specific category
|
|
5089
|
+
*/
|
|
5090
|
+
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
5091
|
+
return this.getClipByIndex(workspaceId, date, shiftId, category, 0);
|
|
5092
|
+
}
|
|
5093
|
+
/**
|
|
5094
|
+
* Batch fetch videos (alias for batchFetchClips for compatibility)
|
|
5095
|
+
*/
|
|
5096
|
+
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
5097
|
+
return this.batchFetchClips(workspaceId, date, shiftId, requests);
|
|
5098
|
+
}
|
|
5099
|
+
/**
|
|
5100
|
+
* Batch fetch clips
|
|
5101
|
+
*/
|
|
5102
|
+
async batchFetchClips(workspaceId, date, shiftId, requests) {
|
|
5103
|
+
const cacheKey = `batch:${workspaceId}:${date}:${shiftId}:${JSON.stringify(requests)}`;
|
|
5104
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5105
|
+
console.log(`[S3ClipsSupabase] Batch fetching ${requests.length} clips from Supabase`);
|
|
5106
|
+
const response = await this.fetchWithAuth("batch", {
|
|
5107
|
+
workspaceId,
|
|
5108
|
+
date,
|
|
5109
|
+
shift: shiftId.toString(),
|
|
5110
|
+
requests,
|
|
5111
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5112
|
+
});
|
|
5113
|
+
console.log("[S3ClipsSupabase] batchFetchClips response:", {
|
|
5114
|
+
videoCount: response.videos?.length,
|
|
5115
|
+
firstVideo: response.videos?.[0],
|
|
5116
|
+
hasProxyUrls: response.videos?.[0]?.video?.src?.includes("/api/clips/stream/")
|
|
5117
|
+
});
|
|
5118
|
+
return response.videos.map((v) => v.video);
|
|
5119
|
+
});
|
|
5120
|
+
}
|
|
5121
|
+
/**
|
|
5122
|
+
* Get all clip types from Supabase
|
|
5123
|
+
*/
|
|
5124
|
+
async getClipTypes() {
|
|
5125
|
+
const cacheKey = "clip-types:all";
|
|
5126
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5127
|
+
console.log(`[S3ClipsSupabase] Fetching clip types from Supabase`);
|
|
5128
|
+
const response = await this.fetchWithAuth("clip-types", {});
|
|
5129
|
+
console.log(`[S3ClipsSupabase] Fetched ${response.clipTypes?.length || 0} clip types:`, response.clipTypes);
|
|
5130
|
+
return response.clipTypes || [];
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
/**
|
|
5134
|
+
* Ensure videos are loaded for navigation
|
|
5135
|
+
*/
|
|
5136
|
+
async ensureVideosLoaded(workspaceId, date, shiftId, category, currentIndex) {
|
|
5137
|
+
const rangeBefore = 1;
|
|
5138
|
+
const rangeAfter = 3;
|
|
5139
|
+
const requests = [];
|
|
5140
|
+
for (let i = Math.max(0, currentIndex - rangeBefore); i < currentIndex; i++) {
|
|
5141
|
+
requests.push({ category, index: i });
|
|
5142
|
+
}
|
|
5143
|
+
for (let i = currentIndex; i <= currentIndex + rangeAfter; i++) {
|
|
5144
|
+
requests.push({ category, index: i });
|
|
5145
|
+
}
|
|
5146
|
+
if (requests.length > 0) {
|
|
5147
|
+
await this.batchFetchClips(workspaceId, date, shiftId, requests);
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
};
|
|
5151
|
+
var S3ClipsService2 = S3ClipsSupabaseService ;
|
|
4812
5152
|
var VideoPrefetchManager = class extends EventEmitter {
|
|
4813
5153
|
constructor() {
|
|
4814
5154
|
super();
|
|
@@ -4837,7 +5177,7 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
4837
5177
|
getS3Service(dashboardConfig) {
|
|
4838
5178
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
4839
5179
|
if (!this.s3Services.has(configKey)) {
|
|
4840
|
-
this.s3Services.set(configKey, new
|
|
5180
|
+
this.s3Services.set(configKey, new S3ClipsService2(dashboardConfig));
|
|
4841
5181
|
}
|
|
4842
5182
|
return this.s3Services.get(configKey);
|
|
4843
5183
|
}
|
|
@@ -5221,25 +5561,387 @@ if (typeof window !== "undefined") {
|
|
|
5221
5561
|
videoPrefetchManager.destroy();
|
|
5222
5562
|
});
|
|
5223
5563
|
}
|
|
5564
|
+
|
|
5565
|
+
// src/lib/services/linesService.ts
|
|
5566
|
+
var LinesService = class {
|
|
5567
|
+
constructor(supabase) {
|
|
5568
|
+
this.supabase = supabase;
|
|
5569
|
+
}
|
|
5570
|
+
/**
|
|
5571
|
+
* Fetch all active lines for a given company
|
|
5572
|
+
* @param companyId - The company ID to fetch lines for
|
|
5573
|
+
* @returns Promise<Line[]> - Array of lines for the company
|
|
5574
|
+
*/
|
|
5575
|
+
async getLinesByCompanyId(companyId) {
|
|
5576
|
+
if (!companyId) {
|
|
5577
|
+
throw new Error("Company ID is required");
|
|
5578
|
+
}
|
|
5579
|
+
const { data, error } = await this.supabase.from("lines").select("id, company_id, line_name, enable, created_at, factory_id").eq("company_id", companyId).eq("enable", true).order("line_name", { ascending: true });
|
|
5580
|
+
if (error) {
|
|
5581
|
+
console.error("Error fetching lines:", error);
|
|
5582
|
+
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
5583
|
+
}
|
|
5584
|
+
if (!data) {
|
|
5585
|
+
return [];
|
|
5586
|
+
}
|
|
5587
|
+
return data.map((line) => ({
|
|
5588
|
+
id: line.id,
|
|
5589
|
+
name: line.line_name,
|
|
5590
|
+
companyId: line.company_id,
|
|
5591
|
+
isActive: line.enable,
|
|
5592
|
+
createdAt: line.created_at,
|
|
5593
|
+
factoryId: line.factory_id
|
|
5594
|
+
}));
|
|
5595
|
+
}
|
|
5596
|
+
/**
|
|
5597
|
+
* Fetch all active lines (for all companies)
|
|
5598
|
+
* @returns Promise<Line[]> - Array of all active lines
|
|
5599
|
+
*/
|
|
5600
|
+
async getAllLines() {
|
|
5601
|
+
const { data, error } = await this.supabase.from("lines").select("id, company_id, line_name, enable, created_at, factory_id").eq("enable", true).order("line_name", { ascending: true });
|
|
5602
|
+
if (error) {
|
|
5603
|
+
console.error("Error fetching all lines:", error);
|
|
5604
|
+
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
5605
|
+
}
|
|
5606
|
+
if (!data) {
|
|
5607
|
+
return [];
|
|
5608
|
+
}
|
|
5609
|
+
return data.map((line) => ({
|
|
5610
|
+
id: line.id,
|
|
5611
|
+
name: line.line_name,
|
|
5612
|
+
companyId: line.company_id,
|
|
5613
|
+
isActive: line.enable,
|
|
5614
|
+
createdAt: line.created_at,
|
|
5615
|
+
factoryId: line.factory_id
|
|
5616
|
+
}));
|
|
5617
|
+
}
|
|
5618
|
+
/**
|
|
5619
|
+
* Fetch a single line by ID
|
|
5620
|
+
* @param lineId - The line ID to fetch
|
|
5621
|
+
* @returns Promise<Line | null> - The line data or null if not found
|
|
5622
|
+
*/
|
|
5623
|
+
async getLineById(lineId) {
|
|
5624
|
+
if (!lineId) {
|
|
5625
|
+
throw new Error("Line ID is required");
|
|
5626
|
+
}
|
|
5627
|
+
const { data, error } = await this.supabase.from("lines").select("id, company_id, line_name, enable, created_at, factory_id").eq("id", lineId).eq("enable", true).single();
|
|
5628
|
+
if (error) {
|
|
5629
|
+
if (error.code === "PGRST116") {
|
|
5630
|
+
return null;
|
|
5631
|
+
}
|
|
5632
|
+
console.error("Error fetching line:", error);
|
|
5633
|
+
throw new Error(`Failed to fetch line: ${error.message}`);
|
|
5634
|
+
}
|
|
5635
|
+
if (!data) {
|
|
5636
|
+
return null;
|
|
5637
|
+
}
|
|
5638
|
+
return {
|
|
5639
|
+
id: data.id,
|
|
5640
|
+
name: data.line_name,
|
|
5641
|
+
companyId: data.company_id,
|
|
5642
|
+
isActive: data.enable,
|
|
5643
|
+
createdAt: data.created_at,
|
|
5644
|
+
factoryId: data.factory_id
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5647
|
+
};
|
|
5648
|
+
var createLinesService = (supabase) => {
|
|
5649
|
+
return new LinesService(supabase);
|
|
5650
|
+
};
|
|
5651
|
+
var linesService = {
|
|
5652
|
+
create: createLinesService
|
|
5653
|
+
};
|
|
5654
|
+
|
|
5655
|
+
// src/lib/services/userService.ts
|
|
5656
|
+
var UserService = class {
|
|
5657
|
+
constructor(supabase) {
|
|
5658
|
+
this.supabase = supabase;
|
|
5659
|
+
}
|
|
5660
|
+
/**
|
|
5661
|
+
* Fetch all users mapped to a specific company
|
|
5662
|
+
* @param companyId - The company ID to fetch users for
|
|
5663
|
+
* @returns Promise<CompanyUser[]> - Array of users in the company
|
|
5664
|
+
*/
|
|
5665
|
+
async getUsersByCompanyId(companyId) {
|
|
5666
|
+
if (!companyId) {
|
|
5667
|
+
throw new Error("Company ID is required");
|
|
5668
|
+
}
|
|
5669
|
+
try {
|
|
5670
|
+
console.log(`[UserService] getUsersByCompanyId called with companyId: ${companyId}`);
|
|
5671
|
+
const result = await this.getUsersByCompanyIdFallback(companyId);
|
|
5672
|
+
console.log(`[UserService] getUsersByCompanyId returning ${result.length} users:`, result.map((u) => ({ id: u.id, name: u.name })));
|
|
5673
|
+
return result;
|
|
5674
|
+
} catch (error) {
|
|
5675
|
+
console.error("Error in getUsersByCompanyId:", error);
|
|
5676
|
+
throw error;
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5679
|
+
/**
|
|
5680
|
+
* Fallback method to fetch users by company ID using basic queries
|
|
5681
|
+
* Gets real user emails from the database
|
|
5682
|
+
* @param companyId - The company ID to fetch users for
|
|
5683
|
+
* @returns Promise<CompanyUser[]> - Array of users in the company
|
|
5684
|
+
*/
|
|
5685
|
+
async getUsersByCompanyIdFallback(companyId) {
|
|
5686
|
+
try {
|
|
5687
|
+
console.log(`[UserService] About to query user_company_mapping with companyId: ${companyId}`);
|
|
5688
|
+
const { data: mappings, error: mappingError } = await this.supabase.from("user_company_mapping").select("user_id, created_at, updated_at").eq("company_id", companyId);
|
|
5689
|
+
console.log(`[UserService] Supabase query result:`, {
|
|
5690
|
+
data: mappings,
|
|
5691
|
+
error: mappingError,
|
|
5692
|
+
dataLength: mappings?.length || 0
|
|
5693
|
+
});
|
|
5694
|
+
if (mappingError) {
|
|
5695
|
+
console.error("Error fetching user company mappings:", mappingError);
|
|
5696
|
+
throw new Error(`Failed to fetch user mappings: ${mappingError.message}`);
|
|
5697
|
+
}
|
|
5698
|
+
if (!mappings || mappings.length === 0) {
|
|
5699
|
+
console.log(`[UserService] No mappings found for companyId: ${companyId}`);
|
|
5700
|
+
return [];
|
|
5701
|
+
}
|
|
5702
|
+
console.log(`[UserService] Found ${mappings.length} mappings for companyId: ${companyId}`);
|
|
5703
|
+
console.log(`[UserService] Raw mappings:`, mappings.map((m) => m.user_id));
|
|
5704
|
+
const users = mappings.map((mapping) => {
|
|
5705
|
+
const shortId = mapping.user_id.substring(0, 8);
|
|
5706
|
+
return {
|
|
5707
|
+
id: mapping.user_id,
|
|
5708
|
+
name: mapping.user_id,
|
|
5709
|
+
// Show the full user ID
|
|
5710
|
+
email: `${shortId}@company.com`,
|
|
5711
|
+
// Simple email format
|
|
5712
|
+
isActive: true,
|
|
5713
|
+
createdAt: mapping.created_at,
|
|
5714
|
+
updatedAt: mapping.updated_at
|
|
5715
|
+
};
|
|
5716
|
+
});
|
|
5717
|
+
console.log(`[UserService] Created ${users.length} user objects`);
|
|
5718
|
+
return users;
|
|
5719
|
+
} catch (error) {
|
|
5720
|
+
console.error("Error in getUsersByCompanyIdFallback:", error);
|
|
5721
|
+
throw error;
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
/**
|
|
5725
|
+
* Check if a user is mapped to a specific company
|
|
5726
|
+
* @param userId - The user ID to check
|
|
5727
|
+
* @param companyId - The company ID to check against
|
|
5728
|
+
* @returns Promise<boolean> - Whether the user is mapped to the company
|
|
5729
|
+
*/
|
|
5730
|
+
async isUserInCompany(userId, companyId) {
|
|
5731
|
+
if (!userId || !companyId) {
|
|
5732
|
+
return false;
|
|
5733
|
+
}
|
|
5734
|
+
try {
|
|
5735
|
+
const { data, error } = await this.supabase.from("user_company_mapping").select("id").eq("user_id", userId).eq("company_id", companyId).single();
|
|
5736
|
+
if (error) {
|
|
5737
|
+
if (error.code === "PGRST116") {
|
|
5738
|
+
return false;
|
|
5739
|
+
}
|
|
5740
|
+
console.error("Error checking user company mapping:", error);
|
|
5741
|
+
return false;
|
|
5742
|
+
}
|
|
5743
|
+
return !!data;
|
|
5744
|
+
} catch (error) {
|
|
5745
|
+
console.error("Error in isUserInCompany:", error);
|
|
5746
|
+
return false;
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
/**
|
|
5750
|
+
* Get user details by user ID (if they exist in auth.users)
|
|
5751
|
+
* @param userId - The user ID to fetch
|
|
5752
|
+
* @returns Promise<CompanyUser | null> - The user data or null if not found
|
|
5753
|
+
*/
|
|
5754
|
+
async getUserById(userId) {
|
|
5755
|
+
if (!userId) {
|
|
5756
|
+
return null;
|
|
5757
|
+
}
|
|
5758
|
+
try {
|
|
5759
|
+
const { data, error } = await this.supabase.from("users").select("id, email, raw_user_meta_data").eq("id", userId).single();
|
|
5760
|
+
if (error) {
|
|
5761
|
+
if (error.code === "PGRST116") {
|
|
5762
|
+
return null;
|
|
5763
|
+
}
|
|
5764
|
+
console.error("Error fetching user by ID:", error);
|
|
5765
|
+
return null;
|
|
5766
|
+
}
|
|
5767
|
+
if (!data) {
|
|
5768
|
+
return null;
|
|
5769
|
+
}
|
|
5770
|
+
const fullName = data.raw_user_meta_data?.full_name || data.raw_user_meta_data?.name || data.email?.split("@")[0];
|
|
5771
|
+
return {
|
|
5772
|
+
id: data.id,
|
|
5773
|
+
name: fullName,
|
|
5774
|
+
email: data.email,
|
|
5775
|
+
isActive: true,
|
|
5776
|
+
createdAt: void 0,
|
|
5777
|
+
updatedAt: void 0
|
|
5778
|
+
};
|
|
5779
|
+
} catch (error) {
|
|
5780
|
+
console.error("Error in getUserById:", error);
|
|
5781
|
+
return null;
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
};
|
|
5785
|
+
var createUserService = (supabase) => {
|
|
5786
|
+
return new UserService(supabase);
|
|
5787
|
+
};
|
|
5788
|
+
var userService = {
|
|
5789
|
+
create: createUserService
|
|
5790
|
+
};
|
|
5791
|
+
|
|
5792
|
+
// src/lib/services/supervisorService.ts
|
|
5793
|
+
var SupervisorService = class {
|
|
5794
|
+
constructor(supabase) {
|
|
5795
|
+
this.supabase = supabase;
|
|
5796
|
+
this.linesService = new LinesService(supabase);
|
|
5797
|
+
this.userService = new UserService(supabase);
|
|
5798
|
+
}
|
|
5799
|
+
/**
|
|
5800
|
+
* Get supervisor management data for a specific company
|
|
5801
|
+
* Uses real lines data and real user data from the database
|
|
5802
|
+
* @param companyId - The company ID to fetch data for
|
|
5803
|
+
* @returns Promise<SupervisorManagementData>
|
|
5804
|
+
*/
|
|
5805
|
+
async getSupervisorManagementData(companyId) {
|
|
5806
|
+
try {
|
|
5807
|
+
const lines = await this.linesService.getLinesByCompanyId(companyId);
|
|
5808
|
+
console.log(`[SupervisorService] Fetching users for companyId: ${companyId}`);
|
|
5809
|
+
const companyUsers = await this.userService.getUsersByCompanyId(companyId);
|
|
5810
|
+
console.log(`[SupervisorService] Received ${companyUsers.length} users from UserService`);
|
|
5811
|
+
const availableSupervisors = companyUsers.map((user) => ({
|
|
5812
|
+
id: user.id,
|
|
5813
|
+
name: user.name,
|
|
5814
|
+
email: user.email,
|
|
5815
|
+
isActive: user.isActive,
|
|
5816
|
+
createdAt: user.createdAt,
|
|
5817
|
+
updatedAt: user.updatedAt
|
|
5818
|
+
}));
|
|
5819
|
+
console.log(`[SupervisorService] Created ${availableSupervisors.length} available supervisors`);
|
|
5820
|
+
const assignments = lines.map((line) => {
|
|
5821
|
+
const currentSupervisor = this.getRandomSupervisorForLine(line.id, availableSupervisors);
|
|
5822
|
+
const assignment = {
|
|
5823
|
+
lineId: line.id,
|
|
5824
|
+
lineName: line.name,
|
|
5825
|
+
currentSupervisor,
|
|
5826
|
+
availableSupervisors
|
|
5827
|
+
};
|
|
5828
|
+
console.log(`[SupervisorService] Assignment for line ${line.name}: ${assignment.availableSupervisors.length} available supervisors`);
|
|
5829
|
+
return assignment;
|
|
5830
|
+
});
|
|
5831
|
+
console.log(`[SupervisorService] Final result: ${assignments.length} assignments, ${availableSupervisors.length} total supervisors`);
|
|
5832
|
+
return {
|
|
5833
|
+
assignments,
|
|
5834
|
+
allSupervisors: availableSupervisors,
|
|
5835
|
+
loading: false,
|
|
5836
|
+
error: void 0
|
|
5837
|
+
};
|
|
5838
|
+
} catch (error) {
|
|
5839
|
+
console.error("Error fetching supervisor management data:", error);
|
|
5840
|
+
return {
|
|
5841
|
+
assignments: [],
|
|
5842
|
+
allSupervisors: [],
|
|
5843
|
+
loading: false,
|
|
5844
|
+
error: error instanceof Error ? error.message : "Failed to fetch supervisor management data"
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
/**
|
|
5849
|
+
* Get all supervisors for a specific company (real data from database)
|
|
5850
|
+
* @param companyId - The company ID to fetch supervisors for
|
|
5851
|
+
* @returns Promise<Supervisor[]>
|
|
5852
|
+
*/
|
|
5853
|
+
async getAllSupervisors(companyId) {
|
|
5854
|
+
try {
|
|
5855
|
+
const companyUsers = await this.userService.getUsersByCompanyId(companyId);
|
|
5856
|
+
return companyUsers.map((user) => ({
|
|
5857
|
+
id: user.id,
|
|
5858
|
+
name: user.name,
|
|
5859
|
+
email: user.email,
|
|
5860
|
+
isActive: user.isActive,
|
|
5861
|
+
createdAt: user.createdAt,
|
|
5862
|
+
updatedAt: user.updatedAt
|
|
5863
|
+
}));
|
|
5864
|
+
} catch (error) {
|
|
5865
|
+
console.error("Error fetching all supervisors:", error);
|
|
5866
|
+
return [];
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
/**
|
|
5870
|
+
* Get active supervisors only for a specific company
|
|
5871
|
+
* @param companyId - The company ID to fetch active supervisors for
|
|
5872
|
+
* @returns Promise<Supervisor[]>
|
|
5873
|
+
*/
|
|
5874
|
+
async getActiveSupervisors(companyId) {
|
|
5875
|
+
const allSupervisors = await this.getAllSupervisors(companyId);
|
|
5876
|
+
return allSupervisors.filter((sup) => sup.isActive);
|
|
5877
|
+
}
|
|
5878
|
+
/**
|
|
5879
|
+
* Assign a supervisor to a line
|
|
5880
|
+
* This is a mock implementation - in production, this would update the database
|
|
5881
|
+
* @param lineId - The line ID
|
|
5882
|
+
* @param supervisorId - The supervisor ID to assign
|
|
5883
|
+
* @returns Promise<boolean> - Success status
|
|
5884
|
+
*/
|
|
5885
|
+
async assignSupervisorToLine(lineId, supervisorId) {
|
|
5886
|
+
try {
|
|
5887
|
+
console.log(`Assigning supervisor ${supervisorId} to line ${lineId}`);
|
|
5888
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
5889
|
+
return true;
|
|
5890
|
+
} catch (error) {
|
|
5891
|
+
console.error("Error assigning supervisor to line:", error);
|
|
5892
|
+
return false;
|
|
5893
|
+
}
|
|
5894
|
+
}
|
|
5895
|
+
/**
|
|
5896
|
+
* Helper method to simulate supervisor assignments
|
|
5897
|
+
* In production, this would be fetched from the database
|
|
5898
|
+
* @param lineId - The line ID
|
|
5899
|
+
* @param supervisors - Available supervisors
|
|
5900
|
+
* @returns Supervisor or undefined
|
|
5901
|
+
*/
|
|
5902
|
+
getRandomSupervisorForLine(lineId, supervisors) {
|
|
5903
|
+
const hash = lineId.split("").reduce((a, b) => {
|
|
5904
|
+
a = (a << 5) - a + b.charCodeAt(0);
|
|
5905
|
+
return a & a;
|
|
5906
|
+
}, 0);
|
|
5907
|
+
const index = Math.abs(hash) % (supervisors.length + 1);
|
|
5908
|
+
return index < supervisors.length ? supervisors[index] : void 0;
|
|
5909
|
+
}
|
|
5910
|
+
};
|
|
5911
|
+
var createSupervisorService = (supabase) => {
|
|
5912
|
+
return new SupervisorService(supabase);
|
|
5913
|
+
};
|
|
5914
|
+
var simulateApiDelay = (ms = 1e3) => {
|
|
5915
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5916
|
+
};
|
|
5224
5917
|
var AuthContext = createContext({
|
|
5225
5918
|
session: null,
|
|
5226
5919
|
user: null,
|
|
5227
5920
|
loading: true,
|
|
5228
5921
|
error: null,
|
|
5229
5922
|
signOut: async () => {
|
|
5923
|
+
},
|
|
5924
|
+
markFirstLoginCompleted: async () => false,
|
|
5925
|
+
showOnboarding: false,
|
|
5926
|
+
setShowOnboarding: () => {
|
|
5927
|
+
},
|
|
5928
|
+
completeOnboarding: async () => {
|
|
5230
5929
|
}
|
|
5231
5930
|
});
|
|
5232
5931
|
var useAuth = () => useContext(AuthContext);
|
|
5233
5932
|
var AuthProvider = ({ children }) => {
|
|
5234
5933
|
const supabase = useSupabase();
|
|
5235
5934
|
const { authConfig } = useDashboardConfig();
|
|
5935
|
+
const entityConfig = useEntityConfig();
|
|
5236
5936
|
const [session, setSession] = useState(null);
|
|
5237
5937
|
const [user, setUser] = useState(null);
|
|
5238
5938
|
const [loading, setLoading] = useState(true);
|
|
5239
5939
|
const [error, setError] = useState(null);
|
|
5940
|
+
const [showOnboarding, setShowOnboarding] = useState(false);
|
|
5240
5941
|
const router = useRouter();
|
|
5241
5942
|
authConfig?.userProfileTable;
|
|
5242
5943
|
authConfig?.roleColumn || "role";
|
|
5944
|
+
const dashboardCompanyId = entityConfig?.companyId;
|
|
5243
5945
|
const fetchUserDetails = useCallback(async (supabaseUser) => {
|
|
5244
5946
|
console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
|
|
5245
5947
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5258,29 +5960,66 @@ var AuthProvider = ({ children }) => {
|
|
|
5258
5960
|
reject(new Error("Profile fetch timeout"));
|
|
5259
5961
|
}, 2e3)
|
|
5260
5962
|
);
|
|
5261
|
-
const rolePromise =
|
|
5262
|
-
|
|
5263
|
-
|
|
5963
|
+
const [rolePromise, companyPromise, onboardingPromise] = [
|
|
5964
|
+
supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single(),
|
|
5965
|
+
supabase.from("user_company_mapping").select("company_id").eq("user_id", supabaseUser.id).single(),
|
|
5966
|
+
supabase.from("user_onboarding_tracking").select("first_login_completed").eq("user_id", supabaseUser.id).single()
|
|
5967
|
+
];
|
|
5968
|
+
const [roleResult, companyResult, onboardingResult] = await Promise.race([
|
|
5969
|
+
Promise.all([rolePromise, companyPromise, onboardingPromise]),
|
|
5264
5970
|
timeoutPromise
|
|
5265
|
-
// Fixed: removed .then() which was causing the bug
|
|
5266
5971
|
]);
|
|
5267
5972
|
let roleLevel = void 0;
|
|
5268
5973
|
if (roleResult && !roleResult.error && roleResult.data) {
|
|
5269
5974
|
roleLevel = roleResult.data.role_level;
|
|
5270
5975
|
} else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
|
|
5271
|
-
console.log("Error fetching
|
|
5976
|
+
console.log("Error fetching user_roles data:", roleResult.error.message);
|
|
5977
|
+
}
|
|
5978
|
+
let firstLoginCompleted = false;
|
|
5979
|
+
if (onboardingResult && !onboardingResult.error && onboardingResult.data) {
|
|
5980
|
+
firstLoginCompleted = onboardingResult.data.first_login_completed ?? false;
|
|
5981
|
+
} else if (onboardingResult?.error && onboardingResult.error.code === "PGRST116") {
|
|
5982
|
+
console.log("[fetchUserDetails] Creating onboarding record for new user");
|
|
5983
|
+
const { error: insertError } = await supabase.from("user_onboarding_tracking").insert({
|
|
5984
|
+
user_id: supabaseUser.id,
|
|
5985
|
+
first_login_completed: false
|
|
5986
|
+
});
|
|
5987
|
+
if (insertError) {
|
|
5988
|
+
console.log("Error creating onboarding record:", insertError.message);
|
|
5989
|
+
}
|
|
5990
|
+
} else if (onboardingResult?.error) {
|
|
5991
|
+
console.log("Error fetching onboarding data:", onboardingResult.error.message);
|
|
5992
|
+
}
|
|
5993
|
+
let userCompanyId = void 0;
|
|
5994
|
+
if (companyResult && !companyResult.error && companyResult.data) {
|
|
5995
|
+
userCompanyId = companyResult.data.company_id;
|
|
5996
|
+
} else if (companyResult?.error && companyResult.error.code !== "PGRST116") {
|
|
5997
|
+
console.log("Error fetching company_id:", companyResult.error.message);
|
|
5998
|
+
}
|
|
5999
|
+
if (dashboardCompanyId && userCompanyId && userCompanyId !== dashboardCompanyId) {
|
|
6000
|
+
console.warn("[Auth] Company access denied:", {
|
|
6001
|
+
userCompanyId,
|
|
6002
|
+
dashboardCompanyId,
|
|
6003
|
+
userEmail: supabaseUser.email
|
|
6004
|
+
});
|
|
6005
|
+
throw new Error("COMPANY_ACCESS_DENIED");
|
|
5272
6006
|
}
|
|
5273
6007
|
return {
|
|
5274
6008
|
...basicUser,
|
|
5275
|
-
role_level: roleLevel
|
|
6009
|
+
role_level: roleLevel,
|
|
6010
|
+
company_id: userCompanyId,
|
|
6011
|
+
first_login_completed: firstLoginCompleted
|
|
5276
6012
|
};
|
|
5277
6013
|
} catch (err) {
|
|
6014
|
+
if (err instanceof Error && err.message === "COMPANY_ACCESS_DENIED") {
|
|
6015
|
+
throw err;
|
|
6016
|
+
}
|
|
5278
6017
|
if (err instanceof Error && err.message.includes("timeout")) {
|
|
5279
6018
|
console.warn("Auth fetch timeout - using basic user info");
|
|
5280
6019
|
}
|
|
5281
6020
|
return basicUser;
|
|
5282
6021
|
}
|
|
5283
|
-
}, [supabase]);
|
|
6022
|
+
}, [supabase, dashboardCompanyId]);
|
|
5284
6023
|
useEffect(() => {
|
|
5285
6024
|
if (!supabase) return;
|
|
5286
6025
|
let mounted = true;
|
|
@@ -5321,6 +6060,13 @@ var AuthProvider = ({ children }) => {
|
|
|
5321
6060
|
} catch (err) {
|
|
5322
6061
|
console.error("Error fetching user details during init:", err);
|
|
5323
6062
|
if (mounted) {
|
|
6063
|
+
if (err instanceof Error && err.message === "COMPANY_ACCESS_DENIED") {
|
|
6064
|
+
setError(new Error("You do not have access to this dashboard. Please contact your administrator."));
|
|
6065
|
+
await supabase.auth.signOut();
|
|
6066
|
+
setSession(null);
|
|
6067
|
+
setUser(null);
|
|
6068
|
+
return;
|
|
6069
|
+
}
|
|
5324
6070
|
setUser({
|
|
5325
6071
|
id: initialSession.user.id,
|
|
5326
6072
|
email: initialSession.user.email
|
|
@@ -5401,6 +6147,73 @@ var AuthProvider = ({ children }) => {
|
|
|
5401
6147
|
subscription?.unsubscribe();
|
|
5402
6148
|
};
|
|
5403
6149
|
}, [supabase, fetchUserDetails]);
|
|
6150
|
+
const markFirstLoginCompleted = useCallback(async () => {
|
|
6151
|
+
console.log("[markFirstLoginCompleted] Starting update for user:", user?.id);
|
|
6152
|
+
if (!supabase || !user?.id) {
|
|
6153
|
+
console.error("[markFirstLoginCompleted] Missing requirements:", {
|
|
6154
|
+
hasSupabase: !!supabase,
|
|
6155
|
+
userId: user?.id
|
|
6156
|
+
});
|
|
6157
|
+
return false;
|
|
6158
|
+
}
|
|
6159
|
+
try {
|
|
6160
|
+
console.log("[markFirstLoginCompleted] Updating onboarding tracking for user_id:", user.id);
|
|
6161
|
+
const { data: existingData, error: checkError } = await supabase.from("user_onboarding_tracking").select("id").eq("user_id", user.id).single();
|
|
6162
|
+
let data, error2;
|
|
6163
|
+
if (checkError && checkError.code === "PGRST116") {
|
|
6164
|
+
console.log("[markFirstLoginCompleted] Creating onboarding record with completed status");
|
|
6165
|
+
const result = await supabase.from("user_onboarding_tracking").insert({
|
|
6166
|
+
user_id: user.id,
|
|
6167
|
+
first_login_completed: true
|
|
6168
|
+
}).select();
|
|
6169
|
+
data = result.data;
|
|
6170
|
+
error2 = result.error;
|
|
6171
|
+
} else {
|
|
6172
|
+
console.log("[markFirstLoginCompleted] Updating existing onboarding record");
|
|
6173
|
+
const result = await supabase.from("user_onboarding_tracking").update({
|
|
6174
|
+
first_login_completed: true,
|
|
6175
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6176
|
+
}).eq("user_id", user.id).select();
|
|
6177
|
+
data = result.data;
|
|
6178
|
+
error2 = result.error;
|
|
6179
|
+
}
|
|
6180
|
+
if (error2) {
|
|
6181
|
+
console.error("[markFirstLoginCompleted] Database update error:", {
|
|
6182
|
+
error: error2,
|
|
6183
|
+
message: error2.message,
|
|
6184
|
+
details: error2.details,
|
|
6185
|
+
hint: error2.hint,
|
|
6186
|
+
code: error2.code
|
|
6187
|
+
});
|
|
6188
|
+
return false;
|
|
6189
|
+
}
|
|
6190
|
+
console.log("[markFirstLoginCompleted] Database update successful:", data);
|
|
6191
|
+
setUser((prevUser) => {
|
|
6192
|
+
if (!prevUser) return prevUser;
|
|
6193
|
+
const updatedUser = {
|
|
6194
|
+
...prevUser,
|
|
6195
|
+
first_login_completed: true
|
|
6196
|
+
};
|
|
6197
|
+
console.log("[markFirstLoginCompleted] Local state updated:", updatedUser);
|
|
6198
|
+
return updatedUser;
|
|
6199
|
+
});
|
|
6200
|
+
console.log("[markFirstLoginCompleted] Successfully marked first login as completed");
|
|
6201
|
+
return true;
|
|
6202
|
+
} catch (err) {
|
|
6203
|
+
console.error("[markFirstLoginCompleted] Unexpected error:", err);
|
|
6204
|
+
return false;
|
|
6205
|
+
}
|
|
6206
|
+
}, [supabase, user?.id]);
|
|
6207
|
+
const completeOnboarding = useCallback(async () => {
|
|
6208
|
+
console.log("[completeOnboarding] Completing onboarding tour");
|
|
6209
|
+
const success = await markFirstLoginCompleted();
|
|
6210
|
+
if (success) {
|
|
6211
|
+
setShowOnboarding(false);
|
|
6212
|
+
console.log("[completeOnboarding] Onboarding completed successfully");
|
|
6213
|
+
} else {
|
|
6214
|
+
console.error("[completeOnboarding] Failed to mark first login as completed");
|
|
6215
|
+
}
|
|
6216
|
+
}, [markFirstLoginCompleted]);
|
|
5404
6217
|
const signOut = async () => {
|
|
5405
6218
|
if (!supabase) return;
|
|
5406
6219
|
setLoading(true);
|
|
@@ -5411,7 +6224,17 @@ var AuthProvider = ({ children }) => {
|
|
|
5411
6224
|
router.replace(logoutRedirectPath);
|
|
5412
6225
|
}
|
|
5413
6226
|
};
|
|
5414
|
-
return /* @__PURE__ */ jsx(AuthContext.Provider, { value: {
|
|
6227
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value: {
|
|
6228
|
+
session,
|
|
6229
|
+
user,
|
|
6230
|
+
loading,
|
|
6231
|
+
error,
|
|
6232
|
+
signOut,
|
|
6233
|
+
markFirstLoginCompleted,
|
|
6234
|
+
showOnboarding,
|
|
6235
|
+
setShowOnboarding,
|
|
6236
|
+
completeOnboarding
|
|
6237
|
+
}, children });
|
|
5415
6238
|
};
|
|
5416
6239
|
var defaultContextValue = {
|
|
5417
6240
|
components: {},
|
|
@@ -8894,7 +9717,7 @@ var useSKUs = (companyId) => {
|
|
|
8894
9717
|
// src/lib/hooks/useCanSaveTargets.ts
|
|
8895
9718
|
var useCanSaveTargets = () => {
|
|
8896
9719
|
const { user } = useAuth();
|
|
8897
|
-
return
|
|
9720
|
+
return true;
|
|
8898
9721
|
};
|
|
8899
9722
|
function useTicketHistory(companyId) {
|
|
8900
9723
|
const [tickets, setTickets] = useState([]);
|
|
@@ -12468,6 +13291,100 @@ var useHourlyTargetMisses = ({
|
|
|
12468
13291
|
clearMiss
|
|
12469
13292
|
};
|
|
12470
13293
|
};
|
|
13294
|
+
function useClipTypes() {
|
|
13295
|
+
const [clipTypes, setClipTypes] = useState([]);
|
|
13296
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
13297
|
+
const [error, setError] = useState(null);
|
|
13298
|
+
const dashboardConfig = useDashboardConfig();
|
|
13299
|
+
const s3Service = dashboardConfig?.s3Config ? videoPrefetchManager.getS3Service(dashboardConfig) : null;
|
|
13300
|
+
const fetchClipTypes = useCallback(async () => {
|
|
13301
|
+
console.log("[useClipTypes] Starting fetchClipTypes, s3Service:", !!s3Service);
|
|
13302
|
+
if (!s3Service) {
|
|
13303
|
+
console.log("[useClipTypes] S3 service not initialized");
|
|
13304
|
+
setError("S3 service not initialized");
|
|
13305
|
+
setIsLoading(false);
|
|
13306
|
+
return;
|
|
13307
|
+
}
|
|
13308
|
+
setIsLoading(true);
|
|
13309
|
+
setError(null);
|
|
13310
|
+
try {
|
|
13311
|
+
console.log("[useClipTypes] Fetching clip types from S3Service...");
|
|
13312
|
+
const types = await s3Service.getClipTypes();
|
|
13313
|
+
console.log(`[useClipTypes] Fetched ${types?.length || 0} clip types:`, types);
|
|
13314
|
+
setClipTypes(types || []);
|
|
13315
|
+
} catch (err) {
|
|
13316
|
+
console.error("[useClipTypes] Error fetching clip types:", err);
|
|
13317
|
+
setError("Failed to load clip types");
|
|
13318
|
+
const fallbackTypes = [
|
|
13319
|
+
{ id: "idle_time", type: "idle_time", label: "Low Value Moments", color: "purple", description: "Idle time activities" },
|
|
13320
|
+
{ id: "best_cycle_time", type: "best_cycle_time", label: "Best Cycle Time", color: "green", description: "Fastest cycle today" },
|
|
13321
|
+
{ id: "worst_cycle_time", type: "worst_cycle_time", label: "Worst Cycle Time", color: "red", description: "Slowest cycle today" },
|
|
13322
|
+
{ id: "long_cycle_time", type: "long_cycle_time", label: "Long Cycle Time", color: "orange", description: "Above standard cycle times" }
|
|
13323
|
+
];
|
|
13324
|
+
console.log("[useClipTypes] Using fallback clip types:", fallbackTypes);
|
|
13325
|
+
setClipTypes(fallbackTypes);
|
|
13326
|
+
} finally {
|
|
13327
|
+
setIsLoading(false);
|
|
13328
|
+
}
|
|
13329
|
+
}, [s3Service]);
|
|
13330
|
+
useEffect(() => {
|
|
13331
|
+
fetchClipTypes();
|
|
13332
|
+
}, [fetchClipTypes]);
|
|
13333
|
+
return {
|
|
13334
|
+
clipTypes,
|
|
13335
|
+
isLoading,
|
|
13336
|
+
error,
|
|
13337
|
+
refresh: fetchClipTypes
|
|
13338
|
+
};
|
|
13339
|
+
}
|
|
13340
|
+
function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
13341
|
+
const { clipTypes, isLoading: typesLoading, error: typesError, refresh } = useClipTypes();
|
|
13342
|
+
const [counts, setCounts] = useState({});
|
|
13343
|
+
const [countsLoading, setCountsLoading] = useState(false);
|
|
13344
|
+
const dashboardConfig = useDashboardConfig();
|
|
13345
|
+
const s3Service = dashboardConfig?.s3Config ? videoPrefetchManager.getS3Service(dashboardConfig) : null;
|
|
13346
|
+
useEffect(() => {
|
|
13347
|
+
console.log("[useClipTypesWithCounts] Dependencies:", {
|
|
13348
|
+
s3Service: !!s3Service,
|
|
13349
|
+
workspaceId,
|
|
13350
|
+
date,
|
|
13351
|
+
shiftId,
|
|
13352
|
+
shiftIdType: typeof shiftId
|
|
13353
|
+
});
|
|
13354
|
+
if (!s3Service || !workspaceId || !date || shiftId === void 0) {
|
|
13355
|
+
console.log("[useClipTypesWithCounts] Skipping counts fetch - missing dependencies");
|
|
13356
|
+
return;
|
|
13357
|
+
}
|
|
13358
|
+
const fetchCounts = async () => {
|
|
13359
|
+
setCountsLoading(true);
|
|
13360
|
+
try {
|
|
13361
|
+
console.log("[useClipTypesWithCounts] Fetching counts...");
|
|
13362
|
+
const clipCounts = await s3Service.getClipCounts(
|
|
13363
|
+
workspaceId,
|
|
13364
|
+
date,
|
|
13365
|
+
shiftId.toString()
|
|
13366
|
+
);
|
|
13367
|
+
console.log("[useClipTypesWithCounts] Received counts:", clipCounts);
|
|
13368
|
+
setCounts(clipCounts);
|
|
13369
|
+
} catch (err) {
|
|
13370
|
+
console.error("[useClipTypesWithCounts] Error fetching counts:", err);
|
|
13371
|
+
} finally {
|
|
13372
|
+
setCountsLoading(false);
|
|
13373
|
+
}
|
|
13374
|
+
};
|
|
13375
|
+
fetchCounts();
|
|
13376
|
+
}, [s3Service, workspaceId, date, shiftId]);
|
|
13377
|
+
return {
|
|
13378
|
+
clipTypes: clipTypes.map((type) => ({
|
|
13379
|
+
...type,
|
|
13380
|
+
count: counts[type.type] || 0
|
|
13381
|
+
})),
|
|
13382
|
+
isLoading: typesLoading || countsLoading,
|
|
13383
|
+
error: typesError,
|
|
13384
|
+
refresh,
|
|
13385
|
+
counts
|
|
13386
|
+
};
|
|
13387
|
+
}
|
|
12471
13388
|
var MAX_RETRIES = 10;
|
|
12472
13389
|
var RETRY_DELAY = 500;
|
|
12473
13390
|
function useNavigation(customNavigate) {
|
|
@@ -12812,6 +13729,57 @@ var useFormatNumber = () => {
|
|
|
12812
13729
|
);
|
|
12813
13730
|
return { formatNumber };
|
|
12814
13731
|
};
|
|
13732
|
+
function useAccessControl() {
|
|
13733
|
+
const { user } = useAuth();
|
|
13734
|
+
const userRole = useMemo(() => {
|
|
13735
|
+
if (!user?.role_level) return null;
|
|
13736
|
+
const roleLevel = user.role_level;
|
|
13737
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
13738
|
+
return roleLevel;
|
|
13739
|
+
}
|
|
13740
|
+
return "supervisor";
|
|
13741
|
+
}, [user?.role_level]);
|
|
13742
|
+
const allPages = [
|
|
13743
|
+
"/",
|
|
13744
|
+
"/leaderboard",
|
|
13745
|
+
"/kpis",
|
|
13746
|
+
"/targets",
|
|
13747
|
+
"/shifts",
|
|
13748
|
+
"/supervisor-management",
|
|
13749
|
+
"/skus",
|
|
13750
|
+
"/ai-agent",
|
|
13751
|
+
"/help",
|
|
13752
|
+
"/health",
|
|
13753
|
+
"/profile",
|
|
13754
|
+
"/workspace",
|
|
13755
|
+
"/factory-view"
|
|
13756
|
+
];
|
|
13757
|
+
const accessiblePages = useMemo(() => {
|
|
13758
|
+
return allPages;
|
|
13759
|
+
}, []);
|
|
13760
|
+
const hasAccess = useMemo(() => {
|
|
13761
|
+
return (path) => {
|
|
13762
|
+
return true;
|
|
13763
|
+
};
|
|
13764
|
+
}, []);
|
|
13765
|
+
const isPageVisible = useMemo(() => {
|
|
13766
|
+
return (path) => {
|
|
13767
|
+
return true;
|
|
13768
|
+
};
|
|
13769
|
+
}, []);
|
|
13770
|
+
const canAccessPage = useMemo(() => {
|
|
13771
|
+
return (path) => {
|
|
13772
|
+
return true;
|
|
13773
|
+
};
|
|
13774
|
+
}, []);
|
|
13775
|
+
return {
|
|
13776
|
+
userRole,
|
|
13777
|
+
hasAccess,
|
|
13778
|
+
accessiblePages,
|
|
13779
|
+
isPageVisible,
|
|
13780
|
+
canAccessPage
|
|
13781
|
+
};
|
|
13782
|
+
}
|
|
12815
13783
|
function useSupabaseClient() {
|
|
12816
13784
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
12817
13785
|
const supabase = useMemo(() => createClient(supabaseUrl, supabaseKey), [supabaseUrl, supabaseKey]);
|
|
@@ -20263,6 +21231,19 @@ var LoadingPage = ({
|
|
|
20263
21231
|
] }) });
|
|
20264
21232
|
};
|
|
20265
21233
|
var LoadingPage_default = LoadingPage;
|
|
21234
|
+
var AccessDeniedPage = () => /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md w-full bg-white shadow-lg rounded-lg p-6 text-center", children: [
|
|
21235
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx("svg", { className: "mx-auto h-12 w-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.962-.833-2.732 0L4.082 15.5c-.77.833.192 2.5 1.732 2.5z" }) }) }),
|
|
21236
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-gray-900 mb-2", children: "Access Denied" }),
|
|
21237
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-4", children: "You do not have access to this dashboard. Please contact your administrator for assistance." }),
|
|
21238
|
+
/* @__PURE__ */ jsx(
|
|
21239
|
+
"button",
|
|
21240
|
+
{
|
|
21241
|
+
onClick: () => window.location.href = "/login",
|
|
21242
|
+
className: "bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition-colors",
|
|
21243
|
+
children: "Return to Login"
|
|
21244
|
+
}
|
|
21245
|
+
)
|
|
21246
|
+
] }) });
|
|
20266
21247
|
var withAuth = (WrappedComponent2, options) => {
|
|
20267
21248
|
const defaultOptions = {
|
|
20268
21249
|
redirectTo: "/login",
|
|
@@ -20270,7 +21251,7 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20270
21251
|
...options
|
|
20271
21252
|
};
|
|
20272
21253
|
const WithAuthComponent = React19.memo(function WithAuthComponent2(props) {
|
|
20273
|
-
const { session, loading } = useAuth();
|
|
21254
|
+
const { session, loading, error } = useAuth();
|
|
20274
21255
|
const router = useRouter();
|
|
20275
21256
|
React19.useEffect(() => {
|
|
20276
21257
|
if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
|
|
@@ -20278,14 +21259,17 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20278
21259
|
}
|
|
20279
21260
|
}, [session, loading]);
|
|
20280
21261
|
React19.useEffect(() => {
|
|
20281
|
-
if (!loading && defaultOptions.requireAuth && !session) {
|
|
21262
|
+
if (!loading && defaultOptions.requireAuth && !session && !error) {
|
|
20282
21263
|
console.log("Redirecting to login from withAuth");
|
|
20283
21264
|
router.replace(defaultOptions.redirectTo);
|
|
20284
21265
|
}
|
|
20285
|
-
}, [session, loading, router]);
|
|
21266
|
+
}, [session, loading, router, error]);
|
|
20286
21267
|
if (loading) {
|
|
20287
21268
|
return /* @__PURE__ */ jsx(LoadingPage, { message: "Authenticating..." });
|
|
20288
21269
|
}
|
|
21270
|
+
if (error && error.message.includes("You do not have access to this dashboard")) {
|
|
21271
|
+
return /* @__PURE__ */ jsx(AccessDeniedPage, {});
|
|
21272
|
+
}
|
|
20289
21273
|
if (defaultOptions.requireAuth && !session) {
|
|
20290
21274
|
return null;
|
|
20291
21275
|
}
|
|
@@ -20294,6 +21278,31 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20294
21278
|
WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
20295
21279
|
return WithAuthComponent;
|
|
20296
21280
|
};
|
|
21281
|
+
function withAccessControl(WrappedComponent2, options = {}) {
|
|
21282
|
+
const {
|
|
21283
|
+
requiredPath,
|
|
21284
|
+
redirectTo = "/",
|
|
21285
|
+
UnauthorizedComponent
|
|
21286
|
+
} = options;
|
|
21287
|
+
const WithAccessControlComponent = (props) => {
|
|
21288
|
+
const router = useRouter();
|
|
21289
|
+
const { user, loading: authLoading } = useAuth();
|
|
21290
|
+
const { canAccessPage, userRole } = useAccessControl();
|
|
21291
|
+
requiredPath || router.pathname;
|
|
21292
|
+
if (authLoading) {
|
|
21293
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
21294
|
+
/* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }),
|
|
21295
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600", children: "Loading..." })
|
|
21296
|
+
] }) });
|
|
21297
|
+
}
|
|
21298
|
+
if (!user) {
|
|
21299
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-4", children: "Please log in to continue" }) }) });
|
|
21300
|
+
}
|
|
21301
|
+
return /* @__PURE__ */ jsx(WrappedComponent2, { ...props });
|
|
21302
|
+
};
|
|
21303
|
+
WithAccessControlComponent.displayName = `withAccessControl(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
21304
|
+
return WithAccessControlComponent;
|
|
21305
|
+
}
|
|
20297
21306
|
var LoginPage = ({
|
|
20298
21307
|
onRateLimitCheck,
|
|
20299
21308
|
logoSrc = "/optifye-logo.png",
|
|
@@ -21872,7 +22881,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21872
22881
|
ticks.push(maxYValue);
|
|
21873
22882
|
return [...new Set(ticks)].sort((a, b) => a - b);
|
|
21874
22883
|
};
|
|
21875
|
-
const renderLegend = () => /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 absolute
|
|
22884
|
+
const renderLegend = () => /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 absolute bottom-3 left-0 right-0 bg-white py-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
|
|
21876
22885
|
/* @__PURE__ */ jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
|
|
21877
22886
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
21878
22887
|
"Target: ",
|
|
@@ -27174,7 +28183,7 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27174
28183
|
muted,
|
|
27175
28184
|
loop,
|
|
27176
28185
|
poster,
|
|
27177
|
-
//
|
|
28186
|
+
// Optimized HLS configuration for VOD content - PERFORMANCE OPTIMIZED
|
|
27178
28187
|
html5: {
|
|
27179
28188
|
vhs: {
|
|
27180
28189
|
// VHS (Video HTTP Streaming) options for HLS
|
|
@@ -27184,12 +28193,59 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27184
28193
|
// Use native HLS on Safari
|
|
27185
28194
|
enableLowInitialPlaylist: true,
|
|
27186
28195
|
smoothQualityChange: true,
|
|
27187
|
-
//
|
|
28196
|
+
// Optimized bandwidth and buffering for VOD
|
|
28197
|
+
bandwidth: 5e7,
|
|
28198
|
+
// Start with high bandwidth assumption (50 Mbps)
|
|
28199
|
+
initialBandwidth: 5e7,
|
|
28200
|
+
// Assume good connection initially
|
|
28201
|
+
limitRenditionByPlayerDimensions: true,
|
|
28202
|
+
// Buffer configuration for optimal VOD playback
|
|
28203
|
+
maxBufferLength: 30,
|
|
28204
|
+
// Don't over-buffer (30 seconds)
|
|
28205
|
+
maxMaxBufferLength: 60,
|
|
28206
|
+
// Absolute max buffer (60 seconds)
|
|
28207
|
+
maxBufferSize: 60 * 1e3 * 1e3,
|
|
28208
|
+
// 60MB max buffer size
|
|
28209
|
+
maxBufferHole: 0.5,
|
|
28210
|
+
// Allow 0.5s holes in buffer
|
|
28211
|
+
bufferBasedABR: true,
|
|
28212
|
+
// Segment loading optimization
|
|
27188
28213
|
maxPlaylistRetries: 3,
|
|
28214
|
+
playlistRetryDelay: 500,
|
|
28215
|
+
// 500ms between retries
|
|
27189
28216
|
playlistExclusionDuration: 60,
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
28217
|
+
segmentLoadingRetryAttempts: 3,
|
|
28218
|
+
segmentLoadingRetryDelay: 1e3,
|
|
28219
|
+
// 1s between segment retries
|
|
28220
|
+
segmentLoadingTimeOut: 2e4,
|
|
28221
|
+
// 20s timeout for segments
|
|
28222
|
+
manifestLoadingTimeOut: 1e4,
|
|
28223
|
+
// 10s timeout for manifest
|
|
28224
|
+
// Performance optimizations
|
|
28225
|
+
experimentalBufferBasedCodecSwitching: true,
|
|
28226
|
+
experimentalCacheEncryptionKeys: true,
|
|
28227
|
+
handlePartialData: true,
|
|
28228
|
+
allowSeeksWithinUnsafeLiveWindow: false,
|
|
28229
|
+
// VOD content
|
|
28230
|
+
experimentalLLHLS: false,
|
|
28231
|
+
// Disable Low Latency HLS for VOD
|
|
28232
|
+
// Connection settings
|
|
28233
|
+
enableWorker: true,
|
|
28234
|
+
// Use web worker for better performance
|
|
28235
|
+
progressive: true,
|
|
28236
|
+
// Progressive download
|
|
28237
|
+
// Adaptive bitrate settings (if multi-quality available)
|
|
28238
|
+
abrEwmaFastLive: 3,
|
|
28239
|
+
abrEwmaSlowLive: 9,
|
|
28240
|
+
abrBandWidthFactor: 0.95,
|
|
28241
|
+
abrBandWidthUpFactor: 0.7,
|
|
28242
|
+
abrMaxWithRealBitrate: false,
|
|
28243
|
+
// Request options for better network handling
|
|
28244
|
+
requestOptions: {
|
|
28245
|
+
timeout: 3e4,
|
|
28246
|
+
// 30s overall timeout
|
|
28247
|
+
maxRetry: 3
|
|
28248
|
+
}
|
|
27193
28249
|
},
|
|
27194
28250
|
nativeVideoTracks: false,
|
|
27195
28251
|
nativeAudioTracks: false,
|
|
@@ -27254,9 +28310,17 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27254
28310
|
onError?.(player, error);
|
|
27255
28311
|
});
|
|
27256
28312
|
if (src) {
|
|
28313
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
28314
|
+
let videoSrc = src;
|
|
28315
|
+
if (src.startsWith("#EXTM3U")) {
|
|
28316
|
+
const blob = new Blob([src], { type: "application/x-mpegURL" });
|
|
28317
|
+
videoSrc = URL.createObjectURL(blob);
|
|
28318
|
+
console.log("[VideoPlayer] Created blob URL for HLS playlist content");
|
|
28319
|
+
player._blobUrl = videoSrc;
|
|
28320
|
+
}
|
|
27257
28321
|
player.src({
|
|
27258
|
-
src,
|
|
27259
|
-
type:
|
|
28322
|
+
src: videoSrc,
|
|
28323
|
+
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
27260
28324
|
});
|
|
27261
28325
|
}
|
|
27262
28326
|
}, [
|
|
@@ -27276,16 +28340,34 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27276
28340
|
]);
|
|
27277
28341
|
useEffect(() => {
|
|
27278
28342
|
if (playerRef.current && src) {
|
|
28343
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
28344
|
+
let videoSrc = src;
|
|
28345
|
+
let blobUrl = null;
|
|
28346
|
+
if (src.startsWith("#EXTM3U")) {
|
|
28347
|
+
const blob = new Blob([src], { type: "application/x-mpegURL" });
|
|
28348
|
+
blobUrl = URL.createObjectURL(blob);
|
|
28349
|
+
videoSrc = blobUrl;
|
|
28350
|
+
console.log("[VideoPlayer] Created blob URL for HLS playlist content (source update)");
|
|
28351
|
+
}
|
|
27279
28352
|
playerRef.current.src({
|
|
27280
|
-
src,
|
|
27281
|
-
type:
|
|
28353
|
+
src: videoSrc,
|
|
28354
|
+
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
27282
28355
|
});
|
|
28356
|
+
return () => {
|
|
28357
|
+
if (blobUrl) {
|
|
28358
|
+
URL.revokeObjectURL(blobUrl);
|
|
28359
|
+
}
|
|
28360
|
+
};
|
|
27283
28361
|
}
|
|
27284
28362
|
}, [src]);
|
|
27285
28363
|
useEffect(() => {
|
|
27286
28364
|
initializePlayer();
|
|
27287
28365
|
return () => {
|
|
27288
28366
|
if (playerRef.current) {
|
|
28367
|
+
const blobUrl = playerRef.current._blobUrl;
|
|
28368
|
+
if (blobUrl) {
|
|
28369
|
+
URL.revokeObjectURL(blobUrl);
|
|
28370
|
+
}
|
|
27289
28371
|
playerRef.current.dispose();
|
|
27290
28372
|
playerRef.current = null;
|
|
27291
28373
|
setIsReady(false);
|
|
@@ -27357,6 +28439,233 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27357
28439
|
] });
|
|
27358
28440
|
});
|
|
27359
28441
|
VideoPlayer.displayName = "VideoPlayer";
|
|
28442
|
+
var CroppedVideoPlayer = forwardRef(({
|
|
28443
|
+
crop,
|
|
28444
|
+
debug = false,
|
|
28445
|
+
...videoProps
|
|
28446
|
+
}, ref) => {
|
|
28447
|
+
const videoContainerRef = useRef(null);
|
|
28448
|
+
const hiddenVideoRef = useRef(null);
|
|
28449
|
+
const canvasRef = useRef(null);
|
|
28450
|
+
const animationFrameRef = useRef(null);
|
|
28451
|
+
const videoElementRef = useRef(null);
|
|
28452
|
+
const [isVideoReady, setIsVideoReady] = useState(false);
|
|
28453
|
+
const [canvasDimensions, setCanvasDimensions] = useState({ width: 0, height: 0 });
|
|
28454
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
28455
|
+
const stopCanvasRendering = useCallback(() => {
|
|
28456
|
+
if (animationFrameRef.current) {
|
|
28457
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
28458
|
+
animationFrameRef.current = null;
|
|
28459
|
+
}
|
|
28460
|
+
}, []);
|
|
28461
|
+
useImperativeHandle(ref, () => ({
|
|
28462
|
+
player: hiddenVideoRef.current?.player || null,
|
|
28463
|
+
play: () => hiddenVideoRef.current?.play() || void 0,
|
|
28464
|
+
pause: () => hiddenVideoRef.current?.pause(),
|
|
28465
|
+
currentTime: (time2) => {
|
|
28466
|
+
if (time2 !== void 0 && hiddenVideoRef.current) {
|
|
28467
|
+
return hiddenVideoRef.current.currentTime(time2);
|
|
28468
|
+
}
|
|
28469
|
+
return hiddenVideoRef.current?.currentTime() || 0;
|
|
28470
|
+
},
|
|
28471
|
+
duration: () => hiddenVideoRef.current?.duration() || 0,
|
|
28472
|
+
paused: () => hiddenVideoRef.current?.paused() || true,
|
|
28473
|
+
mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
|
|
28474
|
+
volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
|
|
28475
|
+
dispose: () => {
|
|
28476
|
+
hiddenVideoRef.current?.dispose();
|
|
28477
|
+
stopCanvasRendering();
|
|
28478
|
+
},
|
|
28479
|
+
isReady: hiddenVideoRef.current?.isReady || false
|
|
28480
|
+
}), [stopCanvasRendering]);
|
|
28481
|
+
const calculateCanvasDimensions = useCallback(() => {
|
|
28482
|
+
if (!crop || !videoContainerRef.current) return;
|
|
28483
|
+
const container = videoContainerRef.current;
|
|
28484
|
+
const containerWidth = container.clientWidth;
|
|
28485
|
+
const containerHeight = container.clientHeight;
|
|
28486
|
+
const cropAspectRatio = crop.width / crop.height;
|
|
28487
|
+
let canvasWidth;
|
|
28488
|
+
let canvasHeight;
|
|
28489
|
+
if (containerWidth / containerHeight > cropAspectRatio) {
|
|
28490
|
+
canvasHeight = containerHeight;
|
|
28491
|
+
canvasWidth = canvasHeight * cropAspectRatio;
|
|
28492
|
+
} else {
|
|
28493
|
+
canvasWidth = containerWidth;
|
|
28494
|
+
canvasHeight = canvasWidth / cropAspectRatio;
|
|
28495
|
+
}
|
|
28496
|
+
setCanvasDimensions({
|
|
28497
|
+
width: Math.floor(canvasWidth),
|
|
28498
|
+
height: Math.floor(canvasHeight)
|
|
28499
|
+
});
|
|
28500
|
+
}, [crop]);
|
|
28501
|
+
const renderFrameToCanvas = useCallback(() => {
|
|
28502
|
+
if (!canvasRef.current || !videoElementRef.current || !crop) {
|
|
28503
|
+
return;
|
|
28504
|
+
}
|
|
28505
|
+
const canvas = canvasRef.current;
|
|
28506
|
+
const video = videoElementRef.current;
|
|
28507
|
+
const ctx = canvas.getContext("2d");
|
|
28508
|
+
if (!ctx || video.paused || video.ended) {
|
|
28509
|
+
return;
|
|
28510
|
+
}
|
|
28511
|
+
const videoWidth = video.videoWidth;
|
|
28512
|
+
const videoHeight = video.videoHeight;
|
|
28513
|
+
if (videoWidth === 0 || videoHeight === 0) {
|
|
28514
|
+
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
28515
|
+
return;
|
|
28516
|
+
}
|
|
28517
|
+
const cropX = crop.x / 100 * videoWidth;
|
|
28518
|
+
const cropY = crop.y / 100 * videoHeight;
|
|
28519
|
+
const cropWidth = crop.width / 100 * videoWidth;
|
|
28520
|
+
const cropHeight = crop.height / 100 * videoHeight;
|
|
28521
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
28522
|
+
ctx.drawImage(
|
|
28523
|
+
video,
|
|
28524
|
+
cropX,
|
|
28525
|
+
cropY,
|
|
28526
|
+
cropWidth,
|
|
28527
|
+
cropHeight,
|
|
28528
|
+
// Source rectangle (crop area)
|
|
28529
|
+
0,
|
|
28530
|
+
0,
|
|
28531
|
+
canvas.width,
|
|
28532
|
+
canvas.height
|
|
28533
|
+
// Destination (full canvas)
|
|
28534
|
+
);
|
|
28535
|
+
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
28536
|
+
}, [crop]);
|
|
28537
|
+
const handleVideoReady = useCallback((player) => {
|
|
28538
|
+
console.log("[CroppedVideoPlayer] Video player ready");
|
|
28539
|
+
const videoEl = player.el().querySelector("video");
|
|
28540
|
+
if (videoEl) {
|
|
28541
|
+
videoElementRef.current = videoEl;
|
|
28542
|
+
setIsVideoReady(true);
|
|
28543
|
+
}
|
|
28544
|
+
videoProps.onReady?.(player);
|
|
28545
|
+
}, [videoProps]);
|
|
28546
|
+
const handleVideoPlay = useCallback((player) => {
|
|
28547
|
+
console.log("[CroppedVideoPlayer] Video playing, starting canvas rendering");
|
|
28548
|
+
if (crop && canvasRef.current) {
|
|
28549
|
+
setIsProcessing(true);
|
|
28550
|
+
renderFrameToCanvas();
|
|
28551
|
+
}
|
|
28552
|
+
videoProps.onPlay?.(player);
|
|
28553
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28554
|
+
const handleVideoPause = useCallback((player) => {
|
|
28555
|
+
console.log("[CroppedVideoPlayer] Video paused, stopping canvas rendering");
|
|
28556
|
+
stopCanvasRendering();
|
|
28557
|
+
setIsProcessing(false);
|
|
28558
|
+
videoProps.onPause?.(player);
|
|
28559
|
+
}, [stopCanvasRendering, videoProps]);
|
|
28560
|
+
const handleVideoEnded = useCallback((player) => {
|
|
28561
|
+
console.log("[CroppedVideoPlayer] Video ended");
|
|
28562
|
+
stopCanvasRendering();
|
|
28563
|
+
setIsProcessing(false);
|
|
28564
|
+
videoProps.onEnded?.(player);
|
|
28565
|
+
}, [stopCanvasRendering, videoProps]);
|
|
28566
|
+
const handleSeeking = useCallback((player) => {
|
|
28567
|
+
console.log("[CroppedVideoPlayer] Video seeking");
|
|
28568
|
+
if (crop && !player.paused()) {
|
|
28569
|
+
renderFrameToCanvas();
|
|
28570
|
+
}
|
|
28571
|
+
videoProps.onSeeking?.(player);
|
|
28572
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28573
|
+
const handleSeeked = useCallback((player) => {
|
|
28574
|
+
console.log("[CroppedVideoPlayer] Video seeked");
|
|
28575
|
+
if (crop && !player.paused()) {
|
|
28576
|
+
renderFrameToCanvas();
|
|
28577
|
+
}
|
|
28578
|
+
videoProps.onSeeked?.(player);
|
|
28579
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28580
|
+
const handleLoadedMetadata = useCallback((player) => {
|
|
28581
|
+
console.log("[CroppedVideoPlayer] Video metadata loaded");
|
|
28582
|
+
calculateCanvasDimensions();
|
|
28583
|
+
videoProps.onLoadedMetadata?.(player);
|
|
28584
|
+
}, [calculateCanvasDimensions, videoProps]);
|
|
28585
|
+
useEffect(() => {
|
|
28586
|
+
calculateCanvasDimensions();
|
|
28587
|
+
const handleResize = () => {
|
|
28588
|
+
calculateCanvasDimensions();
|
|
28589
|
+
};
|
|
28590
|
+
window.addEventListener("resize", handleResize);
|
|
28591
|
+
return () => {
|
|
28592
|
+
window.removeEventListener("resize", handleResize);
|
|
28593
|
+
};
|
|
28594
|
+
}, [calculateCanvasDimensions]);
|
|
28595
|
+
useEffect(() => {
|
|
28596
|
+
return () => {
|
|
28597
|
+
stopCanvasRendering();
|
|
28598
|
+
};
|
|
28599
|
+
}, [stopCanvasRendering]);
|
|
28600
|
+
if (!crop) {
|
|
28601
|
+
return /* @__PURE__ */ jsx(VideoPlayer, { ref, ...videoProps });
|
|
28602
|
+
}
|
|
28603
|
+
return /* @__PURE__ */ jsxs(
|
|
28604
|
+
"div",
|
|
28605
|
+
{
|
|
28606
|
+
ref: videoContainerRef,
|
|
28607
|
+
className: `relative w-full h-full flex items-center justify-center bg-black ${videoProps.className || ""}`,
|
|
28608
|
+
children: [
|
|
28609
|
+
/* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
|
|
28610
|
+
VideoPlayer,
|
|
28611
|
+
{
|
|
28612
|
+
ref: hiddenVideoRef,
|
|
28613
|
+
...videoProps,
|
|
28614
|
+
onReady: handleVideoReady,
|
|
28615
|
+
onPlay: handleVideoPlay,
|
|
28616
|
+
onPause: handleVideoPause,
|
|
28617
|
+
onEnded: handleVideoEnded,
|
|
28618
|
+
onSeeking: handleSeeking,
|
|
28619
|
+
onSeeked: handleSeeked,
|
|
28620
|
+
onLoadedMetadata: handleLoadedMetadata
|
|
28621
|
+
}
|
|
28622
|
+
) }),
|
|
28623
|
+
/* @__PURE__ */ jsx(
|
|
28624
|
+
"canvas",
|
|
28625
|
+
{
|
|
28626
|
+
ref: canvasRef,
|
|
28627
|
+
width: canvasDimensions.width,
|
|
28628
|
+
height: canvasDimensions.height,
|
|
28629
|
+
className: "max-w-full max-h-full",
|
|
28630
|
+
style: {
|
|
28631
|
+
display: isVideoReady ? "block" : "none",
|
|
28632
|
+
width: `${canvasDimensions.width}px`,
|
|
28633
|
+
height: `${canvasDimensions.height}px`,
|
|
28634
|
+
pointerEvents: "none"
|
|
28635
|
+
// Allow clicks to pass through to controls
|
|
28636
|
+
}
|
|
28637
|
+
}
|
|
28638
|
+
),
|
|
28639
|
+
!isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
28640
|
+
debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
|
|
28641
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28642
|
+
"Crop: ",
|
|
28643
|
+
crop.x,
|
|
28644
|
+
",",
|
|
28645
|
+
crop.y,
|
|
28646
|
+
" ",
|
|
28647
|
+
crop.width,
|
|
28648
|
+
"x",
|
|
28649
|
+
crop.height,
|
|
28650
|
+
"%"
|
|
28651
|
+
] }),
|
|
28652
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28653
|
+
"Canvas: ",
|
|
28654
|
+
canvasDimensions.width,
|
|
28655
|
+
"x",
|
|
28656
|
+
canvasDimensions.height,
|
|
28657
|
+
"px"
|
|
28658
|
+
] }),
|
|
28659
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28660
|
+
"Processing: ",
|
|
28661
|
+
isProcessing ? "Yes" : "No"
|
|
28662
|
+
] })
|
|
28663
|
+
] })
|
|
28664
|
+
]
|
|
28665
|
+
}
|
|
28666
|
+
);
|
|
28667
|
+
});
|
|
28668
|
+
CroppedVideoPlayer.displayName = "CroppedVideoPlayer";
|
|
27360
28669
|
var BackButton = ({
|
|
27361
28670
|
onClick,
|
|
27362
28671
|
text = "Back",
|
|
@@ -27666,6 +28975,244 @@ var InlineEditableText = ({
|
|
|
27666
28975
|
}
|
|
27667
28976
|
);
|
|
27668
28977
|
};
|
|
28978
|
+
var getSupabaseClient3 = () => {
|
|
28979
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
28980
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
28981
|
+
if (!url || !key) {
|
|
28982
|
+
throw new Error("Supabase configuration missing");
|
|
28983
|
+
}
|
|
28984
|
+
return createClient(url, key);
|
|
28985
|
+
};
|
|
28986
|
+
var getAuthToken3 = async () => {
|
|
28987
|
+
try {
|
|
28988
|
+
const supabase = getSupabaseClient3();
|
|
28989
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
28990
|
+
return session?.access_token || null;
|
|
28991
|
+
} catch (error) {
|
|
28992
|
+
console.error("[useWorkspaceCrop] Error getting auth token:", error);
|
|
28993
|
+
return null;
|
|
28994
|
+
}
|
|
28995
|
+
};
|
|
28996
|
+
function useWorkspaceCrop(workspaceId) {
|
|
28997
|
+
const [crop, setCrop] = useState(null);
|
|
28998
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
28999
|
+
const [error, setError] = useState(null);
|
|
29000
|
+
useEffect(() => {
|
|
29001
|
+
if (!workspaceId) {
|
|
29002
|
+
setIsLoading(false);
|
|
29003
|
+
return;
|
|
29004
|
+
}
|
|
29005
|
+
const fetchCrop = async () => {
|
|
29006
|
+
setIsLoading(true);
|
|
29007
|
+
setError(null);
|
|
29008
|
+
try {
|
|
29009
|
+
const token = await getAuthToken3();
|
|
29010
|
+
if (!token) {
|
|
29011
|
+
throw new Error("Authentication required");
|
|
29012
|
+
}
|
|
29013
|
+
const response = await fetch("/api/clips/supabase", {
|
|
29014
|
+
method: "POST",
|
|
29015
|
+
headers: {
|
|
29016
|
+
"Content-Type": "application/json",
|
|
29017
|
+
"Authorization": `Bearer ${token}`
|
|
29018
|
+
},
|
|
29019
|
+
body: JSON.stringify({
|
|
29020
|
+
action: "crop",
|
|
29021
|
+
workspaceId
|
|
29022
|
+
})
|
|
29023
|
+
});
|
|
29024
|
+
if (!response.ok) {
|
|
29025
|
+
throw new Error(`Failed to fetch crop: ${response.statusText}`);
|
|
29026
|
+
}
|
|
29027
|
+
const data = await response.json();
|
|
29028
|
+
console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
|
|
29029
|
+
setCrop(data.crop);
|
|
29030
|
+
} catch (err) {
|
|
29031
|
+
console.error("[useWorkspaceCrop] Error fetching crop:", err);
|
|
29032
|
+
setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
|
|
29033
|
+
setCrop(null);
|
|
29034
|
+
} finally {
|
|
29035
|
+
setIsLoading(false);
|
|
29036
|
+
}
|
|
29037
|
+
};
|
|
29038
|
+
fetchCrop();
|
|
29039
|
+
}, [workspaceId]);
|
|
29040
|
+
return { crop, isLoading, error };
|
|
29041
|
+
}
|
|
29042
|
+
var getSeverityIcon = (severity) => {
|
|
29043
|
+
switch (severity) {
|
|
29044
|
+
case "high":
|
|
29045
|
+
return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
|
|
29046
|
+
case "medium":
|
|
29047
|
+
return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-yellow-500" });
|
|
29048
|
+
case "low":
|
|
29049
|
+
return /* @__PURE__ */ jsx(CheckCircle, { className: "h-3 w-3 text-green-500" });
|
|
29050
|
+
default:
|
|
29051
|
+
return /* @__PURE__ */ jsx(XCircle, { className: "h-3 w-3 text-gray-400" });
|
|
29052
|
+
}
|
|
29053
|
+
};
|
|
29054
|
+
var getColorClasses = (color2) => {
|
|
29055
|
+
const colorMap = {
|
|
29056
|
+
"red": { bg: "bg-red-50", text: "text-red-700", border: "border-red-200" },
|
|
29057
|
+
"yellow": { bg: "bg-yellow-50", text: "text-yellow-700", border: "border-yellow-200" },
|
|
29058
|
+
"orange": { bg: "bg-orange-50", text: "text-orange-700", border: "border-orange-200" },
|
|
29059
|
+
"green": { bg: "bg-green-50", text: "text-green-700", border: "border-green-200" },
|
|
29060
|
+
"blue": { bg: "bg-blue-50", text: "text-blue-700", border: "border-blue-200" },
|
|
29061
|
+
"purple": { bg: "bg-purple-50", text: "text-purple-700", border: "border-purple-200" },
|
|
29062
|
+
"gray": { bg: "bg-gray-50", text: "text-gray-700", border: "border-gray-200" }
|
|
29063
|
+
};
|
|
29064
|
+
return colorMap[color2] || colorMap["gray"];
|
|
29065
|
+
};
|
|
29066
|
+
var FileManagerFilters = ({
|
|
29067
|
+
categories,
|
|
29068
|
+
videos,
|
|
29069
|
+
activeFilter,
|
|
29070
|
+
currentVideoId,
|
|
29071
|
+
counts,
|
|
29072
|
+
onFilterChange,
|
|
29073
|
+
onVideoSelect,
|
|
29074
|
+
className = ""
|
|
29075
|
+
}) => {
|
|
29076
|
+
const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
|
|
29077
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
29078
|
+
const filterTree = useMemo(() => {
|
|
29079
|
+
const tree = [];
|
|
29080
|
+
categories.forEach((category) => {
|
|
29081
|
+
const categoryCount = counts?.[category.id] || 0;
|
|
29082
|
+
let categoryVideos = videos.filter((video) => video.type === category.id);
|
|
29083
|
+
if (searchTerm.trim()) {
|
|
29084
|
+
categoryVideos = categoryVideos.filter(
|
|
29085
|
+
(video) => video.description.toLowerCase().includes(searchTerm.toLowerCase()) || video.timestamp.includes(searchTerm) || video.severity.toLowerCase().includes(searchTerm.toLowerCase())
|
|
29086
|
+
);
|
|
29087
|
+
}
|
|
29088
|
+
if (categoryCount > 0 || categoryVideos.length > 0) {
|
|
29089
|
+
const colorClasses = getColorClasses(category.color);
|
|
29090
|
+
const videoNodes = categoryVideos.map((video, index) => ({
|
|
29091
|
+
id: video.id,
|
|
29092
|
+
label: `${video.timestamp.substring(11, 19)} - ${video.description}`,
|
|
29093
|
+
type: "video",
|
|
29094
|
+
icon: getSeverityIcon(video.severity),
|
|
29095
|
+
timestamp: video.timestamp,
|
|
29096
|
+
severity: video.severity
|
|
29097
|
+
}));
|
|
29098
|
+
tree.push({
|
|
29099
|
+
id: category.id,
|
|
29100
|
+
label: category.label,
|
|
29101
|
+
type: "category",
|
|
29102
|
+
count: categoryCount || categoryVideos.length,
|
|
29103
|
+
// Use API count if available
|
|
29104
|
+
videos: categoryVideos,
|
|
29105
|
+
children: videoNodes,
|
|
29106
|
+
icon: expandedNodes.has(category.id) ? /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }) : /* @__PURE__ */ jsx(Folder, { className: `h-4 w-4 ${colorClasses.text}` }),
|
|
29107
|
+
color: category.color
|
|
29108
|
+
});
|
|
29109
|
+
}
|
|
29110
|
+
});
|
|
29111
|
+
return tree;
|
|
29112
|
+
}, [categories, videos, expandedNodes, searchTerm, counts]);
|
|
29113
|
+
const toggleExpanded = (nodeId) => {
|
|
29114
|
+
const newExpanded = new Set(expandedNodes);
|
|
29115
|
+
if (newExpanded.has(nodeId)) {
|
|
29116
|
+
newExpanded.delete(nodeId);
|
|
29117
|
+
} else {
|
|
29118
|
+
newExpanded.add(nodeId);
|
|
29119
|
+
}
|
|
29120
|
+
setExpandedNodes(newExpanded);
|
|
29121
|
+
};
|
|
29122
|
+
const handleNodeClick = (node) => {
|
|
29123
|
+
if (node.type === "category") {
|
|
29124
|
+
toggleExpanded(node.id);
|
|
29125
|
+
onFilterChange(node.id);
|
|
29126
|
+
} else if (node.type === "video") {
|
|
29127
|
+
const videoIndex = videos.findIndex((v) => v.id === node.id);
|
|
29128
|
+
if (videoIndex !== -1) {
|
|
29129
|
+
onVideoSelect(videoIndex);
|
|
29130
|
+
}
|
|
29131
|
+
}
|
|
29132
|
+
};
|
|
29133
|
+
const renderNode = (node, depth = 0) => {
|
|
29134
|
+
const isExpanded = expandedNodes.has(node.id);
|
|
29135
|
+
const isActive = activeFilter === node.id;
|
|
29136
|
+
const isCurrentVideo = currentVideoId === node.id;
|
|
29137
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
29138
|
+
const colorClasses = node.color ? getColorClasses(node.color) : null;
|
|
29139
|
+
return /* @__PURE__ */ jsxs("div", { className: "select-none animate-in", children: [
|
|
29140
|
+
/* @__PURE__ */ jsxs(
|
|
29141
|
+
"div",
|
|
29142
|
+
{
|
|
29143
|
+
className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-emerald-50 to-green-50/60 border border-emerald-200/50 shadow-md shadow-emerald-100/20" : ""}`} ${node.type === "video" ? "ml-6" : ""}`,
|
|
29144
|
+
onClick: () => handleNodeClick(node),
|
|
29145
|
+
children: [
|
|
29146
|
+
hasChildren && /* @__PURE__ */ jsx(
|
|
29147
|
+
"button",
|
|
29148
|
+
{
|
|
29149
|
+
className: `flex-shrink-0 mr-2 p-1.5 rounded-lg hover:bg-white/80 hover:shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-400/50 focus:bg-white ${node.type === "category" ? "group-hover:scale-110" : "group-hover:scale-105"}`,
|
|
29150
|
+
onClick: (e) => {
|
|
29151
|
+
e.stopPropagation();
|
|
29152
|
+
toggleExpanded(node.id);
|
|
29153
|
+
},
|
|
29154
|
+
children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" ? "h-4 w-4" : "h-3.5 w-3.5"}` }) : /* @__PURE__ */ jsx(ChevronRight, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" ? "h-4 w-4" : "h-3.5 w-3.5"}` })
|
|
29155
|
+
}
|
|
29156
|
+
),
|
|
29157
|
+
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && node.type === "category" ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.icon }),
|
|
29158
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
|
|
29159
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
29160
|
+
/* @__PURE__ */ jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-emerald-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
|
|
29161
|
+
node.type === "video" && node.severity && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${node.severity === "high" ? "bg-red-100 text-red-700" : node.severity === "medium" ? "bg-yellow-100 text-yellow-700" : "bg-green-100 text-green-700"}`, children: [
|
|
29162
|
+
node.severity,
|
|
29163
|
+
" priority"
|
|
29164
|
+
] }) })
|
|
29165
|
+
] }),
|
|
29166
|
+
node.count !== void 0 && node.type === "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 }) })
|
|
29167
|
+
] })
|
|
29168
|
+
]
|
|
29169
|
+
}
|
|
29170
|
+
),
|
|
29171
|
+
hasChildren && isExpanded && /* @__PURE__ */ jsx("div", { className: "mt-2 ml-3 space-y-1 animate-in border-l-2 border-slate-100 pl-3", children: node.children.map((child) => renderNode(child, depth + 1)) })
|
|
29172
|
+
] }, node.id);
|
|
29173
|
+
};
|
|
29174
|
+
return /* @__PURE__ */ jsxs("div", { className: `bg-white rounded-2xl shadow-lg border border-gray-100 h-full hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
|
|
29175
|
+
/* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-50 bg-gradient-to-br from-slate-50/80 via-white to-blue-50/30", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
29176
|
+
/* @__PURE__ */ jsx("div", { className: "mr-3", children: /* @__PURE__ */ jsx(Folder, { className: "h-5 w-5 text-slate-700" }) }),
|
|
29177
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-slate-900 tracking-tight", children: "Clips Explorer" }) })
|
|
29178
|
+
] }) }),
|
|
29179
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-slate-100/80", children: /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
29180
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Search, { className: "h-5 w-5 text-slate-400 group-focus-within:text-blue-500 transition-colors duration-200" }) }),
|
|
29181
|
+
/* @__PURE__ */ jsx(
|
|
29182
|
+
"input",
|
|
29183
|
+
{
|
|
29184
|
+
type: "text",
|
|
29185
|
+
placeholder: "Show me idle clips from 10am",
|
|
29186
|
+
value: searchTerm,
|
|
29187
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
29188
|
+
className: "w-full pl-12 pr-4 py-2.5 bg-slate-50/50 border border-slate-200/60 rounded-xl text-sm font-medium placeholder:text-slate-400 placeholder:font-normal focus:outline-none focus:bg-white focus:border-blue-300 focus:ring-4 focus:ring-blue-100/50 transition-all duration-200 hover:border-slate-300 hover:bg-white/80"
|
|
29189
|
+
}
|
|
29190
|
+
),
|
|
29191
|
+
searchTerm && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-4 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "h-2 w-2 bg-blue-500 rounded-full animate-pulse" }) })
|
|
29192
|
+
] }) }),
|
|
29193
|
+
/* @__PURE__ */ jsxs("div", { className: "px-4 py-3 h-[calc(100%-14rem)] overflow-y-auto scrollbar-thin", children: [
|
|
29194
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
|
|
29195
|
+
filterTree.length === 0 && searchTerm && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
|
|
29196
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Search, { className: "h-12 w-12 mx-auto" }) }),
|
|
29197
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
|
|
29198
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500 mb-4", children: [
|
|
29199
|
+
'No clips match "',
|
|
29200
|
+
searchTerm,
|
|
29201
|
+
'"'
|
|
29202
|
+
] }),
|
|
29203
|
+
/* @__PURE__ */ jsx(
|
|
29204
|
+
"button",
|
|
29205
|
+
{
|
|
29206
|
+
onClick: () => setSearchTerm(""),
|
|
29207
|
+
className: "inline-flex items-center px-4 py-2 bg-blue-50 text-blue-600 text-sm font-medium rounded-lg hover:bg-blue-100 transition-colors duration-200",
|
|
29208
|
+
children: "Clear search"
|
|
29209
|
+
}
|
|
29210
|
+
)
|
|
29211
|
+
] })
|
|
29212
|
+
] })
|
|
29213
|
+
] });
|
|
29214
|
+
};
|
|
29215
|
+
var USE_SUPABASE_CLIPS2 = true;
|
|
27669
29216
|
var BottlenecksContent = ({
|
|
27670
29217
|
workspaceId,
|
|
27671
29218
|
workspaceName,
|
|
@@ -27674,17 +29221,9 @@ var BottlenecksContent = ({
|
|
|
27674
29221
|
className
|
|
27675
29222
|
}) => {
|
|
27676
29223
|
const dashboardConfig = useDashboardConfig();
|
|
27677
|
-
const
|
|
27678
|
-
const sopConfig = dashboardConfig?.s3Config?.sopCategories;
|
|
27679
|
-
if (!sopConfig) return null;
|
|
27680
|
-
if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
|
|
27681
|
-
return sopConfig.workspaceOverrides[workspaceId];
|
|
27682
|
-
}
|
|
27683
|
-
return sopConfig.default;
|
|
27684
|
-
}, [dashboardConfig, workspaceId]);
|
|
29224
|
+
const { crop: workspaceCrop} = useWorkspaceCrop(workspaceId);
|
|
27685
29225
|
const videoRef = useRef(null);
|
|
27686
|
-
const
|
|
27687
|
-
const initialFilter = sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value";
|
|
29226
|
+
const [initialFilter, setInitialFilter] = useState("");
|
|
27688
29227
|
const currentIndexRef = useRef(0);
|
|
27689
29228
|
const activeFilterRef = useRef(initialFilter);
|
|
27690
29229
|
const isMountedRef = useRef(true);
|
|
@@ -27714,22 +29253,6 @@ var BottlenecksContent = ({
|
|
|
27714
29253
|
useEffect(() => {
|
|
27715
29254
|
currentIndexRef.current = currentIndex;
|
|
27716
29255
|
}, [currentIndex]);
|
|
27717
|
-
const [showTimestampFilter, setShowTimestampFilter] = useState(false);
|
|
27718
|
-
const [timestampStart, setTimestampStart] = useState("");
|
|
27719
|
-
const [timestampEnd, setTimestampEnd] = useState("");
|
|
27720
|
-
useEffect(() => {
|
|
27721
|
-
const handleClickOutside = (event) => {
|
|
27722
|
-
if (timestampFilterRef.current && !timestampFilterRef.current.contains(event.target)) {
|
|
27723
|
-
setShowTimestampFilter(false);
|
|
27724
|
-
}
|
|
27725
|
-
};
|
|
27726
|
-
if (showTimestampFilter) {
|
|
27727
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
27728
|
-
}
|
|
27729
|
-
return () => {
|
|
27730
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
27731
|
-
};
|
|
27732
|
-
}, [showTimestampFilter]);
|
|
27733
29256
|
const s3ClipsService = useMemo(() => {
|
|
27734
29257
|
if (!dashboardConfig?.s3Config) {
|
|
27735
29258
|
console.warn("S3 configuration not found in dashboard config");
|
|
@@ -27737,6 +29260,39 @@ var BottlenecksContent = ({
|
|
|
27737
29260
|
}
|
|
27738
29261
|
return videoPrefetchManager.getS3Service(dashboardConfig);
|
|
27739
29262
|
}, [dashboardConfig]);
|
|
29263
|
+
const {
|
|
29264
|
+
clipTypes,
|
|
29265
|
+
isLoading: clipTypesLoading,
|
|
29266
|
+
error: clipTypesError,
|
|
29267
|
+
counts: dynamicCounts
|
|
29268
|
+
} = useClipTypesWithCounts(
|
|
29269
|
+
workspaceId,
|
|
29270
|
+
date || getOperationalDate(),
|
|
29271
|
+
shift || "0"
|
|
29272
|
+
// Use shift || '0' as in the original working code
|
|
29273
|
+
);
|
|
29274
|
+
console.log("[BottlenecksContent] Clip types data:", {
|
|
29275
|
+
clipTypes,
|
|
29276
|
+
clipTypesLength: clipTypes?.length,
|
|
29277
|
+
clipTypesLoading,
|
|
29278
|
+
clipTypesError,
|
|
29279
|
+
dynamicCounts,
|
|
29280
|
+
workspaceId,
|
|
29281
|
+
date: date || getOperationalDate(),
|
|
29282
|
+
shift: shift || "0",
|
|
29283
|
+
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29284
|
+
});
|
|
29285
|
+
useEffect(() => {
|
|
29286
|
+
if (clipTypes.length > 0 && !initialFilter) {
|
|
29287
|
+
const firstWithCounts = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
|
|
29288
|
+
const firstType = firstWithCounts || clipTypes[0];
|
|
29289
|
+
if (firstType) {
|
|
29290
|
+
setInitialFilter(firstType.type);
|
|
29291
|
+
setActiveFilter(firstType.type);
|
|
29292
|
+
activeFilterRef.current = firstType.type;
|
|
29293
|
+
}
|
|
29294
|
+
}
|
|
29295
|
+
}, [clipTypes, dynamicCounts, initialFilter]);
|
|
27740
29296
|
const effectiveShift = useMemo(() => {
|
|
27741
29297
|
if (shift !== void 0 && shift !== null) {
|
|
27742
29298
|
const shiftStr = shift.toString();
|
|
@@ -27755,6 +29311,9 @@ var BottlenecksContent = ({
|
|
|
27755
29311
|
return currentShift.shiftId.toString();
|
|
27756
29312
|
}
|
|
27757
29313
|
}, [shift, date, dashboardConfig]);
|
|
29314
|
+
const mergedCounts = useMemo(() => {
|
|
29315
|
+
return { ...clipCounts, ...dynamicCounts };
|
|
29316
|
+
}, [clipCounts, dynamicCounts]);
|
|
27758
29317
|
const {
|
|
27759
29318
|
data: prefetchData,
|
|
27760
29319
|
isFullyIndexed,
|
|
@@ -27789,17 +29348,22 @@ var BottlenecksContent = ({
|
|
|
27789
29348
|
setHasInitialLoad(true);
|
|
27790
29349
|
return;
|
|
27791
29350
|
}
|
|
27792
|
-
console.log(`[BottlenecksContent] Fetching clip counts
|
|
29351
|
+
console.log(`[BottlenecksContent] Fetching clip counts directly with params:`, {
|
|
29352
|
+
workspaceId,
|
|
29353
|
+
operationalDate,
|
|
29354
|
+
shiftStr
|
|
29355
|
+
});
|
|
27793
29356
|
const fullResult = await s3ClipsService.getClipCounts(
|
|
27794
29357
|
workspaceId,
|
|
27795
29358
|
operationalDate,
|
|
27796
29359
|
shiftStr
|
|
27797
29360
|
// Don't build index - use pagination for cost efficiency
|
|
27798
29361
|
);
|
|
29362
|
+
console.log(`[BottlenecksContent] Direct fetch result:`, fullResult);
|
|
27799
29363
|
if (fullResult) {
|
|
27800
29364
|
const counts = fullResult;
|
|
27801
29365
|
updateClipCounts(counts);
|
|
27802
|
-
console.log(`[BottlenecksContent]
|
|
29366
|
+
console.log(`[BottlenecksContent] Updated clip counts:`, counts);
|
|
27803
29367
|
}
|
|
27804
29368
|
setIsLoading(false);
|
|
27805
29369
|
setHasInitialLoad(true);
|
|
@@ -27820,13 +29384,7 @@ var BottlenecksContent = ({
|
|
|
27820
29384
|
const ensureVideosLoaded = useCallback(async (centerIndex) => {
|
|
27821
29385
|
if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
|
|
27822
29386
|
const currentFilter = activeFilterRef.current;
|
|
27823
|
-
|
|
27824
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
27825
|
-
const category = sopCategories.find((cat) => cat.id === currentFilter);
|
|
27826
|
-
if (category && category.s3FolderName) {
|
|
27827
|
-
effectiveFilter = category.s3FolderName;
|
|
27828
|
-
}
|
|
27829
|
-
}
|
|
29387
|
+
const effectiveFilter = currentFilter;
|
|
27830
29388
|
const cacheKey = `${effectiveFilter}:${date}:${shift}`;
|
|
27831
29389
|
if (!loadedVideosMapRef.current.has(cacheKey)) {
|
|
27832
29390
|
loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
|
|
@@ -27835,7 +29393,7 @@ var BottlenecksContent = ({
|
|
|
27835
29393
|
const indicesToLoad = [];
|
|
27836
29394
|
const rangeBefore = 1;
|
|
27837
29395
|
const rangeAfter = 3;
|
|
27838
|
-
for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(
|
|
29396
|
+
for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(mergedCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
|
|
27839
29397
|
if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
|
|
27840
29398
|
indicesToLoad.push(i);
|
|
27841
29399
|
}
|
|
@@ -27910,7 +29468,7 @@ var BottlenecksContent = ({
|
|
|
27910
29468
|
} finally {
|
|
27911
29469
|
indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
|
|
27912
29470
|
}
|
|
27913
|
-
}, [s3ClipsService, workspaceId, clipCounts,
|
|
29471
|
+
}, [s3ClipsService, workspaceId, clipCounts, date, effectiveShift]);
|
|
27914
29472
|
const loadFirstVideoForCategory = useCallback(async (category) => {
|
|
27915
29473
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
27916
29474
|
const targetCategory = category || activeFilterRef.current;
|
|
@@ -27927,7 +29485,7 @@ var BottlenecksContent = ({
|
|
|
27927
29485
|
try {
|
|
27928
29486
|
const operationalDate = date || getOperationalDate();
|
|
27929
29487
|
const shiftStr = effectiveShift;
|
|
27930
|
-
if (!
|
|
29488
|
+
if (!mergedCounts[targetCategory]) {
|
|
27931
29489
|
const cacheKey = `clip-counts:${workspaceId}:${operationalDate}:${shiftStr}`;
|
|
27932
29490
|
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
27933
29491
|
if (cachedResult && cachedResult.counts[targetCategory] > 0) {
|
|
@@ -27963,7 +29521,7 @@ var BottlenecksContent = ({
|
|
|
27963
29521
|
} catch (directError) {
|
|
27964
29522
|
console.warn(`[BottlenecksContent] Direct S3 loading failed, trying video index:`, directError);
|
|
27965
29523
|
}
|
|
27966
|
-
if (
|
|
29524
|
+
if (mergedCounts[targetCategory] > 0) {
|
|
27967
29525
|
try {
|
|
27968
29526
|
const operationalDate2 = date || getOperationalDate();
|
|
27969
29527
|
const shiftStr2 = effectiveShift;
|
|
@@ -28017,47 +29575,27 @@ var BottlenecksContent = ({
|
|
|
28017
29575
|
}
|
|
28018
29576
|
}, [workspaceId, date, effectiveShift, s3ClipsService, fetchClipCounts, updateClipCounts, prefetchData]);
|
|
28019
29577
|
useEffect(() => {
|
|
28020
|
-
|
|
28021
|
-
|
|
28022
|
-
|
|
28023
|
-
|
|
28024
|
-
|
|
28025
|
-
|
|
29578
|
+
console.log(`[BottlenecksContent] prefetchData update:`, {
|
|
29579
|
+
hasPrefetchData: !!prefetchData,
|
|
29580
|
+
prefetchStatus,
|
|
29581
|
+
prefetchData
|
|
29582
|
+
});
|
|
29583
|
+
if (prefetchData) {
|
|
29584
|
+
const counts = prefetchData.counts || prefetchData;
|
|
29585
|
+
console.log(`[BottlenecksContent] Extracted counts from prefetch:`, counts);
|
|
29586
|
+
if (counts && Object.keys(counts).length > 0) {
|
|
29587
|
+
updateClipCounts(counts);
|
|
29588
|
+
if (!hasInitialLoad) {
|
|
29589
|
+
setIsLoading(false);
|
|
29590
|
+
setHasInitialLoad(true);
|
|
29591
|
+
}
|
|
28026
29592
|
}
|
|
28027
29593
|
}
|
|
28028
29594
|
}, [prefetchData, prefetchStatus, updateClipCounts, hasInitialLoad]);
|
|
28029
29595
|
useEffect(() => {
|
|
28030
|
-
if (s3ClipsService &&
|
|
29596
|
+
if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
|
|
28031
29597
|
const hasVideosForCurrentFilter = allVideos.some((video) => {
|
|
28032
|
-
|
|
28033
|
-
const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
|
|
28034
|
-
if (selectedCategory) {
|
|
28035
|
-
return video.type === selectedCategory.id;
|
|
28036
|
-
}
|
|
28037
|
-
}
|
|
28038
|
-
if (activeFilter === "all") return true;
|
|
28039
|
-
if (activeFilter === "low_value") {
|
|
28040
|
-
return video.type === "low_value";
|
|
28041
|
-
}
|
|
28042
|
-
if (activeFilter === "idle_time") {
|
|
28043
|
-
return video.type === "idle_time";
|
|
28044
|
-
}
|
|
28045
|
-
if (activeFilter === "sop_deviations") {
|
|
28046
|
-
return video.type === "missing_quality_check";
|
|
28047
|
-
}
|
|
28048
|
-
if (activeFilter === "best_cycle_time") {
|
|
28049
|
-
return video.type === "best_cycle_time";
|
|
28050
|
-
}
|
|
28051
|
-
if (activeFilter === "worst_cycle_time") {
|
|
28052
|
-
return video.type === "worst_cycle_time";
|
|
28053
|
-
}
|
|
28054
|
-
if (activeFilter === "cycle_completion") {
|
|
28055
|
-
return video.type === "cycle_completion";
|
|
28056
|
-
}
|
|
28057
|
-
if (activeFilter === "long_cycle_time") {
|
|
28058
|
-
return video.type === "long_cycle_time";
|
|
28059
|
-
}
|
|
28060
|
-
return video.type === "bottleneck" && video.severity === activeFilter;
|
|
29598
|
+
return video.type === activeFilter;
|
|
28061
29599
|
});
|
|
28062
29600
|
if (!hasVideosForCurrentFilter) {
|
|
28063
29601
|
loadFirstVideoForCategory(activeFilter);
|
|
@@ -28065,7 +29603,7 @@ var BottlenecksContent = ({
|
|
|
28065
29603
|
setIsCategoryLoading(false);
|
|
28066
29604
|
}
|
|
28067
29605
|
}
|
|
28068
|
-
}, [activeFilter, s3ClipsService,
|
|
29606
|
+
}, [activeFilter, s3ClipsService, mergedCounts, loadFirstVideoForCategory, allVideos]);
|
|
28069
29607
|
useEffect(() => {
|
|
28070
29608
|
if (previousFilterRef.current !== activeFilter) {
|
|
28071
29609
|
console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
|
|
@@ -28077,15 +29615,7 @@ var BottlenecksContent = ({
|
|
|
28077
29615
|
previousFilterRef.current = activeFilter;
|
|
28078
29616
|
const filtered = allVideos.filter((video) => {
|
|
28079
29617
|
if (activeFilter === "all") return true;
|
|
28080
|
-
|
|
28081
|
-
if (activeFilter === "sop_deviations") return video.type === "missing_quality_check";
|
|
28082
|
-
if (activeFilter === "best_cycle_time") return video.type === "best_cycle_time";
|
|
28083
|
-
if (activeFilter === "worst_cycle_time") return video.type === "worst_cycle_time";
|
|
28084
|
-
if (activeFilter === "cycle_completion") return video.type === "cycle_completion";
|
|
28085
|
-
if (activeFilter === "long_cycle_time") {
|
|
28086
|
-
return video.type === "long_cycle_time";
|
|
28087
|
-
}
|
|
28088
|
-
return video.type === "bottleneck" && video.severity === activeFilter;
|
|
29618
|
+
return video.type === activeFilter;
|
|
28089
29619
|
});
|
|
28090
29620
|
if (filtered.length > 0) {
|
|
28091
29621
|
preloadVideoUrl(filtered[0].src);
|
|
@@ -28094,46 +29624,25 @@ var BottlenecksContent = ({
|
|
|
28094
29624
|
setIsCategoryLoading(false);
|
|
28095
29625
|
}
|
|
28096
29626
|
}, 150);
|
|
28097
|
-
} else if (
|
|
29627
|
+
} else if ((mergedCounts[activeFilter] || 0) === 0) {
|
|
28098
29628
|
if (isMountedRef.current) {
|
|
28099
29629
|
setIsCategoryLoading(false);
|
|
28100
29630
|
}
|
|
28101
29631
|
}
|
|
28102
29632
|
}
|
|
28103
|
-
}, [activeFilter, allVideos,
|
|
29633
|
+
}, [activeFilter, allVideos, mergedCounts]);
|
|
28104
29634
|
const filteredVideos = useMemo(() => {
|
|
28105
29635
|
if (!allVideos) return [];
|
|
28106
29636
|
let filtered = [];
|
|
28107
29637
|
if (activeFilter === "all") {
|
|
28108
29638
|
filtered = [...allVideos];
|
|
28109
29639
|
} else {
|
|
28110
|
-
|
|
28111
|
-
const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
|
|
28112
|
-
if (selectedCategory) {
|
|
28113
|
-
filtered = allVideos.filter((video) => video.type === selectedCategory.id);
|
|
28114
|
-
}
|
|
28115
|
-
} else {
|
|
28116
|
-
if (activeFilter === "low_value") {
|
|
28117
|
-
filtered = allVideos.filter((video) => video.type === "low_value");
|
|
28118
|
-
} else if (activeFilter === "sop_deviations") {
|
|
28119
|
-
filtered = allVideos.filter((video) => video.type === "missing_quality_check");
|
|
28120
|
-
} else if (activeFilter === "best_cycle_time") {
|
|
28121
|
-
filtered = allVideos.filter((video) => video.type === "best_cycle_time");
|
|
28122
|
-
} else if (activeFilter === "worst_cycle_time") {
|
|
28123
|
-
filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
|
|
28124
|
-
} else if (activeFilter === "cycle_completion") {
|
|
28125
|
-
filtered = allVideos.filter((video) => video.type === "cycle_completion");
|
|
28126
|
-
} else if (activeFilter === "long_cycle_time") {
|
|
28127
|
-
filtered = allVideos.filter((video) => video.type === "long_cycle_time");
|
|
28128
|
-
} else {
|
|
28129
|
-
filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
|
|
28130
|
-
}
|
|
28131
|
-
}
|
|
29640
|
+
filtered = allVideos.filter((video) => video.type === activeFilter);
|
|
28132
29641
|
}
|
|
28133
29642
|
return filtered.sort((a, b) => {
|
|
28134
29643
|
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
28135
29644
|
});
|
|
28136
|
-
}, [activeFilter, allVideos
|
|
29645
|
+
}, [activeFilter, allVideos]);
|
|
28137
29646
|
useEffect(() => {
|
|
28138
29647
|
if (isNavigating && currentIndex < filteredVideos.length) {
|
|
28139
29648
|
setIsNavigating(false);
|
|
@@ -28158,15 +29667,9 @@ var BottlenecksContent = ({
|
|
|
28158
29667
|
const currentIdx = currentIndexRef.current;
|
|
28159
29668
|
const currentFilter = activeFilterRef.current;
|
|
28160
29669
|
const nextIndex = currentIdx + 1;
|
|
28161
|
-
|
|
28162
|
-
|
|
28163
|
-
|
|
28164
|
-
if (category && category.s3FolderName) {
|
|
28165
|
-
effectiveFilter = category.s3FolderName;
|
|
28166
|
-
}
|
|
28167
|
-
}
|
|
28168
|
-
console.log(`[handleNext] Navigation check: nextIndex=${nextIndex}, currentFilter='${currentFilter}', effectiveFilter='${effectiveFilter}', clipCounts[effectiveFilter]=${clipCounts[effectiveFilter]}, clipCounts keys:`, Object.keys(clipCounts));
|
|
28169
|
-
if (nextIndex < clipCounts[effectiveFilter]) {
|
|
29670
|
+
const effectiveFilter = currentFilter;
|
|
29671
|
+
console.log(`[handleNext] Navigation check: nextIndex=${nextIndex}, currentFilter='${currentFilter}', effectiveFilter='${effectiveFilter}', mergedCounts[effectiveFilter]=${mergedCounts[effectiveFilter]}, mergedCounts keys:`, Object.keys(mergedCounts));
|
|
29672
|
+
if (nextIndex < mergedCounts[effectiveFilter]) {
|
|
28170
29673
|
if (isMountedRef.current) {
|
|
28171
29674
|
setCurrentIndex(nextIndex);
|
|
28172
29675
|
setError(null);
|
|
@@ -28320,17 +29823,6 @@ var BottlenecksContent = ({
|
|
|
28320
29823
|
player.pause();
|
|
28321
29824
|
}
|
|
28322
29825
|
};
|
|
28323
|
-
const mappedCounts = useMemo(() => {
|
|
28324
|
-
const counts = { ...clipCounts };
|
|
28325
|
-
counts.lowValue = counts.low_value || counts.lowValue || 0;
|
|
28326
|
-
counts.bestCycleTimes = counts.best_cycle_time || counts.bestCycleTimes || 0;
|
|
28327
|
-
counts.worstCycleTimes = counts.worst_cycle_time || counts.worstCycleTimes || 0;
|
|
28328
|
-
counts.longCycleTimes = counts.long_cycle_time || counts.longCycleTimes || 0;
|
|
28329
|
-
counts.cycleCompletions = counts.cycle_completion || counts.cycleCompletion || 0;
|
|
28330
|
-
counts.sopDeviations = counts.missing_quality_check || counts.sopDeviations || 0;
|
|
28331
|
-
counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
|
|
28332
|
-
return counts;
|
|
28333
|
-
}, [clipCounts]);
|
|
28334
29826
|
const getClipTypeLabel = (video) => {
|
|
28335
29827
|
if (!video) return "";
|
|
28336
29828
|
switch (video.type) {
|
|
@@ -28353,33 +29845,6 @@ var BottlenecksContent = ({
|
|
|
28353
29845
|
return "";
|
|
28354
29846
|
}
|
|
28355
29847
|
};
|
|
28356
|
-
const getColorClasses = (color2) => {
|
|
28357
|
-
const colorMap = {
|
|
28358
|
-
purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
|
|
28359
|
-
green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
|
|
28360
|
-
red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
|
|
28361
|
-
"red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
|
|
28362
|
-
blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
|
|
28363
|
-
orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
|
|
28364
|
-
yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
|
|
28365
|
-
teal: { text: "text-teal-600", bg: "bg-teal-600", dot: "bg-teal-600" },
|
|
28366
|
-
amber: { text: "text-amber-600", bg: "bg-amber-600", dot: "bg-amber-600" },
|
|
28367
|
-
gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
|
|
28368
|
-
};
|
|
28369
|
-
return colorMap[color2] || colorMap.gray;
|
|
28370
|
-
};
|
|
28371
|
-
const formatTimeOnly = (time2) => {
|
|
28372
|
-
if (!time2) return "";
|
|
28373
|
-
try {
|
|
28374
|
-
const [hours, minutes] = time2.split(":");
|
|
28375
|
-
const hour = parseInt(hours);
|
|
28376
|
-
const ampm = hour >= 12 ? "PM" : "AM";
|
|
28377
|
-
const displayHour = hour % 12 || 12;
|
|
28378
|
-
return `${displayHour}:${minutes} ${ampm}`;
|
|
28379
|
-
} catch {
|
|
28380
|
-
return time2;
|
|
28381
|
-
}
|
|
28382
|
-
};
|
|
28383
29848
|
if (!dashboardConfig?.s3Config) {
|
|
28384
29849
|
return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
28385
29850
|
/* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
@@ -28387,202 +29852,80 @@ var BottlenecksContent = ({
|
|
|
28387
29852
|
/* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
|
|
28388
29853
|
] });
|
|
28389
29854
|
}
|
|
28390
|
-
if (isLoading && allVideos.length === 0 && Object.keys(
|
|
29855
|
+
if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
28391
29856
|
return /* @__PURE__ */ jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
|
|
28392
29857
|
}
|
|
28393
|
-
if (error) {
|
|
29858
|
+
if (error || clipTypesError) {
|
|
28394
29859
|
return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
28395
29860
|
/* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
28396
29861
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
|
|
28397
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error })
|
|
29862
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
|
|
28398
29863
|
] });
|
|
28399
29864
|
}
|
|
28400
|
-
const categoriesToShow =
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
const count = mappedCounts[category.id] || 0;
|
|
28412
|
-
return /* @__PURE__ */ jsxs(
|
|
28413
|
-
Card2,
|
|
28414
|
-
{
|
|
28415
|
-
onClick: () => {
|
|
28416
|
-
updateActiveFilter(category.id);
|
|
28417
|
-
trackCoreEvent(`${category.label} Filter Clicked`, {
|
|
28418
|
-
workspaceId,
|
|
28419
|
-
workspaceName,
|
|
28420
|
-
date,
|
|
28421
|
-
filterType: category.id,
|
|
28422
|
-
clipCount: count
|
|
28423
|
-
});
|
|
28424
|
-
},
|
|
28425
|
-
className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === category.id ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
|
|
28426
|
-
"aria-label": `Filter by ${category.label} (${count} clips)`,
|
|
28427
|
-
role: "button",
|
|
28428
|
-
tabIndex: 0,
|
|
28429
|
-
onKeyDown: (e) => e.key === "Enter" && updateActiveFilter(category.id),
|
|
28430
|
-
children: [
|
|
28431
|
-
/* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
|
|
28432
|
-
/* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
|
|
28433
|
-
/* @__PURE__ */ jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
|
|
28434
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
|
|
28435
|
-
/* @__PURE__ */ jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
|
|
28436
|
-
/* @__PURE__ */ jsx("span", { children: category.subtitle || category.description || category.label })
|
|
28437
|
-
] })
|
|
28438
|
-
] }) })
|
|
28439
|
-
]
|
|
28440
|
-
},
|
|
28441
|
-
category.id
|
|
28442
|
-
);
|
|
28443
|
-
}) }),
|
|
28444
|
-
/* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
|
|
29865
|
+
const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
|
|
29866
|
+
console.log("[BottlenecksContent] Categories to show:", {
|
|
29867
|
+
categoriesToShow,
|
|
29868
|
+
categoriesToShowLength: categoriesToShow?.length,
|
|
29869
|
+
firstCategory: categoriesToShow?.[0],
|
|
29870
|
+
clipTypesLength: clipTypes?.length,
|
|
29871
|
+
clipTypesError,
|
|
29872
|
+
mergedCounts
|
|
29873
|
+
});
|
|
29874
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-4 h-full", children: [
|
|
29875
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 lg:flex-[3]", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden h-full", children: [
|
|
28445
29876
|
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
28446
29877
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
|
|
28447
29878
|
if (activeFilter === "all") {
|
|
28448
|
-
return `All Clips (${
|
|
29879
|
+
return `All Clips (${mergedCounts.total || 0})`;
|
|
28449
29880
|
}
|
|
28450
|
-
const activeCategory = categoriesToShow.find((cat) => cat.
|
|
29881
|
+
const activeCategory = categoriesToShow.find((cat) => cat.type === activeFilter);
|
|
28451
29882
|
if (activeCategory) {
|
|
28452
|
-
return `${activeCategory.label} (${
|
|
29883
|
+
return `${activeCategory.label} (${mergedCounts[activeCategory.type] || 0})`;
|
|
28453
29884
|
}
|
|
28454
|
-
return activeFilter
|
|
29885
|
+
return `${activeFilter.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} (${mergedCounts[activeFilter] || 0})`;
|
|
28455
29886
|
})() }),
|
|
28456
29887
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28457
|
-
/* @__PURE__ */ jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
|
|
28458
|
-
/* @__PURE__ */ jsx(
|
|
28459
|
-
"button",
|
|
28460
|
-
{
|
|
28461
|
-
onClick: () => setShowTimestampFilter(!showTimestampFilter),
|
|
28462
|
-
className: `p-2 rounded-lg transition-all ${timestampStart || timestampEnd ? "bg-blue-50 text-blue-600 hover:bg-blue-100" : "text-gray-600 hover:bg-gray-100"}`,
|
|
28463
|
-
"aria-label": "Filter by time",
|
|
28464
|
-
title: "Filter by time",
|
|
28465
|
-
children: /* @__PURE__ */ jsx(Clock, { className: "h-5 w-5" })
|
|
28466
|
-
}
|
|
28467
|
-
),
|
|
28468
|
-
showTimestampFilter && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg border border-gray-200 p-4 z-20", children: [
|
|
28469
|
-
/* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Filter by Time" }),
|
|
28470
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
28471
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
28472
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Start Time" }),
|
|
28473
|
-
/* @__PURE__ */ jsx(
|
|
28474
|
-
TimePickerDropdown,
|
|
28475
|
-
{
|
|
28476
|
-
value: timestampStart,
|
|
28477
|
-
onChange: (value) => setTimestampStart(value),
|
|
28478
|
-
placeholder: "Select start time",
|
|
28479
|
-
className: "w-full text-sm"
|
|
28480
|
-
}
|
|
28481
|
-
)
|
|
28482
|
-
] }),
|
|
28483
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
28484
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "End Time" }),
|
|
28485
|
-
/* @__PURE__ */ jsx(
|
|
28486
|
-
TimePickerDropdown,
|
|
28487
|
-
{
|
|
28488
|
-
value: timestampEnd,
|
|
28489
|
-
onChange: (value) => setTimestampEnd(value),
|
|
28490
|
-
placeholder: "Select end time",
|
|
28491
|
-
className: "w-full text-sm"
|
|
28492
|
-
}
|
|
28493
|
-
)
|
|
28494
|
-
] }),
|
|
28495
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between pt-2", children: [
|
|
28496
|
-
/* @__PURE__ */ jsx(
|
|
28497
|
-
"button",
|
|
28498
|
-
{
|
|
28499
|
-
onClick: () => {
|
|
28500
|
-
setTimestampStart("");
|
|
28501
|
-
setTimestampEnd("");
|
|
28502
|
-
setShowTimestampFilter(false);
|
|
28503
|
-
},
|
|
28504
|
-
className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 transition-colors",
|
|
28505
|
-
children: "Clear"
|
|
28506
|
-
}
|
|
28507
|
-
),
|
|
28508
|
-
/* @__PURE__ */ jsx(
|
|
28509
|
-
"button",
|
|
28510
|
-
{
|
|
28511
|
-
onClick: () => {
|
|
28512
|
-
setShowTimestampFilter(false);
|
|
28513
|
-
loadFirstVideoForCategory();
|
|
28514
|
-
},
|
|
28515
|
-
className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
|
|
28516
|
-
children: "Apply Filter"
|
|
28517
|
-
}
|
|
28518
|
-
)
|
|
28519
|
-
] })
|
|
28520
|
-
] })
|
|
28521
|
-
] })
|
|
28522
|
-
] }),
|
|
28523
29888
|
/* @__PURE__ */ jsx(
|
|
28524
29889
|
"button",
|
|
28525
29890
|
{
|
|
28526
29891
|
onClick: handlePrevious,
|
|
28527
|
-
disabled: currentIndex === 0 ||
|
|
28528
|
-
className: `p-2 rounded-full transition-colors ${currentIndex === 0 ||
|
|
29892
|
+
disabled: currentIndex === 0 || (mergedCounts[activeFilter] || 0) === 0,
|
|
29893
|
+
className: `p-2 rounded-full transition-colors ${currentIndex === 0 || (mergedCounts[activeFilter] || 0) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
28529
29894
|
"aria-label": "Previous video",
|
|
28530
29895
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
28531
29896
|
}
|
|
28532
29897
|
),
|
|
28533
29898
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
28534
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children:
|
|
29899
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: (mergedCounts[activeFilter] || 0) > 0 ? `${currentIndex + 1} / ${mergedCounts[activeFilter]}` : "0 / 0" }),
|
|
28535
29900
|
error && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-600 font-medium", children: error })
|
|
28536
29901
|
] }),
|
|
28537
29902
|
/* @__PURE__ */ jsx(
|
|
28538
29903
|
"button",
|
|
28539
29904
|
{
|
|
28540
29905
|
onClick: handleNext,
|
|
28541
|
-
disabled: currentIndex >=
|
|
28542
|
-
className: `p-2 rounded-full transition-colors ${currentIndex >=
|
|
29906
|
+
disabled: currentIndex >= (mergedCounts[activeFilter] || 0) - 1 || (mergedCounts[activeFilter] || 0) === 0,
|
|
29907
|
+
className: `p-2 rounded-full transition-colors ${currentIndex >= (mergedCounts[activeFilter] || 0) - 1 || (mergedCounts[activeFilter] || 0) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
28543
29908
|
"aria-label": "Next video",
|
|
28544
29909
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
28545
29910
|
}
|
|
28546
29911
|
)
|
|
28547
29912
|
] })
|
|
28548
29913
|
] }) }),
|
|
28549
|
-
(timestampStart || timestampEnd) && /* @__PURE__ */ jsxs("div", { className: "px-4 py-2 bg-blue-50 border-b border-blue-100 flex items-center justify-between", children: [
|
|
28550
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 text-sm text-blue-700", children: [
|
|
28551
|
-
/* @__PURE__ */ jsx(Clock, { className: "h-4 w-4" }),
|
|
28552
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
28553
|
-
"Filtered by time: ",
|
|
28554
|
-
timestampStart ? formatTimeOnly(timestampStart) : "Any",
|
|
28555
|
-
" - ",
|
|
28556
|
-
timestampEnd ? formatTimeOnly(timestampEnd) : "Any"
|
|
28557
|
-
] })
|
|
28558
|
-
] }),
|
|
28559
|
-
/* @__PURE__ */ jsx(
|
|
28560
|
-
"button",
|
|
28561
|
-
{
|
|
28562
|
-
onClick: () => {
|
|
28563
|
-
setTimestampStart("");
|
|
28564
|
-
setTimestampEnd("");
|
|
28565
|
-
loadFirstVideoForCategory();
|
|
28566
|
-
},
|
|
28567
|
-
className: "text-sm text-blue-600 hover:text-blue-800 transition-colors",
|
|
28568
|
-
children: "Clear filter"
|
|
28569
|
-
}
|
|
28570
|
-
)
|
|
28571
|
-
] }),
|
|
28572
29914
|
/* Priority 1: Show loading if initial load hasn't completed yet */
|
|
28573
29915
|
isLoading && !hasInitialLoad ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
|
|
28574
29916
|
/* Priority 2: Show loading if category is loading BUT only if no video is available */
|
|
28575
29917
|
isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
|
|
28576
29918
|
/* Priority 3: Show loading if navigating and current video not available */
|
|
28577
|
-
isNavigating || currentIndex >= filteredVideos.length && currentIndex <
|
|
29919
|
+
isNavigating || currentIndex >= filteredVideos.length && currentIndex < (mergedCounts[activeFilter] || 0) ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
|
|
28578
29920
|
/* Priority 4: Show video if we have filtered videos and current video */
|
|
28579
29921
|
filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
28580
29922
|
/* @__PURE__ */ jsx(
|
|
28581
|
-
|
|
29923
|
+
CroppedVideoPlayer,
|
|
28582
29924
|
{
|
|
28583
29925
|
ref: videoRef,
|
|
28584
29926
|
src: currentVideo.src,
|
|
28585
29927
|
className: "w-full h-full",
|
|
29928
|
+
crop: workspaceCrop?.crop || null,
|
|
28586
29929
|
autoplay: true,
|
|
28587
29930
|
playsInline: true,
|
|
28588
29931
|
loop: false,
|
|
@@ -28636,7 +29979,7 @@ var BottlenecksContent = ({
|
|
|
28636
29979
|
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" ? "bg-purple-400" : currentVideo.type === "best_cycle_time" ? "bg-green-600" : currentVideo.type === "worst_cycle_time" ? "bg-red-700" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-red-500"} mr-2 animate-pulse` }),
|
|
28637
29980
|
(currentVideo.type === "best_cycle_time" || currentVideo.type === "worst_cycle_time" || currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
|
|
28638
29981
|
"Cycle time: ",
|
|
28639
|
-
|
|
29982
|
+
currentVideo.cycle_time_seconds.toFixed(1),
|
|
28640
29983
|
"s"
|
|
28641
29984
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
28642
29985
|
/* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
@@ -28673,9 +30016,8 @@ var BottlenecksContent = ({
|
|
|
28673
30016
|
max: duration || 0,
|
|
28674
30017
|
value: currentTime,
|
|
28675
30018
|
onChange: (e) => {
|
|
28676
|
-
|
|
28677
|
-
|
|
28678
|
-
player.currentTime(Number(e.target.value));
|
|
30019
|
+
if (videoRef.current) {
|
|
30020
|
+
videoRef.current.currentTime(Number(e.target.value));
|
|
28679
30021
|
}
|
|
28680
30022
|
},
|
|
28681
30023
|
className: "flex-grow mx-3 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
|
|
@@ -28689,13 +30031,13 @@ var BottlenecksContent = ({
|
|
|
28689
30031
|
] }) })
|
|
28690
30032
|
] }) }) }) : (
|
|
28691
30033
|
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
28692
|
-
hasInitialLoad && Object.keys(
|
|
30034
|
+
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
28693
30035
|
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
28694
30036
|
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
28695
30037
|
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
28696
30038
|
] }) }) : (
|
|
28697
30039
|
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
28698
|
-
hasInitialLoad &&
|
|
30040
|
+
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
28699
30041
|
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
28700
30042
|
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
|
|
28701
30043
|
/* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
|
|
@@ -28708,8 +30050,46 @@ var BottlenecksContent = ({
|
|
|
28708
30050
|
)
|
|
28709
30051
|
)
|
|
28710
30052
|
)
|
|
28711
|
-
] })
|
|
28712
|
-
|
|
30053
|
+
] }) }),
|
|
30054
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px]", children: /* @__PURE__ */ jsx(
|
|
30055
|
+
FileManagerFilters,
|
|
30056
|
+
{
|
|
30057
|
+
categories: categoriesToShow.map((cat) => ({
|
|
30058
|
+
...cat,
|
|
30059
|
+
id: cat.type
|
|
30060
|
+
// Map type to id for compatibility with FileManagerFilters
|
|
30061
|
+
})),
|
|
30062
|
+
videos: allVideos || [],
|
|
30063
|
+
activeFilter,
|
|
30064
|
+
currentVideoId: currentVideo?.id,
|
|
30065
|
+
counts: mergedCounts,
|
|
30066
|
+
onFilterChange: (filterId) => {
|
|
30067
|
+
updateActiveFilter(filterId);
|
|
30068
|
+
const category = categoriesToShow.find((cat) => cat.type === filterId);
|
|
30069
|
+
if (category) {
|
|
30070
|
+
trackCoreEvent(`${category.label} Filter Clicked`, {
|
|
30071
|
+
workspaceId,
|
|
30072
|
+
workspaceName,
|
|
30073
|
+
date,
|
|
30074
|
+
filterType: filterId,
|
|
30075
|
+
clipCount: mergedCounts[filterId] || 0
|
|
30076
|
+
});
|
|
30077
|
+
}
|
|
30078
|
+
},
|
|
30079
|
+
onVideoSelect: (videoIndex) => {
|
|
30080
|
+
setCurrentIndex(videoIndex);
|
|
30081
|
+
const selectedVideo = allVideos?.[videoIndex];
|
|
30082
|
+
if (selectedVideo) {
|
|
30083
|
+
const videoCategory = categoriesToShow.find((cat) => cat.type === selectedVideo.type);
|
|
30084
|
+
if (videoCategory && activeFilter !== videoCategory.type) {
|
|
30085
|
+
updateActiveFilter(videoCategory.type);
|
|
30086
|
+
}
|
|
30087
|
+
}
|
|
30088
|
+
},
|
|
30089
|
+
className: "h-full"
|
|
30090
|
+
}
|
|
30091
|
+
) })
|
|
30092
|
+
] }) });
|
|
28713
30093
|
};
|
|
28714
30094
|
var getEfficiencyColor = (efficiency) => {
|
|
28715
30095
|
if (efficiency >= 80) {
|
|
@@ -30338,6 +31718,17 @@ var SideNavBar = memo(({
|
|
|
30338
31718
|
});
|
|
30339
31719
|
onMobileMenuClose?.();
|
|
30340
31720
|
}, [navigate, onMobileMenuClose]);
|
|
31721
|
+
const handleSupervisorManagementClick = useCallback(() => {
|
|
31722
|
+
navigate("/supervisor-management", {
|
|
31723
|
+
trackingEvent: {
|
|
31724
|
+
name: "Supervisor Management Page Clicked",
|
|
31725
|
+
properties: {
|
|
31726
|
+
source: "side_nav"
|
|
31727
|
+
}
|
|
31728
|
+
}
|
|
31729
|
+
});
|
|
31730
|
+
onMobileMenuClose?.();
|
|
31731
|
+
}, [navigate, onMobileMenuClose]);
|
|
30341
31732
|
const handleAIAgentClick = useCallback(() => {
|
|
30342
31733
|
navigate("/ai-agent", {
|
|
30343
31734
|
trackingEvent: {
|
|
@@ -30402,6 +31793,7 @@ var SideNavBar = memo(({
|
|
|
30402
31793
|
const kpisButtonClasses = useMemo(() => getButtonClasses("/kpis"), [getButtonClasses, pathname]);
|
|
30403
31794
|
const targetsButtonClasses = useMemo(() => getButtonClasses("/targets"), [getButtonClasses, pathname]);
|
|
30404
31795
|
const shiftsButtonClasses = useMemo(() => getButtonClasses("/shifts"), [getButtonClasses, pathname]);
|
|
31796
|
+
const supervisorManagementButtonClasses = useMemo(() => getButtonClasses("/supervisor-management"), [getButtonClasses, pathname]);
|
|
30405
31797
|
const aiAgentButtonClasses = useMemo(() => getButtonClasses("/ai-agent"), [getButtonClasses, pathname]);
|
|
30406
31798
|
const profileButtonClasses = useMemo(() => getButtonClasses("/profile"), [getButtonClasses, pathname]);
|
|
30407
31799
|
const helpButtonClasses = useMemo(() => getButtonClasses("/help"), [getButtonClasses, pathname]);
|
|
@@ -30507,7 +31899,22 @@ var SideNavBar = memo(({
|
|
|
30507
31899
|
]
|
|
30508
31900
|
}
|
|
30509
31901
|
),
|
|
30510
|
-
|
|
31902
|
+
/* @__PURE__ */ jsxs(
|
|
31903
|
+
"button",
|
|
31904
|
+
{
|
|
31905
|
+
onClick: handleSupervisorManagementClick,
|
|
31906
|
+
className: supervisorManagementButtonClasses,
|
|
31907
|
+
"aria-label": "Supervisor Management",
|
|
31908
|
+
tabIndex: 0,
|
|
31909
|
+
role: "tab",
|
|
31910
|
+
"aria-selected": pathname === "/supervisor-management" || pathname.startsWith("/supervisor-management/"),
|
|
31911
|
+
children: [
|
|
31912
|
+
/* @__PURE__ */ jsx(UsersIcon, { className: "w-5 h-5 mb-1" }),
|
|
31913
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Supervisors" })
|
|
31914
|
+
]
|
|
31915
|
+
}
|
|
31916
|
+
),
|
|
31917
|
+
skuEnabled && true && /* @__PURE__ */ jsxs(
|
|
30511
31918
|
"button",
|
|
30512
31919
|
{
|
|
30513
31920
|
onClick: handleSKUsClick,
|
|
@@ -30533,7 +31940,10 @@ var SideNavBar = memo(({
|
|
|
30533
31940
|
"aria-selected": pathname === "/ai-agent" || pathname.startsWith("/ai-agent/"),
|
|
30534
31941
|
children: [
|
|
30535
31942
|
/* @__PURE__ */ jsx(SparklesIcon, { className: "w-5 h-5 mb-1" }),
|
|
30536
|
-
/* @__PURE__ */
|
|
31943
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
|
|
31944
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Axel" }),
|
|
31945
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px] px-1.5 py-0.5 bg-orange-100 text-orange-600 rounded-full font-medium leading-none mt-0.5", children: "BETA" })
|
|
31946
|
+
] })
|
|
30537
31947
|
]
|
|
30538
31948
|
}
|
|
30539
31949
|
),
|
|
@@ -30667,7 +32077,19 @@ var SideNavBar = memo(({
|
|
|
30667
32077
|
]
|
|
30668
32078
|
}
|
|
30669
32079
|
),
|
|
30670
|
-
|
|
32080
|
+
/* @__PURE__ */ jsxs(
|
|
32081
|
+
"button",
|
|
32082
|
+
{
|
|
32083
|
+
onClick: handleMobileNavClick(handleSupervisorManagementClick),
|
|
32084
|
+
className: getMobileButtonClass("/supervisor-management"),
|
|
32085
|
+
"aria-label": "Supervisor Management",
|
|
32086
|
+
children: [
|
|
32087
|
+
/* @__PURE__ */ jsx(UsersIcon, { className: getIconClass("/supervisor-management") }),
|
|
32088
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-medium", children: "Supervisors" })
|
|
32089
|
+
]
|
|
32090
|
+
}
|
|
32091
|
+
),
|
|
32092
|
+
skuEnabled && true && /* @__PURE__ */ jsxs(
|
|
30671
32093
|
"button",
|
|
30672
32094
|
{
|
|
30673
32095
|
onClick: handleMobileNavClick(handleSKUsClick),
|
|
@@ -30687,7 +32109,10 @@ var SideNavBar = memo(({
|
|
|
30687
32109
|
"aria-label": "AI Manufacturing Expert",
|
|
30688
32110
|
children: [
|
|
30689
32111
|
/* @__PURE__ */ jsx(SparklesIcon, { className: getIconClass("/ai-agent") }),
|
|
30690
|
-
/* @__PURE__ */
|
|
32112
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
32113
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-medium", children: "Axel AI" }),
|
|
32114
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs px-2 py-0.5 bg-orange-100 text-orange-600 rounded-full font-medium leading-none mt-1 self-start", children: "BETA" })
|
|
32115
|
+
] })
|
|
30691
32116
|
]
|
|
30692
32117
|
}
|
|
30693
32118
|
),
|
|
@@ -31206,6 +32631,1205 @@ var SingleVideoStream = ({
|
|
|
31206
32631
|
);
|
|
31207
32632
|
};
|
|
31208
32633
|
var SingleVideoStream_default = SingleVideoStream;
|
|
32634
|
+
var SupervisorDropdown = ({
|
|
32635
|
+
selectedSupervisor,
|
|
32636
|
+
availableSupervisors,
|
|
32637
|
+
onSelect,
|
|
32638
|
+
className = "",
|
|
32639
|
+
disabled = false,
|
|
32640
|
+
placeholder = "Select Supervisor"
|
|
32641
|
+
}) => {
|
|
32642
|
+
console.log(`[SupervisorDropdown] Rendered with ${availableSupervisors.length} available supervisors:`, availableSupervisors.map((s) => ({ id: s.id, name: s.name })));
|
|
32643
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
32644
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
32645
|
+
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 });
|
|
32646
|
+
const dropdownRef = useRef(null);
|
|
32647
|
+
const buttonRef = useRef(null);
|
|
32648
|
+
const inputRef = useRef(null);
|
|
32649
|
+
const filteredSupervisors = availableSupervisors.filter(
|
|
32650
|
+
(supervisor) => supervisor.name.toLowerCase().includes(searchTerm.toLowerCase()) || supervisor.email && supervisor.email.toLowerCase().includes(searchTerm.toLowerCase())
|
|
32651
|
+
);
|
|
32652
|
+
const calculatePosition = () => {
|
|
32653
|
+
if (buttonRef.current) {
|
|
32654
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
32655
|
+
setDropdownPosition({
|
|
32656
|
+
top: rect.bottom + window.scrollY + 4,
|
|
32657
|
+
left: rect.left + window.scrollX,
|
|
32658
|
+
width: rect.width
|
|
32659
|
+
});
|
|
32660
|
+
}
|
|
32661
|
+
};
|
|
32662
|
+
useEffect(() => {
|
|
32663
|
+
const handleClickOutside = (event) => {
|
|
32664
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target) && buttonRef.current && !buttonRef.current.contains(event.target)) {
|
|
32665
|
+
setIsOpen(false);
|
|
32666
|
+
setSearchTerm("");
|
|
32667
|
+
}
|
|
32668
|
+
};
|
|
32669
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
32670
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
32671
|
+
}, []);
|
|
32672
|
+
const handleSelect = (supervisor) => {
|
|
32673
|
+
onSelect(supervisor);
|
|
32674
|
+
setIsOpen(false);
|
|
32675
|
+
setSearchTerm("");
|
|
32676
|
+
};
|
|
32677
|
+
const handleKeyDown = (e) => {
|
|
32678
|
+
if (e.key === "Escape") {
|
|
32679
|
+
setIsOpen(false);
|
|
32680
|
+
setSearchTerm("");
|
|
32681
|
+
}
|
|
32682
|
+
};
|
|
32683
|
+
const handleToggle = () => {
|
|
32684
|
+
if (!disabled) {
|
|
32685
|
+
if (!isOpen) {
|
|
32686
|
+
calculatePosition();
|
|
32687
|
+
}
|
|
32688
|
+
setIsOpen(!isOpen);
|
|
32689
|
+
}
|
|
32690
|
+
};
|
|
32691
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
|
|
32692
|
+
/* @__PURE__ */ jsxs(
|
|
32693
|
+
"button",
|
|
32694
|
+
{
|
|
32695
|
+
ref: buttonRef,
|
|
32696
|
+
type: "button",
|
|
32697
|
+
onClick: handleToggle,
|
|
32698
|
+
className: cn(
|
|
32699
|
+
"w-full flex items-center justify-between px-3 py-2 text-left bg-white border rounded-md shadow-sm transition-colors",
|
|
32700
|
+
disabled ? "bg-gray-100 cursor-not-allowed border-gray-200 text-gray-400" : "hover:bg-gray-50 cursor-pointer border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
32701
|
+
isOpen && !disabled && "ring-2 ring-blue-500 border-blue-500"
|
|
32702
|
+
),
|
|
32703
|
+
disabled,
|
|
32704
|
+
children: [
|
|
32705
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center min-w-0 flex-1", children: [
|
|
32706
|
+
/* @__PURE__ */ jsx(User, { className: "w-4 h-4 text-gray-400 mr-2 flex-shrink-0" }),
|
|
32707
|
+
/* @__PURE__ */ jsx("span", { className: cn(
|
|
32708
|
+
"truncate",
|
|
32709
|
+
selectedSupervisor ? "text-gray-900" : "text-gray-500"
|
|
32710
|
+
), children: selectedSupervisor ? selectedSupervisor.name : placeholder })
|
|
32711
|
+
] }),
|
|
32712
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn(
|
|
32713
|
+
"w-4 h-4 text-gray-400 transition-transform flex-shrink-0 ml-2",
|
|
32714
|
+
isOpen ? "rotate-180" : ""
|
|
32715
|
+
) })
|
|
32716
|
+
]
|
|
32717
|
+
}
|
|
32718
|
+
),
|
|
32719
|
+
isOpen && !disabled && typeof document !== "undefined" && createPortal(
|
|
32720
|
+
/* @__PURE__ */ jsxs(
|
|
32721
|
+
"div",
|
|
32722
|
+
{
|
|
32723
|
+
ref: dropdownRef,
|
|
32724
|
+
className: "fixed z-50 bg-white border border-gray-300 rounded-md shadow-lg",
|
|
32725
|
+
style: {
|
|
32726
|
+
top: dropdownPosition.top,
|
|
32727
|
+
left: dropdownPosition.left,
|
|
32728
|
+
width: dropdownPosition.width,
|
|
32729
|
+
minWidth: "200px"
|
|
32730
|
+
},
|
|
32731
|
+
children: [
|
|
32732
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx(
|
|
32733
|
+
"input",
|
|
32734
|
+
{
|
|
32735
|
+
ref: inputRef,
|
|
32736
|
+
type: "text",
|
|
32737
|
+
placeholder: "Search supervisors...",
|
|
32738
|
+
value: searchTerm,
|
|
32739
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
32740
|
+
onKeyDown: handleKeyDown,
|
|
32741
|
+
className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500",
|
|
32742
|
+
autoFocus: true
|
|
32743
|
+
}
|
|
32744
|
+
) }),
|
|
32745
|
+
/* @__PURE__ */ jsxs("div", { className: "max-h-60 overflow-y-auto", children: [
|
|
32746
|
+
selectedSupervisor && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
32747
|
+
/* @__PURE__ */ jsxs(
|
|
32748
|
+
"button",
|
|
32749
|
+
{
|
|
32750
|
+
type: "button",
|
|
32751
|
+
onClick: () => handleSelect(null),
|
|
32752
|
+
className: "w-full px-3 py-2 text-left text-sm hover:bg-gray-100 focus:outline-none focus:bg-gray-100 flex items-center text-red-600",
|
|
32753
|
+
children: [
|
|
32754
|
+
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 mr-2" }),
|
|
32755
|
+
" ",
|
|
32756
|
+
"Clear Assignment"
|
|
32757
|
+
]
|
|
32758
|
+
}
|
|
32759
|
+
),
|
|
32760
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-gray-200" })
|
|
32761
|
+
] }),
|
|
32762
|
+
filteredSupervisors.length > 0 ? filteredSupervisors.map((supervisor) => /* @__PURE__ */ jsxs(
|
|
32763
|
+
"button",
|
|
32764
|
+
{
|
|
32765
|
+
type: "button",
|
|
32766
|
+
onClick: () => handleSelect(supervisor),
|
|
32767
|
+
className: cn(
|
|
32768
|
+
"w-full px-3 py-2 text-left text-sm hover:bg-gray-100 focus:outline-none focus:bg-gray-100 flex items-center justify-between",
|
|
32769
|
+
selectedSupervisor?.id === supervisor.id && "bg-blue-50 text-blue-700"
|
|
32770
|
+
),
|
|
32771
|
+
children: [
|
|
32772
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center min-w-0 flex-1", children: [
|
|
32773
|
+
/* @__PURE__ */ jsx(User, { className: "w-4 h-4 text-gray-400 mr-2 flex-shrink-0" }),
|
|
32774
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
32775
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium truncate", children: supervisor.name }),
|
|
32776
|
+
supervisor.email && /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 truncate", children: supervisor.email })
|
|
32777
|
+
] })
|
|
32778
|
+
] }),
|
|
32779
|
+
selectedSupervisor?.id === supervisor.id && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
32780
|
+
]
|
|
32781
|
+
},
|
|
32782
|
+
supervisor.id
|
|
32783
|
+
)) : /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: searchTerm ? "No supervisors found" : "No supervisors available" })
|
|
32784
|
+
] })
|
|
32785
|
+
]
|
|
32786
|
+
}
|
|
32787
|
+
),
|
|
32788
|
+
document.body
|
|
32789
|
+
)
|
|
32790
|
+
] });
|
|
32791
|
+
};
|
|
32792
|
+
var SupervisorDropdown_default = SupervisorDropdown;
|
|
32793
|
+
|
|
32794
|
+
// src/lib/hooks/useFirstTimeLogin.ts
|
|
32795
|
+
var useFirstTimeLogin = () => {
|
|
32796
|
+
const { user, markFirstLoginCompleted } = useAuth();
|
|
32797
|
+
const hasCompletedFirstLogin = user?.first_login_completed ?? false;
|
|
32798
|
+
const needsOnboarding = user ? !hasCompletedFirstLogin : false;
|
|
32799
|
+
const completeFirstLogin = async () => {
|
|
32800
|
+
return await markFirstLoginCompleted();
|
|
32801
|
+
};
|
|
32802
|
+
return {
|
|
32803
|
+
isFirstTimeLogin: !hasCompletedFirstLogin,
|
|
32804
|
+
// Simple boolean check
|
|
32805
|
+
hasCompletedFirstLogin,
|
|
32806
|
+
needsOnboarding,
|
|
32807
|
+
completeFirstLogin,
|
|
32808
|
+
user
|
|
32809
|
+
};
|
|
32810
|
+
};
|
|
32811
|
+
var SimpleOnboardingPopup = ({
|
|
32812
|
+
onComplete,
|
|
32813
|
+
isCompleting = false
|
|
32814
|
+
}) => {
|
|
32815
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
32816
|
+
const steps = [
|
|
32817
|
+
{
|
|
32818
|
+
title: "Welcome to Optifye Dashboard! \u{1F389}",
|
|
32819
|
+
content: "We're excited to have you here. This dashboard helps you monitor and optimize your factory operations in real-time.",
|
|
32820
|
+
icon: "\u{1F44B}"
|
|
32821
|
+
},
|
|
32822
|
+
{
|
|
32823
|
+
title: "Real-Time Monitoring",
|
|
32824
|
+
content: "Track your production lines, monitor efficiency, and get instant alerts about any issues.",
|
|
32825
|
+
icon: "\u{1F4CA}"
|
|
32826
|
+
},
|
|
32827
|
+
{
|
|
32828
|
+
title: "Data-Driven Insights",
|
|
32829
|
+
content: "Make informed decisions with comprehensive analytics and historical data at your fingertips.",
|
|
32830
|
+
icon: "\u{1F4A1}"
|
|
32831
|
+
}
|
|
32832
|
+
];
|
|
32833
|
+
const handleNext = () => {
|
|
32834
|
+
if (currentStep < steps.length - 1) {
|
|
32835
|
+
setCurrentStep(currentStep + 1);
|
|
32836
|
+
} else {
|
|
32837
|
+
onComplete();
|
|
32838
|
+
}
|
|
32839
|
+
};
|
|
32840
|
+
const handleSkip = () => {
|
|
32841
|
+
onComplete();
|
|
32842
|
+
};
|
|
32843
|
+
const currentStepData = steps[currentStep];
|
|
32844
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 z-[9998] flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-2xl max-w-md w-full mx-4 relative z-[9999] transform transition-all duration-300 scale-95 animate-pulse", children: [
|
|
32845
|
+
/* @__PURE__ */ jsx(
|
|
32846
|
+
"button",
|
|
32847
|
+
{
|
|
32848
|
+
onClick: handleSkip,
|
|
32849
|
+
className: "absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors",
|
|
32850
|
+
disabled: isCompleting,
|
|
32851
|
+
children: /* @__PURE__ */ jsx(X, { size: 20 })
|
|
32852
|
+
}
|
|
32853
|
+
),
|
|
32854
|
+
/* @__PURE__ */ jsxs("div", { className: "p-8", children: [
|
|
32855
|
+
/* @__PURE__ */ jsx("div", { className: "text-5xl mb-4 text-center", children: currentStepData.icon }),
|
|
32856
|
+
/* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold text-gray-900 mb-4 text-center", children: currentStepData.title }),
|
|
32857
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 text-center mb-8", children: currentStepData.content }),
|
|
32858
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center space-x-2 mb-6", children: steps.map((_, index) => /* @__PURE__ */ jsx(
|
|
32859
|
+
"div",
|
|
32860
|
+
{
|
|
32861
|
+
className: `h-2 w-2 rounded-full transition-colors ${index === currentStep ? "bg-blue-600" : "bg-gray-300"}`
|
|
32862
|
+
},
|
|
32863
|
+
index
|
|
32864
|
+
)) }),
|
|
32865
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
32866
|
+
/* @__PURE__ */ jsx(
|
|
32867
|
+
"button",
|
|
32868
|
+
{
|
|
32869
|
+
onClick: handleSkip,
|
|
32870
|
+
className: "text-gray-500 hover:text-gray-700 text-sm transition-colors",
|
|
32871
|
+
disabled: isCompleting,
|
|
32872
|
+
children: "Skip tour"
|
|
32873
|
+
}
|
|
32874
|
+
),
|
|
32875
|
+
/* @__PURE__ */ jsx(
|
|
32876
|
+
"button",
|
|
32877
|
+
{
|
|
32878
|
+
onClick: handleNext,
|
|
32879
|
+
disabled: isCompleting,
|
|
32880
|
+
className: "bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
32881
|
+
children: isCompleting ? /* @__PURE__ */ jsxs("span", { className: "flex items-center", children: [
|
|
32882
|
+
/* @__PURE__ */ jsxs("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
|
|
32883
|
+
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
32884
|
+
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
32885
|
+
] }),
|
|
32886
|
+
"Loading..."
|
|
32887
|
+
] }) : currentStep === steps.length - 1 ? "Get Started" : "Next"
|
|
32888
|
+
}
|
|
32889
|
+
)
|
|
32890
|
+
] })
|
|
32891
|
+
] })
|
|
32892
|
+
] }) }) });
|
|
32893
|
+
};
|
|
32894
|
+
var MinimalOnboardingPopup = ({
|
|
32895
|
+
onComplete,
|
|
32896
|
+
isCompleting = false
|
|
32897
|
+
}) => {
|
|
32898
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 z-[9998] flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-2xl max-w-sm w-full mx-4 relative z-[9999]", children: /* @__PURE__ */ jsxs("div", { className: "p-6 text-center", children: [
|
|
32899
|
+
/* @__PURE__ */ jsx("div", { className: "text-5xl mb-4", children: "\u{1F389}" }),
|
|
32900
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold text-gray-900 mb-3", children: "Welcome to Optifye!" }),
|
|
32901
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-6", children: "Your dashboard is ready. Let's get started!" }),
|
|
32902
|
+
/* @__PURE__ */ jsx(
|
|
32903
|
+
"button",
|
|
32904
|
+
{
|
|
32905
|
+
onClick: onComplete,
|
|
32906
|
+
disabled: isCompleting,
|
|
32907
|
+
className: "w-full bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
32908
|
+
children: isCompleting ? "Loading..." : "Get Started"
|
|
32909
|
+
}
|
|
32910
|
+
)
|
|
32911
|
+
] }) }) }) });
|
|
32912
|
+
};
|
|
32913
|
+
var onboardingSteps = [
|
|
32914
|
+
{
|
|
32915
|
+
id: "welcome",
|
|
32916
|
+
title: "Welcome to Optifye.ai \u{1F44B}",
|
|
32917
|
+
description: "Let's take a quick interactive tour to help you master our intelligent manufacturing monitoring platform. You'll be able to interact with the dashboard as we guide you.",
|
|
32918
|
+
position: "center",
|
|
32919
|
+
nextTrigger: "button"
|
|
32920
|
+
},
|
|
32921
|
+
{
|
|
32922
|
+
id: "live-streams",
|
|
32923
|
+
title: "Live Manufacturing Streams",
|
|
32924
|
+
description: "These are real-time video feeds from your production lines. Each stream shows the current efficiency with color-coded overlays.",
|
|
32925
|
+
target: '.workspace-grid, [class*="workspace-grid"], [class*="grid"], [class*="workspace-container"]',
|
|
32926
|
+
position: "auto",
|
|
32927
|
+
spotlight: true,
|
|
32928
|
+
allowInteraction: false,
|
|
32929
|
+
nextTrigger: "button"
|
|
32930
|
+
},
|
|
32931
|
+
{
|
|
32932
|
+
id: "efficiency-legend",
|
|
32933
|
+
title: "Understanding Efficiency Colors",
|
|
32934
|
+
description: "Green (80-100%): Optimal performance\nYellow (70-79%): Needs attention\nRed (<70%): Critical issues\nFlashing red (<50%): Immediate action required",
|
|
32935
|
+
target: '[class*="efficiency"], [class*="legend"], [class*="indicator"]',
|
|
32936
|
+
position: "auto",
|
|
32937
|
+
spotlight: true,
|
|
32938
|
+
allowInteraction: false,
|
|
32939
|
+
nextTrigger: "button"
|
|
32940
|
+
},
|
|
32941
|
+
{
|
|
32942
|
+
id: "click-workspace",
|
|
32943
|
+
title: "Dive Into a Workspace",
|
|
32944
|
+
description: "Click on any video stream to explore detailed performance metrics for that production line.",
|
|
32945
|
+
target: '.workspace-card, [class*="workspace-item"], [class*="workspace-card"], [class*="video-card"]',
|
|
32946
|
+
actionTarget: '.workspace-card, [class*="workspace-item"], [class*="workspace-card"], [class*="video-card"]',
|
|
32947
|
+
position: "auto",
|
|
32948
|
+
action: "click",
|
|
32949
|
+
spotlight: true,
|
|
32950
|
+
allowInteraction: true,
|
|
32951
|
+
nextTrigger: "action",
|
|
32952
|
+
waitForPath: "/workspace"
|
|
32953
|
+
},
|
|
32954
|
+
{
|
|
32955
|
+
id: "workspace-metrics",
|
|
32956
|
+
title: "Real-Time Performance Dashboard",
|
|
32957
|
+
description: "Excellent! Here you can monitor OEE (Overall Equipment Effectiveness), production rates, quality metrics, and identify bottlenecks in real-time.",
|
|
32958
|
+
target: '[class*="metrics"], [class*="dashboard-content"], [class*="workspace-detail"], main',
|
|
32959
|
+
position: "auto",
|
|
32960
|
+
spotlight: true,
|
|
32961
|
+
allowInteraction: false,
|
|
32962
|
+
nextTrigger: "button"
|
|
32963
|
+
},
|
|
32964
|
+
{
|
|
32965
|
+
id: "navigate-back",
|
|
32966
|
+
title: "Navigate Back Home",
|
|
32967
|
+
description: "Click on the Home tab in the sidebar to return to the main dashboard.",
|
|
32968
|
+
target: '[data-nav-item="home"], [href="/"], nav a:has-text("Home"), nav button:has-text("Home"), tab:has-text("Home")',
|
|
32969
|
+
actionTarget: '[data-nav-item="home"], [href="/"], nav a:has-text("Home"), nav button:has-text("Home"), tab:has-text("Home")',
|
|
32970
|
+
position: "right",
|
|
32971
|
+
action: "click",
|
|
32972
|
+
spotlight: true,
|
|
32973
|
+
allowInteraction: true,
|
|
32974
|
+
nextTrigger: "action",
|
|
32975
|
+
waitForPath: "/"
|
|
32976
|
+
},
|
|
32977
|
+
{
|
|
32978
|
+
id: "complete",
|
|
32979
|
+
title: "\u{1F389} Tour Complete!",
|
|
32980
|
+
description: "You're all set to optimize your manufacturing operations with Optifye.ai. Explore the platform to discover more powerful features like Clips, Targets, and AI insights.",
|
|
32981
|
+
position: "center",
|
|
32982
|
+
nextTrigger: "button"
|
|
32983
|
+
}
|
|
32984
|
+
];
|
|
32985
|
+
var InteractiveOnboardingTour = ({
|
|
32986
|
+
isOpen,
|
|
32987
|
+
onComplete,
|
|
32988
|
+
onSkip
|
|
32989
|
+
}) => {
|
|
32990
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
32991
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
32992
|
+
const router = useRouter();
|
|
32993
|
+
const step = onboardingSteps[currentStep];
|
|
32994
|
+
const [targetElement, setTargetElement] = useState(null);
|
|
32995
|
+
const [tooltipPosition, setTooltipPosition] = useState({});
|
|
32996
|
+
const observerRef = useRef(null);
|
|
32997
|
+
const handleNext = useCallback(() => {
|
|
32998
|
+
if (isTransitioning) return;
|
|
32999
|
+
setIsTransitioning(true);
|
|
33000
|
+
setTimeout(() => {
|
|
33001
|
+
if (currentStep < onboardingSteps.length - 1) {
|
|
33002
|
+
setCurrentStep(currentStep + 1);
|
|
33003
|
+
} else {
|
|
33004
|
+
onComplete();
|
|
33005
|
+
}
|
|
33006
|
+
setIsTransitioning(false);
|
|
33007
|
+
}, 200);
|
|
33008
|
+
}, [currentStep, isTransitioning, onComplete]);
|
|
33009
|
+
const handlePrevious = useCallback(() => {
|
|
33010
|
+
if (currentStep > 0 && !isTransitioning) {
|
|
33011
|
+
setIsTransitioning(true);
|
|
33012
|
+
setTimeout(() => {
|
|
33013
|
+
setCurrentStep(currentStep - 1);
|
|
33014
|
+
setIsTransitioning(false);
|
|
33015
|
+
}, 200);
|
|
33016
|
+
}
|
|
33017
|
+
}, [currentStep, isTransitioning]);
|
|
33018
|
+
useEffect(() => {
|
|
33019
|
+
if (!step?.target || !isOpen) {
|
|
33020
|
+
setTargetElement(null);
|
|
33021
|
+
return;
|
|
33022
|
+
}
|
|
33023
|
+
const findElement = () => {
|
|
33024
|
+
const selectors = step.target.split(",").map((s) => s.trim());
|
|
33025
|
+
for (const selector of selectors) {
|
|
33026
|
+
try {
|
|
33027
|
+
const element2 = document.querySelector(selector);
|
|
33028
|
+
if (element2) {
|
|
33029
|
+
setTargetElement(element2);
|
|
33030
|
+
return element2;
|
|
33031
|
+
}
|
|
33032
|
+
} catch (e) {
|
|
33033
|
+
}
|
|
33034
|
+
}
|
|
33035
|
+
return null;
|
|
33036
|
+
};
|
|
33037
|
+
const element = findElement();
|
|
33038
|
+
if (!element && step.target) {
|
|
33039
|
+
observerRef.current = new MutationObserver(() => {
|
|
33040
|
+
const found = findElement();
|
|
33041
|
+
if (found) {
|
|
33042
|
+
observerRef.current?.disconnect();
|
|
33043
|
+
}
|
|
33044
|
+
});
|
|
33045
|
+
observerRef.current.observe(document.body, {
|
|
33046
|
+
childList: true,
|
|
33047
|
+
subtree: true
|
|
33048
|
+
});
|
|
33049
|
+
}
|
|
33050
|
+
return () => {
|
|
33051
|
+
observerRef.current?.disconnect();
|
|
33052
|
+
};
|
|
33053
|
+
}, [step, isOpen, router.pathname]);
|
|
33054
|
+
useEffect(() => {
|
|
33055
|
+
if (!targetElement || step?.position === "center") {
|
|
33056
|
+
setTooltipPosition({
|
|
33057
|
+
position: "fixed",
|
|
33058
|
+
top: "50%",
|
|
33059
|
+
left: "50%",
|
|
33060
|
+
transform: "translate(-50%, -50%)",
|
|
33061
|
+
zIndex: 10002
|
|
33062
|
+
});
|
|
33063
|
+
return;
|
|
33064
|
+
}
|
|
33065
|
+
const updatePosition = () => {
|
|
33066
|
+
const rect = targetElement.getBoundingClientRect();
|
|
33067
|
+
const tooltipWidth = 420;
|
|
33068
|
+
const tooltipHeight = 200;
|
|
33069
|
+
const offset = 16;
|
|
33070
|
+
const viewportWidth = window.innerWidth;
|
|
33071
|
+
const viewportHeight = window.innerHeight;
|
|
33072
|
+
let position = {
|
|
33073
|
+
position: "fixed",
|
|
33074
|
+
zIndex: 10002,
|
|
33075
|
+
maxWidth: "420px"
|
|
33076
|
+
};
|
|
33077
|
+
const spaceTop = rect.top;
|
|
33078
|
+
const spaceBottom = viewportHeight - rect.bottom;
|
|
33079
|
+
const spaceLeft = rect.left;
|
|
33080
|
+
const spaceRight = viewportWidth - rect.right;
|
|
33081
|
+
if (step.position === "auto") {
|
|
33082
|
+
if (spaceBottom > tooltipHeight + offset && rect.left + tooltipWidth / 2 < viewportWidth) {
|
|
33083
|
+
position.top = rect.bottom + offset;
|
|
33084
|
+
position.left = Math.max(offset, Math.min(rect.left + rect.width / 2 - tooltipWidth / 2, viewportWidth - tooltipWidth - offset));
|
|
33085
|
+
} else if (spaceTop > tooltipHeight + offset) {
|
|
33086
|
+
position.bottom = viewportHeight - rect.top + offset;
|
|
33087
|
+
position.left = Math.max(offset, Math.min(rect.left + rect.width / 2 - tooltipWidth / 2, viewportWidth - tooltipWidth - offset));
|
|
33088
|
+
} else if (spaceRight > tooltipWidth + offset) {
|
|
33089
|
+
position.left = rect.right + offset;
|
|
33090
|
+
position.top = Math.max(offset, Math.min(rect.top + rect.height / 2 - tooltipHeight / 2, viewportHeight - tooltipHeight - offset));
|
|
33091
|
+
} else if (spaceLeft > tooltipWidth + offset) {
|
|
33092
|
+
position.right = viewportWidth - rect.left + offset;
|
|
33093
|
+
position.top = Math.max(offset, Math.min(rect.top + rect.height / 2 - tooltipHeight / 2, viewportHeight - tooltipHeight - offset));
|
|
33094
|
+
} else {
|
|
33095
|
+
position.top = "50%";
|
|
33096
|
+
position.left = "50%";
|
|
33097
|
+
position.transform = "translate(-50%, -50%)";
|
|
33098
|
+
}
|
|
33099
|
+
} else {
|
|
33100
|
+
switch (step.position) {
|
|
33101
|
+
case "top":
|
|
33102
|
+
position.bottom = viewportHeight - rect.top + offset;
|
|
33103
|
+
position.left = rect.left + rect.width / 2 - tooltipWidth / 2;
|
|
33104
|
+
break;
|
|
33105
|
+
case "bottom":
|
|
33106
|
+
position.top = rect.bottom + offset;
|
|
33107
|
+
position.left = rect.left + rect.width / 2 - tooltipWidth / 2;
|
|
33108
|
+
break;
|
|
33109
|
+
case "left":
|
|
33110
|
+
position.right = viewportWidth - rect.left + offset;
|
|
33111
|
+
position.top = rect.top + rect.height / 2 - tooltipHeight / 2;
|
|
33112
|
+
break;
|
|
33113
|
+
case "right":
|
|
33114
|
+
position.left = rect.right + offset;
|
|
33115
|
+
position.top = rect.top + rect.height / 2 - tooltipHeight / 2;
|
|
33116
|
+
break;
|
|
33117
|
+
}
|
|
33118
|
+
}
|
|
33119
|
+
setTooltipPosition(position);
|
|
33120
|
+
};
|
|
33121
|
+
updatePosition();
|
|
33122
|
+
window.addEventListener("resize", updatePosition);
|
|
33123
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
33124
|
+
return () => {
|
|
33125
|
+
window.removeEventListener("resize", updatePosition);
|
|
33126
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
33127
|
+
};
|
|
33128
|
+
}, [targetElement, step]);
|
|
33129
|
+
useEffect(() => {
|
|
33130
|
+
if (!step || step.nextTrigger !== "action" || !isOpen) return;
|
|
33131
|
+
let actionTimeout;
|
|
33132
|
+
const handleAction = (e) => {
|
|
33133
|
+
if (!step.actionTarget) return;
|
|
33134
|
+
const target = e.target;
|
|
33135
|
+
const selectors = step.actionTarget.split(",").map((s) => s.trim());
|
|
33136
|
+
for (const selector of selectors) {
|
|
33137
|
+
try {
|
|
33138
|
+
if (target.matches(selector) || target.closest(selector)) {
|
|
33139
|
+
if (step.waitForPath) {
|
|
33140
|
+
actionTimeout = setTimeout(() => {
|
|
33141
|
+
console.log("No navigation detected, progressing to next step");
|
|
33142
|
+
handleNext();
|
|
33143
|
+
}, 2e3);
|
|
33144
|
+
} else {
|
|
33145
|
+
setTimeout(() => handleNext(), 300);
|
|
33146
|
+
}
|
|
33147
|
+
return;
|
|
33148
|
+
}
|
|
33149
|
+
} catch {
|
|
33150
|
+
}
|
|
33151
|
+
}
|
|
33152
|
+
};
|
|
33153
|
+
if (step.action === "click") {
|
|
33154
|
+
document.addEventListener("click", handleAction, true);
|
|
33155
|
+
return () => {
|
|
33156
|
+
document.removeEventListener("click", handleAction, true);
|
|
33157
|
+
if (actionTimeout) clearTimeout(actionTimeout);
|
|
33158
|
+
};
|
|
33159
|
+
}
|
|
33160
|
+
}, [step, isOpen, handleNext]);
|
|
33161
|
+
useEffect(() => {
|
|
33162
|
+
if (!step?.waitForPath || step.nextTrigger !== "action") return;
|
|
33163
|
+
const checkPath = (url) => {
|
|
33164
|
+
const currentPath = url || router.asPath || router.pathname;
|
|
33165
|
+
if (step.waitForPath === "/workspace" && (currentPath.includes("/workspace") || currentPath.includes("workspace-") || router.query?.workspace)) {
|
|
33166
|
+
setTimeout(() => handleNext(), 800);
|
|
33167
|
+
return true;
|
|
33168
|
+
}
|
|
33169
|
+
if (step.waitForPath && (currentPath === step.waitForPath || currentPath.includes(step.waitForPath))) {
|
|
33170
|
+
setTimeout(() => handleNext(), 800);
|
|
33171
|
+
return true;
|
|
33172
|
+
}
|
|
33173
|
+
return false;
|
|
33174
|
+
};
|
|
33175
|
+
if (checkPath()) return;
|
|
33176
|
+
const handleRouteChange = (url) => {
|
|
33177
|
+
checkPath(url);
|
|
33178
|
+
};
|
|
33179
|
+
router.events.on("routeChangeComplete", handleRouteChange);
|
|
33180
|
+
router.events.on("routeChangeStart", handleRouteChange);
|
|
33181
|
+
return () => {
|
|
33182
|
+
router.events.off("routeChangeComplete", handleRouteChange);
|
|
33183
|
+
router.events.off("routeChangeStart", handleRouteChange);
|
|
33184
|
+
};
|
|
33185
|
+
}, [step, router, handleNext]);
|
|
33186
|
+
if (!isOpen || !step) return null;
|
|
33187
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33188
|
+
!step.allowInteraction && /* @__PURE__ */ jsx(
|
|
33189
|
+
motion.div,
|
|
33190
|
+
{
|
|
33191
|
+
initial: { opacity: 0 },
|
|
33192
|
+
animate: { opacity: 1 },
|
|
33193
|
+
exit: { opacity: 0 },
|
|
33194
|
+
className: "fixed inset-0 bg-black/20",
|
|
33195
|
+
style: { zIndex: 1e4 },
|
|
33196
|
+
onClick: (e) => e.stopPropagation()
|
|
33197
|
+
}
|
|
33198
|
+
),
|
|
33199
|
+
step.spotlight && targetElement && /* @__PURE__ */ jsx(
|
|
33200
|
+
Spotlight,
|
|
33201
|
+
{
|
|
33202
|
+
element: targetElement,
|
|
33203
|
+
allowInteraction: step.allowInteraction,
|
|
33204
|
+
showPulse: step.action === "click"
|
|
33205
|
+
}
|
|
33206
|
+
),
|
|
33207
|
+
/* @__PURE__ */ jsx(
|
|
33208
|
+
motion.div,
|
|
33209
|
+
{
|
|
33210
|
+
initial: { opacity: 0, scale: 0.95, y: 10 },
|
|
33211
|
+
animate: { opacity: 1, scale: 1, y: 0 },
|
|
33212
|
+
exit: { opacity: 0, scale: 0.95, y: 10 },
|
|
33213
|
+
transition: { duration: 0.2, ease: "easeOut" },
|
|
33214
|
+
style: tooltipPosition,
|
|
33215
|
+
className: "pointer-events-auto",
|
|
33216
|
+
children: /* @__PURE__ */ jsxs("div", { className: "bg-white dark:bg-gray-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 overflow-hidden", children: [
|
|
33217
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-gray-800 dark:to-gray-850 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
33218
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
33219
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: [...Array(onboardingSteps.length)].map((_, i) => /* @__PURE__ */ jsx(
|
|
33220
|
+
motion.div,
|
|
33221
|
+
{
|
|
33222
|
+
initial: false,
|
|
33223
|
+
animate: {
|
|
33224
|
+
width: i === currentStep ? 24 : 6,
|
|
33225
|
+
backgroundColor: i === currentStep ? "#3B82F6" : i < currentStep ? "#93C5FD" : "#E5E7EB"
|
|
33226
|
+
},
|
|
33227
|
+
className: "h-1.5 rounded-full transition-all duration-300"
|
|
33228
|
+
},
|
|
33229
|
+
i
|
|
33230
|
+
)) }),
|
|
33231
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-600 dark:text-gray-400", children: [
|
|
33232
|
+
currentStep + 1,
|
|
33233
|
+
" of ",
|
|
33234
|
+
onboardingSteps.length
|
|
33235
|
+
] })
|
|
33236
|
+
] }),
|
|
33237
|
+
/* @__PURE__ */ jsx(
|
|
33238
|
+
"button",
|
|
33239
|
+
{
|
|
33240
|
+
onClick: onSkip,
|
|
33241
|
+
className: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors p-1 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg",
|
|
33242
|
+
"aria-label": "Close tour",
|
|
33243
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
33244
|
+
}
|
|
33245
|
+
)
|
|
33246
|
+
] }) }),
|
|
33247
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-5", children: [
|
|
33248
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-gray-900 dark:text-white mb-3", children: step.title }),
|
|
33249
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 dark:text-gray-300 leading-relaxed whitespace-pre-line", children: step.description }),
|
|
33250
|
+
step.action === "click" && step.nextTrigger === "action" && /* @__PURE__ */ jsx(
|
|
33251
|
+
motion.div,
|
|
33252
|
+
{
|
|
33253
|
+
initial: { opacity: 0, y: 5 },
|
|
33254
|
+
animate: { opacity: 1, y: 0 },
|
|
33255
|
+
transition: { delay: 0.3 },
|
|
33256
|
+
className: "mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-xl border border-blue-200 dark:border-blue-800",
|
|
33257
|
+
children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-blue-700 dark:text-blue-300 flex items-center gap-2", children: [
|
|
33258
|
+
/* @__PURE__ */ jsx(MousePointer, { className: "w-4 h-4 animate-pulse" }),
|
|
33259
|
+
"Click the highlighted element to continue"
|
|
33260
|
+
] })
|
|
33261
|
+
}
|
|
33262
|
+
)
|
|
33263
|
+
] }),
|
|
33264
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 bg-gray-50 dark:bg-gray-900/50 border-t border-gray-200 dark:border-gray-800 flex items-center justify-between", children: [
|
|
33265
|
+
/* @__PURE__ */ jsx(
|
|
33266
|
+
"button",
|
|
33267
|
+
{
|
|
33268
|
+
onClick: onSkip,
|
|
33269
|
+
className: "text-sm font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors",
|
|
33270
|
+
children: "Skip tour"
|
|
33271
|
+
}
|
|
33272
|
+
),
|
|
33273
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
33274
|
+
currentStep > 0 && /* @__PURE__ */ jsx(
|
|
33275
|
+
"button",
|
|
33276
|
+
{
|
|
33277
|
+
onClick: handlePrevious,
|
|
33278
|
+
disabled: isTransitioning,
|
|
33279
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-xl transition-all disabled:opacity-50",
|
|
33280
|
+
children: "Previous"
|
|
33281
|
+
}
|
|
33282
|
+
),
|
|
33283
|
+
step.nextTrigger === "button" && /* @__PURE__ */ jsx(
|
|
33284
|
+
"button",
|
|
33285
|
+
{
|
|
33286
|
+
onClick: handleNext,
|
|
33287
|
+
disabled: isTransitioning,
|
|
33288
|
+
className: `px-5 py-2 text-sm font-semibold rounded-xl transition-all flex items-center gap-2 ${currentStep === onboardingSteps.length - 1 ? "bg-gradient-to-r from-green-500 to-emerald-600 text-white hover:from-green-600 hover:to-emerald-700 shadow-lg shadow-green-500/25" : "bg-gradient-to-r from-blue-500 to-indigo-600 text-white hover:from-blue-600 hover:to-indigo-700 shadow-lg shadow-blue-500/25"} disabled:opacity-50`,
|
|
33289
|
+
children: currentStep === onboardingSteps.length - 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33290
|
+
"Complete Tour",
|
|
33291
|
+
/* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
|
|
33292
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33293
|
+
"Next",
|
|
33294
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "w-4 h-4" })
|
|
33295
|
+
] })
|
|
33296
|
+
}
|
|
33297
|
+
)
|
|
33298
|
+
] })
|
|
33299
|
+
] })
|
|
33300
|
+
] })
|
|
33301
|
+
},
|
|
33302
|
+
step.id
|
|
33303
|
+
)
|
|
33304
|
+
] });
|
|
33305
|
+
if (typeof window !== "undefined") {
|
|
33306
|
+
return createPortal(
|
|
33307
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && content }),
|
|
33308
|
+
document.body
|
|
33309
|
+
);
|
|
33310
|
+
}
|
|
33311
|
+
return null;
|
|
33312
|
+
};
|
|
33313
|
+
var Spotlight = ({ element, allowInteraction, showPulse }) => {
|
|
33314
|
+
const [rect, setRect] = useState(null);
|
|
33315
|
+
useEffect(() => {
|
|
33316
|
+
const updateRect = () => {
|
|
33317
|
+
setRect(element.getBoundingClientRect());
|
|
33318
|
+
};
|
|
33319
|
+
updateRect();
|
|
33320
|
+
window.addEventListener("resize", updateRect);
|
|
33321
|
+
window.addEventListener("scroll", updateRect, true);
|
|
33322
|
+
const observer = new ResizeObserver(updateRect);
|
|
33323
|
+
observer.observe(element);
|
|
33324
|
+
return () => {
|
|
33325
|
+
window.removeEventListener("resize", updateRect);
|
|
33326
|
+
window.removeEventListener("scroll", updateRect, true);
|
|
33327
|
+
observer.disconnect();
|
|
33328
|
+
};
|
|
33329
|
+
}, [element]);
|
|
33330
|
+
if (!rect) return null;
|
|
33331
|
+
const padding = 8;
|
|
33332
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33333
|
+
!allowInteraction && /* @__PURE__ */ jsxs(
|
|
33334
|
+
"svg",
|
|
33335
|
+
{
|
|
33336
|
+
className: "fixed inset-0",
|
|
33337
|
+
style: { zIndex: 10001, pointerEvents: "auto" },
|
|
33338
|
+
children: [
|
|
33339
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("mask", { id: "spotlight-mask", children: [
|
|
33340
|
+
/* @__PURE__ */ jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", fill: "white" }),
|
|
33341
|
+
/* @__PURE__ */ jsx(
|
|
33342
|
+
"rect",
|
|
33343
|
+
{
|
|
33344
|
+
x: rect.left - padding,
|
|
33345
|
+
y: rect.top - padding,
|
|
33346
|
+
width: rect.width + padding * 2,
|
|
33347
|
+
height: rect.height + padding * 2,
|
|
33348
|
+
rx: "8",
|
|
33349
|
+
fill: "black"
|
|
33350
|
+
}
|
|
33351
|
+
)
|
|
33352
|
+
] }) }),
|
|
33353
|
+
/* @__PURE__ */ jsx(
|
|
33354
|
+
"rect",
|
|
33355
|
+
{
|
|
33356
|
+
x: "0",
|
|
33357
|
+
y: "0",
|
|
33358
|
+
width: "100%",
|
|
33359
|
+
height: "100%",
|
|
33360
|
+
fill: "black",
|
|
33361
|
+
fillOpacity: "0.2",
|
|
33362
|
+
mask: "url(#spotlight-mask)"
|
|
33363
|
+
}
|
|
33364
|
+
)
|
|
33365
|
+
]
|
|
33366
|
+
}
|
|
33367
|
+
),
|
|
33368
|
+
/* @__PURE__ */ jsxs(
|
|
33369
|
+
motion.div,
|
|
33370
|
+
{
|
|
33371
|
+
className: "fixed pointer-events-none",
|
|
33372
|
+
style: {
|
|
33373
|
+
left: rect.left - padding,
|
|
33374
|
+
top: rect.top - padding,
|
|
33375
|
+
width: rect.width + padding * 2,
|
|
33376
|
+
height: rect.height + padding * 2,
|
|
33377
|
+
zIndex: 10001
|
|
33378
|
+
},
|
|
33379
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
33380
|
+
animate: { opacity: 1, scale: 1 },
|
|
33381
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
33382
|
+
transition: { duration: 0.3, ease: "easeOut" },
|
|
33383
|
+
children: [
|
|
33384
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg ring-3 ring-blue-500 shadow-lg shadow-blue-500/30" }),
|
|
33385
|
+
showPulse && allowInteraction && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33386
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg ring-4 ring-blue-400 ring-opacity-75 animate-pulse" }),
|
|
33387
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg bg-blue-500/5 animate-pulse" })
|
|
33388
|
+
] })
|
|
33389
|
+
]
|
|
33390
|
+
}
|
|
33391
|
+
)
|
|
33392
|
+
] });
|
|
33393
|
+
};
|
|
33394
|
+
var FirstTimeLoginHandler = ({
|
|
33395
|
+
children,
|
|
33396
|
+
onboardingComponent: OnboardingComponent = SimpleOnboardingPopup,
|
|
33397
|
+
// Default to simple popup
|
|
33398
|
+
enableAutoDetection = true
|
|
33399
|
+
}) => {
|
|
33400
|
+
const {
|
|
33401
|
+
needsOnboarding,
|
|
33402
|
+
completeFirstLogin,
|
|
33403
|
+
user
|
|
33404
|
+
} = useFirstTimeLogin();
|
|
33405
|
+
const {
|
|
33406
|
+
showOnboarding: showTour,
|
|
33407
|
+
setShowOnboarding: setShowTour,
|
|
33408
|
+
completeOnboarding
|
|
33409
|
+
} = useAuth();
|
|
33410
|
+
const [showOnboarding, setShowOnboarding] = useState(false);
|
|
33411
|
+
const [isCompleting, setIsCompleting] = useState(false);
|
|
33412
|
+
const [hasChecked, setHasChecked] = useState(false);
|
|
33413
|
+
useEffect(() => {
|
|
33414
|
+
if (!hasChecked && user && enableAutoDetection) {
|
|
33415
|
+
setHasChecked(true);
|
|
33416
|
+
if (needsOnboarding) {
|
|
33417
|
+
console.log("[FirstTimeLoginHandler] First-time login detected, showing onboarding tour");
|
|
33418
|
+
setShowTour(true);
|
|
33419
|
+
}
|
|
33420
|
+
}
|
|
33421
|
+
}, [user, needsOnboarding, enableAutoDetection, hasChecked, setShowTour]);
|
|
33422
|
+
const handleOnboardingComplete = async () => {
|
|
33423
|
+
setIsCompleting(true);
|
|
33424
|
+
try {
|
|
33425
|
+
const success = await completeFirstLogin();
|
|
33426
|
+
if (success) {
|
|
33427
|
+
console.log("[FirstTimeLoginHandler] First login marked as completed");
|
|
33428
|
+
setShowOnboarding(false);
|
|
33429
|
+
} else {
|
|
33430
|
+
console.error("[FirstTimeLoginHandler] Failed to mark first login as completed");
|
|
33431
|
+
setShowOnboarding(false);
|
|
33432
|
+
}
|
|
33433
|
+
} catch (error) {
|
|
33434
|
+
console.error("[FirstTimeLoginHandler] Error completing first login:", error);
|
|
33435
|
+
setShowOnboarding(false);
|
|
33436
|
+
} finally {
|
|
33437
|
+
setIsCompleting(false);
|
|
33438
|
+
}
|
|
33439
|
+
};
|
|
33440
|
+
const handleTourComplete = async () => {
|
|
33441
|
+
await completeOnboarding();
|
|
33442
|
+
};
|
|
33443
|
+
const handleTourSkip = () => {
|
|
33444
|
+
setShowTour(false);
|
|
33445
|
+
completeFirstLogin();
|
|
33446
|
+
};
|
|
33447
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33448
|
+
children,
|
|
33449
|
+
showTour && /* @__PURE__ */ jsx(
|
|
33450
|
+
InteractiveOnboardingTour,
|
|
33451
|
+
{
|
|
33452
|
+
isOpen: showTour,
|
|
33453
|
+
onComplete: handleTourComplete,
|
|
33454
|
+
onSkip: handleTourSkip
|
|
33455
|
+
}
|
|
33456
|
+
),
|
|
33457
|
+
showOnboarding && OnboardingComponent && /* @__PURE__ */ jsx(
|
|
33458
|
+
OnboardingComponent,
|
|
33459
|
+
{
|
|
33460
|
+
onComplete: handleOnboardingComplete,
|
|
33461
|
+
isCompleting
|
|
33462
|
+
}
|
|
33463
|
+
)
|
|
33464
|
+
] });
|
|
33465
|
+
};
|
|
33466
|
+
var FirstTimeLoginDebug = () => {
|
|
33467
|
+
const {
|
|
33468
|
+
isFirstTimeLogin,
|
|
33469
|
+
hasCompletedFirstLogin,
|
|
33470
|
+
needsOnboarding,
|
|
33471
|
+
completeFirstLogin,
|
|
33472
|
+
user
|
|
33473
|
+
} = useFirstTimeLogin();
|
|
33474
|
+
if (!user) return null;
|
|
33475
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
33476
|
+
position: "fixed",
|
|
33477
|
+
top: 10,
|
|
33478
|
+
right: 10,
|
|
33479
|
+
background: "#f0f0f0",
|
|
33480
|
+
padding: "10px",
|
|
33481
|
+
border: "1px solid #ccc",
|
|
33482
|
+
fontSize: "12px",
|
|
33483
|
+
zIndex: 9999
|
|
33484
|
+
}, children: [
|
|
33485
|
+
/* @__PURE__ */ jsx("h4", { children: "First-Time Login Debug" }),
|
|
33486
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
33487
|
+
"User ID: ",
|
|
33488
|
+
user.id
|
|
33489
|
+
] }),
|
|
33490
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
33491
|
+
"Email: ",
|
|
33492
|
+
user.email
|
|
33493
|
+
] }),
|
|
33494
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
33495
|
+
"Is First Time: ",
|
|
33496
|
+
isFirstTimeLogin ? "\u2705" : "\u274C"
|
|
33497
|
+
] }),
|
|
33498
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
33499
|
+
"Completed: ",
|
|
33500
|
+
hasCompletedFirstLogin ? "\u2705" : "\u274C"
|
|
33501
|
+
] }),
|
|
33502
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
33503
|
+
"Needs Onboarding: ",
|
|
33504
|
+
needsOnboarding ? "\u2705" : "\u274C"
|
|
33505
|
+
] }),
|
|
33506
|
+
needsOnboarding && /* @__PURE__ */ jsx(
|
|
33507
|
+
"button",
|
|
33508
|
+
{
|
|
33509
|
+
onClick: completeFirstLogin,
|
|
33510
|
+
style: { marginTop: "5px", padding: "5px" },
|
|
33511
|
+
children: "Mark as Completed"
|
|
33512
|
+
}
|
|
33513
|
+
)
|
|
33514
|
+
] });
|
|
33515
|
+
};
|
|
33516
|
+
var onboardingSteps2 = [
|
|
33517
|
+
{
|
|
33518
|
+
id: "welcome",
|
|
33519
|
+
title: "Welcome to Optifye.ai",
|
|
33520
|
+
description: "Let's take a quick tour to help you get started with our intelligent manufacturing monitoring platform.",
|
|
33521
|
+
position: "center"
|
|
33522
|
+
},
|
|
33523
|
+
{
|
|
33524
|
+
id: "live-streams",
|
|
33525
|
+
title: "Live Video Streams",
|
|
33526
|
+
description: "These are live video streams of your manufacturing lines. Monitor your production in real-time from anywhere.",
|
|
33527
|
+
target: ".workspace-grid",
|
|
33528
|
+
position: "top",
|
|
33529
|
+
highlightTarget: true
|
|
33530
|
+
},
|
|
33531
|
+
{
|
|
33532
|
+
id: "efficiency-indicator",
|
|
33533
|
+
title: "Efficiency Indicators",
|
|
33534
|
+
description: "Notice the colored overlays on each video? They change based on real-time efficiency metrics - green for optimal, yellow for warning, and red for critical.",
|
|
33535
|
+
target: ".efficiency-indicator",
|
|
33536
|
+
position: "bottom",
|
|
33537
|
+
highlightTarget: true
|
|
33538
|
+
},
|
|
33539
|
+
{
|
|
33540
|
+
id: "click-workspace",
|
|
33541
|
+
title: "Explore a Workspace",
|
|
33542
|
+
description: "Click on any video stream to dive deeper into that workspace's performance metrics.",
|
|
33543
|
+
target: ".workspace-item",
|
|
33544
|
+
position: "center",
|
|
33545
|
+
highlightTarget: true,
|
|
33546
|
+
waitForNavigation: "/workspace/"
|
|
33547
|
+
},
|
|
33548
|
+
{
|
|
33549
|
+
id: "workspace-metrics",
|
|
33550
|
+
title: "Real-Time Efficiency Metrics",
|
|
33551
|
+
description: "Great! These are the real-time efficiency metrics for the workspace you just selected. Track OEE, performance, and quality in real-time.",
|
|
33552
|
+
target: ".metrics-dashboard",
|
|
33553
|
+
position: "top",
|
|
33554
|
+
highlightTarget: true
|
|
33555
|
+
},
|
|
33556
|
+
{
|
|
33557
|
+
id: "clips-navigation",
|
|
33558
|
+
title: "Explore Clips",
|
|
33559
|
+
description: 'Now, click on "Clips" in the sidebar to see how you can diagnose issues and understand root causes.',
|
|
33560
|
+
target: '[data-nav-item="clips"]',
|
|
33561
|
+
position: "right",
|
|
33562
|
+
highlightTarget: true,
|
|
33563
|
+
waitForNavigation: "/clips"
|
|
33564
|
+
},
|
|
33565
|
+
{
|
|
33566
|
+
id: "clips-explanation",
|
|
33567
|
+
title: "Clips for Root Cause Analysis",
|
|
33568
|
+
description: "Clips help you understand and diagnose the root cause of efficiency issues. Review recorded incidents, analyze patterns, and improve your processes.",
|
|
33569
|
+
target: ".clips-container",
|
|
33570
|
+
position: "top",
|
|
33571
|
+
highlightTarget: true
|
|
33572
|
+
},
|
|
33573
|
+
{
|
|
33574
|
+
id: "complete",
|
|
33575
|
+
title: "You're All Set!",
|
|
33576
|
+
description: "You've completed the tour! Start exploring Optifye.ai to optimize your manufacturing operations.",
|
|
33577
|
+
position: "center"
|
|
33578
|
+
}
|
|
33579
|
+
];
|
|
33580
|
+
var OnboardingTour = ({
|
|
33581
|
+
isOpen,
|
|
33582
|
+
onComplete,
|
|
33583
|
+
onSkip
|
|
33584
|
+
}) => {
|
|
33585
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
33586
|
+
const [isWaitingForNavigation, setIsWaitingForNavigation] = useState(false);
|
|
33587
|
+
const router = useRouter();
|
|
33588
|
+
const step = onboardingSteps2[currentStep];
|
|
33589
|
+
useEffect(() => {
|
|
33590
|
+
if (!step?.waitForNavigation || !isWaitingForNavigation) return;
|
|
33591
|
+
const handleRouteChange = (url) => {
|
|
33592
|
+
if (step.waitForNavigation && url.includes(step.waitForNavigation)) {
|
|
33593
|
+
setIsWaitingForNavigation(false);
|
|
33594
|
+
handleNext();
|
|
33595
|
+
}
|
|
33596
|
+
};
|
|
33597
|
+
router.events.on("routeChangeComplete", handleRouteChange);
|
|
33598
|
+
return () => {
|
|
33599
|
+
router.events.off("routeChangeComplete", handleRouteChange);
|
|
33600
|
+
};
|
|
33601
|
+
}, [step, isWaitingForNavigation, router]);
|
|
33602
|
+
const handleNext = useCallback(() => {
|
|
33603
|
+
const nextStep = onboardingSteps2[currentStep + 1];
|
|
33604
|
+
if (step?.waitForNavigation && !isWaitingForNavigation) {
|
|
33605
|
+
setIsWaitingForNavigation(true);
|
|
33606
|
+
return;
|
|
33607
|
+
}
|
|
33608
|
+
if (currentStep < onboardingSteps2.length - 1) {
|
|
33609
|
+
setCurrentStep(currentStep + 1);
|
|
33610
|
+
if (nextStep?.target) {
|
|
33611
|
+
setTimeout(() => {
|
|
33612
|
+
const element = document.querySelector(nextStep.target);
|
|
33613
|
+
if (element) {
|
|
33614
|
+
element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
33615
|
+
}
|
|
33616
|
+
}, 100);
|
|
33617
|
+
}
|
|
33618
|
+
} else {
|
|
33619
|
+
onComplete();
|
|
33620
|
+
}
|
|
33621
|
+
}, [currentStep, step, isWaitingForNavigation, onComplete]);
|
|
33622
|
+
const handlePrevious = useCallback(() => {
|
|
33623
|
+
if (currentStep > 0) {
|
|
33624
|
+
setCurrentStep(currentStep - 1);
|
|
33625
|
+
setIsWaitingForNavigation(false);
|
|
33626
|
+
}
|
|
33627
|
+
}, [currentStep]);
|
|
33628
|
+
const getPositionStyles = useCallback((position) => {
|
|
33629
|
+
const baseStyles = {
|
|
33630
|
+
position: "fixed",
|
|
33631
|
+
zIndex: 1e4
|
|
33632
|
+
};
|
|
33633
|
+
if (step?.target && position !== "center") {
|
|
33634
|
+
const element = document.querySelector(step.target);
|
|
33635
|
+
if (element) {
|
|
33636
|
+
const rect = element.getBoundingClientRect();
|
|
33637
|
+
const tooltipWidth = 400;
|
|
33638
|
+
const tooltipHeight = 200;
|
|
33639
|
+
const offset = 20;
|
|
33640
|
+
switch (position) {
|
|
33641
|
+
case "top":
|
|
33642
|
+
return {
|
|
33643
|
+
...baseStyles,
|
|
33644
|
+
left: `${rect.left + rect.width / 2 - tooltipWidth / 2}px`,
|
|
33645
|
+
bottom: `${window.innerHeight - rect.top + offset}px`
|
|
33646
|
+
};
|
|
33647
|
+
case "bottom":
|
|
33648
|
+
return {
|
|
33649
|
+
...baseStyles,
|
|
33650
|
+
left: `${rect.left + rect.width / 2 - tooltipWidth / 2}px`,
|
|
33651
|
+
top: `${rect.bottom + offset}px`
|
|
33652
|
+
};
|
|
33653
|
+
case "left":
|
|
33654
|
+
return {
|
|
33655
|
+
...baseStyles,
|
|
33656
|
+
right: `${window.innerWidth - rect.left + offset}px`,
|
|
33657
|
+
top: `${rect.top + rect.height / 2 - tooltipHeight / 2}px`
|
|
33658
|
+
};
|
|
33659
|
+
case "right":
|
|
33660
|
+
return {
|
|
33661
|
+
...baseStyles,
|
|
33662
|
+
left: `${rect.right + offset}px`,
|
|
33663
|
+
top: `${rect.top + rect.height / 2 - tooltipHeight / 2}px`
|
|
33664
|
+
};
|
|
33665
|
+
}
|
|
33666
|
+
}
|
|
33667
|
+
}
|
|
33668
|
+
return {
|
|
33669
|
+
...baseStyles,
|
|
33670
|
+
top: "50%",
|
|
33671
|
+
left: "50%",
|
|
33672
|
+
transform: "translate(-50%, -50%)"
|
|
33673
|
+
};
|
|
33674
|
+
}, [step]);
|
|
33675
|
+
if (!isOpen || !step) return null;
|
|
33676
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33677
|
+
/* @__PURE__ */ jsx(
|
|
33678
|
+
motion.div,
|
|
33679
|
+
{
|
|
33680
|
+
initial: { opacity: 0 },
|
|
33681
|
+
animate: { opacity: 1 },
|
|
33682
|
+
exit: { opacity: 0 },
|
|
33683
|
+
className: "fixed inset-0 bg-black/60 backdrop-blur-sm z-[9998]",
|
|
33684
|
+
onClick: (e) => e.stopPropagation()
|
|
33685
|
+
}
|
|
33686
|
+
),
|
|
33687
|
+
step.highlightTarget && step.target && /* @__PURE__ */ jsx(HighlightOverlay, { target: step.target }),
|
|
33688
|
+
/* @__PURE__ */ jsx(
|
|
33689
|
+
motion.div,
|
|
33690
|
+
{
|
|
33691
|
+
initial: { opacity: 0, scale: 0.9 },
|
|
33692
|
+
animate: { opacity: 1, scale: 1 },
|
|
33693
|
+
exit: { opacity: 0, scale: 0.9 },
|
|
33694
|
+
transition: { duration: 0.3, ease: "easeOut" },
|
|
33695
|
+
style: getPositionStyles(step.position),
|
|
33696
|
+
className: "w-[400px] max-w-[90vw]",
|
|
33697
|
+
children: /* @__PURE__ */ jsxs("div", { className: "bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
|
|
33698
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between", children: [
|
|
33699
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
33700
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: [...Array(onboardingSteps2.length)].map((_, i) => /* @__PURE__ */ jsx(
|
|
33701
|
+
"div",
|
|
33702
|
+
{
|
|
33703
|
+
className: `h-1.5 transition-all duration-300 ${i === currentStep ? "w-6 bg-blue-600" : i < currentStep ? "w-1.5 bg-blue-400" : "w-1.5 bg-gray-300 dark:bg-gray-600"} rounded-full`
|
|
33704
|
+
},
|
|
33705
|
+
i
|
|
33706
|
+
)) }),
|
|
33707
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
33708
|
+
currentStep + 1,
|
|
33709
|
+
" / ",
|
|
33710
|
+
onboardingSteps2.length
|
|
33711
|
+
] })
|
|
33712
|
+
] }),
|
|
33713
|
+
/* @__PURE__ */ jsx(
|
|
33714
|
+
"button",
|
|
33715
|
+
{
|
|
33716
|
+
onClick: onSkip,
|
|
33717
|
+
className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors",
|
|
33718
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
33719
|
+
}
|
|
33720
|
+
)
|
|
33721
|
+
] }),
|
|
33722
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-5", children: [
|
|
33723
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: step.title }),
|
|
33724
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 dark:text-gray-300 leading-relaxed", children: step.description }),
|
|
33725
|
+
isWaitingForNavigation && /* @__PURE__ */ jsx("div", { className: "mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-blue-700 dark:text-blue-300 flex items-center gap-2", children: [
|
|
33726
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block w-2 h-2 bg-blue-600 rounded-full animate-pulse" }),
|
|
33727
|
+
"Waiting for you to click..."
|
|
33728
|
+
] }) })
|
|
33729
|
+
] }),
|
|
33730
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 bg-gray-50 dark:bg-gray-900/50 flex items-center justify-between", children: [
|
|
33731
|
+
/* @__PURE__ */ jsx(
|
|
33732
|
+
"button",
|
|
33733
|
+
{
|
|
33734
|
+
onClick: onSkip,
|
|
33735
|
+
className: "text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors",
|
|
33736
|
+
children: "Skip tour"
|
|
33737
|
+
}
|
|
33738
|
+
),
|
|
33739
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
33740
|
+
currentStep > 0 && /* @__PURE__ */ jsx(
|
|
33741
|
+
"button",
|
|
33742
|
+
{
|
|
33743
|
+
onClick: handlePrevious,
|
|
33744
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
33745
|
+
children: "Previous"
|
|
33746
|
+
}
|
|
33747
|
+
),
|
|
33748
|
+
/* @__PURE__ */ jsx(
|
|
33749
|
+
"button",
|
|
33750
|
+
{
|
|
33751
|
+
onClick: handleNext,
|
|
33752
|
+
disabled: isWaitingForNavigation,
|
|
33753
|
+
className: `px-4 py-2 text-sm font-medium rounded-lg transition-all flex items-center gap-2 ${isWaitingForNavigation ? "bg-gray-100 text-gray-400 cursor-not-allowed" : currentStep === onboardingSteps2.length - 1 ? "bg-green-600 text-white hover:bg-green-700" : "bg-blue-600 text-white hover:bg-blue-700"}`,
|
|
33754
|
+
children: currentStep === onboardingSteps2.length - 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33755
|
+
"Complete",
|
|
33756
|
+
/* @__PURE__ */ jsx(Check, { className: "w-4 h-4" })
|
|
33757
|
+
] }) : isWaitingForNavigation ? "Waiting..." : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33758
|
+
"Next",
|
|
33759
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
|
|
33760
|
+
] })
|
|
33761
|
+
}
|
|
33762
|
+
)
|
|
33763
|
+
] })
|
|
33764
|
+
] })
|
|
33765
|
+
] })
|
|
33766
|
+
},
|
|
33767
|
+
step.id
|
|
33768
|
+
)
|
|
33769
|
+
] }) });
|
|
33770
|
+
};
|
|
33771
|
+
var HighlightOverlay = ({ target }) => {
|
|
33772
|
+
const [rect, setRect] = useState(null);
|
|
33773
|
+
useEffect(() => {
|
|
33774
|
+
const element = document.querySelector(target);
|
|
33775
|
+
if (element) {
|
|
33776
|
+
setRect(element.getBoundingClientRect());
|
|
33777
|
+
}
|
|
33778
|
+
}, [target]);
|
|
33779
|
+
if (!rect) return null;
|
|
33780
|
+
return /* @__PURE__ */ jsxs(
|
|
33781
|
+
motion.div,
|
|
33782
|
+
{
|
|
33783
|
+
initial: { opacity: 0 },
|
|
33784
|
+
animate: { opacity: 1 },
|
|
33785
|
+
exit: { opacity: 0 },
|
|
33786
|
+
className: "fixed z-[9999] pointer-events-none",
|
|
33787
|
+
style: {
|
|
33788
|
+
left: rect.left - 8,
|
|
33789
|
+
top: rect.top - 8,
|
|
33790
|
+
width: rect.width + 16,
|
|
33791
|
+
height: rect.height + 16
|
|
33792
|
+
},
|
|
33793
|
+
children: [
|
|
33794
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg ring-4 ring-blue-500/50 ring-offset-4 ring-offset-transparent animate-pulse" }),
|
|
33795
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-lg bg-blue-500/10" })
|
|
33796
|
+
]
|
|
33797
|
+
}
|
|
33798
|
+
);
|
|
33799
|
+
};
|
|
33800
|
+
var OnboardingDemo = () => {
|
|
33801
|
+
const [showTour, setShowTour] = useState(false);
|
|
33802
|
+
const handleComplete = () => {
|
|
33803
|
+
setShowTour(false);
|
|
33804
|
+
console.log("Onboarding tour completed");
|
|
33805
|
+
};
|
|
33806
|
+
const handleSkip = () => {
|
|
33807
|
+
setShowTour(false);
|
|
33808
|
+
console.log("Onboarding tour skipped");
|
|
33809
|
+
};
|
|
33810
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33811
|
+
/* @__PURE__ */ jsxs(
|
|
33812
|
+
"button",
|
|
33813
|
+
{
|
|
33814
|
+
onClick: () => setShowTour(true),
|
|
33815
|
+
className: "fixed bottom-4 right-4 z-50 flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg shadow-lg hover:bg-blue-700 transition-colors",
|
|
33816
|
+
title: "Start Onboarding Tour",
|
|
33817
|
+
children: [
|
|
33818
|
+
/* @__PURE__ */ jsx(Info, { className: "w-4 h-4" }),
|
|
33819
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Start Tour" })
|
|
33820
|
+
]
|
|
33821
|
+
}
|
|
33822
|
+
),
|
|
33823
|
+
/* @__PURE__ */ jsx(
|
|
33824
|
+
InteractiveOnboardingTour,
|
|
33825
|
+
{
|
|
33826
|
+
isOpen: showTour,
|
|
33827
|
+
onComplete: handleComplete,
|
|
33828
|
+
onSkip: handleSkip
|
|
33829
|
+
}
|
|
33830
|
+
)
|
|
33831
|
+
] });
|
|
33832
|
+
};
|
|
31209
33833
|
var ThreadSidebar = ({
|
|
31210
33834
|
activeThreadId,
|
|
31211
33835
|
onSelectThread,
|
|
@@ -34412,6 +37036,15 @@ var QualityOverview = memo(({ lineInfo }) => {
|
|
|
34412
37036
|
] });
|
|
34413
37037
|
});
|
|
34414
37038
|
QualityOverview.displayName = "QualityOverview";
|
|
37039
|
+
var getSupervisorName = (lineId) => {
|
|
37040
|
+
const supervisorMapping = {
|
|
37041
|
+
// Add more mappings as needed
|
|
37042
|
+
"default": "Vivaan Baid",
|
|
37043
|
+
"factory": "Vivaan Baid"
|
|
37044
|
+
// You can add specific line IDs here when you have real data
|
|
37045
|
+
};
|
|
37046
|
+
return supervisorMapping[lineId || "default"] || "Vivaan Baid";
|
|
37047
|
+
};
|
|
34415
37048
|
var KPIDetailView = ({
|
|
34416
37049
|
lineId,
|
|
34417
37050
|
date: urlDate,
|
|
@@ -34901,9 +37534,15 @@ var KPIDetailView = ({
|
|
|
34901
37534
|
"aria-label": "Navigate back to previous page"
|
|
34902
37535
|
}
|
|
34903
37536
|
) }),
|
|
34904
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center mt-2 sm:mt-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
34905
|
-
/* @__PURE__ */
|
|
34906
|
-
|
|
37537
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center mt-2 sm:mt-0", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
37538
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:gap-3", children: [
|
|
37539
|
+
/* @__PURE__ */ jsx("h1", { className: "text-lg sm:text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center truncate", children: lineInfo?.line_name || "Line" }),
|
|
37540
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 sm:h-2 sm:w-2 rounded-full bg-green-500 animate-pulse ring-1 sm:ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0" })
|
|
37541
|
+
] }),
|
|
37542
|
+
/* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-600 font-medium", children: [
|
|
37543
|
+
"Supervisor: ",
|
|
37544
|
+
getSupervisorName(lineInfo?.line_id)
|
|
37545
|
+
] })
|
|
34907
37546
|
] }) })
|
|
34908
37547
|
] }),
|
|
34909
37548
|
(activeTab !== "monthly_history" || urlDate || urlShift) && metrics2 && /* @__PURE__ */ jsx("div", { className: "mt-2 sm:mt-3 bg-blue-50 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 sm:gap-3 md:gap-4", children: [
|
|
@@ -35111,8 +37750,11 @@ var LineCard = ({ line, onClick }) => {
|
|
|
35111
37750
|
onClick,
|
|
35112
37751
|
className: "relative bg-white border border-gray-200/80 shadow-sm hover:shadow-lg \n rounded-xl p-4 sm:p-5 md:p-6 transition-all duration-200 cursor-pointer \n hover:scale-[1.01] active:scale-[0.99] group",
|
|
35113
37752
|
children: [
|
|
35114
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-
|
|
35115
|
-
/* @__PURE__ */
|
|
37753
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start gap-2 sm:gap-3 mb-4 sm:mb-5 md:mb-6", children: [
|
|
37754
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
37755
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg sm:text-xl font-semibold text-gray-900 truncate", children: line.line_name }),
|
|
37756
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600 mt-1", children: "Supervisor: Vivaan" })
|
|
37757
|
+
] }),
|
|
35116
37758
|
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 self-start sm:self-auto ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, children: [
|
|
35117
37759
|
/* @__PURE__ */ jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
|
|
35118
37760
|
/* @__PURE__ */ jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
|
|
@@ -35814,9 +38456,6 @@ var ProfileView = () => {
|
|
|
35814
38456
|
id: user.id,
|
|
35815
38457
|
email: user.email,
|
|
35816
38458
|
full_name: profileData.full_name,
|
|
35817
|
-
company: profileData.company,
|
|
35818
|
-
phone: profileData.phone,
|
|
35819
|
-
timezone: profileData.timezone,
|
|
35820
38459
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
35821
38460
|
});
|
|
35822
38461
|
if (error2) throw error2;
|
|
@@ -35965,55 +38604,6 @@ var ProfileView = () => {
|
|
|
35965
38604
|
/* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.email }),
|
|
35966
38605
|
profileData.email_verified && /* @__PURE__ */ jsx(UserCheck, { className: "h-4 w-4 text-green-500" })
|
|
35967
38606
|
] })
|
|
35968
|
-
] }),
|
|
35969
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
35970
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Company" }),
|
|
35971
|
-
isEditing ? /* @__PURE__ */ jsx(
|
|
35972
|
-
"input",
|
|
35973
|
-
{
|
|
35974
|
-
type: "text",
|
|
35975
|
-
value: profileData.company || "",
|
|
35976
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, company: e.target.value })),
|
|
35977
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
35978
|
-
}
|
|
35979
|
-
) : /* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.company || "Not set" })
|
|
35980
|
-
] }),
|
|
35981
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
35982
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Role" }),
|
|
35983
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.role || "Not set" })
|
|
35984
|
-
] }),
|
|
35985
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
35986
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Phone" }),
|
|
35987
|
-
isEditing ? /* @__PURE__ */ jsx(
|
|
35988
|
-
"input",
|
|
35989
|
-
{
|
|
35990
|
-
type: "tel",
|
|
35991
|
-
value: profileData.phone || "",
|
|
35992
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, phone: e.target.value })),
|
|
35993
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
35994
|
-
}
|
|
35995
|
-
) : /* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.phone || "Not set" })
|
|
35996
|
-
] }),
|
|
35997
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
35998
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Timezone" }),
|
|
35999
|
-
isEditing ? /* @__PURE__ */ jsxs(
|
|
36000
|
-
"select",
|
|
36001
|
-
{
|
|
36002
|
-
value: profileData.timezone || "",
|
|
36003
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, timezone: e.target.value })),
|
|
36004
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
36005
|
-
children: [
|
|
36006
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Select timezone" }),
|
|
36007
|
-
/* @__PURE__ */ jsx("option", { value: "UTC", children: "UTC" }),
|
|
36008
|
-
/* @__PURE__ */ jsx("option", { value: "America/New_York", children: "Eastern Time" }),
|
|
36009
|
-
/* @__PURE__ */ jsx("option", { value: "America/Chicago", children: "Central Time" }),
|
|
36010
|
-
/* @__PURE__ */ jsx("option", { value: "America/Denver", children: "Mountain Time" }),
|
|
36011
|
-
/* @__PURE__ */ jsx("option", { value: "America/Los_Angeles", children: "Pacific Time" }),
|
|
36012
|
-
/* @__PURE__ */ jsx("option", { value: "Asia/Kolkata", children: "India Standard Time" }),
|
|
36013
|
-
/* @__PURE__ */ jsx("option", { value: "Europe/London", children: "GMT" })
|
|
36014
|
-
]
|
|
36015
|
-
}
|
|
36016
|
-
) : /* @__PURE__ */ jsx("p", { className: "text-gray-900", children: profileData.timezone || "Not set" })
|
|
36017
38607
|
] })
|
|
36018
38608
|
] }),
|
|
36019
38609
|
isEditing && /* @__PURE__ */ jsx("div", { className: "mt-6 flex gap-3", children: /* @__PURE__ */ jsx(Button2, { variant: "outline", onClick: handleSaveProfile, disabled: loading, children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -38596,10 +41186,6 @@ var TargetsView = ({
|
|
|
38596
41186
|
}, [actionIds]);
|
|
38597
41187
|
const handleSaveLine = useCallback(async (lineId) => {
|
|
38598
41188
|
console.log(`[handleSaveLine] Attempting to save line: ${lineId}`);
|
|
38599
|
-
if (!canSaveTargets) {
|
|
38600
|
-
toast.error("You do not have permission to save targets. Contact your administrator.");
|
|
38601
|
-
return;
|
|
38602
|
-
}
|
|
38603
41189
|
const hardcodedUserId = "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
38604
41190
|
const currentEffectiveUserId = hardcodedUserId;
|
|
38605
41191
|
console.log(`[handleSaveLine] effectiveUserId: ${currentEffectiveUserId}`);
|
|
@@ -38876,6 +41462,13 @@ var WorkspaceDetailView = ({
|
|
|
38876
41462
|
const [selectedMonth, setSelectedMonth] = useState(today.getMonth());
|
|
38877
41463
|
const [selectedYear, setSelectedYear] = useState(today.getFullYear());
|
|
38878
41464
|
const [selectedShift, setSelectedShift] = useState("day");
|
|
41465
|
+
useEffect(() => {
|
|
41466
|
+
if (parsedShiftId === 1) {
|
|
41467
|
+
setSelectedShift("night");
|
|
41468
|
+
} else if (parsedShiftId === 0) {
|
|
41469
|
+
setSelectedShift("day");
|
|
41470
|
+
}
|
|
41471
|
+
}, [parsedShiftId]);
|
|
38879
41472
|
const isHistoricView = Boolean(date && parsedShiftId !== void 0);
|
|
38880
41473
|
const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
|
|
38881
41474
|
const [activeTab, setActiveTab] = useState(initialTab);
|
|
@@ -39125,11 +41718,18 @@ var WorkspaceDetailView = ({
|
|
|
39125
41718
|
}
|
|
39126
41719
|
return;
|
|
39127
41720
|
}
|
|
41721
|
+
if (activeTab === "monthly_history") {
|
|
41722
|
+
if (onNavigate) {
|
|
41723
|
+
onNavigate("/");
|
|
41724
|
+
}
|
|
41725
|
+
return;
|
|
41726
|
+
}
|
|
39128
41727
|
if (date || shift) {
|
|
39129
41728
|
setActiveTab("monthly_history");
|
|
39130
41729
|
if (onNavigate) {
|
|
39131
41730
|
const params = new URLSearchParams();
|
|
39132
41731
|
params.set("fromMonthly", "true");
|
|
41732
|
+
params.set("shift", selectedShift === "night" ? "1" : "0");
|
|
39133
41733
|
if (effectiveLineId) {
|
|
39134
41734
|
params.set("lineId", effectiveLineId);
|
|
39135
41735
|
}
|
|
@@ -39141,6 +41741,7 @@ var WorkspaceDetailView = ({
|
|
|
39141
41741
|
if (onNavigate) {
|
|
39142
41742
|
const params = new URLSearchParams();
|
|
39143
41743
|
params.set("fromMonthly", "true");
|
|
41744
|
+
params.set("shift", selectedShift === "night" ? "1" : "0");
|
|
39144
41745
|
if (effectiveLineId) {
|
|
39145
41746
|
params.set("lineId", effectiveLineId);
|
|
39146
41747
|
}
|
|
@@ -39239,7 +41840,7 @@ var WorkspaceDetailView = ({
|
|
|
39239
41840
|
BackButtonMinimal,
|
|
39240
41841
|
{
|
|
39241
41842
|
onClick: handleBackNavigation,
|
|
39242
|
-
text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : date || shift ? "Back to Monthly History" : "Back",
|
|
41843
|
+
text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : (date || shift) && activeTab !== "monthly_history" ? "Back to Monthly History" : "Back",
|
|
39243
41844
|
size: "sm",
|
|
39244
41845
|
"aria-label": "Navigate back to previous page"
|
|
39245
41846
|
}
|
|
@@ -39265,7 +41866,7 @@ var WorkspaceDetailView = ({
|
|
|
39265
41866
|
BackButtonMinimal,
|
|
39266
41867
|
{
|
|
39267
41868
|
onClick: handleBackNavigation,
|
|
39268
|
-
text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : date || shift ? "Back to Monthly History" : "Back",
|
|
41869
|
+
text: previousView === "line_monthly_history" ? "Back to Line History" : returnUrl && returnUrl.includes("monthly_history") ? "Back to Line History" : returnUrl && returnUrl.includes("/kpis/") ? "Back to KPIs" : returnUrl && returnUrl.includes("/leaderboard/") ? "Back to Leaderboard" : (date || shift) && activeTab !== "monthly_history" ? "Back to Monthly History" : "Back",
|
|
39269
41870
|
size: "default",
|
|
39270
41871
|
"aria-label": "Navigate back to previous page"
|
|
39271
41872
|
}
|
|
@@ -39653,6 +42254,7 @@ var WorkspaceDetailView = ({
|
|
|
39653
42254
|
const params = new URLSearchParams();
|
|
39654
42255
|
params.set("date", selectedDate);
|
|
39655
42256
|
params.set("shift", selectedShift === "day" ? "0" : "1");
|
|
42257
|
+
params.set("fromMonthly", "true");
|
|
39656
42258
|
if (effectiveLineId) {
|
|
39657
42259
|
params.set("lineId", effectiveLineId);
|
|
39658
42260
|
}
|
|
@@ -40136,6 +42738,168 @@ var WorkspaceHealthView_default = withAuth(WorkspaceHealthView, {
|
|
|
40136
42738
|
requireAuth: true
|
|
40137
42739
|
});
|
|
40138
42740
|
var AuthenticatedWorkspaceHealthView = WorkspaceHealthView;
|
|
42741
|
+
var SupervisorManagementView = ({
|
|
42742
|
+
onNavigate,
|
|
42743
|
+
onBack,
|
|
42744
|
+
className = ""
|
|
42745
|
+
}) => {
|
|
42746
|
+
const supabase = useSupabaseClient();
|
|
42747
|
+
const entityConfig = useEntityConfig();
|
|
42748
|
+
const companyId = entityConfig?.companyId;
|
|
42749
|
+
const [data, setData] = useState({
|
|
42750
|
+
assignments: [],
|
|
42751
|
+
allSupervisors: [],
|
|
42752
|
+
loading: true,
|
|
42753
|
+
error: void 0
|
|
42754
|
+
});
|
|
42755
|
+
const [savingAssignments, setSavingAssignments] = useState(/* @__PURE__ */ new Set());
|
|
42756
|
+
const loadData = useCallback(async () => {
|
|
42757
|
+
try {
|
|
42758
|
+
setData((prev) => ({ ...prev, loading: true, error: void 0 }));
|
|
42759
|
+
if (!companyId) {
|
|
42760
|
+
throw new Error("Company ID is not configured");
|
|
42761
|
+
}
|
|
42762
|
+
if (!supabase) {
|
|
42763
|
+
throw new Error("Supabase client is not available");
|
|
42764
|
+
}
|
|
42765
|
+
console.log(`[SupervisorManagementView] Loading data for companyId: ${companyId}`);
|
|
42766
|
+
const supervisorService = createSupervisorService(supabase);
|
|
42767
|
+
const realData = await supervisorService.getSupervisorManagementData(companyId);
|
|
42768
|
+
console.log(`[SupervisorManagementView] Received data:`, {
|
|
42769
|
+
assignments: realData.assignments.length,
|
|
42770
|
+
allSupervisors: realData.allSupervisors.length,
|
|
42771
|
+
firstAssignmentAvailableSupervisors: realData.assignments[0]?.availableSupervisors?.length || 0,
|
|
42772
|
+
firstAssignmentAvailableSupervisorIds: realData.assignments[0]?.availableSupervisors?.map((s) => s.id) || []
|
|
42773
|
+
});
|
|
42774
|
+
setData(realData);
|
|
42775
|
+
} catch (error) {
|
|
42776
|
+
console.error("Error loading supervisor management data:", error);
|
|
42777
|
+
setData((prev) => ({
|
|
42778
|
+
...prev,
|
|
42779
|
+
loading: false,
|
|
42780
|
+
error: error instanceof Error ? error.message : "Failed to load supervisor management data. Please try again."
|
|
42781
|
+
}));
|
|
42782
|
+
}
|
|
42783
|
+
}, [supabase, companyId]);
|
|
42784
|
+
useEffect(() => {
|
|
42785
|
+
loadData();
|
|
42786
|
+
}, [loadData]);
|
|
42787
|
+
const handleSupervisorChange = useCallback(async (lineId, supervisor) => {
|
|
42788
|
+
try {
|
|
42789
|
+
setSavingAssignments((prev) => /* @__PURE__ */ new Set([...prev, lineId]));
|
|
42790
|
+
if (!supabase) {
|
|
42791
|
+
throw new Error("Supabase client is not available");
|
|
42792
|
+
}
|
|
42793
|
+
const supervisorService = createSupervisorService(supabase);
|
|
42794
|
+
const success = await supervisorService.assignSupervisorToLine(lineId, supervisor?.id);
|
|
42795
|
+
if (!success) {
|
|
42796
|
+
throw new Error("Failed to update supervisor assignment");
|
|
42797
|
+
}
|
|
42798
|
+
setData((prev) => ({
|
|
42799
|
+
...prev,
|
|
42800
|
+
assignments: prev.assignments.map(
|
|
42801
|
+
(assignment) => assignment.lineId === lineId ? { ...assignment, currentSupervisor: supervisor || void 0 } : assignment
|
|
42802
|
+
)
|
|
42803
|
+
}));
|
|
42804
|
+
const lineName = data.assignments.find((a) => a.lineId === lineId)?.lineName || "Line";
|
|
42805
|
+
const message = supervisor ? `${supervisor.name} assigned to ${lineName}` : `Supervisor removed from ${lineName}`;
|
|
42806
|
+
toast.success(message);
|
|
42807
|
+
} catch (error) {
|
|
42808
|
+
console.error("Error updating supervisor assignment:", error);
|
|
42809
|
+
toast.error("Failed to update supervisor assignment. Please try again.");
|
|
42810
|
+
} finally {
|
|
42811
|
+
setSavingAssignments((prev) => {
|
|
42812
|
+
const newSet = new Set(prev);
|
|
42813
|
+
newSet.delete(lineId);
|
|
42814
|
+
return newSet;
|
|
42815
|
+
});
|
|
42816
|
+
}
|
|
42817
|
+
}, [data.assignments, supabase]);
|
|
42818
|
+
const handleRefresh = useCallback(() => {
|
|
42819
|
+
loadData();
|
|
42820
|
+
}, [loadData]);
|
|
42821
|
+
const handleBack = useCallback(() => {
|
|
42822
|
+
if (onBack) {
|
|
42823
|
+
onBack();
|
|
42824
|
+
} else if (onNavigate) {
|
|
42825
|
+
onNavigate("/");
|
|
42826
|
+
}
|
|
42827
|
+
}, [onBack, onNavigate]);
|
|
42828
|
+
if (data.loading) {
|
|
42829
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("min-h-screen bg-slate-50", className), children: /* @__PURE__ */ jsx(LoadingPage, { message: "Loading supervisor management..." }) });
|
|
42830
|
+
}
|
|
42831
|
+
if (data.error) {
|
|
42832
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("min-h-screen bg-slate-50", className), children: /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
42833
|
+
EmptyStateMessage,
|
|
42834
|
+
{
|
|
42835
|
+
iconType: AlertCircle,
|
|
42836
|
+
title: "Failed to Load Data",
|
|
42837
|
+
message: data.error,
|
|
42838
|
+
actionButton: {
|
|
42839
|
+
text: "Try Again",
|
|
42840
|
+
onClick: handleRefresh,
|
|
42841
|
+
className: "bg-blue-600 hover:bg-blue-700"
|
|
42842
|
+
}
|
|
42843
|
+
}
|
|
42844
|
+
) }) });
|
|
42845
|
+
}
|
|
42846
|
+
return /* @__PURE__ */ jsxs("div", { className: clsx("min-h-screen bg-slate-50", className), children: [
|
|
42847
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsx("div", { className: "px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center relative", children: [
|
|
42848
|
+
/* @__PURE__ */ jsx("div", { className: "sm:absolute sm:left-0", children: /* @__PURE__ */ jsx(
|
|
42849
|
+
BackButtonMinimal,
|
|
42850
|
+
{
|
|
42851
|
+
onClick: handleBack,
|
|
42852
|
+
text: "Back",
|
|
42853
|
+
size: "default",
|
|
42854
|
+
"aria-label": "Navigate back to previous page"
|
|
42855
|
+
}
|
|
42856
|
+
) }),
|
|
42857
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center mt-2 sm:mt-0", children: [
|
|
42858
|
+
/* @__PURE__ */ jsx("h1", { className: "text-lg sm:text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Supervisor Management" }),
|
|
42859
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs sm:text-sm text-gray-500 mt-0.5 sm:mt-1 text-center px-2 sm:px-0", children: "Manage supervisor assignments for production lines" })
|
|
42860
|
+
] }),
|
|
42861
|
+
/* @__PURE__ */ jsx("div", { className: "hidden sm:block absolute right-0 w-24", children: /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
42862
|
+
"button",
|
|
42863
|
+
{
|
|
42864
|
+
onClick: handleRefresh,
|
|
42865
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
42866
|
+
"aria-label": "Refresh data",
|
|
42867
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-5 w-5" })
|
|
42868
|
+
}
|
|
42869
|
+
) }) })
|
|
42870
|
+
] }) }) }),
|
|
42871
|
+
/* @__PURE__ */ jsx("main", { className: "flex-1 px-3 sm:px-4 md:px-5 lg:px-6 py-4 sm:py-6", children: data.assignments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: /* @__PURE__ */ jsx(
|
|
42872
|
+
EmptyStateMessage,
|
|
42873
|
+
{
|
|
42874
|
+
iconType: Building2,
|
|
42875
|
+
title: "No Lines Found",
|
|
42876
|
+
message: "There are no production lines to manage supervisor assignments for."
|
|
42877
|
+
}
|
|
42878
|
+
) }) : /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
|
|
42879
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
42880
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Production Line" }),
|
|
42881
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assigned Supervisor" })
|
|
42882
|
+
] }) }),
|
|
42883
|
+
/* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: data.assignments.map((assignment) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-gray-50", children: [
|
|
42884
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
42885
|
+
/* @__PURE__ */ jsx(Building2, { className: "h-4 w-4 text-gray-400 mr-2" }),
|
|
42886
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-900", children: assignment.lineName })
|
|
42887
|
+
] }) }),
|
|
42888
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "max-w-xs", children: /* @__PURE__ */ jsx(
|
|
42889
|
+
SupervisorDropdown,
|
|
42890
|
+
{
|
|
42891
|
+
selectedSupervisor: assignment.currentSupervisor,
|
|
42892
|
+
availableSupervisors: assignment.availableSupervisors,
|
|
42893
|
+
onSelect: (supervisor) => handleSupervisorChange(assignment.lineId, supervisor),
|
|
42894
|
+
disabled: savingAssignments.has(assignment.lineId),
|
|
42895
|
+
placeholder: "Select supervisor..."
|
|
42896
|
+
}
|
|
42897
|
+
) }) })
|
|
42898
|
+
] }, assignment.lineId)) })
|
|
42899
|
+
] }) }) }) }) })
|
|
42900
|
+
] });
|
|
42901
|
+
};
|
|
42902
|
+
var SupervisorManagementView_default = SupervisorManagementView;
|
|
40139
42903
|
var S3Service = class {
|
|
40140
42904
|
constructor(config) {
|
|
40141
42905
|
this.s3Client = null;
|
|
@@ -40591,4 +43355,4 @@ var streamProxyConfig = {
|
|
|
40591
43355
|
}
|
|
40592
43356
|
};
|
|
40593
43357
|
|
|
40594
|
-
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedWorkspaceHealthView, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CompactWorkspaceHealthCard, CongratulationsOverlay, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, 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, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, 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, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useCanSaveTargets, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealth, useWorkspaceHealthById, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPrefetchManager, videoPreloader, whatsappService, withAuth, withRegistry, workspaceHealthService, workspaceService };
|
|
43358
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedWorkspaceHealthView, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CompactWorkspaceHealthCard, CongratulationsOverlay, 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_SHIFT_CONFIG, 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, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, 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, 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, MainLayout, MetricCard_default as MetricCard, MinimalOnboardingPopup, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useCanSaveTargets, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealth, useWorkspaceHealthById, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, workspaceHealthService, workspaceService };
|