@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.js
CHANGED
|
@@ -23,6 +23,7 @@ var jsPDF = require('jspdf');
|
|
|
23
23
|
var SelectPrimitive = require('@radix-ui/react-select');
|
|
24
24
|
var videojs = require('video.js');
|
|
25
25
|
require('video.js/dist/video-js.css');
|
|
26
|
+
var reactDom = require('react-dom');
|
|
26
27
|
var sonner = require('sonner');
|
|
27
28
|
var clientS3 = require('@aws-sdk/client-s3');
|
|
28
29
|
var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
|
|
@@ -4837,8 +4838,347 @@ var S3ClipsService = class {
|
|
|
4837
4838
|
return this.apiClient.getStats();
|
|
4838
4839
|
}
|
|
4839
4840
|
};
|
|
4840
|
-
|
|
4841
|
-
|
|
4841
|
+
var getSupabaseClient2 = () => {
|
|
4842
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
4843
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
4844
|
+
if (!url || !key) {
|
|
4845
|
+
throw new Error("Supabase configuration missing");
|
|
4846
|
+
}
|
|
4847
|
+
return supabaseJs.createClient(url, key);
|
|
4848
|
+
};
|
|
4849
|
+
var getAuthToken2 = async () => {
|
|
4850
|
+
try {
|
|
4851
|
+
const supabase = getSupabaseClient2();
|
|
4852
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
4853
|
+
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4854
|
+
return session?.access_token || null;
|
|
4855
|
+
} catch (error) {
|
|
4856
|
+
console.error("[S3ClipsSupabase] Error getting auth token:", error);
|
|
4857
|
+
return null;
|
|
4858
|
+
}
|
|
4859
|
+
};
|
|
4860
|
+
var S3ClipsSupabaseService = class {
|
|
4861
|
+
constructor(config) {
|
|
4862
|
+
this.requestCache = /* @__PURE__ */ new Map();
|
|
4863
|
+
// Flags for compatibility
|
|
4864
|
+
this.isIndexBuilding = false;
|
|
4865
|
+
this.isPrefetching = false;
|
|
4866
|
+
this.currentMetadataFetches = 0;
|
|
4867
|
+
this.MAX_CONCURRENT_METADATA = 3;
|
|
4868
|
+
this.config = config;
|
|
4869
|
+
if (!config.s3Config) {
|
|
4870
|
+
throw new Error("S3 configuration is required");
|
|
4871
|
+
}
|
|
4872
|
+
const processing = config.s3Config.processing || {};
|
|
4873
|
+
this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
|
|
4874
|
+
this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
|
|
4875
|
+
this.concurrencyLimit = processing.concurrencyLimit || 10;
|
|
4876
|
+
this.maxInitialFetch = processing.maxInitialFetch || 60;
|
|
4877
|
+
console.log("[S3ClipsSupabase] \u2705 Initialized with Supabase backend - Direct database queries!");
|
|
4878
|
+
}
|
|
4879
|
+
/**
|
|
4880
|
+
* Fetch with authentication and error handling
|
|
4881
|
+
*/
|
|
4882
|
+
async fetchWithAuth(endpoint, body) {
|
|
4883
|
+
const token = await getAuthToken2();
|
|
4884
|
+
if (!token) {
|
|
4885
|
+
throw new Error("Authentication required");
|
|
4886
|
+
}
|
|
4887
|
+
const apiEndpoint = "/api/clips/supabase";
|
|
4888
|
+
const requestBody = {
|
|
4889
|
+
...body,
|
|
4890
|
+
action: endpoint.replace("/api/clips/supabase/", "")
|
|
4891
|
+
};
|
|
4892
|
+
console.log(`[S3ClipsSupabase] Making request to ${apiEndpoint} with action: ${requestBody.action}, body:`, requestBody);
|
|
4893
|
+
const response = await fetch(apiEndpoint, {
|
|
4894
|
+
method: "POST",
|
|
4895
|
+
headers: {
|
|
4896
|
+
"Authorization": `Bearer ${token}`,
|
|
4897
|
+
"Content-Type": "application/json"
|
|
4898
|
+
},
|
|
4899
|
+
body: JSON.stringify(requestBody)
|
|
4900
|
+
});
|
|
4901
|
+
console.log(`[S3ClipsSupabase] Response status: ${response.status}`);
|
|
4902
|
+
if (!response.ok) {
|
|
4903
|
+
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
4904
|
+
console.error(`[S3ClipsSupabase] API error:`, error);
|
|
4905
|
+
throw new Error(error.error || `API error: ${response.status}`);
|
|
4906
|
+
}
|
|
4907
|
+
const data = await response.json();
|
|
4908
|
+
if (requestBody.action === "by-index" || requestBody.action === "batch") {
|
|
4909
|
+
console.log(`[S3ClipsSupabase] API Response for ${requestBody.action}:`, {
|
|
4910
|
+
action: requestBody.action,
|
|
4911
|
+
hasData: !!data,
|
|
4912
|
+
dataKeys: Object.keys(data || {}),
|
|
4913
|
+
video: requestBody.action === "by-index" ? data?.video : void 0,
|
|
4914
|
+
videosCount: requestBody.action === "batch" ? data?.videos?.length : void 0
|
|
4915
|
+
});
|
|
4916
|
+
} else if (requestBody.action === "clip-types") {
|
|
4917
|
+
console.log(`[S3ClipsSupabase] API Response for clip-types:`, {
|
|
4918
|
+
action: requestBody.action,
|
|
4919
|
+
hasData: !!data,
|
|
4920
|
+
dataKeys: Object.keys(data || {}),
|
|
4921
|
+
clipTypesCount: data?.clipTypes?.length,
|
|
4922
|
+
clipTypes: data?.clipTypes
|
|
4923
|
+
});
|
|
4924
|
+
} else if (requestBody.action === "count") {
|
|
4925
|
+
console.log(`[S3ClipsSupabase] API Response for count:`, {
|
|
4926
|
+
action: requestBody.action,
|
|
4927
|
+
hasData: !!data,
|
|
4928
|
+
counts: data?.counts
|
|
4929
|
+
});
|
|
4930
|
+
}
|
|
4931
|
+
return data;
|
|
4932
|
+
}
|
|
4933
|
+
/**
|
|
4934
|
+
* Deduplicate requests to prevent multiple API calls
|
|
4935
|
+
*/
|
|
4936
|
+
async deduplicate(key, factory) {
|
|
4937
|
+
if (this.requestCache.has(key)) {
|
|
4938
|
+
console.log(`[S3ClipsSupabase] Deduplicating request: ${key}`);
|
|
4939
|
+
return this.requestCache.get(key);
|
|
4940
|
+
}
|
|
4941
|
+
const promise = factory().finally(() => {
|
|
4942
|
+
this.requestCache.delete(key);
|
|
4943
|
+
});
|
|
4944
|
+
this.requestCache.set(key, promise);
|
|
4945
|
+
return promise;
|
|
4946
|
+
}
|
|
4947
|
+
/**
|
|
4948
|
+
* Lists clips using Supabase API
|
|
4949
|
+
*/
|
|
4950
|
+
async listS3Clips(params) {
|
|
4951
|
+
const { workspaceId, date, shiftId } = params;
|
|
4952
|
+
if (!isValidShiftId(shiftId)) {
|
|
4953
|
+
console.error(`[S3ClipsSupabase] Invalid shift ID: ${shiftId}`);
|
|
4954
|
+
return [];
|
|
4955
|
+
}
|
|
4956
|
+
console.log(`[S3ClipsSupabase] Listing clips via Supabase for workspace: ${workspaceId}`);
|
|
4957
|
+
try {
|
|
4958
|
+
const response = await this.fetchWithAuth("list", {
|
|
4959
|
+
workspaceId,
|
|
4960
|
+
date,
|
|
4961
|
+
shift: shiftId,
|
|
4962
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
4963
|
+
});
|
|
4964
|
+
return response.clips.map((clip) => clip.originalUri || `clips:${clip.id}`);
|
|
4965
|
+
} catch (error) {
|
|
4966
|
+
console.error("[S3ClipsSupabase] Error listing clips:", error);
|
|
4967
|
+
return [];
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
/**
|
|
4971
|
+
* Get metadata cycle time
|
|
4972
|
+
*/
|
|
4973
|
+
async getMetadataCycleTime(clipId) {
|
|
4974
|
+
const id3 = clipId.startsWith("clips:") ? clipId.substring(6) : clipId;
|
|
4975
|
+
try {
|
|
4976
|
+
console.log(`[S3ClipsSupabase] Metadata cycle time requested for clip: ${id3}`);
|
|
4977
|
+
return null;
|
|
4978
|
+
} catch (error) {
|
|
4979
|
+
console.error("[S3ClipsSupabase] Error fetching metadata cycle time:", error);
|
|
4980
|
+
return null;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
/**
|
|
4984
|
+
* Control prefetch mode
|
|
4985
|
+
*/
|
|
4986
|
+
setPrefetchMode(enabled) {
|
|
4987
|
+
this.isPrefetching = enabled;
|
|
4988
|
+
console.log(`[S3ClipsSupabase] Prefetch mode ${enabled ? "enabled" : "disabled"}`);
|
|
4989
|
+
}
|
|
4990
|
+
/**
|
|
4991
|
+
* Get full metadata
|
|
4992
|
+
*/
|
|
4993
|
+
async getFullMetadata(clipId) {
|
|
4994
|
+
if (this.isIndexBuilding || this.isPrefetching) {
|
|
4995
|
+
console.warn("[S3ClipsSupabase] Skipping metadata - operation in progress");
|
|
4996
|
+
return null;
|
|
4997
|
+
}
|
|
4998
|
+
return null;
|
|
4999
|
+
}
|
|
5000
|
+
/**
|
|
5001
|
+
* Get clip counts with optional video index
|
|
5002
|
+
*/
|
|
5003
|
+
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false) {
|
|
5004
|
+
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
5005
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5006
|
+
console.log(`[S3ClipsSupabase] Fetching clip counts from Supabase for:`, {
|
|
5007
|
+
workspaceId,
|
|
5008
|
+
date,
|
|
5009
|
+
shift: shiftId
|
|
5010
|
+
});
|
|
5011
|
+
const response = await this.fetchWithAuth("count", {
|
|
5012
|
+
workspaceId,
|
|
5013
|
+
date,
|
|
5014
|
+
shift: shiftId.toString()
|
|
5015
|
+
});
|
|
5016
|
+
console.log(`[S3ClipsSupabase] Count API response:`, response);
|
|
5017
|
+
const counts = response.counts || {};
|
|
5018
|
+
console.log(`[S3ClipsSupabase] Extracted counts:`, counts);
|
|
5019
|
+
if (buildIndex) {
|
|
5020
|
+
const videoIndex = {
|
|
5021
|
+
byCategory: /* @__PURE__ */ new Map(),
|
|
5022
|
+
allVideos: [],
|
|
5023
|
+
counts,
|
|
5024
|
+
workspaceId,
|
|
5025
|
+
date,
|
|
5026
|
+
shiftId: shiftId.toString(),
|
|
5027
|
+
lastUpdated: /* @__PURE__ */ new Date(),
|
|
5028
|
+
_debugId: `supabase_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
5029
|
+
};
|
|
5030
|
+
if (buildIndex) {
|
|
5031
|
+
const categories = Object.keys(counts).filter((k) => k !== "total");
|
|
5032
|
+
for (const category of categories) {
|
|
5033
|
+
if (counts[category] > 0) {
|
|
5034
|
+
const categoryResponse = await this.fetchWithAuth("list", {
|
|
5035
|
+
workspaceId,
|
|
5036
|
+
date,
|
|
5037
|
+
shift: shiftId.toString(),
|
|
5038
|
+
category,
|
|
5039
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5040
|
+
});
|
|
5041
|
+
const entries = categoryResponse.clips.map((clip) => ({
|
|
5042
|
+
uri: clip.originalUri || `clips:${clip.id}`,
|
|
5043
|
+
category,
|
|
5044
|
+
timestamp: clip.timestamp,
|
|
5045
|
+
videoId: clip.id,
|
|
5046
|
+
workspaceId,
|
|
5047
|
+
date,
|
|
5048
|
+
shiftId: shiftId.toString()
|
|
5049
|
+
}));
|
|
5050
|
+
videoIndex.byCategory.set(category, entries);
|
|
5051
|
+
videoIndex.allVideos.push(...entries);
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
return {
|
|
5056
|
+
counts,
|
|
5057
|
+
videoIndex
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
return counts;
|
|
5061
|
+
});
|
|
5062
|
+
}
|
|
5063
|
+
/**
|
|
5064
|
+
* Get clip counts (simplified version)
|
|
5065
|
+
*/
|
|
5066
|
+
async getClipCounts(workspaceId, date, shiftId) {
|
|
5067
|
+
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false);
|
|
5068
|
+
if (typeof result === "object" && "counts" in result) {
|
|
5069
|
+
return result.counts;
|
|
5070
|
+
}
|
|
5071
|
+
return result;
|
|
5072
|
+
}
|
|
5073
|
+
/**
|
|
5074
|
+
* Get clip by index
|
|
5075
|
+
*/
|
|
5076
|
+
async getClipByIndex(workspaceId, date, shiftId, category, index) {
|
|
5077
|
+
const cacheKey = `clip:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
5078
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5079
|
+
console.log(`[S3ClipsSupabase] Fetching clip by index from Supabase for category: ${category}, index: ${index}`);
|
|
5080
|
+
const response = await this.fetchWithAuth("by-index", {
|
|
5081
|
+
workspaceId,
|
|
5082
|
+
date,
|
|
5083
|
+
shift: shiftId.toString(),
|
|
5084
|
+
category,
|
|
5085
|
+
index,
|
|
5086
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5087
|
+
});
|
|
5088
|
+
const video = response.video;
|
|
5089
|
+
console.log("[S3ClipsSupabase] getClipByIndex response:", {
|
|
5090
|
+
hasVideo: !!video,
|
|
5091
|
+
hasSrc: !!video?.src,
|
|
5092
|
+
srcType: typeof video?.src,
|
|
5093
|
+
srcPreview: video?.src ? video.src.substring(0, 50) : "no src",
|
|
5094
|
+
isProxyUrl: video?.src?.includes("/api/clips/stream/")
|
|
5095
|
+
});
|
|
5096
|
+
return video || null;
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
5099
|
+
/**
|
|
5100
|
+
* Get first clip for all categories
|
|
5101
|
+
*/
|
|
5102
|
+
async getFirstClipsForAllCategories(workspaceId, date, shiftId) {
|
|
5103
|
+
console.log(`[S3ClipsSupabase] Getting first clips for all categories`);
|
|
5104
|
+
const counts = await this.getClipCounts(workspaceId, date, shiftId);
|
|
5105
|
+
const categories = Object.keys(counts).filter((k) => k !== "total" && counts[k] > 0);
|
|
5106
|
+
const promises = categories.map(async (category) => {
|
|
5107
|
+
const clip = await this.getClipByIndex(workspaceId, date, shiftId, category, 0);
|
|
5108
|
+
return { category, clip };
|
|
5109
|
+
});
|
|
5110
|
+
const results = await Promise.all(promises);
|
|
5111
|
+
const firstClips = {};
|
|
5112
|
+
for (const { category, clip } of results) {
|
|
5113
|
+
firstClips[category] = clip;
|
|
5114
|
+
}
|
|
5115
|
+
return firstClips;
|
|
5116
|
+
}
|
|
5117
|
+
/**
|
|
5118
|
+
* Get first clip for a specific category
|
|
5119
|
+
*/
|
|
5120
|
+
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
5121
|
+
return this.getClipByIndex(workspaceId, date, shiftId, category, 0);
|
|
5122
|
+
}
|
|
5123
|
+
/**
|
|
5124
|
+
* Batch fetch videos (alias for batchFetchClips for compatibility)
|
|
5125
|
+
*/
|
|
5126
|
+
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
5127
|
+
return this.batchFetchClips(workspaceId, date, shiftId, requests);
|
|
5128
|
+
}
|
|
5129
|
+
/**
|
|
5130
|
+
* Batch fetch clips
|
|
5131
|
+
*/
|
|
5132
|
+
async batchFetchClips(workspaceId, date, shiftId, requests) {
|
|
5133
|
+
const cacheKey = `batch:${workspaceId}:${date}:${shiftId}:${JSON.stringify(requests)}`;
|
|
5134
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5135
|
+
console.log(`[S3ClipsSupabase] Batch fetching ${requests.length} clips from Supabase`);
|
|
5136
|
+
const response = await this.fetchWithAuth("batch", {
|
|
5137
|
+
workspaceId,
|
|
5138
|
+
date,
|
|
5139
|
+
shift: shiftId.toString(),
|
|
5140
|
+
requests,
|
|
5141
|
+
sopCategories: this.config.s3Config?.sopCategories?.default
|
|
5142
|
+
});
|
|
5143
|
+
console.log("[S3ClipsSupabase] batchFetchClips response:", {
|
|
5144
|
+
videoCount: response.videos?.length,
|
|
5145
|
+
firstVideo: response.videos?.[0],
|
|
5146
|
+
hasProxyUrls: response.videos?.[0]?.video?.src?.includes("/api/clips/stream/")
|
|
5147
|
+
});
|
|
5148
|
+
return response.videos.map((v) => v.video);
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
/**
|
|
5152
|
+
* Get all clip types from Supabase
|
|
5153
|
+
*/
|
|
5154
|
+
async getClipTypes() {
|
|
5155
|
+
const cacheKey = "clip-types:all";
|
|
5156
|
+
return this.deduplicate(cacheKey, async () => {
|
|
5157
|
+
console.log(`[S3ClipsSupabase] Fetching clip types from Supabase`);
|
|
5158
|
+
const response = await this.fetchWithAuth("clip-types", {});
|
|
5159
|
+
console.log(`[S3ClipsSupabase] Fetched ${response.clipTypes?.length || 0} clip types:`, response.clipTypes);
|
|
5160
|
+
return response.clipTypes || [];
|
|
5161
|
+
});
|
|
5162
|
+
}
|
|
5163
|
+
/**
|
|
5164
|
+
* Ensure videos are loaded for navigation
|
|
5165
|
+
*/
|
|
5166
|
+
async ensureVideosLoaded(workspaceId, date, shiftId, category, currentIndex) {
|
|
5167
|
+
const rangeBefore = 1;
|
|
5168
|
+
const rangeAfter = 3;
|
|
5169
|
+
const requests = [];
|
|
5170
|
+
for (let i = Math.max(0, currentIndex - rangeBefore); i < currentIndex; i++) {
|
|
5171
|
+
requests.push({ category, index: i });
|
|
5172
|
+
}
|
|
5173
|
+
for (let i = currentIndex; i <= currentIndex + rangeAfter; i++) {
|
|
5174
|
+
requests.push({ category, index: i });
|
|
5175
|
+
}
|
|
5176
|
+
if (requests.length > 0) {
|
|
5177
|
+
await this.batchFetchClips(workspaceId, date, shiftId, requests);
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
};
|
|
5181
|
+
var S3ClipsService2 = S3ClipsSupabaseService ;
|
|
4842
5182
|
var VideoPrefetchManager = class extends events.EventEmitter {
|
|
4843
5183
|
constructor() {
|
|
4844
5184
|
super();
|
|
@@ -4867,7 +5207,7 @@ var VideoPrefetchManager = class extends events.EventEmitter {
|
|
|
4867
5207
|
getS3Service(dashboardConfig) {
|
|
4868
5208
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
4869
5209
|
if (!this.s3Services.has(configKey)) {
|
|
4870
|
-
this.s3Services.set(configKey, new
|
|
5210
|
+
this.s3Services.set(configKey, new S3ClipsService2(dashboardConfig));
|
|
4871
5211
|
}
|
|
4872
5212
|
return this.s3Services.get(configKey);
|
|
4873
5213
|
}
|
|
@@ -5251,25 +5591,387 @@ if (typeof window !== "undefined") {
|
|
|
5251
5591
|
videoPrefetchManager.destroy();
|
|
5252
5592
|
});
|
|
5253
5593
|
}
|
|
5594
|
+
|
|
5595
|
+
// src/lib/services/linesService.ts
|
|
5596
|
+
var LinesService = class {
|
|
5597
|
+
constructor(supabase) {
|
|
5598
|
+
this.supabase = supabase;
|
|
5599
|
+
}
|
|
5600
|
+
/**
|
|
5601
|
+
* Fetch all active lines for a given company
|
|
5602
|
+
* @param companyId - The company ID to fetch lines for
|
|
5603
|
+
* @returns Promise<Line[]> - Array of lines for the company
|
|
5604
|
+
*/
|
|
5605
|
+
async getLinesByCompanyId(companyId) {
|
|
5606
|
+
if (!companyId) {
|
|
5607
|
+
throw new Error("Company ID is required");
|
|
5608
|
+
}
|
|
5609
|
+
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 });
|
|
5610
|
+
if (error) {
|
|
5611
|
+
console.error("Error fetching lines:", error);
|
|
5612
|
+
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
5613
|
+
}
|
|
5614
|
+
if (!data) {
|
|
5615
|
+
return [];
|
|
5616
|
+
}
|
|
5617
|
+
return data.map((line) => ({
|
|
5618
|
+
id: line.id,
|
|
5619
|
+
name: line.line_name,
|
|
5620
|
+
companyId: line.company_id,
|
|
5621
|
+
isActive: line.enable,
|
|
5622
|
+
createdAt: line.created_at,
|
|
5623
|
+
factoryId: line.factory_id
|
|
5624
|
+
}));
|
|
5625
|
+
}
|
|
5626
|
+
/**
|
|
5627
|
+
* Fetch all active lines (for all companies)
|
|
5628
|
+
* @returns Promise<Line[]> - Array of all active lines
|
|
5629
|
+
*/
|
|
5630
|
+
async getAllLines() {
|
|
5631
|
+
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 });
|
|
5632
|
+
if (error) {
|
|
5633
|
+
console.error("Error fetching all lines:", error);
|
|
5634
|
+
throw new Error(`Failed to fetch lines: ${error.message}`);
|
|
5635
|
+
}
|
|
5636
|
+
if (!data) {
|
|
5637
|
+
return [];
|
|
5638
|
+
}
|
|
5639
|
+
return data.map((line) => ({
|
|
5640
|
+
id: line.id,
|
|
5641
|
+
name: line.line_name,
|
|
5642
|
+
companyId: line.company_id,
|
|
5643
|
+
isActive: line.enable,
|
|
5644
|
+
createdAt: line.created_at,
|
|
5645
|
+
factoryId: line.factory_id
|
|
5646
|
+
}));
|
|
5647
|
+
}
|
|
5648
|
+
/**
|
|
5649
|
+
* Fetch a single line by ID
|
|
5650
|
+
* @param lineId - The line ID to fetch
|
|
5651
|
+
* @returns Promise<Line | null> - The line data or null if not found
|
|
5652
|
+
*/
|
|
5653
|
+
async getLineById(lineId) {
|
|
5654
|
+
if (!lineId) {
|
|
5655
|
+
throw new Error("Line ID is required");
|
|
5656
|
+
}
|
|
5657
|
+
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();
|
|
5658
|
+
if (error) {
|
|
5659
|
+
if (error.code === "PGRST116") {
|
|
5660
|
+
return null;
|
|
5661
|
+
}
|
|
5662
|
+
console.error("Error fetching line:", error);
|
|
5663
|
+
throw new Error(`Failed to fetch line: ${error.message}`);
|
|
5664
|
+
}
|
|
5665
|
+
if (!data) {
|
|
5666
|
+
return null;
|
|
5667
|
+
}
|
|
5668
|
+
return {
|
|
5669
|
+
id: data.id,
|
|
5670
|
+
name: data.line_name,
|
|
5671
|
+
companyId: data.company_id,
|
|
5672
|
+
isActive: data.enable,
|
|
5673
|
+
createdAt: data.created_at,
|
|
5674
|
+
factoryId: data.factory_id
|
|
5675
|
+
};
|
|
5676
|
+
}
|
|
5677
|
+
};
|
|
5678
|
+
var createLinesService = (supabase) => {
|
|
5679
|
+
return new LinesService(supabase);
|
|
5680
|
+
};
|
|
5681
|
+
var linesService = {
|
|
5682
|
+
create: createLinesService
|
|
5683
|
+
};
|
|
5684
|
+
|
|
5685
|
+
// src/lib/services/userService.ts
|
|
5686
|
+
var UserService = class {
|
|
5687
|
+
constructor(supabase) {
|
|
5688
|
+
this.supabase = supabase;
|
|
5689
|
+
}
|
|
5690
|
+
/**
|
|
5691
|
+
* Fetch all users mapped to a specific company
|
|
5692
|
+
* @param companyId - The company ID to fetch users for
|
|
5693
|
+
* @returns Promise<CompanyUser[]> - Array of users in the company
|
|
5694
|
+
*/
|
|
5695
|
+
async getUsersByCompanyId(companyId) {
|
|
5696
|
+
if (!companyId) {
|
|
5697
|
+
throw new Error("Company ID is required");
|
|
5698
|
+
}
|
|
5699
|
+
try {
|
|
5700
|
+
console.log(`[UserService] getUsersByCompanyId called with companyId: ${companyId}`);
|
|
5701
|
+
const result = await this.getUsersByCompanyIdFallback(companyId);
|
|
5702
|
+
console.log(`[UserService] getUsersByCompanyId returning ${result.length} users:`, result.map((u) => ({ id: u.id, name: u.name })));
|
|
5703
|
+
return result;
|
|
5704
|
+
} catch (error) {
|
|
5705
|
+
console.error("Error in getUsersByCompanyId:", error);
|
|
5706
|
+
throw error;
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
/**
|
|
5710
|
+
* Fallback method to fetch users by company ID using basic queries
|
|
5711
|
+
* Gets real user emails from the database
|
|
5712
|
+
* @param companyId - The company ID to fetch users for
|
|
5713
|
+
* @returns Promise<CompanyUser[]> - Array of users in the company
|
|
5714
|
+
*/
|
|
5715
|
+
async getUsersByCompanyIdFallback(companyId) {
|
|
5716
|
+
try {
|
|
5717
|
+
console.log(`[UserService] About to query user_company_mapping with companyId: ${companyId}`);
|
|
5718
|
+
const { data: mappings, error: mappingError } = await this.supabase.from("user_company_mapping").select("user_id, created_at, updated_at").eq("company_id", companyId);
|
|
5719
|
+
console.log(`[UserService] Supabase query result:`, {
|
|
5720
|
+
data: mappings,
|
|
5721
|
+
error: mappingError,
|
|
5722
|
+
dataLength: mappings?.length || 0
|
|
5723
|
+
});
|
|
5724
|
+
if (mappingError) {
|
|
5725
|
+
console.error("Error fetching user company mappings:", mappingError);
|
|
5726
|
+
throw new Error(`Failed to fetch user mappings: ${mappingError.message}`);
|
|
5727
|
+
}
|
|
5728
|
+
if (!mappings || mappings.length === 0) {
|
|
5729
|
+
console.log(`[UserService] No mappings found for companyId: ${companyId}`);
|
|
5730
|
+
return [];
|
|
5731
|
+
}
|
|
5732
|
+
console.log(`[UserService] Found ${mappings.length} mappings for companyId: ${companyId}`);
|
|
5733
|
+
console.log(`[UserService] Raw mappings:`, mappings.map((m) => m.user_id));
|
|
5734
|
+
const users = mappings.map((mapping) => {
|
|
5735
|
+
const shortId = mapping.user_id.substring(0, 8);
|
|
5736
|
+
return {
|
|
5737
|
+
id: mapping.user_id,
|
|
5738
|
+
name: mapping.user_id,
|
|
5739
|
+
// Show the full user ID
|
|
5740
|
+
email: `${shortId}@company.com`,
|
|
5741
|
+
// Simple email format
|
|
5742
|
+
isActive: true,
|
|
5743
|
+
createdAt: mapping.created_at,
|
|
5744
|
+
updatedAt: mapping.updated_at
|
|
5745
|
+
};
|
|
5746
|
+
});
|
|
5747
|
+
console.log(`[UserService] Created ${users.length} user objects`);
|
|
5748
|
+
return users;
|
|
5749
|
+
} catch (error) {
|
|
5750
|
+
console.error("Error in getUsersByCompanyIdFallback:", error);
|
|
5751
|
+
throw error;
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
/**
|
|
5755
|
+
* Check if a user is mapped to a specific company
|
|
5756
|
+
* @param userId - The user ID to check
|
|
5757
|
+
* @param companyId - The company ID to check against
|
|
5758
|
+
* @returns Promise<boolean> - Whether the user is mapped to the company
|
|
5759
|
+
*/
|
|
5760
|
+
async isUserInCompany(userId, companyId) {
|
|
5761
|
+
if (!userId || !companyId) {
|
|
5762
|
+
return false;
|
|
5763
|
+
}
|
|
5764
|
+
try {
|
|
5765
|
+
const { data, error } = await this.supabase.from("user_company_mapping").select("id").eq("user_id", userId).eq("company_id", companyId).single();
|
|
5766
|
+
if (error) {
|
|
5767
|
+
if (error.code === "PGRST116") {
|
|
5768
|
+
return false;
|
|
5769
|
+
}
|
|
5770
|
+
console.error("Error checking user company mapping:", error);
|
|
5771
|
+
return false;
|
|
5772
|
+
}
|
|
5773
|
+
return !!data;
|
|
5774
|
+
} catch (error) {
|
|
5775
|
+
console.error("Error in isUserInCompany:", error);
|
|
5776
|
+
return false;
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
/**
|
|
5780
|
+
* Get user details by user ID (if they exist in auth.users)
|
|
5781
|
+
* @param userId - The user ID to fetch
|
|
5782
|
+
* @returns Promise<CompanyUser | null> - The user data or null if not found
|
|
5783
|
+
*/
|
|
5784
|
+
async getUserById(userId) {
|
|
5785
|
+
if (!userId) {
|
|
5786
|
+
return null;
|
|
5787
|
+
}
|
|
5788
|
+
try {
|
|
5789
|
+
const { data, error } = await this.supabase.from("users").select("id, email, raw_user_meta_data").eq("id", userId).single();
|
|
5790
|
+
if (error) {
|
|
5791
|
+
if (error.code === "PGRST116") {
|
|
5792
|
+
return null;
|
|
5793
|
+
}
|
|
5794
|
+
console.error("Error fetching user by ID:", error);
|
|
5795
|
+
return null;
|
|
5796
|
+
}
|
|
5797
|
+
if (!data) {
|
|
5798
|
+
return null;
|
|
5799
|
+
}
|
|
5800
|
+
const fullName = data.raw_user_meta_data?.full_name || data.raw_user_meta_data?.name || data.email?.split("@")[0];
|
|
5801
|
+
return {
|
|
5802
|
+
id: data.id,
|
|
5803
|
+
name: fullName,
|
|
5804
|
+
email: data.email,
|
|
5805
|
+
isActive: true,
|
|
5806
|
+
createdAt: void 0,
|
|
5807
|
+
updatedAt: void 0
|
|
5808
|
+
};
|
|
5809
|
+
} catch (error) {
|
|
5810
|
+
console.error("Error in getUserById:", error);
|
|
5811
|
+
return null;
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
};
|
|
5815
|
+
var createUserService = (supabase) => {
|
|
5816
|
+
return new UserService(supabase);
|
|
5817
|
+
};
|
|
5818
|
+
var userService = {
|
|
5819
|
+
create: createUserService
|
|
5820
|
+
};
|
|
5821
|
+
|
|
5822
|
+
// src/lib/services/supervisorService.ts
|
|
5823
|
+
var SupervisorService = class {
|
|
5824
|
+
constructor(supabase) {
|
|
5825
|
+
this.supabase = supabase;
|
|
5826
|
+
this.linesService = new LinesService(supabase);
|
|
5827
|
+
this.userService = new UserService(supabase);
|
|
5828
|
+
}
|
|
5829
|
+
/**
|
|
5830
|
+
* Get supervisor management data for a specific company
|
|
5831
|
+
* Uses real lines data and real user data from the database
|
|
5832
|
+
* @param companyId - The company ID to fetch data for
|
|
5833
|
+
* @returns Promise<SupervisorManagementData>
|
|
5834
|
+
*/
|
|
5835
|
+
async getSupervisorManagementData(companyId) {
|
|
5836
|
+
try {
|
|
5837
|
+
const lines = await this.linesService.getLinesByCompanyId(companyId);
|
|
5838
|
+
console.log(`[SupervisorService] Fetching users for companyId: ${companyId}`);
|
|
5839
|
+
const companyUsers = await this.userService.getUsersByCompanyId(companyId);
|
|
5840
|
+
console.log(`[SupervisorService] Received ${companyUsers.length} users from UserService`);
|
|
5841
|
+
const availableSupervisors = companyUsers.map((user) => ({
|
|
5842
|
+
id: user.id,
|
|
5843
|
+
name: user.name,
|
|
5844
|
+
email: user.email,
|
|
5845
|
+
isActive: user.isActive,
|
|
5846
|
+
createdAt: user.createdAt,
|
|
5847
|
+
updatedAt: user.updatedAt
|
|
5848
|
+
}));
|
|
5849
|
+
console.log(`[SupervisorService] Created ${availableSupervisors.length} available supervisors`);
|
|
5850
|
+
const assignments = lines.map((line) => {
|
|
5851
|
+
const currentSupervisor = this.getRandomSupervisorForLine(line.id, availableSupervisors);
|
|
5852
|
+
const assignment = {
|
|
5853
|
+
lineId: line.id,
|
|
5854
|
+
lineName: line.name,
|
|
5855
|
+
currentSupervisor,
|
|
5856
|
+
availableSupervisors
|
|
5857
|
+
};
|
|
5858
|
+
console.log(`[SupervisorService] Assignment for line ${line.name}: ${assignment.availableSupervisors.length} available supervisors`);
|
|
5859
|
+
return assignment;
|
|
5860
|
+
});
|
|
5861
|
+
console.log(`[SupervisorService] Final result: ${assignments.length} assignments, ${availableSupervisors.length} total supervisors`);
|
|
5862
|
+
return {
|
|
5863
|
+
assignments,
|
|
5864
|
+
allSupervisors: availableSupervisors,
|
|
5865
|
+
loading: false,
|
|
5866
|
+
error: void 0
|
|
5867
|
+
};
|
|
5868
|
+
} catch (error) {
|
|
5869
|
+
console.error("Error fetching supervisor management data:", error);
|
|
5870
|
+
return {
|
|
5871
|
+
assignments: [],
|
|
5872
|
+
allSupervisors: [],
|
|
5873
|
+
loading: false,
|
|
5874
|
+
error: error instanceof Error ? error.message : "Failed to fetch supervisor management data"
|
|
5875
|
+
};
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5878
|
+
/**
|
|
5879
|
+
* Get all supervisors for a specific company (real data from database)
|
|
5880
|
+
* @param companyId - The company ID to fetch supervisors for
|
|
5881
|
+
* @returns Promise<Supervisor[]>
|
|
5882
|
+
*/
|
|
5883
|
+
async getAllSupervisors(companyId) {
|
|
5884
|
+
try {
|
|
5885
|
+
const companyUsers = await this.userService.getUsersByCompanyId(companyId);
|
|
5886
|
+
return companyUsers.map((user) => ({
|
|
5887
|
+
id: user.id,
|
|
5888
|
+
name: user.name,
|
|
5889
|
+
email: user.email,
|
|
5890
|
+
isActive: user.isActive,
|
|
5891
|
+
createdAt: user.createdAt,
|
|
5892
|
+
updatedAt: user.updatedAt
|
|
5893
|
+
}));
|
|
5894
|
+
} catch (error) {
|
|
5895
|
+
console.error("Error fetching all supervisors:", error);
|
|
5896
|
+
return [];
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
/**
|
|
5900
|
+
* Get active supervisors only for a specific company
|
|
5901
|
+
* @param companyId - The company ID to fetch active supervisors for
|
|
5902
|
+
* @returns Promise<Supervisor[]>
|
|
5903
|
+
*/
|
|
5904
|
+
async getActiveSupervisors(companyId) {
|
|
5905
|
+
const allSupervisors = await this.getAllSupervisors(companyId);
|
|
5906
|
+
return allSupervisors.filter((sup) => sup.isActive);
|
|
5907
|
+
}
|
|
5908
|
+
/**
|
|
5909
|
+
* Assign a supervisor to a line
|
|
5910
|
+
* This is a mock implementation - in production, this would update the database
|
|
5911
|
+
* @param lineId - The line ID
|
|
5912
|
+
* @param supervisorId - The supervisor ID to assign
|
|
5913
|
+
* @returns Promise<boolean> - Success status
|
|
5914
|
+
*/
|
|
5915
|
+
async assignSupervisorToLine(lineId, supervisorId) {
|
|
5916
|
+
try {
|
|
5917
|
+
console.log(`Assigning supervisor ${supervisorId} to line ${lineId}`);
|
|
5918
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
5919
|
+
return true;
|
|
5920
|
+
} catch (error) {
|
|
5921
|
+
console.error("Error assigning supervisor to line:", error);
|
|
5922
|
+
return false;
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
/**
|
|
5926
|
+
* Helper method to simulate supervisor assignments
|
|
5927
|
+
* In production, this would be fetched from the database
|
|
5928
|
+
* @param lineId - The line ID
|
|
5929
|
+
* @param supervisors - Available supervisors
|
|
5930
|
+
* @returns Supervisor or undefined
|
|
5931
|
+
*/
|
|
5932
|
+
getRandomSupervisorForLine(lineId, supervisors) {
|
|
5933
|
+
const hash = lineId.split("").reduce((a, b) => {
|
|
5934
|
+
a = (a << 5) - a + b.charCodeAt(0);
|
|
5935
|
+
return a & a;
|
|
5936
|
+
}, 0);
|
|
5937
|
+
const index = Math.abs(hash) % (supervisors.length + 1);
|
|
5938
|
+
return index < supervisors.length ? supervisors[index] : void 0;
|
|
5939
|
+
}
|
|
5940
|
+
};
|
|
5941
|
+
var createSupervisorService = (supabase) => {
|
|
5942
|
+
return new SupervisorService(supabase);
|
|
5943
|
+
};
|
|
5944
|
+
var simulateApiDelay = (ms = 1e3) => {
|
|
5945
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5946
|
+
};
|
|
5254
5947
|
var AuthContext = React19.createContext({
|
|
5255
5948
|
session: null,
|
|
5256
5949
|
user: null,
|
|
5257
5950
|
loading: true,
|
|
5258
5951
|
error: null,
|
|
5259
5952
|
signOut: async () => {
|
|
5953
|
+
},
|
|
5954
|
+
markFirstLoginCompleted: async () => false,
|
|
5955
|
+
showOnboarding: false,
|
|
5956
|
+
setShowOnboarding: () => {
|
|
5957
|
+
},
|
|
5958
|
+
completeOnboarding: async () => {
|
|
5260
5959
|
}
|
|
5261
5960
|
});
|
|
5262
5961
|
var useAuth = () => React19.useContext(AuthContext);
|
|
5263
5962
|
var AuthProvider = ({ children }) => {
|
|
5264
5963
|
const supabase = useSupabase();
|
|
5265
5964
|
const { authConfig } = useDashboardConfig();
|
|
5965
|
+
const entityConfig = useEntityConfig();
|
|
5266
5966
|
const [session, setSession] = React19.useState(null);
|
|
5267
5967
|
const [user, setUser] = React19.useState(null);
|
|
5268
5968
|
const [loading, setLoading] = React19.useState(true);
|
|
5269
5969
|
const [error, setError] = React19.useState(null);
|
|
5970
|
+
const [showOnboarding, setShowOnboarding] = React19.useState(false);
|
|
5270
5971
|
const router$1 = router.useRouter();
|
|
5271
5972
|
authConfig?.userProfileTable;
|
|
5272
5973
|
authConfig?.roleColumn || "role";
|
|
5974
|
+
const dashboardCompanyId = entityConfig?.companyId;
|
|
5273
5975
|
const fetchUserDetails = React19.useCallback(async (supabaseUser) => {
|
|
5274
5976
|
console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
|
|
5275
5977
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5288,29 +5990,66 @@ var AuthProvider = ({ children }) => {
|
|
|
5288
5990
|
reject(new Error("Profile fetch timeout"));
|
|
5289
5991
|
}, 2e3)
|
|
5290
5992
|
);
|
|
5291
|
-
const rolePromise =
|
|
5292
|
-
|
|
5293
|
-
|
|
5993
|
+
const [rolePromise, companyPromise, onboardingPromise] = [
|
|
5994
|
+
supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single(),
|
|
5995
|
+
supabase.from("user_company_mapping").select("company_id").eq("user_id", supabaseUser.id).single(),
|
|
5996
|
+
supabase.from("user_onboarding_tracking").select("first_login_completed").eq("user_id", supabaseUser.id).single()
|
|
5997
|
+
];
|
|
5998
|
+
const [roleResult, companyResult, onboardingResult] = await Promise.race([
|
|
5999
|
+
Promise.all([rolePromise, companyPromise, onboardingPromise]),
|
|
5294
6000
|
timeoutPromise
|
|
5295
|
-
// Fixed: removed .then() which was causing the bug
|
|
5296
6001
|
]);
|
|
5297
6002
|
let roleLevel = void 0;
|
|
5298
6003
|
if (roleResult && !roleResult.error && roleResult.data) {
|
|
5299
6004
|
roleLevel = roleResult.data.role_level;
|
|
5300
6005
|
} else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
|
|
5301
|
-
console.log("Error fetching
|
|
6006
|
+
console.log("Error fetching user_roles data:", roleResult.error.message);
|
|
6007
|
+
}
|
|
6008
|
+
let firstLoginCompleted = false;
|
|
6009
|
+
if (onboardingResult && !onboardingResult.error && onboardingResult.data) {
|
|
6010
|
+
firstLoginCompleted = onboardingResult.data.first_login_completed ?? false;
|
|
6011
|
+
} else if (onboardingResult?.error && onboardingResult.error.code === "PGRST116") {
|
|
6012
|
+
console.log("[fetchUserDetails] Creating onboarding record for new user");
|
|
6013
|
+
const { error: insertError } = await supabase.from("user_onboarding_tracking").insert({
|
|
6014
|
+
user_id: supabaseUser.id,
|
|
6015
|
+
first_login_completed: false
|
|
6016
|
+
});
|
|
6017
|
+
if (insertError) {
|
|
6018
|
+
console.log("Error creating onboarding record:", insertError.message);
|
|
6019
|
+
}
|
|
6020
|
+
} else if (onboardingResult?.error) {
|
|
6021
|
+
console.log("Error fetching onboarding data:", onboardingResult.error.message);
|
|
6022
|
+
}
|
|
6023
|
+
let userCompanyId = void 0;
|
|
6024
|
+
if (companyResult && !companyResult.error && companyResult.data) {
|
|
6025
|
+
userCompanyId = companyResult.data.company_id;
|
|
6026
|
+
} else if (companyResult?.error && companyResult.error.code !== "PGRST116") {
|
|
6027
|
+
console.log("Error fetching company_id:", companyResult.error.message);
|
|
6028
|
+
}
|
|
6029
|
+
if (dashboardCompanyId && userCompanyId && userCompanyId !== dashboardCompanyId) {
|
|
6030
|
+
console.warn("[Auth] Company access denied:", {
|
|
6031
|
+
userCompanyId,
|
|
6032
|
+
dashboardCompanyId,
|
|
6033
|
+
userEmail: supabaseUser.email
|
|
6034
|
+
});
|
|
6035
|
+
throw new Error("COMPANY_ACCESS_DENIED");
|
|
5302
6036
|
}
|
|
5303
6037
|
return {
|
|
5304
6038
|
...basicUser,
|
|
5305
|
-
role_level: roleLevel
|
|
6039
|
+
role_level: roleLevel,
|
|
6040
|
+
company_id: userCompanyId,
|
|
6041
|
+
first_login_completed: firstLoginCompleted
|
|
5306
6042
|
};
|
|
5307
6043
|
} catch (err) {
|
|
6044
|
+
if (err instanceof Error && err.message === "COMPANY_ACCESS_DENIED") {
|
|
6045
|
+
throw err;
|
|
6046
|
+
}
|
|
5308
6047
|
if (err instanceof Error && err.message.includes("timeout")) {
|
|
5309
6048
|
console.warn("Auth fetch timeout - using basic user info");
|
|
5310
6049
|
}
|
|
5311
6050
|
return basicUser;
|
|
5312
6051
|
}
|
|
5313
|
-
}, [supabase]);
|
|
6052
|
+
}, [supabase, dashboardCompanyId]);
|
|
5314
6053
|
React19.useEffect(() => {
|
|
5315
6054
|
if (!supabase) return;
|
|
5316
6055
|
let mounted = true;
|
|
@@ -5351,6 +6090,13 @@ var AuthProvider = ({ children }) => {
|
|
|
5351
6090
|
} catch (err) {
|
|
5352
6091
|
console.error("Error fetching user details during init:", err);
|
|
5353
6092
|
if (mounted) {
|
|
6093
|
+
if (err instanceof Error && err.message === "COMPANY_ACCESS_DENIED") {
|
|
6094
|
+
setError(new Error("You do not have access to this dashboard. Please contact your administrator."));
|
|
6095
|
+
await supabase.auth.signOut();
|
|
6096
|
+
setSession(null);
|
|
6097
|
+
setUser(null);
|
|
6098
|
+
return;
|
|
6099
|
+
}
|
|
5354
6100
|
setUser({
|
|
5355
6101
|
id: initialSession.user.id,
|
|
5356
6102
|
email: initialSession.user.email
|
|
@@ -5431,6 +6177,73 @@ var AuthProvider = ({ children }) => {
|
|
|
5431
6177
|
subscription?.unsubscribe();
|
|
5432
6178
|
};
|
|
5433
6179
|
}, [supabase, fetchUserDetails]);
|
|
6180
|
+
const markFirstLoginCompleted = React19.useCallback(async () => {
|
|
6181
|
+
console.log("[markFirstLoginCompleted] Starting update for user:", user?.id);
|
|
6182
|
+
if (!supabase || !user?.id) {
|
|
6183
|
+
console.error("[markFirstLoginCompleted] Missing requirements:", {
|
|
6184
|
+
hasSupabase: !!supabase,
|
|
6185
|
+
userId: user?.id
|
|
6186
|
+
});
|
|
6187
|
+
return false;
|
|
6188
|
+
}
|
|
6189
|
+
try {
|
|
6190
|
+
console.log("[markFirstLoginCompleted] Updating onboarding tracking for user_id:", user.id);
|
|
6191
|
+
const { data: existingData, error: checkError } = await supabase.from("user_onboarding_tracking").select("id").eq("user_id", user.id).single();
|
|
6192
|
+
let data, error2;
|
|
6193
|
+
if (checkError && checkError.code === "PGRST116") {
|
|
6194
|
+
console.log("[markFirstLoginCompleted] Creating onboarding record with completed status");
|
|
6195
|
+
const result = await supabase.from("user_onboarding_tracking").insert({
|
|
6196
|
+
user_id: user.id,
|
|
6197
|
+
first_login_completed: true
|
|
6198
|
+
}).select();
|
|
6199
|
+
data = result.data;
|
|
6200
|
+
error2 = result.error;
|
|
6201
|
+
} else {
|
|
6202
|
+
console.log("[markFirstLoginCompleted] Updating existing onboarding record");
|
|
6203
|
+
const result = await supabase.from("user_onboarding_tracking").update({
|
|
6204
|
+
first_login_completed: true,
|
|
6205
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6206
|
+
}).eq("user_id", user.id).select();
|
|
6207
|
+
data = result.data;
|
|
6208
|
+
error2 = result.error;
|
|
6209
|
+
}
|
|
6210
|
+
if (error2) {
|
|
6211
|
+
console.error("[markFirstLoginCompleted] Database update error:", {
|
|
6212
|
+
error: error2,
|
|
6213
|
+
message: error2.message,
|
|
6214
|
+
details: error2.details,
|
|
6215
|
+
hint: error2.hint,
|
|
6216
|
+
code: error2.code
|
|
6217
|
+
});
|
|
6218
|
+
return false;
|
|
6219
|
+
}
|
|
6220
|
+
console.log("[markFirstLoginCompleted] Database update successful:", data);
|
|
6221
|
+
setUser((prevUser) => {
|
|
6222
|
+
if (!prevUser) return prevUser;
|
|
6223
|
+
const updatedUser = {
|
|
6224
|
+
...prevUser,
|
|
6225
|
+
first_login_completed: true
|
|
6226
|
+
};
|
|
6227
|
+
console.log("[markFirstLoginCompleted] Local state updated:", updatedUser);
|
|
6228
|
+
return updatedUser;
|
|
6229
|
+
});
|
|
6230
|
+
console.log("[markFirstLoginCompleted] Successfully marked first login as completed");
|
|
6231
|
+
return true;
|
|
6232
|
+
} catch (err) {
|
|
6233
|
+
console.error("[markFirstLoginCompleted] Unexpected error:", err);
|
|
6234
|
+
return false;
|
|
6235
|
+
}
|
|
6236
|
+
}, [supabase, user?.id]);
|
|
6237
|
+
const completeOnboarding = React19.useCallback(async () => {
|
|
6238
|
+
console.log("[completeOnboarding] Completing onboarding tour");
|
|
6239
|
+
const success = await markFirstLoginCompleted();
|
|
6240
|
+
if (success) {
|
|
6241
|
+
setShowOnboarding(false);
|
|
6242
|
+
console.log("[completeOnboarding] Onboarding completed successfully");
|
|
6243
|
+
} else {
|
|
6244
|
+
console.error("[completeOnboarding] Failed to mark first login as completed");
|
|
6245
|
+
}
|
|
6246
|
+
}, [markFirstLoginCompleted]);
|
|
5434
6247
|
const signOut = async () => {
|
|
5435
6248
|
if (!supabase) return;
|
|
5436
6249
|
setLoading(true);
|
|
@@ -5441,7 +6254,17 @@ var AuthProvider = ({ children }) => {
|
|
|
5441
6254
|
router$1.replace(logoutRedirectPath);
|
|
5442
6255
|
}
|
|
5443
6256
|
};
|
|
5444
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value: {
|
|
6257
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value: {
|
|
6258
|
+
session,
|
|
6259
|
+
user,
|
|
6260
|
+
loading,
|
|
6261
|
+
error,
|
|
6262
|
+
signOut,
|
|
6263
|
+
markFirstLoginCompleted,
|
|
6264
|
+
showOnboarding,
|
|
6265
|
+
setShowOnboarding,
|
|
6266
|
+
completeOnboarding
|
|
6267
|
+
}, children });
|
|
5445
6268
|
};
|
|
5446
6269
|
var defaultContextValue = {
|
|
5447
6270
|
components: {},
|
|
@@ -8924,7 +9747,7 @@ var useSKUs = (companyId) => {
|
|
|
8924
9747
|
// src/lib/hooks/useCanSaveTargets.ts
|
|
8925
9748
|
var useCanSaveTargets = () => {
|
|
8926
9749
|
const { user } = useAuth();
|
|
8927
|
-
return
|
|
9750
|
+
return true;
|
|
8928
9751
|
};
|
|
8929
9752
|
function useTicketHistory(companyId) {
|
|
8930
9753
|
const [tickets, setTickets] = React19.useState([]);
|
|
@@ -12498,6 +13321,100 @@ var useHourlyTargetMisses = ({
|
|
|
12498
13321
|
clearMiss
|
|
12499
13322
|
};
|
|
12500
13323
|
};
|
|
13324
|
+
function useClipTypes() {
|
|
13325
|
+
const [clipTypes, setClipTypes] = React19.useState([]);
|
|
13326
|
+
const [isLoading, setIsLoading] = React19.useState(true);
|
|
13327
|
+
const [error, setError] = React19.useState(null);
|
|
13328
|
+
const dashboardConfig = useDashboardConfig();
|
|
13329
|
+
const s3Service = dashboardConfig?.s3Config ? videoPrefetchManager.getS3Service(dashboardConfig) : null;
|
|
13330
|
+
const fetchClipTypes = React19.useCallback(async () => {
|
|
13331
|
+
console.log("[useClipTypes] Starting fetchClipTypes, s3Service:", !!s3Service);
|
|
13332
|
+
if (!s3Service) {
|
|
13333
|
+
console.log("[useClipTypes] S3 service not initialized");
|
|
13334
|
+
setError("S3 service not initialized");
|
|
13335
|
+
setIsLoading(false);
|
|
13336
|
+
return;
|
|
13337
|
+
}
|
|
13338
|
+
setIsLoading(true);
|
|
13339
|
+
setError(null);
|
|
13340
|
+
try {
|
|
13341
|
+
console.log("[useClipTypes] Fetching clip types from S3Service...");
|
|
13342
|
+
const types = await s3Service.getClipTypes();
|
|
13343
|
+
console.log(`[useClipTypes] Fetched ${types?.length || 0} clip types:`, types);
|
|
13344
|
+
setClipTypes(types || []);
|
|
13345
|
+
} catch (err) {
|
|
13346
|
+
console.error("[useClipTypes] Error fetching clip types:", err);
|
|
13347
|
+
setError("Failed to load clip types");
|
|
13348
|
+
const fallbackTypes = [
|
|
13349
|
+
{ id: "idle_time", type: "idle_time", label: "Low Value Moments", color: "purple", description: "Idle time activities" },
|
|
13350
|
+
{ id: "best_cycle_time", type: "best_cycle_time", label: "Best Cycle Time", color: "green", description: "Fastest cycle today" },
|
|
13351
|
+
{ id: "worst_cycle_time", type: "worst_cycle_time", label: "Worst Cycle Time", color: "red", description: "Slowest cycle today" },
|
|
13352
|
+
{ id: "long_cycle_time", type: "long_cycle_time", label: "Long Cycle Time", color: "orange", description: "Above standard cycle times" }
|
|
13353
|
+
];
|
|
13354
|
+
console.log("[useClipTypes] Using fallback clip types:", fallbackTypes);
|
|
13355
|
+
setClipTypes(fallbackTypes);
|
|
13356
|
+
} finally {
|
|
13357
|
+
setIsLoading(false);
|
|
13358
|
+
}
|
|
13359
|
+
}, [s3Service]);
|
|
13360
|
+
React19.useEffect(() => {
|
|
13361
|
+
fetchClipTypes();
|
|
13362
|
+
}, [fetchClipTypes]);
|
|
13363
|
+
return {
|
|
13364
|
+
clipTypes,
|
|
13365
|
+
isLoading,
|
|
13366
|
+
error,
|
|
13367
|
+
refresh: fetchClipTypes
|
|
13368
|
+
};
|
|
13369
|
+
}
|
|
13370
|
+
function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
13371
|
+
const { clipTypes, isLoading: typesLoading, error: typesError, refresh } = useClipTypes();
|
|
13372
|
+
const [counts, setCounts] = React19.useState({});
|
|
13373
|
+
const [countsLoading, setCountsLoading] = React19.useState(false);
|
|
13374
|
+
const dashboardConfig = useDashboardConfig();
|
|
13375
|
+
const s3Service = dashboardConfig?.s3Config ? videoPrefetchManager.getS3Service(dashboardConfig) : null;
|
|
13376
|
+
React19.useEffect(() => {
|
|
13377
|
+
console.log("[useClipTypesWithCounts] Dependencies:", {
|
|
13378
|
+
s3Service: !!s3Service,
|
|
13379
|
+
workspaceId,
|
|
13380
|
+
date,
|
|
13381
|
+
shiftId,
|
|
13382
|
+
shiftIdType: typeof shiftId
|
|
13383
|
+
});
|
|
13384
|
+
if (!s3Service || !workspaceId || !date || shiftId === void 0) {
|
|
13385
|
+
console.log("[useClipTypesWithCounts] Skipping counts fetch - missing dependencies");
|
|
13386
|
+
return;
|
|
13387
|
+
}
|
|
13388
|
+
const fetchCounts = async () => {
|
|
13389
|
+
setCountsLoading(true);
|
|
13390
|
+
try {
|
|
13391
|
+
console.log("[useClipTypesWithCounts] Fetching counts...");
|
|
13392
|
+
const clipCounts = await s3Service.getClipCounts(
|
|
13393
|
+
workspaceId,
|
|
13394
|
+
date,
|
|
13395
|
+
shiftId.toString()
|
|
13396
|
+
);
|
|
13397
|
+
console.log("[useClipTypesWithCounts] Received counts:", clipCounts);
|
|
13398
|
+
setCounts(clipCounts);
|
|
13399
|
+
} catch (err) {
|
|
13400
|
+
console.error("[useClipTypesWithCounts] Error fetching counts:", err);
|
|
13401
|
+
} finally {
|
|
13402
|
+
setCountsLoading(false);
|
|
13403
|
+
}
|
|
13404
|
+
};
|
|
13405
|
+
fetchCounts();
|
|
13406
|
+
}, [s3Service, workspaceId, date, shiftId]);
|
|
13407
|
+
return {
|
|
13408
|
+
clipTypes: clipTypes.map((type) => ({
|
|
13409
|
+
...type,
|
|
13410
|
+
count: counts[type.type] || 0
|
|
13411
|
+
})),
|
|
13412
|
+
isLoading: typesLoading || countsLoading,
|
|
13413
|
+
error: typesError,
|
|
13414
|
+
refresh,
|
|
13415
|
+
counts
|
|
13416
|
+
};
|
|
13417
|
+
}
|
|
12501
13418
|
var MAX_RETRIES = 10;
|
|
12502
13419
|
var RETRY_DELAY = 500;
|
|
12503
13420
|
function useNavigation(customNavigate) {
|
|
@@ -12842,6 +13759,57 @@ var useFormatNumber = () => {
|
|
|
12842
13759
|
);
|
|
12843
13760
|
return { formatNumber };
|
|
12844
13761
|
};
|
|
13762
|
+
function useAccessControl() {
|
|
13763
|
+
const { user } = useAuth();
|
|
13764
|
+
const userRole = React19.useMemo(() => {
|
|
13765
|
+
if (!user?.role_level) return null;
|
|
13766
|
+
const roleLevel = user.role_level;
|
|
13767
|
+
if (roleLevel === "owner" || roleLevel === "plant_head" || roleLevel === "supervisor") {
|
|
13768
|
+
return roleLevel;
|
|
13769
|
+
}
|
|
13770
|
+
return "supervisor";
|
|
13771
|
+
}, [user?.role_level]);
|
|
13772
|
+
const allPages = [
|
|
13773
|
+
"/",
|
|
13774
|
+
"/leaderboard",
|
|
13775
|
+
"/kpis",
|
|
13776
|
+
"/targets",
|
|
13777
|
+
"/shifts",
|
|
13778
|
+
"/supervisor-management",
|
|
13779
|
+
"/skus",
|
|
13780
|
+
"/ai-agent",
|
|
13781
|
+
"/help",
|
|
13782
|
+
"/health",
|
|
13783
|
+
"/profile",
|
|
13784
|
+
"/workspace",
|
|
13785
|
+
"/factory-view"
|
|
13786
|
+
];
|
|
13787
|
+
const accessiblePages = React19.useMemo(() => {
|
|
13788
|
+
return allPages;
|
|
13789
|
+
}, []);
|
|
13790
|
+
const hasAccess = React19.useMemo(() => {
|
|
13791
|
+
return (path) => {
|
|
13792
|
+
return true;
|
|
13793
|
+
};
|
|
13794
|
+
}, []);
|
|
13795
|
+
const isPageVisible = React19.useMemo(() => {
|
|
13796
|
+
return (path) => {
|
|
13797
|
+
return true;
|
|
13798
|
+
};
|
|
13799
|
+
}, []);
|
|
13800
|
+
const canAccessPage = React19.useMemo(() => {
|
|
13801
|
+
return (path) => {
|
|
13802
|
+
return true;
|
|
13803
|
+
};
|
|
13804
|
+
}, []);
|
|
13805
|
+
return {
|
|
13806
|
+
userRole,
|
|
13807
|
+
hasAccess,
|
|
13808
|
+
accessiblePages,
|
|
13809
|
+
isPageVisible,
|
|
13810
|
+
canAccessPage
|
|
13811
|
+
};
|
|
13812
|
+
}
|
|
12845
13813
|
function useSupabaseClient() {
|
|
12846
13814
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
12847
13815
|
const supabase = React19.useMemo(() => supabaseJs.createClient(supabaseUrl, supabaseKey), [supabaseUrl, supabaseKey]);
|
|
@@ -20293,6 +21261,19 @@ var LoadingPage = ({
|
|
|
20293
21261
|
] }) });
|
|
20294
21262
|
};
|
|
20295
21263
|
var LoadingPage_default = LoadingPage;
|
|
21264
|
+
var AccessDeniedPage = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md w-full bg-white shadow-lg rounded-lg p-6 text-center", children: [
|
|
21265
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "mx-auto h-12 w-12 text-red-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.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" }) }) }),
|
|
21266
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-900 mb-2", children: "Access Denied" }),
|
|
21267
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 mb-4", children: "You do not have access to this dashboard. Please contact your administrator for assistance." }),
|
|
21268
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
21269
|
+
"button",
|
|
21270
|
+
{
|
|
21271
|
+
onClick: () => window.location.href = "/login",
|
|
21272
|
+
className: "bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition-colors",
|
|
21273
|
+
children: "Return to Login"
|
|
21274
|
+
}
|
|
21275
|
+
)
|
|
21276
|
+
] }) });
|
|
20296
21277
|
var withAuth = (WrappedComponent2, options) => {
|
|
20297
21278
|
const defaultOptions = {
|
|
20298
21279
|
redirectTo: "/login",
|
|
@@ -20300,7 +21281,7 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20300
21281
|
...options
|
|
20301
21282
|
};
|
|
20302
21283
|
const WithAuthComponent = React19__namespace.memo(function WithAuthComponent2(props) {
|
|
20303
|
-
const { session, loading } = useAuth();
|
|
21284
|
+
const { session, loading, error } = useAuth();
|
|
20304
21285
|
const router$1 = router.useRouter();
|
|
20305
21286
|
React19__namespace.useEffect(() => {
|
|
20306
21287
|
if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
|
|
@@ -20308,14 +21289,17 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20308
21289
|
}
|
|
20309
21290
|
}, [session, loading]);
|
|
20310
21291
|
React19__namespace.useEffect(() => {
|
|
20311
|
-
if (!loading && defaultOptions.requireAuth && !session) {
|
|
21292
|
+
if (!loading && defaultOptions.requireAuth && !session && !error) {
|
|
20312
21293
|
console.log("Redirecting to login from withAuth");
|
|
20313
21294
|
router$1.replace(defaultOptions.redirectTo);
|
|
20314
21295
|
}
|
|
20315
|
-
}, [session, loading, router$1]);
|
|
21296
|
+
}, [session, loading, router$1, error]);
|
|
20316
21297
|
if (loading) {
|
|
20317
21298
|
return /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: "Authenticating..." });
|
|
20318
21299
|
}
|
|
21300
|
+
if (error && error.message.includes("You do not have access to this dashboard")) {
|
|
21301
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AccessDeniedPage, {});
|
|
21302
|
+
}
|
|
20319
21303
|
if (defaultOptions.requireAuth && !session) {
|
|
20320
21304
|
return null;
|
|
20321
21305
|
}
|
|
@@ -20324,6 +21308,31 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20324
21308
|
WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
20325
21309
|
return WithAuthComponent;
|
|
20326
21310
|
};
|
|
21311
|
+
function withAccessControl(WrappedComponent2, options = {}) {
|
|
21312
|
+
const {
|
|
21313
|
+
requiredPath,
|
|
21314
|
+
redirectTo = "/",
|
|
21315
|
+
UnauthorizedComponent
|
|
21316
|
+
} = options;
|
|
21317
|
+
const WithAccessControlComponent = (props) => {
|
|
21318
|
+
const router$1 = router.useRouter();
|
|
21319
|
+
const { user, loading: authLoading } = useAuth();
|
|
21320
|
+
const { canAccessPage, userRole } = useAccessControl();
|
|
21321
|
+
requiredPath || router$1.pathname;
|
|
21322
|
+
if (authLoading) {
|
|
21323
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
21324
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }),
|
|
21325
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600", children: "Loading..." })
|
|
21326
|
+
] }) });
|
|
21327
|
+
}
|
|
21328
|
+
if (!user) {
|
|
21329
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 mb-4", children: "Please log in to continue" }) }) });
|
|
21330
|
+
}
|
|
21331
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent2, { ...props });
|
|
21332
|
+
};
|
|
21333
|
+
WithAccessControlComponent.displayName = `withAccessControl(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
21334
|
+
return WithAccessControlComponent;
|
|
21335
|
+
}
|
|
20327
21336
|
var LoginPage = ({
|
|
20328
21337
|
onRateLimitCheck,
|
|
20329
21338
|
logoSrc = "/optifye-logo.png",
|
|
@@ -21902,7 +22911,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21902
22911
|
ticks.push(maxYValue);
|
|
21903
22912
|
return [...new Set(ticks)].sort((a, b) => a - b);
|
|
21904
22913
|
};
|
|
21905
|
-
const renderLegend = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center text-sm text-gray-600 absolute
|
|
22914
|
+
const renderLegend = () => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border border-gray-100 rounded-full px-3 py-1", children: [
|
|
21906
22915
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t-2 border-[#E34329] border-dashed" }) }),
|
|
21907
22916
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21908
22917
|
"Target: ",
|
|
@@ -27204,7 +28213,7 @@ var VideoPlayer = React19__namespace.default.forwardRef(({
|
|
|
27204
28213
|
muted,
|
|
27205
28214
|
loop,
|
|
27206
28215
|
poster,
|
|
27207
|
-
//
|
|
28216
|
+
// Optimized HLS configuration for VOD content - PERFORMANCE OPTIMIZED
|
|
27208
28217
|
html5: {
|
|
27209
28218
|
vhs: {
|
|
27210
28219
|
// VHS (Video HTTP Streaming) options for HLS
|
|
@@ -27214,12 +28223,59 @@ var VideoPlayer = React19__namespace.default.forwardRef(({
|
|
|
27214
28223
|
// Use native HLS on Safari
|
|
27215
28224
|
enableLowInitialPlaylist: true,
|
|
27216
28225
|
smoothQualityChange: true,
|
|
27217
|
-
//
|
|
28226
|
+
// Optimized bandwidth and buffering for VOD
|
|
28227
|
+
bandwidth: 5e7,
|
|
28228
|
+
// Start with high bandwidth assumption (50 Mbps)
|
|
28229
|
+
initialBandwidth: 5e7,
|
|
28230
|
+
// Assume good connection initially
|
|
28231
|
+
limitRenditionByPlayerDimensions: true,
|
|
28232
|
+
// Buffer configuration for optimal VOD playback
|
|
28233
|
+
maxBufferLength: 30,
|
|
28234
|
+
// Don't over-buffer (30 seconds)
|
|
28235
|
+
maxMaxBufferLength: 60,
|
|
28236
|
+
// Absolute max buffer (60 seconds)
|
|
28237
|
+
maxBufferSize: 60 * 1e3 * 1e3,
|
|
28238
|
+
// 60MB max buffer size
|
|
28239
|
+
maxBufferHole: 0.5,
|
|
28240
|
+
// Allow 0.5s holes in buffer
|
|
28241
|
+
bufferBasedABR: true,
|
|
28242
|
+
// Segment loading optimization
|
|
27218
28243
|
maxPlaylistRetries: 3,
|
|
28244
|
+
playlistRetryDelay: 500,
|
|
28245
|
+
// 500ms between retries
|
|
27219
28246
|
playlistExclusionDuration: 60,
|
|
27220
|
-
|
|
27221
|
-
|
|
27222
|
-
|
|
28247
|
+
segmentLoadingRetryAttempts: 3,
|
|
28248
|
+
segmentLoadingRetryDelay: 1e3,
|
|
28249
|
+
// 1s between segment retries
|
|
28250
|
+
segmentLoadingTimeOut: 2e4,
|
|
28251
|
+
// 20s timeout for segments
|
|
28252
|
+
manifestLoadingTimeOut: 1e4,
|
|
28253
|
+
// 10s timeout for manifest
|
|
28254
|
+
// Performance optimizations
|
|
28255
|
+
experimentalBufferBasedCodecSwitching: true,
|
|
28256
|
+
experimentalCacheEncryptionKeys: true,
|
|
28257
|
+
handlePartialData: true,
|
|
28258
|
+
allowSeeksWithinUnsafeLiveWindow: false,
|
|
28259
|
+
// VOD content
|
|
28260
|
+
experimentalLLHLS: false,
|
|
28261
|
+
// Disable Low Latency HLS for VOD
|
|
28262
|
+
// Connection settings
|
|
28263
|
+
enableWorker: true,
|
|
28264
|
+
// Use web worker for better performance
|
|
28265
|
+
progressive: true,
|
|
28266
|
+
// Progressive download
|
|
28267
|
+
// Adaptive bitrate settings (if multi-quality available)
|
|
28268
|
+
abrEwmaFastLive: 3,
|
|
28269
|
+
abrEwmaSlowLive: 9,
|
|
28270
|
+
abrBandWidthFactor: 0.95,
|
|
28271
|
+
abrBandWidthUpFactor: 0.7,
|
|
28272
|
+
abrMaxWithRealBitrate: false,
|
|
28273
|
+
// Request options for better network handling
|
|
28274
|
+
requestOptions: {
|
|
28275
|
+
timeout: 3e4,
|
|
28276
|
+
// 30s overall timeout
|
|
28277
|
+
maxRetry: 3
|
|
28278
|
+
}
|
|
27223
28279
|
},
|
|
27224
28280
|
nativeVideoTracks: false,
|
|
27225
28281
|
nativeAudioTracks: false,
|
|
@@ -27284,9 +28340,17 @@ var VideoPlayer = React19__namespace.default.forwardRef(({
|
|
|
27284
28340
|
onError?.(player, error);
|
|
27285
28341
|
});
|
|
27286
28342
|
if (src) {
|
|
28343
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
28344
|
+
let videoSrc = src;
|
|
28345
|
+
if (src.startsWith("#EXTM3U")) {
|
|
28346
|
+
const blob = new Blob([src], { type: "application/x-mpegURL" });
|
|
28347
|
+
videoSrc = URL.createObjectURL(blob);
|
|
28348
|
+
console.log("[VideoPlayer] Created blob URL for HLS playlist content");
|
|
28349
|
+
player._blobUrl = videoSrc;
|
|
28350
|
+
}
|
|
27287
28351
|
player.src({
|
|
27288
|
-
src,
|
|
27289
|
-
type:
|
|
28352
|
+
src: videoSrc,
|
|
28353
|
+
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
27290
28354
|
});
|
|
27291
28355
|
}
|
|
27292
28356
|
}, [
|
|
@@ -27306,16 +28370,34 @@ var VideoPlayer = React19__namespace.default.forwardRef(({
|
|
|
27306
28370
|
]);
|
|
27307
28371
|
React19.useEffect(() => {
|
|
27308
28372
|
if (playerRef.current && src) {
|
|
28373
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
28374
|
+
let videoSrc = src;
|
|
28375
|
+
let blobUrl = null;
|
|
28376
|
+
if (src.startsWith("#EXTM3U")) {
|
|
28377
|
+
const blob = new Blob([src], { type: "application/x-mpegURL" });
|
|
28378
|
+
blobUrl = URL.createObjectURL(blob);
|
|
28379
|
+
videoSrc = blobUrl;
|
|
28380
|
+
console.log("[VideoPlayer] Created blob URL for HLS playlist content (source update)");
|
|
28381
|
+
}
|
|
27309
28382
|
playerRef.current.src({
|
|
27310
|
-
src,
|
|
27311
|
-
type:
|
|
28383
|
+
src: videoSrc,
|
|
28384
|
+
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
27312
28385
|
});
|
|
28386
|
+
return () => {
|
|
28387
|
+
if (blobUrl) {
|
|
28388
|
+
URL.revokeObjectURL(blobUrl);
|
|
28389
|
+
}
|
|
28390
|
+
};
|
|
27313
28391
|
}
|
|
27314
28392
|
}, [src]);
|
|
27315
28393
|
React19.useEffect(() => {
|
|
27316
28394
|
initializePlayer();
|
|
27317
28395
|
return () => {
|
|
27318
28396
|
if (playerRef.current) {
|
|
28397
|
+
const blobUrl = playerRef.current._blobUrl;
|
|
28398
|
+
if (blobUrl) {
|
|
28399
|
+
URL.revokeObjectURL(blobUrl);
|
|
28400
|
+
}
|
|
27319
28401
|
playerRef.current.dispose();
|
|
27320
28402
|
playerRef.current = null;
|
|
27321
28403
|
setIsReady(false);
|
|
@@ -27387,6 +28469,233 @@ var VideoPlayer = React19__namespace.default.forwardRef(({
|
|
|
27387
28469
|
] });
|
|
27388
28470
|
});
|
|
27389
28471
|
VideoPlayer.displayName = "VideoPlayer";
|
|
28472
|
+
var CroppedVideoPlayer = React19.forwardRef(({
|
|
28473
|
+
crop,
|
|
28474
|
+
debug = false,
|
|
28475
|
+
...videoProps
|
|
28476
|
+
}, ref) => {
|
|
28477
|
+
const videoContainerRef = React19.useRef(null);
|
|
28478
|
+
const hiddenVideoRef = React19.useRef(null);
|
|
28479
|
+
const canvasRef = React19.useRef(null);
|
|
28480
|
+
const animationFrameRef = React19.useRef(null);
|
|
28481
|
+
const videoElementRef = React19.useRef(null);
|
|
28482
|
+
const [isVideoReady, setIsVideoReady] = React19.useState(false);
|
|
28483
|
+
const [canvasDimensions, setCanvasDimensions] = React19.useState({ width: 0, height: 0 });
|
|
28484
|
+
const [isProcessing, setIsProcessing] = React19.useState(false);
|
|
28485
|
+
const stopCanvasRendering = React19.useCallback(() => {
|
|
28486
|
+
if (animationFrameRef.current) {
|
|
28487
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
28488
|
+
animationFrameRef.current = null;
|
|
28489
|
+
}
|
|
28490
|
+
}, []);
|
|
28491
|
+
React19.useImperativeHandle(ref, () => ({
|
|
28492
|
+
player: hiddenVideoRef.current?.player || null,
|
|
28493
|
+
play: () => hiddenVideoRef.current?.play() || void 0,
|
|
28494
|
+
pause: () => hiddenVideoRef.current?.pause(),
|
|
28495
|
+
currentTime: (time2) => {
|
|
28496
|
+
if (time2 !== void 0 && hiddenVideoRef.current) {
|
|
28497
|
+
return hiddenVideoRef.current.currentTime(time2);
|
|
28498
|
+
}
|
|
28499
|
+
return hiddenVideoRef.current?.currentTime() || 0;
|
|
28500
|
+
},
|
|
28501
|
+
duration: () => hiddenVideoRef.current?.duration() || 0,
|
|
28502
|
+
paused: () => hiddenVideoRef.current?.paused() || true,
|
|
28503
|
+
mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
|
|
28504
|
+
volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
|
|
28505
|
+
dispose: () => {
|
|
28506
|
+
hiddenVideoRef.current?.dispose();
|
|
28507
|
+
stopCanvasRendering();
|
|
28508
|
+
},
|
|
28509
|
+
isReady: hiddenVideoRef.current?.isReady || false
|
|
28510
|
+
}), [stopCanvasRendering]);
|
|
28511
|
+
const calculateCanvasDimensions = React19.useCallback(() => {
|
|
28512
|
+
if (!crop || !videoContainerRef.current) return;
|
|
28513
|
+
const container = videoContainerRef.current;
|
|
28514
|
+
const containerWidth = container.clientWidth;
|
|
28515
|
+
const containerHeight = container.clientHeight;
|
|
28516
|
+
const cropAspectRatio = crop.width / crop.height;
|
|
28517
|
+
let canvasWidth;
|
|
28518
|
+
let canvasHeight;
|
|
28519
|
+
if (containerWidth / containerHeight > cropAspectRatio) {
|
|
28520
|
+
canvasHeight = containerHeight;
|
|
28521
|
+
canvasWidth = canvasHeight * cropAspectRatio;
|
|
28522
|
+
} else {
|
|
28523
|
+
canvasWidth = containerWidth;
|
|
28524
|
+
canvasHeight = canvasWidth / cropAspectRatio;
|
|
28525
|
+
}
|
|
28526
|
+
setCanvasDimensions({
|
|
28527
|
+
width: Math.floor(canvasWidth),
|
|
28528
|
+
height: Math.floor(canvasHeight)
|
|
28529
|
+
});
|
|
28530
|
+
}, [crop]);
|
|
28531
|
+
const renderFrameToCanvas = React19.useCallback(() => {
|
|
28532
|
+
if (!canvasRef.current || !videoElementRef.current || !crop) {
|
|
28533
|
+
return;
|
|
28534
|
+
}
|
|
28535
|
+
const canvas = canvasRef.current;
|
|
28536
|
+
const video = videoElementRef.current;
|
|
28537
|
+
const ctx = canvas.getContext("2d");
|
|
28538
|
+
if (!ctx || video.paused || video.ended) {
|
|
28539
|
+
return;
|
|
28540
|
+
}
|
|
28541
|
+
const videoWidth = video.videoWidth;
|
|
28542
|
+
const videoHeight = video.videoHeight;
|
|
28543
|
+
if (videoWidth === 0 || videoHeight === 0) {
|
|
28544
|
+
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
28545
|
+
return;
|
|
28546
|
+
}
|
|
28547
|
+
const cropX = crop.x / 100 * videoWidth;
|
|
28548
|
+
const cropY = crop.y / 100 * videoHeight;
|
|
28549
|
+
const cropWidth = crop.width / 100 * videoWidth;
|
|
28550
|
+
const cropHeight = crop.height / 100 * videoHeight;
|
|
28551
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
28552
|
+
ctx.drawImage(
|
|
28553
|
+
video,
|
|
28554
|
+
cropX,
|
|
28555
|
+
cropY,
|
|
28556
|
+
cropWidth,
|
|
28557
|
+
cropHeight,
|
|
28558
|
+
// Source rectangle (crop area)
|
|
28559
|
+
0,
|
|
28560
|
+
0,
|
|
28561
|
+
canvas.width,
|
|
28562
|
+
canvas.height
|
|
28563
|
+
// Destination (full canvas)
|
|
28564
|
+
);
|
|
28565
|
+
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
28566
|
+
}, [crop]);
|
|
28567
|
+
const handleVideoReady = React19.useCallback((player) => {
|
|
28568
|
+
console.log("[CroppedVideoPlayer] Video player ready");
|
|
28569
|
+
const videoEl = player.el().querySelector("video");
|
|
28570
|
+
if (videoEl) {
|
|
28571
|
+
videoElementRef.current = videoEl;
|
|
28572
|
+
setIsVideoReady(true);
|
|
28573
|
+
}
|
|
28574
|
+
videoProps.onReady?.(player);
|
|
28575
|
+
}, [videoProps]);
|
|
28576
|
+
const handleVideoPlay = React19.useCallback((player) => {
|
|
28577
|
+
console.log("[CroppedVideoPlayer] Video playing, starting canvas rendering");
|
|
28578
|
+
if (crop && canvasRef.current) {
|
|
28579
|
+
setIsProcessing(true);
|
|
28580
|
+
renderFrameToCanvas();
|
|
28581
|
+
}
|
|
28582
|
+
videoProps.onPlay?.(player);
|
|
28583
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28584
|
+
const handleVideoPause = React19.useCallback((player) => {
|
|
28585
|
+
console.log("[CroppedVideoPlayer] Video paused, stopping canvas rendering");
|
|
28586
|
+
stopCanvasRendering();
|
|
28587
|
+
setIsProcessing(false);
|
|
28588
|
+
videoProps.onPause?.(player);
|
|
28589
|
+
}, [stopCanvasRendering, videoProps]);
|
|
28590
|
+
const handleVideoEnded = React19.useCallback((player) => {
|
|
28591
|
+
console.log("[CroppedVideoPlayer] Video ended");
|
|
28592
|
+
stopCanvasRendering();
|
|
28593
|
+
setIsProcessing(false);
|
|
28594
|
+
videoProps.onEnded?.(player);
|
|
28595
|
+
}, [stopCanvasRendering, videoProps]);
|
|
28596
|
+
const handleSeeking = React19.useCallback((player) => {
|
|
28597
|
+
console.log("[CroppedVideoPlayer] Video seeking");
|
|
28598
|
+
if (crop && !player.paused()) {
|
|
28599
|
+
renderFrameToCanvas();
|
|
28600
|
+
}
|
|
28601
|
+
videoProps.onSeeking?.(player);
|
|
28602
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28603
|
+
const handleSeeked = React19.useCallback((player) => {
|
|
28604
|
+
console.log("[CroppedVideoPlayer] Video seeked");
|
|
28605
|
+
if (crop && !player.paused()) {
|
|
28606
|
+
renderFrameToCanvas();
|
|
28607
|
+
}
|
|
28608
|
+
videoProps.onSeeked?.(player);
|
|
28609
|
+
}, [crop, renderFrameToCanvas, videoProps]);
|
|
28610
|
+
const handleLoadedMetadata = React19.useCallback((player) => {
|
|
28611
|
+
console.log("[CroppedVideoPlayer] Video metadata loaded");
|
|
28612
|
+
calculateCanvasDimensions();
|
|
28613
|
+
videoProps.onLoadedMetadata?.(player);
|
|
28614
|
+
}, [calculateCanvasDimensions, videoProps]);
|
|
28615
|
+
React19.useEffect(() => {
|
|
28616
|
+
calculateCanvasDimensions();
|
|
28617
|
+
const handleResize = () => {
|
|
28618
|
+
calculateCanvasDimensions();
|
|
28619
|
+
};
|
|
28620
|
+
window.addEventListener("resize", handleResize);
|
|
28621
|
+
return () => {
|
|
28622
|
+
window.removeEventListener("resize", handleResize);
|
|
28623
|
+
};
|
|
28624
|
+
}, [calculateCanvasDimensions]);
|
|
28625
|
+
React19.useEffect(() => {
|
|
28626
|
+
return () => {
|
|
28627
|
+
stopCanvasRendering();
|
|
28628
|
+
};
|
|
28629
|
+
}, [stopCanvasRendering]);
|
|
28630
|
+
if (!crop) {
|
|
28631
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VideoPlayer, { ref, ...videoProps });
|
|
28632
|
+
}
|
|
28633
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
28634
|
+
"div",
|
|
28635
|
+
{
|
|
28636
|
+
ref: videoContainerRef,
|
|
28637
|
+
className: `relative w-full h-full flex items-center justify-center bg-black ${videoProps.className || ""}`,
|
|
28638
|
+
children: [
|
|
28639
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28640
|
+
VideoPlayer,
|
|
28641
|
+
{
|
|
28642
|
+
ref: hiddenVideoRef,
|
|
28643
|
+
...videoProps,
|
|
28644
|
+
onReady: handleVideoReady,
|
|
28645
|
+
onPlay: handleVideoPlay,
|
|
28646
|
+
onPause: handleVideoPause,
|
|
28647
|
+
onEnded: handleVideoEnded,
|
|
28648
|
+
onSeeking: handleSeeking,
|
|
28649
|
+
onSeeked: handleSeeked,
|
|
28650
|
+
onLoadedMetadata: handleLoadedMetadata
|
|
28651
|
+
}
|
|
28652
|
+
) }),
|
|
28653
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28654
|
+
"canvas",
|
|
28655
|
+
{
|
|
28656
|
+
ref: canvasRef,
|
|
28657
|
+
width: canvasDimensions.width,
|
|
28658
|
+
height: canvasDimensions.height,
|
|
28659
|
+
className: "max-w-full max-h-full",
|
|
28660
|
+
style: {
|
|
28661
|
+
display: isVideoReady ? "block" : "none",
|
|
28662
|
+
width: `${canvasDimensions.width}px`,
|
|
28663
|
+
height: `${canvasDimensions.height}px`,
|
|
28664
|
+
pointerEvents: "none"
|
|
28665
|
+
// Allow clicks to pass through to controls
|
|
28666
|
+
}
|
|
28667
|
+
}
|
|
28668
|
+
),
|
|
28669
|
+
!isVideoReady && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
28670
|
+
debug && isVideoReady && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
|
|
28671
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28672
|
+
"Crop: ",
|
|
28673
|
+
crop.x,
|
|
28674
|
+
",",
|
|
28675
|
+
crop.y,
|
|
28676
|
+
" ",
|
|
28677
|
+
crop.width,
|
|
28678
|
+
"x",
|
|
28679
|
+
crop.height,
|
|
28680
|
+
"%"
|
|
28681
|
+
] }),
|
|
28682
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28683
|
+
"Canvas: ",
|
|
28684
|
+
canvasDimensions.width,
|
|
28685
|
+
"x",
|
|
28686
|
+
canvasDimensions.height,
|
|
28687
|
+
"px"
|
|
28688
|
+
] }),
|
|
28689
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28690
|
+
"Processing: ",
|
|
28691
|
+
isProcessing ? "Yes" : "No"
|
|
28692
|
+
] })
|
|
28693
|
+
] })
|
|
28694
|
+
]
|
|
28695
|
+
}
|
|
28696
|
+
);
|
|
28697
|
+
});
|
|
28698
|
+
CroppedVideoPlayer.displayName = "CroppedVideoPlayer";
|
|
27390
28699
|
var BackButton = ({
|
|
27391
28700
|
onClick,
|
|
27392
28701
|
text = "Back",
|
|
@@ -27696,6 +29005,244 @@ var InlineEditableText = ({
|
|
|
27696
29005
|
}
|
|
27697
29006
|
);
|
|
27698
29007
|
};
|
|
29008
|
+
var getSupabaseClient3 = () => {
|
|
29009
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
29010
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
29011
|
+
if (!url || !key) {
|
|
29012
|
+
throw new Error("Supabase configuration missing");
|
|
29013
|
+
}
|
|
29014
|
+
return supabaseJs.createClient(url, key);
|
|
29015
|
+
};
|
|
29016
|
+
var getAuthToken3 = async () => {
|
|
29017
|
+
try {
|
|
29018
|
+
const supabase = getSupabaseClient3();
|
|
29019
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
29020
|
+
return session?.access_token || null;
|
|
29021
|
+
} catch (error) {
|
|
29022
|
+
console.error("[useWorkspaceCrop] Error getting auth token:", error);
|
|
29023
|
+
return null;
|
|
29024
|
+
}
|
|
29025
|
+
};
|
|
29026
|
+
function useWorkspaceCrop(workspaceId) {
|
|
29027
|
+
const [crop, setCrop] = React19.useState(null);
|
|
29028
|
+
const [isLoading, setIsLoading] = React19.useState(true);
|
|
29029
|
+
const [error, setError] = React19.useState(null);
|
|
29030
|
+
React19.useEffect(() => {
|
|
29031
|
+
if (!workspaceId) {
|
|
29032
|
+
setIsLoading(false);
|
|
29033
|
+
return;
|
|
29034
|
+
}
|
|
29035
|
+
const fetchCrop = async () => {
|
|
29036
|
+
setIsLoading(true);
|
|
29037
|
+
setError(null);
|
|
29038
|
+
try {
|
|
29039
|
+
const token = await getAuthToken3();
|
|
29040
|
+
if (!token) {
|
|
29041
|
+
throw new Error("Authentication required");
|
|
29042
|
+
}
|
|
29043
|
+
const response = await fetch("/api/clips/supabase", {
|
|
29044
|
+
method: "POST",
|
|
29045
|
+
headers: {
|
|
29046
|
+
"Content-Type": "application/json",
|
|
29047
|
+
"Authorization": `Bearer ${token}`
|
|
29048
|
+
},
|
|
29049
|
+
body: JSON.stringify({
|
|
29050
|
+
action: "crop",
|
|
29051
|
+
workspaceId
|
|
29052
|
+
})
|
|
29053
|
+
});
|
|
29054
|
+
if (!response.ok) {
|
|
29055
|
+
throw new Error(`Failed to fetch crop: ${response.statusText}`);
|
|
29056
|
+
}
|
|
29057
|
+
const data = await response.json();
|
|
29058
|
+
console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
|
|
29059
|
+
setCrop(data.crop);
|
|
29060
|
+
} catch (err) {
|
|
29061
|
+
console.error("[useWorkspaceCrop] Error fetching crop:", err);
|
|
29062
|
+
setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
|
|
29063
|
+
setCrop(null);
|
|
29064
|
+
} finally {
|
|
29065
|
+
setIsLoading(false);
|
|
29066
|
+
}
|
|
29067
|
+
};
|
|
29068
|
+
fetchCrop();
|
|
29069
|
+
}, [workspaceId]);
|
|
29070
|
+
return { crop, isLoading, error };
|
|
29071
|
+
}
|
|
29072
|
+
var getSeverityIcon = (severity) => {
|
|
29073
|
+
switch (severity) {
|
|
29074
|
+
case "high":
|
|
29075
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
|
|
29076
|
+
case "medium":
|
|
29077
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-yellow-500" });
|
|
29078
|
+
case "low":
|
|
29079
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-3 w-3 text-green-500" });
|
|
29080
|
+
default:
|
|
29081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "h-3 w-3 text-gray-400" });
|
|
29082
|
+
}
|
|
29083
|
+
};
|
|
29084
|
+
var getColorClasses = (color2) => {
|
|
29085
|
+
const colorMap = {
|
|
29086
|
+
"red": { bg: "bg-red-50", text: "text-red-700", border: "border-red-200" },
|
|
29087
|
+
"yellow": { bg: "bg-yellow-50", text: "text-yellow-700", border: "border-yellow-200" },
|
|
29088
|
+
"orange": { bg: "bg-orange-50", text: "text-orange-700", border: "border-orange-200" },
|
|
29089
|
+
"green": { bg: "bg-green-50", text: "text-green-700", border: "border-green-200" },
|
|
29090
|
+
"blue": { bg: "bg-blue-50", text: "text-blue-700", border: "border-blue-200" },
|
|
29091
|
+
"purple": { bg: "bg-purple-50", text: "text-purple-700", border: "border-purple-200" },
|
|
29092
|
+
"gray": { bg: "bg-gray-50", text: "text-gray-700", border: "border-gray-200" }
|
|
29093
|
+
};
|
|
29094
|
+
return colorMap[color2] || colorMap["gray"];
|
|
29095
|
+
};
|
|
29096
|
+
var FileManagerFilters = ({
|
|
29097
|
+
categories,
|
|
29098
|
+
videos,
|
|
29099
|
+
activeFilter,
|
|
29100
|
+
currentVideoId,
|
|
29101
|
+
counts,
|
|
29102
|
+
onFilterChange,
|
|
29103
|
+
onVideoSelect,
|
|
29104
|
+
className = ""
|
|
29105
|
+
}) => {
|
|
29106
|
+
const [expandedNodes, setExpandedNodes] = React19.useState(/* @__PURE__ */ new Set());
|
|
29107
|
+
const [searchTerm, setSearchTerm] = React19.useState("");
|
|
29108
|
+
const filterTree = React19.useMemo(() => {
|
|
29109
|
+
const tree = [];
|
|
29110
|
+
categories.forEach((category) => {
|
|
29111
|
+
const categoryCount = counts?.[category.id] || 0;
|
|
29112
|
+
let categoryVideos = videos.filter((video) => video.type === category.id);
|
|
29113
|
+
if (searchTerm.trim()) {
|
|
29114
|
+
categoryVideos = categoryVideos.filter(
|
|
29115
|
+
(video) => video.description.toLowerCase().includes(searchTerm.toLowerCase()) || video.timestamp.includes(searchTerm) || video.severity.toLowerCase().includes(searchTerm.toLowerCase())
|
|
29116
|
+
);
|
|
29117
|
+
}
|
|
29118
|
+
if (categoryCount > 0 || categoryVideos.length > 0) {
|
|
29119
|
+
const colorClasses = getColorClasses(category.color);
|
|
29120
|
+
const videoNodes = categoryVideos.map((video, index) => ({
|
|
29121
|
+
id: video.id,
|
|
29122
|
+
label: `${video.timestamp.substring(11, 19)} - ${video.description}`,
|
|
29123
|
+
type: "video",
|
|
29124
|
+
icon: getSeverityIcon(video.severity),
|
|
29125
|
+
timestamp: video.timestamp,
|
|
29126
|
+
severity: video.severity
|
|
29127
|
+
}));
|
|
29128
|
+
tree.push({
|
|
29129
|
+
id: category.id,
|
|
29130
|
+
label: category.label,
|
|
29131
|
+
type: "category",
|
|
29132
|
+
count: categoryCount || categoryVideos.length,
|
|
29133
|
+
// Use API count if available
|
|
29134
|
+
videos: categoryVideos,
|
|
29135
|
+
children: videoNodes,
|
|
29136
|
+
icon: expandedNodes.has(category.id) ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Folder, { className: `h-4 w-4 ${colorClasses.text}` }),
|
|
29137
|
+
color: category.color
|
|
29138
|
+
});
|
|
29139
|
+
}
|
|
29140
|
+
});
|
|
29141
|
+
return tree;
|
|
29142
|
+
}, [categories, videos, expandedNodes, searchTerm, counts]);
|
|
29143
|
+
const toggleExpanded = (nodeId) => {
|
|
29144
|
+
const newExpanded = new Set(expandedNodes);
|
|
29145
|
+
if (newExpanded.has(nodeId)) {
|
|
29146
|
+
newExpanded.delete(nodeId);
|
|
29147
|
+
} else {
|
|
29148
|
+
newExpanded.add(nodeId);
|
|
29149
|
+
}
|
|
29150
|
+
setExpandedNodes(newExpanded);
|
|
29151
|
+
};
|
|
29152
|
+
const handleNodeClick = (node) => {
|
|
29153
|
+
if (node.type === "category") {
|
|
29154
|
+
toggleExpanded(node.id);
|
|
29155
|
+
onFilterChange(node.id);
|
|
29156
|
+
} else if (node.type === "video") {
|
|
29157
|
+
const videoIndex = videos.findIndex((v) => v.id === node.id);
|
|
29158
|
+
if (videoIndex !== -1) {
|
|
29159
|
+
onVideoSelect(videoIndex);
|
|
29160
|
+
}
|
|
29161
|
+
}
|
|
29162
|
+
};
|
|
29163
|
+
const renderNode = (node, depth = 0) => {
|
|
29164
|
+
const isExpanded = expandedNodes.has(node.id);
|
|
29165
|
+
const isActive = activeFilter === node.id;
|
|
29166
|
+
const isCurrentVideo = currentVideoId === node.id;
|
|
29167
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
29168
|
+
const colorClasses = node.color ? getColorClasses(node.color) : null;
|
|
29169
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "select-none animate-in", children: [
|
|
29170
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
29171
|
+
"div",
|
|
29172
|
+
{
|
|
29173
|
+
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" : ""}`,
|
|
29174
|
+
onClick: () => handleNodeClick(node),
|
|
29175
|
+
children: [
|
|
29176
|
+
hasChildren && /* @__PURE__ */ jsxRuntime.jsx(
|
|
29177
|
+
"button",
|
|
29178
|
+
{
|
|
29179
|
+
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"}`,
|
|
29180
|
+
onClick: (e) => {
|
|
29181
|
+
e.stopPropagation();
|
|
29182
|
+
toggleExpanded(node.id);
|
|
29183
|
+
},
|
|
29184
|
+
children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.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__ */ jsxRuntime.jsx(lucideReact.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"}` })
|
|
29185
|
+
}
|
|
29186
|
+
),
|
|
29187
|
+
/* @__PURE__ */ jsxRuntime.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 }),
|
|
29188
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
|
|
29189
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
29190
|
+
/* @__PURE__ */ jsxRuntime.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 }),
|
|
29191
|
+
node.type === "video" && node.severity && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
29192
|
+
node.severity,
|
|
29193
|
+
" priority"
|
|
29194
|
+
] }) })
|
|
29195
|
+
] }),
|
|
29196
|
+
node.count !== void 0 && node.type === "category" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsxRuntime.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 }) })
|
|
29197
|
+
] })
|
|
29198
|
+
]
|
|
29199
|
+
}
|
|
29200
|
+
),
|
|
29201
|
+
hasChildren && isExpanded && /* @__PURE__ */ jsxRuntime.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)) })
|
|
29202
|
+
] }, node.id);
|
|
29203
|
+
};
|
|
29204
|
+
return /* @__PURE__ */ jsxRuntime.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: [
|
|
29205
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
29206
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Folder, { className: "h-5 w-5 text-slate-700" }) }),
|
|
29207
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold text-slate-900 tracking-tight", children: "Clips Explorer" }) })
|
|
29208
|
+
] }) }),
|
|
29209
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-slate-100/80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
|
|
29210
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "h-5 w-5 text-slate-400 group-focus-within:text-blue-500 transition-colors duration-200" }) }),
|
|
29211
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29212
|
+
"input",
|
|
29213
|
+
{
|
|
29214
|
+
type: "text",
|
|
29215
|
+
placeholder: "Show me idle clips from 10am",
|
|
29216
|
+
value: searchTerm,
|
|
29217
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
29218
|
+
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"
|
|
29219
|
+
}
|
|
29220
|
+
),
|
|
29221
|
+
searchTerm && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-4 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 bg-blue-500 rounded-full animate-pulse" }) })
|
|
29222
|
+
] }) }),
|
|
29223
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 h-[calc(100%-14rem)] overflow-y-auto scrollbar-thin", children: [
|
|
29224
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
|
|
29225
|
+
filterTree.length === 0 && searchTerm && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
|
|
29226
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "h-12 w-12 mx-auto" }) }),
|
|
29227
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
|
|
29228
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-slate-500 mb-4", children: [
|
|
29229
|
+
'No clips match "',
|
|
29230
|
+
searchTerm,
|
|
29231
|
+
'"'
|
|
29232
|
+
] }),
|
|
29233
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
29234
|
+
"button",
|
|
29235
|
+
{
|
|
29236
|
+
onClick: () => setSearchTerm(""),
|
|
29237
|
+
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",
|
|
29238
|
+
children: "Clear search"
|
|
29239
|
+
}
|
|
29240
|
+
)
|
|
29241
|
+
] })
|
|
29242
|
+
] })
|
|
29243
|
+
] });
|
|
29244
|
+
};
|
|
29245
|
+
var USE_SUPABASE_CLIPS2 = true;
|
|
27699
29246
|
var BottlenecksContent = ({
|
|
27700
29247
|
workspaceId,
|
|
27701
29248
|
workspaceName,
|
|
@@ -27704,17 +29251,9 @@ var BottlenecksContent = ({
|
|
|
27704
29251
|
className
|
|
27705
29252
|
}) => {
|
|
27706
29253
|
const dashboardConfig = useDashboardConfig();
|
|
27707
|
-
const
|
|
27708
|
-
const sopConfig = dashboardConfig?.s3Config?.sopCategories;
|
|
27709
|
-
if (!sopConfig) return null;
|
|
27710
|
-
if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
|
|
27711
|
-
return sopConfig.workspaceOverrides[workspaceId];
|
|
27712
|
-
}
|
|
27713
|
-
return sopConfig.default;
|
|
27714
|
-
}, [dashboardConfig, workspaceId]);
|
|
29254
|
+
const { crop: workspaceCrop} = useWorkspaceCrop(workspaceId);
|
|
27715
29255
|
const videoRef = React19.useRef(null);
|
|
27716
|
-
const
|
|
27717
|
-
const initialFilter = sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value";
|
|
29256
|
+
const [initialFilter, setInitialFilter] = React19.useState("");
|
|
27718
29257
|
const currentIndexRef = React19.useRef(0);
|
|
27719
29258
|
const activeFilterRef = React19.useRef(initialFilter);
|
|
27720
29259
|
const isMountedRef = React19.useRef(true);
|
|
@@ -27744,22 +29283,6 @@ var BottlenecksContent = ({
|
|
|
27744
29283
|
React19.useEffect(() => {
|
|
27745
29284
|
currentIndexRef.current = currentIndex;
|
|
27746
29285
|
}, [currentIndex]);
|
|
27747
|
-
const [showTimestampFilter, setShowTimestampFilter] = React19.useState(false);
|
|
27748
|
-
const [timestampStart, setTimestampStart] = React19.useState("");
|
|
27749
|
-
const [timestampEnd, setTimestampEnd] = React19.useState("");
|
|
27750
|
-
React19.useEffect(() => {
|
|
27751
|
-
const handleClickOutside = (event) => {
|
|
27752
|
-
if (timestampFilterRef.current && !timestampFilterRef.current.contains(event.target)) {
|
|
27753
|
-
setShowTimestampFilter(false);
|
|
27754
|
-
}
|
|
27755
|
-
};
|
|
27756
|
-
if (showTimestampFilter) {
|
|
27757
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
27758
|
-
}
|
|
27759
|
-
return () => {
|
|
27760
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
27761
|
-
};
|
|
27762
|
-
}, [showTimestampFilter]);
|
|
27763
29286
|
const s3ClipsService = React19.useMemo(() => {
|
|
27764
29287
|
if (!dashboardConfig?.s3Config) {
|
|
27765
29288
|
console.warn("S3 configuration not found in dashboard config");
|
|
@@ -27767,6 +29290,39 @@ var BottlenecksContent = ({
|
|
|
27767
29290
|
}
|
|
27768
29291
|
return videoPrefetchManager.getS3Service(dashboardConfig);
|
|
27769
29292
|
}, [dashboardConfig]);
|
|
29293
|
+
const {
|
|
29294
|
+
clipTypes,
|
|
29295
|
+
isLoading: clipTypesLoading,
|
|
29296
|
+
error: clipTypesError,
|
|
29297
|
+
counts: dynamicCounts
|
|
29298
|
+
} = useClipTypesWithCounts(
|
|
29299
|
+
workspaceId,
|
|
29300
|
+
date || getOperationalDate(),
|
|
29301
|
+
shift || "0"
|
|
29302
|
+
// Use shift || '0' as in the original working code
|
|
29303
|
+
);
|
|
29304
|
+
console.log("[BottlenecksContent] Clip types data:", {
|
|
29305
|
+
clipTypes,
|
|
29306
|
+
clipTypesLength: clipTypes?.length,
|
|
29307
|
+
clipTypesLoading,
|
|
29308
|
+
clipTypesError,
|
|
29309
|
+
dynamicCounts,
|
|
29310
|
+
workspaceId,
|
|
29311
|
+
date: date || getOperationalDate(),
|
|
29312
|
+
shift: shift || "0",
|
|
29313
|
+
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29314
|
+
});
|
|
29315
|
+
React19.useEffect(() => {
|
|
29316
|
+
if (clipTypes.length > 0 && !initialFilter) {
|
|
29317
|
+
const firstWithCounts = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
|
|
29318
|
+
const firstType = firstWithCounts || clipTypes[0];
|
|
29319
|
+
if (firstType) {
|
|
29320
|
+
setInitialFilter(firstType.type);
|
|
29321
|
+
setActiveFilter(firstType.type);
|
|
29322
|
+
activeFilterRef.current = firstType.type;
|
|
29323
|
+
}
|
|
29324
|
+
}
|
|
29325
|
+
}, [clipTypes, dynamicCounts, initialFilter]);
|
|
27770
29326
|
const effectiveShift = React19.useMemo(() => {
|
|
27771
29327
|
if (shift !== void 0 && shift !== null) {
|
|
27772
29328
|
const shiftStr = shift.toString();
|
|
@@ -27785,6 +29341,9 @@ var BottlenecksContent = ({
|
|
|
27785
29341
|
return currentShift.shiftId.toString();
|
|
27786
29342
|
}
|
|
27787
29343
|
}, [shift, date, dashboardConfig]);
|
|
29344
|
+
const mergedCounts = React19.useMemo(() => {
|
|
29345
|
+
return { ...clipCounts, ...dynamicCounts };
|
|
29346
|
+
}, [clipCounts, dynamicCounts]);
|
|
27788
29347
|
const {
|
|
27789
29348
|
data: prefetchData,
|
|
27790
29349
|
isFullyIndexed,
|
|
@@ -27819,17 +29378,22 @@ var BottlenecksContent = ({
|
|
|
27819
29378
|
setHasInitialLoad(true);
|
|
27820
29379
|
return;
|
|
27821
29380
|
}
|
|
27822
|
-
console.log(`[BottlenecksContent] Fetching clip counts
|
|
29381
|
+
console.log(`[BottlenecksContent] Fetching clip counts directly with params:`, {
|
|
29382
|
+
workspaceId,
|
|
29383
|
+
operationalDate,
|
|
29384
|
+
shiftStr
|
|
29385
|
+
});
|
|
27823
29386
|
const fullResult = await s3ClipsService.getClipCounts(
|
|
27824
29387
|
workspaceId,
|
|
27825
29388
|
operationalDate,
|
|
27826
29389
|
shiftStr
|
|
27827
29390
|
// Don't build index - use pagination for cost efficiency
|
|
27828
29391
|
);
|
|
29392
|
+
console.log(`[BottlenecksContent] Direct fetch result:`, fullResult);
|
|
27829
29393
|
if (fullResult) {
|
|
27830
29394
|
const counts = fullResult;
|
|
27831
29395
|
updateClipCounts(counts);
|
|
27832
|
-
console.log(`[BottlenecksContent]
|
|
29396
|
+
console.log(`[BottlenecksContent] Updated clip counts:`, counts);
|
|
27833
29397
|
}
|
|
27834
29398
|
setIsLoading(false);
|
|
27835
29399
|
setHasInitialLoad(true);
|
|
@@ -27850,13 +29414,7 @@ var BottlenecksContent = ({
|
|
|
27850
29414
|
const ensureVideosLoaded = React19.useCallback(async (centerIndex) => {
|
|
27851
29415
|
if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
|
|
27852
29416
|
const currentFilter = activeFilterRef.current;
|
|
27853
|
-
|
|
27854
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
27855
|
-
const category = sopCategories.find((cat) => cat.id === currentFilter);
|
|
27856
|
-
if (category && category.s3FolderName) {
|
|
27857
|
-
effectiveFilter = category.s3FolderName;
|
|
27858
|
-
}
|
|
27859
|
-
}
|
|
29417
|
+
const effectiveFilter = currentFilter;
|
|
27860
29418
|
const cacheKey = `${effectiveFilter}:${date}:${shift}`;
|
|
27861
29419
|
if (!loadedVideosMapRef.current.has(cacheKey)) {
|
|
27862
29420
|
loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
|
|
@@ -27865,7 +29423,7 @@ var BottlenecksContent = ({
|
|
|
27865
29423
|
const indicesToLoad = [];
|
|
27866
29424
|
const rangeBefore = 1;
|
|
27867
29425
|
const rangeAfter = 3;
|
|
27868
|
-
for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(
|
|
29426
|
+
for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(mergedCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
|
|
27869
29427
|
if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
|
|
27870
29428
|
indicesToLoad.push(i);
|
|
27871
29429
|
}
|
|
@@ -27940,7 +29498,7 @@ var BottlenecksContent = ({
|
|
|
27940
29498
|
} finally {
|
|
27941
29499
|
indicesToLoad.forEach((idx) => loadingVideosRef.current.delete(idx));
|
|
27942
29500
|
}
|
|
27943
|
-
}, [s3ClipsService, workspaceId, clipCounts,
|
|
29501
|
+
}, [s3ClipsService, workspaceId, clipCounts, date, effectiveShift]);
|
|
27944
29502
|
const loadFirstVideoForCategory = React19.useCallback(async (category) => {
|
|
27945
29503
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
27946
29504
|
const targetCategory = category || activeFilterRef.current;
|
|
@@ -27957,7 +29515,7 @@ var BottlenecksContent = ({
|
|
|
27957
29515
|
try {
|
|
27958
29516
|
const operationalDate = date || getOperationalDate();
|
|
27959
29517
|
const shiftStr = effectiveShift;
|
|
27960
|
-
if (!
|
|
29518
|
+
if (!mergedCounts[targetCategory]) {
|
|
27961
29519
|
const cacheKey = `clip-counts:${workspaceId}:${operationalDate}:${shiftStr}`;
|
|
27962
29520
|
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
27963
29521
|
if (cachedResult && cachedResult.counts[targetCategory] > 0) {
|
|
@@ -27993,7 +29551,7 @@ var BottlenecksContent = ({
|
|
|
27993
29551
|
} catch (directError) {
|
|
27994
29552
|
console.warn(`[BottlenecksContent] Direct S3 loading failed, trying video index:`, directError);
|
|
27995
29553
|
}
|
|
27996
|
-
if (
|
|
29554
|
+
if (mergedCounts[targetCategory] > 0) {
|
|
27997
29555
|
try {
|
|
27998
29556
|
const operationalDate2 = date || getOperationalDate();
|
|
27999
29557
|
const shiftStr2 = effectiveShift;
|
|
@@ -28047,47 +29605,27 @@ var BottlenecksContent = ({
|
|
|
28047
29605
|
}
|
|
28048
29606
|
}, [workspaceId, date, effectiveShift, s3ClipsService, fetchClipCounts, updateClipCounts, prefetchData]);
|
|
28049
29607
|
React19.useEffect(() => {
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
29608
|
+
console.log(`[BottlenecksContent] prefetchData update:`, {
|
|
29609
|
+
hasPrefetchData: !!prefetchData,
|
|
29610
|
+
prefetchStatus,
|
|
29611
|
+
prefetchData
|
|
29612
|
+
});
|
|
29613
|
+
if (prefetchData) {
|
|
29614
|
+
const counts = prefetchData.counts || prefetchData;
|
|
29615
|
+
console.log(`[BottlenecksContent] Extracted counts from prefetch:`, counts);
|
|
29616
|
+
if (counts && Object.keys(counts).length > 0) {
|
|
29617
|
+
updateClipCounts(counts);
|
|
29618
|
+
if (!hasInitialLoad) {
|
|
29619
|
+
setIsLoading(false);
|
|
29620
|
+
setHasInitialLoad(true);
|
|
29621
|
+
}
|
|
28056
29622
|
}
|
|
28057
29623
|
}
|
|
28058
29624
|
}, [prefetchData, prefetchStatus, updateClipCounts, hasInitialLoad]);
|
|
28059
29625
|
React19.useEffect(() => {
|
|
28060
|
-
if (s3ClipsService &&
|
|
29626
|
+
if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
|
|
28061
29627
|
const hasVideosForCurrentFilter = allVideos.some((video) => {
|
|
28062
|
-
|
|
28063
|
-
const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
|
|
28064
|
-
if (selectedCategory) {
|
|
28065
|
-
return video.type === selectedCategory.id;
|
|
28066
|
-
}
|
|
28067
|
-
}
|
|
28068
|
-
if (activeFilter === "all") return true;
|
|
28069
|
-
if (activeFilter === "low_value") {
|
|
28070
|
-
return video.type === "low_value";
|
|
28071
|
-
}
|
|
28072
|
-
if (activeFilter === "idle_time") {
|
|
28073
|
-
return video.type === "idle_time";
|
|
28074
|
-
}
|
|
28075
|
-
if (activeFilter === "sop_deviations") {
|
|
28076
|
-
return video.type === "missing_quality_check";
|
|
28077
|
-
}
|
|
28078
|
-
if (activeFilter === "best_cycle_time") {
|
|
28079
|
-
return video.type === "best_cycle_time";
|
|
28080
|
-
}
|
|
28081
|
-
if (activeFilter === "worst_cycle_time") {
|
|
28082
|
-
return video.type === "worst_cycle_time";
|
|
28083
|
-
}
|
|
28084
|
-
if (activeFilter === "cycle_completion") {
|
|
28085
|
-
return video.type === "cycle_completion";
|
|
28086
|
-
}
|
|
28087
|
-
if (activeFilter === "long_cycle_time") {
|
|
28088
|
-
return video.type === "long_cycle_time";
|
|
28089
|
-
}
|
|
28090
|
-
return video.type === "bottleneck" && video.severity === activeFilter;
|
|
29628
|
+
return video.type === activeFilter;
|
|
28091
29629
|
});
|
|
28092
29630
|
if (!hasVideosForCurrentFilter) {
|
|
28093
29631
|
loadFirstVideoForCategory(activeFilter);
|
|
@@ -28095,7 +29633,7 @@ var BottlenecksContent = ({
|
|
|
28095
29633
|
setIsCategoryLoading(false);
|
|
28096
29634
|
}
|
|
28097
29635
|
}
|
|
28098
|
-
}, [activeFilter, s3ClipsService,
|
|
29636
|
+
}, [activeFilter, s3ClipsService, mergedCounts, loadFirstVideoForCategory, allVideos]);
|
|
28099
29637
|
React19.useEffect(() => {
|
|
28100
29638
|
if (previousFilterRef.current !== activeFilter) {
|
|
28101
29639
|
console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
|
|
@@ -28107,15 +29645,7 @@ var BottlenecksContent = ({
|
|
|
28107
29645
|
previousFilterRef.current = activeFilter;
|
|
28108
29646
|
const filtered = allVideos.filter((video) => {
|
|
28109
29647
|
if (activeFilter === "all") return true;
|
|
28110
|
-
|
|
28111
|
-
if (activeFilter === "sop_deviations") return video.type === "missing_quality_check";
|
|
28112
|
-
if (activeFilter === "best_cycle_time") return video.type === "best_cycle_time";
|
|
28113
|
-
if (activeFilter === "worst_cycle_time") return video.type === "worst_cycle_time";
|
|
28114
|
-
if (activeFilter === "cycle_completion") return video.type === "cycle_completion";
|
|
28115
|
-
if (activeFilter === "long_cycle_time") {
|
|
28116
|
-
return video.type === "long_cycle_time";
|
|
28117
|
-
}
|
|
28118
|
-
return video.type === "bottleneck" && video.severity === activeFilter;
|
|
29648
|
+
return video.type === activeFilter;
|
|
28119
29649
|
});
|
|
28120
29650
|
if (filtered.length > 0) {
|
|
28121
29651
|
preloadVideoUrl(filtered[0].src);
|
|
@@ -28124,46 +29654,25 @@ var BottlenecksContent = ({
|
|
|
28124
29654
|
setIsCategoryLoading(false);
|
|
28125
29655
|
}
|
|
28126
29656
|
}, 150);
|
|
28127
|
-
} else if (
|
|
29657
|
+
} else if ((mergedCounts[activeFilter] || 0) === 0) {
|
|
28128
29658
|
if (isMountedRef.current) {
|
|
28129
29659
|
setIsCategoryLoading(false);
|
|
28130
29660
|
}
|
|
28131
29661
|
}
|
|
28132
29662
|
}
|
|
28133
|
-
}, [activeFilter, allVideos,
|
|
29663
|
+
}, [activeFilter, allVideos, mergedCounts]);
|
|
28134
29664
|
const filteredVideos = React19.useMemo(() => {
|
|
28135
29665
|
if (!allVideos) return [];
|
|
28136
29666
|
let filtered = [];
|
|
28137
29667
|
if (activeFilter === "all") {
|
|
28138
29668
|
filtered = [...allVideos];
|
|
28139
29669
|
} else {
|
|
28140
|
-
|
|
28141
|
-
const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
|
|
28142
|
-
if (selectedCategory) {
|
|
28143
|
-
filtered = allVideos.filter((video) => video.type === selectedCategory.id);
|
|
28144
|
-
}
|
|
28145
|
-
} else {
|
|
28146
|
-
if (activeFilter === "low_value") {
|
|
28147
|
-
filtered = allVideos.filter((video) => video.type === "low_value");
|
|
28148
|
-
} else if (activeFilter === "sop_deviations") {
|
|
28149
|
-
filtered = allVideos.filter((video) => video.type === "missing_quality_check");
|
|
28150
|
-
} else if (activeFilter === "best_cycle_time") {
|
|
28151
|
-
filtered = allVideos.filter((video) => video.type === "best_cycle_time");
|
|
28152
|
-
} else if (activeFilter === "worst_cycle_time") {
|
|
28153
|
-
filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
|
|
28154
|
-
} else if (activeFilter === "cycle_completion") {
|
|
28155
|
-
filtered = allVideos.filter((video) => video.type === "cycle_completion");
|
|
28156
|
-
} else if (activeFilter === "long_cycle_time") {
|
|
28157
|
-
filtered = allVideos.filter((video) => video.type === "long_cycle_time");
|
|
28158
|
-
} else {
|
|
28159
|
-
filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
|
|
28160
|
-
}
|
|
28161
|
-
}
|
|
29670
|
+
filtered = allVideos.filter((video) => video.type === activeFilter);
|
|
28162
29671
|
}
|
|
28163
29672
|
return filtered.sort((a, b) => {
|
|
28164
29673
|
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
28165
29674
|
});
|
|
28166
|
-
}, [activeFilter, allVideos
|
|
29675
|
+
}, [activeFilter, allVideos]);
|
|
28167
29676
|
React19.useEffect(() => {
|
|
28168
29677
|
if (isNavigating && currentIndex < filteredVideos.length) {
|
|
28169
29678
|
setIsNavigating(false);
|
|
@@ -28188,15 +29697,9 @@ var BottlenecksContent = ({
|
|
|
28188
29697
|
const currentIdx = currentIndexRef.current;
|
|
28189
29698
|
const currentFilter = activeFilterRef.current;
|
|
28190
29699
|
const nextIndex = currentIdx + 1;
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
if (category && category.s3FolderName) {
|
|
28195
|
-
effectiveFilter = category.s3FolderName;
|
|
28196
|
-
}
|
|
28197
|
-
}
|
|
28198
|
-
console.log(`[handleNext] Navigation check: nextIndex=${nextIndex}, currentFilter='${currentFilter}', effectiveFilter='${effectiveFilter}', clipCounts[effectiveFilter]=${clipCounts[effectiveFilter]}, clipCounts keys:`, Object.keys(clipCounts));
|
|
28199
|
-
if (nextIndex < clipCounts[effectiveFilter]) {
|
|
29700
|
+
const effectiveFilter = currentFilter;
|
|
29701
|
+
console.log(`[handleNext] Navigation check: nextIndex=${nextIndex}, currentFilter='${currentFilter}', effectiveFilter='${effectiveFilter}', mergedCounts[effectiveFilter]=${mergedCounts[effectiveFilter]}, mergedCounts keys:`, Object.keys(mergedCounts));
|
|
29702
|
+
if (nextIndex < mergedCounts[effectiveFilter]) {
|
|
28200
29703
|
if (isMountedRef.current) {
|
|
28201
29704
|
setCurrentIndex(nextIndex);
|
|
28202
29705
|
setError(null);
|
|
@@ -28350,17 +29853,6 @@ var BottlenecksContent = ({
|
|
|
28350
29853
|
player.pause();
|
|
28351
29854
|
}
|
|
28352
29855
|
};
|
|
28353
|
-
const mappedCounts = React19.useMemo(() => {
|
|
28354
|
-
const counts = { ...clipCounts };
|
|
28355
|
-
counts.lowValue = counts.low_value || counts.lowValue || 0;
|
|
28356
|
-
counts.bestCycleTimes = counts.best_cycle_time || counts.bestCycleTimes || 0;
|
|
28357
|
-
counts.worstCycleTimes = counts.worst_cycle_time || counts.worstCycleTimes || 0;
|
|
28358
|
-
counts.longCycleTimes = counts.long_cycle_time || counts.longCycleTimes || 0;
|
|
28359
|
-
counts.cycleCompletions = counts.cycle_completion || counts.cycleCompletion || 0;
|
|
28360
|
-
counts.sopDeviations = counts.missing_quality_check || counts.sopDeviations || 0;
|
|
28361
|
-
counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
|
|
28362
|
-
return counts;
|
|
28363
|
-
}, [clipCounts]);
|
|
28364
29856
|
const getClipTypeLabel = (video) => {
|
|
28365
29857
|
if (!video) return "";
|
|
28366
29858
|
switch (video.type) {
|
|
@@ -28383,33 +29875,6 @@ var BottlenecksContent = ({
|
|
|
28383
29875
|
return "";
|
|
28384
29876
|
}
|
|
28385
29877
|
};
|
|
28386
|
-
const getColorClasses = (color2) => {
|
|
28387
|
-
const colorMap = {
|
|
28388
|
-
purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
|
|
28389
|
-
green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
|
|
28390
|
-
red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
|
|
28391
|
-
"red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
|
|
28392
|
-
blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
|
|
28393
|
-
orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
|
|
28394
|
-
yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
|
|
28395
|
-
teal: { text: "text-teal-600", bg: "bg-teal-600", dot: "bg-teal-600" },
|
|
28396
|
-
amber: { text: "text-amber-600", bg: "bg-amber-600", dot: "bg-amber-600" },
|
|
28397
|
-
gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
|
|
28398
|
-
};
|
|
28399
|
-
return colorMap[color2] || colorMap.gray;
|
|
28400
|
-
};
|
|
28401
|
-
const formatTimeOnly = (time2) => {
|
|
28402
|
-
if (!time2) return "";
|
|
28403
|
-
try {
|
|
28404
|
-
const [hours, minutes] = time2.split(":");
|
|
28405
|
-
const hour = parseInt(hours);
|
|
28406
|
-
const ampm = hour >= 12 ? "PM" : "AM";
|
|
28407
|
-
const displayHour = hour % 12 || 12;
|
|
28408
|
-
return `${displayHour}:${minutes} ${ampm}`;
|
|
28409
|
-
} catch {
|
|
28410
|
-
return time2;
|
|
28411
|
-
}
|
|
28412
|
-
};
|
|
28413
29878
|
if (!dashboardConfig?.s3Config) {
|
|
28414
29879
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
28415
29880
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
@@ -28417,202 +29882,80 @@ var BottlenecksContent = ({
|
|
|
28417
29882
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
|
|
28418
29883
|
] });
|
|
28419
29884
|
}
|
|
28420
|
-
if (isLoading && allVideos.length === 0 && Object.keys(
|
|
29885
|
+
if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
28421
29886
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
|
|
28422
29887
|
}
|
|
28423
|
-
if (error) {
|
|
29888
|
+
if (error || clipTypesError) {
|
|
28424
29889
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
28425
29890
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
28426
29891
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
|
|
28427
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error })
|
|
29892
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
|
|
28428
29893
|
] });
|
|
28429
29894
|
}
|
|
28430
|
-
const categoriesToShow =
|
|
28431
|
-
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
|
|
28435
|
-
|
|
28436
|
-
|
|
28437
|
-
|
|
28438
|
-
|
|
28439
|
-
|
|
28440
|
-
|
|
28441
|
-
const count = mappedCounts[category.id] || 0;
|
|
28442
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
28443
|
-
Card2,
|
|
28444
|
-
{
|
|
28445
|
-
onClick: () => {
|
|
28446
|
-
updateActiveFilter(category.id);
|
|
28447
|
-
trackCoreEvent(`${category.label} Filter Clicked`, {
|
|
28448
|
-
workspaceId,
|
|
28449
|
-
workspaceName,
|
|
28450
|
-
date,
|
|
28451
|
-
filterType: category.id,
|
|
28452
|
-
clipCount: count
|
|
28453
|
-
});
|
|
28454
|
-
},
|
|
28455
|
-
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" : ""}`,
|
|
28456
|
-
"aria-label": `Filter by ${category.label} (${count} clips)`,
|
|
28457
|
-
role: "button",
|
|
28458
|
-
tabIndex: 0,
|
|
28459
|
-
onKeyDown: (e) => e.key === "Enter" && updateActiveFilter(category.id),
|
|
28460
|
-
children: [
|
|
28461
|
-
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
|
|
28462
|
-
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
|
|
28463
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
|
|
28464
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
|
|
28465
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
|
|
28466
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: category.subtitle || category.description || category.label })
|
|
28467
|
-
] })
|
|
28468
|
-
] }) })
|
|
28469
|
-
]
|
|
28470
|
-
},
|
|
28471
|
-
category.id
|
|
28472
|
-
);
|
|
28473
|
-
}) }),
|
|
28474
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
|
|
29895
|
+
const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
|
|
29896
|
+
console.log("[BottlenecksContent] Categories to show:", {
|
|
29897
|
+
categoriesToShow,
|
|
29898
|
+
categoriesToShowLength: categoriesToShow?.length,
|
|
29899
|
+
firstCategory: categoriesToShow?.[0],
|
|
29900
|
+
clipTypesLength: clipTypes?.length,
|
|
29901
|
+
clipTypesError,
|
|
29902
|
+
mergedCounts
|
|
29903
|
+
});
|
|
29904
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col lg:flex-row gap-4 h-full", children: [
|
|
29905
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0 lg:flex-[3]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden h-full", children: [
|
|
28475
29906
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
28476
29907
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
|
|
28477
29908
|
if (activeFilter === "all") {
|
|
28478
|
-
return `All Clips (${
|
|
29909
|
+
return `All Clips (${mergedCounts.total || 0})`;
|
|
28479
29910
|
}
|
|
28480
|
-
const activeCategory = categoriesToShow.find((cat) => cat.
|
|
29911
|
+
const activeCategory = categoriesToShow.find((cat) => cat.type === activeFilter);
|
|
28481
29912
|
if (activeCategory) {
|
|
28482
|
-
return `${activeCategory.label} (${
|
|
29913
|
+
return `${activeCategory.label} (${mergedCounts[activeCategory.type] || 0})`;
|
|
28483
29914
|
}
|
|
28484
|
-
return activeFilter
|
|
29915
|
+
return `${activeFilter.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} (${mergedCounts[activeFilter] || 0})`;
|
|
28485
29916
|
})() }),
|
|
28486
29917
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28487
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
|
|
28488
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28489
|
-
"button",
|
|
28490
|
-
{
|
|
28491
|
-
onClick: () => setShowTimestampFilter(!showTimestampFilter),
|
|
28492
|
-
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"}`,
|
|
28493
|
-
"aria-label": "Filter by time",
|
|
28494
|
-
title: "Filter by time",
|
|
28495
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-5 w-5" })
|
|
28496
|
-
}
|
|
28497
|
-
),
|
|
28498
|
-
showTimestampFilter && /* @__PURE__ */ jsxRuntime.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: [
|
|
28499
|
-
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Filter by Time" }),
|
|
28500
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
28501
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28502
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Start Time" }),
|
|
28503
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28504
|
-
TimePickerDropdown,
|
|
28505
|
-
{
|
|
28506
|
-
value: timestampStart,
|
|
28507
|
-
onChange: (value) => setTimestampStart(value),
|
|
28508
|
-
placeholder: "Select start time",
|
|
28509
|
-
className: "w-full text-sm"
|
|
28510
|
-
}
|
|
28511
|
-
)
|
|
28512
|
-
] }),
|
|
28513
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28514
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "End Time" }),
|
|
28515
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28516
|
-
TimePickerDropdown,
|
|
28517
|
-
{
|
|
28518
|
-
value: timestampEnd,
|
|
28519
|
-
onChange: (value) => setTimestampEnd(value),
|
|
28520
|
-
placeholder: "Select end time",
|
|
28521
|
-
className: "w-full text-sm"
|
|
28522
|
-
}
|
|
28523
|
-
)
|
|
28524
|
-
] }),
|
|
28525
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between pt-2", children: [
|
|
28526
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28527
|
-
"button",
|
|
28528
|
-
{
|
|
28529
|
-
onClick: () => {
|
|
28530
|
-
setTimestampStart("");
|
|
28531
|
-
setTimestampEnd("");
|
|
28532
|
-
setShowTimestampFilter(false);
|
|
28533
|
-
},
|
|
28534
|
-
className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 transition-colors",
|
|
28535
|
-
children: "Clear"
|
|
28536
|
-
}
|
|
28537
|
-
),
|
|
28538
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28539
|
-
"button",
|
|
28540
|
-
{
|
|
28541
|
-
onClick: () => {
|
|
28542
|
-
setShowTimestampFilter(false);
|
|
28543
|
-
loadFirstVideoForCategory();
|
|
28544
|
-
},
|
|
28545
|
-
className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
|
|
28546
|
-
children: "Apply Filter"
|
|
28547
|
-
}
|
|
28548
|
-
)
|
|
28549
|
-
] })
|
|
28550
|
-
] })
|
|
28551
|
-
] })
|
|
28552
|
-
] }),
|
|
28553
29918
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28554
29919
|
"button",
|
|
28555
29920
|
{
|
|
28556
29921
|
onClick: handlePrevious,
|
|
28557
|
-
disabled: currentIndex === 0 ||
|
|
28558
|
-
className: `p-2 rounded-full transition-colors ${currentIndex === 0 ||
|
|
29922
|
+
disabled: currentIndex === 0 || (mergedCounts[activeFilter] || 0) === 0,
|
|
29923
|
+
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"}`,
|
|
28559
29924
|
"aria-label": "Previous video",
|
|
28560
29925
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-5 w-5" })
|
|
28561
29926
|
}
|
|
28562
29927
|
),
|
|
28563
29928
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
28564
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children:
|
|
29929
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
28565
29930
|
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-red-600 font-medium", children: error })
|
|
28566
29931
|
] }),
|
|
28567
29932
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28568
29933
|
"button",
|
|
28569
29934
|
{
|
|
28570
29935
|
onClick: handleNext,
|
|
28571
|
-
disabled: currentIndex >=
|
|
28572
|
-
className: `p-2 rounded-full transition-colors ${currentIndex >=
|
|
29936
|
+
disabled: currentIndex >= (mergedCounts[activeFilter] || 0) - 1 || (mergedCounts[activeFilter] || 0) === 0,
|
|
29937
|
+
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"}`,
|
|
28573
29938
|
"aria-label": "Next video",
|
|
28574
29939
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5" })
|
|
28575
29940
|
}
|
|
28576
29941
|
)
|
|
28577
29942
|
] })
|
|
28578
29943
|
] }) }),
|
|
28579
|
-
(timestampStart || timestampEnd) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 bg-blue-50 border-b border-blue-100 flex items-center justify-between", children: [
|
|
28580
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 text-sm text-blue-700", children: [
|
|
28581
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4" }),
|
|
28582
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
28583
|
-
"Filtered by time: ",
|
|
28584
|
-
timestampStart ? formatTimeOnly(timestampStart) : "Any",
|
|
28585
|
-
" - ",
|
|
28586
|
-
timestampEnd ? formatTimeOnly(timestampEnd) : "Any"
|
|
28587
|
-
] })
|
|
28588
|
-
] }),
|
|
28589
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28590
|
-
"button",
|
|
28591
|
-
{
|
|
28592
|
-
onClick: () => {
|
|
28593
|
-
setTimestampStart("");
|
|
28594
|
-
setTimestampEnd("");
|
|
28595
|
-
loadFirstVideoForCategory();
|
|
28596
|
-
},
|
|
28597
|
-
className: "text-sm text-blue-600 hover:text-blue-800 transition-colors",
|
|
28598
|
-
children: "Clear filter"
|
|
28599
|
-
}
|
|
28600
|
-
)
|
|
28601
|
-
] }),
|
|
28602
29944
|
/* Priority 1: Show loading if initial load hasn't completed yet */
|
|
28603
29945
|
isLoading && !hasInitialLoad ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
|
|
28604
29946
|
/* Priority 2: Show loading if category is loading BUT only if no video is available */
|
|
28605
29947
|
isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
|
|
28606
29948
|
/* Priority 3: Show loading if navigating and current video not available */
|
|
28607
|
-
isNavigating || currentIndex >= filteredVideos.length && currentIndex <
|
|
29949
|
+
isNavigating || currentIndex >= filteredVideos.length && currentIndex < (mergedCounts[activeFilter] || 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
|
|
28608
29950
|
/* Priority 4: Show video if we have filtered videos and current video */
|
|
28609
29951
|
filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
28610
29952
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28611
|
-
|
|
29953
|
+
CroppedVideoPlayer,
|
|
28612
29954
|
{
|
|
28613
29955
|
ref: videoRef,
|
|
28614
29956
|
src: currentVideo.src,
|
|
28615
29957
|
className: "w-full h-full",
|
|
29958
|
+
crop: workspaceCrop?.crop || null,
|
|
28616
29959
|
autoplay: true,
|
|
28617
29960
|
playsInline: true,
|
|
28618
29961
|
loop: false,
|
|
@@ -28666,7 +30009,7 @@ var BottlenecksContent = ({
|
|
|
28666
30009
|
/* @__PURE__ */ jsxRuntime.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` }),
|
|
28667
30010
|
(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__ */ jsxRuntime.jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
|
|
28668
30011
|
"Cycle time: ",
|
|
28669
|
-
|
|
30012
|
+
currentVideo.cycle_time_seconds.toFixed(1),
|
|
28670
30013
|
"s"
|
|
28671
30014
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
28672
30015
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
@@ -28703,9 +30046,8 @@ var BottlenecksContent = ({
|
|
|
28703
30046
|
max: duration || 0,
|
|
28704
30047
|
value: currentTime,
|
|
28705
30048
|
onChange: (e) => {
|
|
28706
|
-
|
|
28707
|
-
|
|
28708
|
-
player.currentTime(Number(e.target.value));
|
|
30049
|
+
if (videoRef.current) {
|
|
30050
|
+
videoRef.current.currentTime(Number(e.target.value));
|
|
28709
30051
|
}
|
|
28710
30052
|
},
|
|
28711
30053
|
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",
|
|
@@ -28719,13 +30061,13 @@ var BottlenecksContent = ({
|
|
|
28719
30061
|
] }) })
|
|
28720
30062
|
] }) }) }) : (
|
|
28721
30063
|
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
28722
|
-
hasInitialLoad && Object.keys(
|
|
30064
|
+
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
28723
30065
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
28724
30066
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
28725
30067
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
28726
30068
|
] }) }) : (
|
|
28727
30069
|
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
28728
|
-
hasInitialLoad &&
|
|
30070
|
+
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
28729
30071
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
28730
30072
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
|
|
28731
30073
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
|
|
@@ -28738,8 +30080,46 @@ var BottlenecksContent = ({
|
|
|
28738
30080
|
)
|
|
28739
30081
|
)
|
|
28740
30082
|
)
|
|
28741
|
-
] })
|
|
28742
|
-
|
|
30083
|
+
] }) }),
|
|
30084
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
30085
|
+
FileManagerFilters,
|
|
30086
|
+
{
|
|
30087
|
+
categories: categoriesToShow.map((cat) => ({
|
|
30088
|
+
...cat,
|
|
30089
|
+
id: cat.type
|
|
30090
|
+
// Map type to id for compatibility with FileManagerFilters
|
|
30091
|
+
})),
|
|
30092
|
+
videos: allVideos || [],
|
|
30093
|
+
activeFilter,
|
|
30094
|
+
currentVideoId: currentVideo?.id,
|
|
30095
|
+
counts: mergedCounts,
|
|
30096
|
+
onFilterChange: (filterId) => {
|
|
30097
|
+
updateActiveFilter(filterId);
|
|
30098
|
+
const category = categoriesToShow.find((cat) => cat.type === filterId);
|
|
30099
|
+
if (category) {
|
|
30100
|
+
trackCoreEvent(`${category.label} Filter Clicked`, {
|
|
30101
|
+
workspaceId,
|
|
30102
|
+
workspaceName,
|
|
30103
|
+
date,
|
|
30104
|
+
filterType: filterId,
|
|
30105
|
+
clipCount: mergedCounts[filterId] || 0
|
|
30106
|
+
});
|
|
30107
|
+
}
|
|
30108
|
+
},
|
|
30109
|
+
onVideoSelect: (videoIndex) => {
|
|
30110
|
+
setCurrentIndex(videoIndex);
|
|
30111
|
+
const selectedVideo = allVideos?.[videoIndex];
|
|
30112
|
+
if (selectedVideo) {
|
|
30113
|
+
const videoCategory = categoriesToShow.find((cat) => cat.type === selectedVideo.type);
|
|
30114
|
+
if (videoCategory && activeFilter !== videoCategory.type) {
|
|
30115
|
+
updateActiveFilter(videoCategory.type);
|
|
30116
|
+
}
|
|
30117
|
+
}
|
|
30118
|
+
},
|
|
30119
|
+
className: "h-full"
|
|
30120
|
+
}
|
|
30121
|
+
) })
|
|
30122
|
+
] }) });
|
|
28743
30123
|
};
|
|
28744
30124
|
var getEfficiencyColor = (efficiency) => {
|
|
28745
30125
|
if (efficiency >= 80) {
|
|
@@ -30368,6 +31748,17 @@ var SideNavBar = React19.memo(({
|
|
|
30368
31748
|
});
|
|
30369
31749
|
onMobileMenuClose?.();
|
|
30370
31750
|
}, [navigate, onMobileMenuClose]);
|
|
31751
|
+
const handleSupervisorManagementClick = React19.useCallback(() => {
|
|
31752
|
+
navigate("/supervisor-management", {
|
|
31753
|
+
trackingEvent: {
|
|
31754
|
+
name: "Supervisor Management Page Clicked",
|
|
31755
|
+
properties: {
|
|
31756
|
+
source: "side_nav"
|
|
31757
|
+
}
|
|
31758
|
+
}
|
|
31759
|
+
});
|
|
31760
|
+
onMobileMenuClose?.();
|
|
31761
|
+
}, [navigate, onMobileMenuClose]);
|
|
30371
31762
|
const handleAIAgentClick = React19.useCallback(() => {
|
|
30372
31763
|
navigate("/ai-agent", {
|
|
30373
31764
|
trackingEvent: {
|
|
@@ -30432,6 +31823,7 @@ var SideNavBar = React19.memo(({
|
|
|
30432
31823
|
const kpisButtonClasses = React19.useMemo(() => getButtonClasses("/kpis"), [getButtonClasses, pathname]);
|
|
30433
31824
|
const targetsButtonClasses = React19.useMemo(() => getButtonClasses("/targets"), [getButtonClasses, pathname]);
|
|
30434
31825
|
const shiftsButtonClasses = React19.useMemo(() => getButtonClasses("/shifts"), [getButtonClasses, pathname]);
|
|
31826
|
+
const supervisorManagementButtonClasses = React19.useMemo(() => getButtonClasses("/supervisor-management"), [getButtonClasses, pathname]);
|
|
30435
31827
|
const aiAgentButtonClasses = React19.useMemo(() => getButtonClasses("/ai-agent"), [getButtonClasses, pathname]);
|
|
30436
31828
|
const profileButtonClasses = React19.useMemo(() => getButtonClasses("/profile"), [getButtonClasses, pathname]);
|
|
30437
31829
|
const helpButtonClasses = React19.useMemo(() => getButtonClasses("/help"), [getButtonClasses, pathname]);
|
|
@@ -30537,7 +31929,22 @@ var SideNavBar = React19.memo(({
|
|
|
30537
31929
|
]
|
|
30538
31930
|
}
|
|
30539
31931
|
),
|
|
30540
|
-
|
|
31932
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
31933
|
+
"button",
|
|
31934
|
+
{
|
|
31935
|
+
onClick: handleSupervisorManagementClick,
|
|
31936
|
+
className: supervisorManagementButtonClasses,
|
|
31937
|
+
"aria-label": "Supervisor Management",
|
|
31938
|
+
tabIndex: 0,
|
|
31939
|
+
role: "tab",
|
|
31940
|
+
"aria-selected": pathname === "/supervisor-management" || pathname.startsWith("/supervisor-management/"),
|
|
31941
|
+
children: [
|
|
31942
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.UsersIcon, { className: "w-5 h-5 mb-1" }),
|
|
31943
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Supervisors" })
|
|
31944
|
+
]
|
|
31945
|
+
}
|
|
31946
|
+
),
|
|
31947
|
+
skuEnabled && true && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30541
31948
|
"button",
|
|
30542
31949
|
{
|
|
30543
31950
|
onClick: handleSKUsClick,
|
|
@@ -30563,7 +31970,10 @@ var SideNavBar = React19.memo(({
|
|
|
30563
31970
|
"aria-selected": pathname === "/ai-agent" || pathname.startsWith("/ai-agent/"),
|
|
30564
31971
|
children: [
|
|
30565
31972
|
/* @__PURE__ */ jsxRuntime.jsx(outline.SparklesIcon, { className: "w-5 h-5 mb-1" }),
|
|
30566
|
-
/* @__PURE__ */ jsxRuntime.
|
|
31973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
31974
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Axel" }),
|
|
31975
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
31976
|
+
] })
|
|
30567
31977
|
]
|
|
30568
31978
|
}
|
|
30569
31979
|
),
|
|
@@ -30697,7 +32107,19 @@ var SideNavBar = React19.memo(({
|
|
|
30697
32107
|
]
|
|
30698
32108
|
}
|
|
30699
32109
|
),
|
|
30700
|
-
|
|
32110
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
32111
|
+
"button",
|
|
32112
|
+
{
|
|
32113
|
+
onClick: handleMobileNavClick(handleSupervisorManagementClick),
|
|
32114
|
+
className: getMobileButtonClass("/supervisor-management"),
|
|
32115
|
+
"aria-label": "Supervisor Management",
|
|
32116
|
+
children: [
|
|
32117
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.UsersIcon, { className: getIconClass("/supervisor-management") }),
|
|
32118
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Supervisors" })
|
|
32119
|
+
]
|
|
32120
|
+
}
|
|
32121
|
+
),
|
|
32122
|
+
skuEnabled && true && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30701
32123
|
"button",
|
|
30702
32124
|
{
|
|
30703
32125
|
onClick: handleMobileNavClick(handleSKUsClick),
|
|
@@ -30717,7 +32139,10 @@ var SideNavBar = React19.memo(({
|
|
|
30717
32139
|
"aria-label": "AI Manufacturing Expert",
|
|
30718
32140
|
children: [
|
|
30719
32141
|
/* @__PURE__ */ jsxRuntime.jsx(outline.SparklesIcon, { className: getIconClass("/ai-agent") }),
|
|
30720
|
-
/* @__PURE__ */ jsxRuntime.
|
|
32142
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
32143
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Axel AI" }),
|
|
32144
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
32145
|
+
] })
|
|
30721
32146
|
]
|
|
30722
32147
|
}
|
|
30723
32148
|
),
|
|
@@ -31236,6 +32661,1205 @@ var SingleVideoStream = ({
|
|
|
31236
32661
|
);
|
|
31237
32662
|
};
|
|
31238
32663
|
var SingleVideoStream_default = SingleVideoStream;
|
|
32664
|
+
var SupervisorDropdown = ({
|
|
32665
|
+
selectedSupervisor,
|
|
32666
|
+
availableSupervisors,
|
|
32667
|
+
onSelect,
|
|
32668
|
+
className = "",
|
|
32669
|
+
disabled = false,
|
|
32670
|
+
placeholder = "Select Supervisor"
|
|
32671
|
+
}) => {
|
|
32672
|
+
console.log(`[SupervisorDropdown] Rendered with ${availableSupervisors.length} available supervisors:`, availableSupervisors.map((s) => ({ id: s.id, name: s.name })));
|
|
32673
|
+
const [isOpen, setIsOpen] = React19.useState(false);
|
|
32674
|
+
const [searchTerm, setSearchTerm] = React19.useState("");
|
|
32675
|
+
const [dropdownPosition, setDropdownPosition] = React19.useState({ top: 0, left: 0, width: 0 });
|
|
32676
|
+
const dropdownRef = React19.useRef(null);
|
|
32677
|
+
const buttonRef = React19.useRef(null);
|
|
32678
|
+
const inputRef = React19.useRef(null);
|
|
32679
|
+
const filteredSupervisors = availableSupervisors.filter(
|
|
32680
|
+
(supervisor) => supervisor.name.toLowerCase().includes(searchTerm.toLowerCase()) || supervisor.email && supervisor.email.toLowerCase().includes(searchTerm.toLowerCase())
|
|
32681
|
+
);
|
|
32682
|
+
const calculatePosition = () => {
|
|
32683
|
+
if (buttonRef.current) {
|
|
32684
|
+
const rect = buttonRef.current.getBoundingClientRect();
|
|
32685
|
+
setDropdownPosition({
|
|
32686
|
+
top: rect.bottom + window.scrollY + 4,
|
|
32687
|
+
left: rect.left + window.scrollX,
|
|
32688
|
+
width: rect.width
|
|
32689
|
+
});
|
|
32690
|
+
}
|
|
32691
|
+
};
|
|
32692
|
+
React19.useEffect(() => {
|
|
32693
|
+
const handleClickOutside = (event) => {
|
|
32694
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target) && buttonRef.current && !buttonRef.current.contains(event.target)) {
|
|
32695
|
+
setIsOpen(false);
|
|
32696
|
+
setSearchTerm("");
|
|
32697
|
+
}
|
|
32698
|
+
};
|
|
32699
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
32700
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
32701
|
+
}, []);
|
|
32702
|
+
const handleSelect = (supervisor) => {
|
|
32703
|
+
onSelect(supervisor);
|
|
32704
|
+
setIsOpen(false);
|
|
32705
|
+
setSearchTerm("");
|
|
32706
|
+
};
|
|
32707
|
+
const handleKeyDown = (e) => {
|
|
32708
|
+
if (e.key === "Escape") {
|
|
32709
|
+
setIsOpen(false);
|
|
32710
|
+
setSearchTerm("");
|
|
32711
|
+
}
|
|
32712
|
+
};
|
|
32713
|
+
const handleToggle = () => {
|
|
32714
|
+
if (!disabled) {
|
|
32715
|
+
if (!isOpen) {
|
|
32716
|
+
calculatePosition();
|
|
32717
|
+
}
|
|
32718
|
+
setIsOpen(!isOpen);
|
|
32719
|
+
}
|
|
32720
|
+
};
|
|
32721
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
|
|
32722
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
32723
|
+
"button",
|
|
32724
|
+
{
|
|
32725
|
+
ref: buttonRef,
|
|
32726
|
+
type: "button",
|
|
32727
|
+
onClick: handleToggle,
|
|
32728
|
+
className: cn(
|
|
32729
|
+
"w-full flex items-center justify-between px-3 py-2 text-left bg-white border rounded-md shadow-sm transition-colors",
|
|
32730
|
+
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",
|
|
32731
|
+
isOpen && !disabled && "ring-2 ring-blue-500 border-blue-500"
|
|
32732
|
+
),
|
|
32733
|
+
disabled,
|
|
32734
|
+
children: [
|
|
32735
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center min-w-0 flex-1", children: [
|
|
32736
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-4 h-4 text-gray-400 mr-2 flex-shrink-0" }),
|
|
32737
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
32738
|
+
"truncate",
|
|
32739
|
+
selectedSupervisor ? "text-gray-900" : "text-gray-500"
|
|
32740
|
+
), children: selectedSupervisor ? selectedSupervisor.name : placeholder })
|
|
32741
|
+
] }),
|
|
32742
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn(
|
|
32743
|
+
"w-4 h-4 text-gray-400 transition-transform flex-shrink-0 ml-2",
|
|
32744
|
+
isOpen ? "rotate-180" : ""
|
|
32745
|
+
) })
|
|
32746
|
+
]
|
|
32747
|
+
}
|
|
32748
|
+
),
|
|
32749
|
+
isOpen && !disabled && typeof document !== "undefined" && reactDom.createPortal(
|
|
32750
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
32751
|
+
"div",
|
|
32752
|
+
{
|
|
32753
|
+
ref: dropdownRef,
|
|
32754
|
+
className: "fixed z-50 bg-white border border-gray-300 rounded-md shadow-lg",
|
|
32755
|
+
style: {
|
|
32756
|
+
top: dropdownPosition.top,
|
|
32757
|
+
left: dropdownPosition.left,
|
|
32758
|
+
width: dropdownPosition.width,
|
|
32759
|
+
minWidth: "200px"
|
|
32760
|
+
},
|
|
32761
|
+
children: [
|
|
32762
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
32763
|
+
"input",
|
|
32764
|
+
{
|
|
32765
|
+
ref: inputRef,
|
|
32766
|
+
type: "text",
|
|
32767
|
+
placeholder: "Search supervisors...",
|
|
32768
|
+
value: searchTerm,
|
|
32769
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
32770
|
+
onKeyDown: handleKeyDown,
|
|
32771
|
+
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",
|
|
32772
|
+
autoFocus: true
|
|
32773
|
+
}
|
|
32774
|
+
) }),
|
|
32775
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-60 overflow-y-auto", children: [
|
|
32776
|
+
selectedSupervisor && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32777
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
32778
|
+
"button",
|
|
32779
|
+
{
|
|
32780
|
+
type: "button",
|
|
32781
|
+
onClick: () => handleSelect(null),
|
|
32782
|
+
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",
|
|
32783
|
+
children: [
|
|
32784
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4 h-4 mr-2" }),
|
|
32785
|
+
" ",
|
|
32786
|
+
"Clear Assignment"
|
|
32787
|
+
]
|
|
32788
|
+
}
|
|
32789
|
+
),
|
|
32790
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-gray-200" })
|
|
32791
|
+
] }),
|
|
32792
|
+
filteredSupervisors.length > 0 ? filteredSupervisors.map((supervisor) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
32793
|
+
"button",
|
|
32794
|
+
{
|
|
32795
|
+
type: "button",
|
|
32796
|
+
onClick: () => handleSelect(supervisor),
|
|
32797
|
+
className: cn(
|
|
32798
|
+
"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",
|
|
32799
|
+
selectedSupervisor?.id === supervisor.id && "bg-blue-50 text-blue-700"
|
|
32800
|
+
),
|
|
32801
|
+
children: [
|
|
32802
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center min-w-0 flex-1", children: [
|
|
32803
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "w-4 h-4 text-gray-400 mr-2 flex-shrink-0" }),
|
|
32804
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
32805
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium truncate", children: supervisor.name }),
|
|
32806
|
+
supervisor.email && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 truncate", children: supervisor.email })
|
|
32807
|
+
] })
|
|
32808
|
+
] }),
|
|
32809
|
+
selectedSupervisor?.id === supervisor.id && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
32810
|
+
]
|
|
32811
|
+
},
|
|
32812
|
+
supervisor.id
|
|
32813
|
+
)) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: searchTerm ? "No supervisors found" : "No supervisors available" })
|
|
32814
|
+
] })
|
|
32815
|
+
]
|
|
32816
|
+
}
|
|
32817
|
+
),
|
|
32818
|
+
document.body
|
|
32819
|
+
)
|
|
32820
|
+
] });
|
|
32821
|
+
};
|
|
32822
|
+
var SupervisorDropdown_default = SupervisorDropdown;
|
|
32823
|
+
|
|
32824
|
+
// src/lib/hooks/useFirstTimeLogin.ts
|
|
32825
|
+
var useFirstTimeLogin = () => {
|
|
32826
|
+
const { user, markFirstLoginCompleted } = useAuth();
|
|
32827
|
+
const hasCompletedFirstLogin = user?.first_login_completed ?? false;
|
|
32828
|
+
const needsOnboarding = user ? !hasCompletedFirstLogin : false;
|
|
32829
|
+
const completeFirstLogin = async () => {
|
|
32830
|
+
return await markFirstLoginCompleted();
|
|
32831
|
+
};
|
|
32832
|
+
return {
|
|
32833
|
+
isFirstTimeLogin: !hasCompletedFirstLogin,
|
|
32834
|
+
// Simple boolean check
|
|
32835
|
+
hasCompletedFirstLogin,
|
|
32836
|
+
needsOnboarding,
|
|
32837
|
+
completeFirstLogin,
|
|
32838
|
+
user
|
|
32839
|
+
};
|
|
32840
|
+
};
|
|
32841
|
+
var SimpleOnboardingPopup = ({
|
|
32842
|
+
onComplete,
|
|
32843
|
+
isCompleting = false
|
|
32844
|
+
}) => {
|
|
32845
|
+
const [currentStep, setCurrentStep] = React19.useState(0);
|
|
32846
|
+
const steps = [
|
|
32847
|
+
{
|
|
32848
|
+
title: "Welcome to Optifye Dashboard! \u{1F389}",
|
|
32849
|
+
content: "We're excited to have you here. This dashboard helps you monitor and optimize your factory operations in real-time.",
|
|
32850
|
+
icon: "\u{1F44B}"
|
|
32851
|
+
},
|
|
32852
|
+
{
|
|
32853
|
+
title: "Real-Time Monitoring",
|
|
32854
|
+
content: "Track your production lines, monitor efficiency, and get instant alerts about any issues.",
|
|
32855
|
+
icon: "\u{1F4CA}"
|
|
32856
|
+
},
|
|
32857
|
+
{
|
|
32858
|
+
title: "Data-Driven Insights",
|
|
32859
|
+
content: "Make informed decisions with comprehensive analytics and historical data at your fingertips.",
|
|
32860
|
+
icon: "\u{1F4A1}"
|
|
32861
|
+
}
|
|
32862
|
+
];
|
|
32863
|
+
const handleNext = () => {
|
|
32864
|
+
if (currentStep < steps.length - 1) {
|
|
32865
|
+
setCurrentStep(currentStep + 1);
|
|
32866
|
+
} else {
|
|
32867
|
+
onComplete();
|
|
32868
|
+
}
|
|
32869
|
+
};
|
|
32870
|
+
const handleSkip = () => {
|
|
32871
|
+
onComplete();
|
|
32872
|
+
};
|
|
32873
|
+
const currentStepData = steps[currentStep];
|
|
32874
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 z-[9998] flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
32875
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32876
|
+
"button",
|
|
32877
|
+
{
|
|
32878
|
+
onClick: handleSkip,
|
|
32879
|
+
className: "absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors",
|
|
32880
|
+
disabled: isCompleting,
|
|
32881
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
|
|
32882
|
+
}
|
|
32883
|
+
),
|
|
32884
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8", children: [
|
|
32885
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-5xl mb-4 text-center", children: currentStepData.icon }),
|
|
32886
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900 mb-4 text-center", children: currentStepData.title }),
|
|
32887
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 text-center mb-8", children: currentStepData.content }),
|
|
32888
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center space-x-2 mb-6", children: steps.map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
32889
|
+
"div",
|
|
32890
|
+
{
|
|
32891
|
+
className: `h-2 w-2 rounded-full transition-colors ${index === currentStep ? "bg-blue-600" : "bg-gray-300"}`
|
|
32892
|
+
},
|
|
32893
|
+
index
|
|
32894
|
+
)) }),
|
|
32895
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
32896
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32897
|
+
"button",
|
|
32898
|
+
{
|
|
32899
|
+
onClick: handleSkip,
|
|
32900
|
+
className: "text-gray-500 hover:text-gray-700 text-sm transition-colors",
|
|
32901
|
+
disabled: isCompleting,
|
|
32902
|
+
children: "Skip tour"
|
|
32903
|
+
}
|
|
32904
|
+
),
|
|
32905
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32906
|
+
"button",
|
|
32907
|
+
{
|
|
32908
|
+
onClick: handleNext,
|
|
32909
|
+
disabled: isCompleting,
|
|
32910
|
+
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",
|
|
32911
|
+
children: isCompleting ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center", children: [
|
|
32912
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
32913
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
32914
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
32915
|
+
] }),
|
|
32916
|
+
"Loading..."
|
|
32917
|
+
] }) : currentStep === steps.length - 1 ? "Get Started" : "Next"
|
|
32918
|
+
}
|
|
32919
|
+
)
|
|
32920
|
+
] })
|
|
32921
|
+
] })
|
|
32922
|
+
] }) }) });
|
|
32923
|
+
};
|
|
32924
|
+
var MinimalOnboardingPopup = ({
|
|
32925
|
+
onComplete,
|
|
32926
|
+
isCompleting = false
|
|
32927
|
+
}) => {
|
|
32928
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black bg-opacity-50 z-[9998] flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-2xl max-w-sm w-full mx-4 relative z-[9999]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 text-center", children: [
|
|
32929
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-5xl mb-4", children: "\u{1F389}" }),
|
|
32930
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-bold text-gray-900 mb-3", children: "Welcome to Optifye!" }),
|
|
32931
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 mb-6", children: "Your dashboard is ready. Let's get started!" }),
|
|
32932
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32933
|
+
"button",
|
|
32934
|
+
{
|
|
32935
|
+
onClick: onComplete,
|
|
32936
|
+
disabled: isCompleting,
|
|
32937
|
+
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",
|
|
32938
|
+
children: isCompleting ? "Loading..." : "Get Started"
|
|
32939
|
+
}
|
|
32940
|
+
)
|
|
32941
|
+
] }) }) }) });
|
|
32942
|
+
};
|
|
32943
|
+
var onboardingSteps = [
|
|
32944
|
+
{
|
|
32945
|
+
id: "welcome",
|
|
32946
|
+
title: "Welcome to Optifye.ai \u{1F44B}",
|
|
32947
|
+
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.",
|
|
32948
|
+
position: "center",
|
|
32949
|
+
nextTrigger: "button"
|
|
32950
|
+
},
|
|
32951
|
+
{
|
|
32952
|
+
id: "live-streams",
|
|
32953
|
+
title: "Live Manufacturing Streams",
|
|
32954
|
+
description: "These are real-time video feeds from your production lines. Each stream shows the current efficiency with color-coded overlays.",
|
|
32955
|
+
target: '.workspace-grid, [class*="workspace-grid"], [class*="grid"], [class*="workspace-container"]',
|
|
32956
|
+
position: "auto",
|
|
32957
|
+
spotlight: true,
|
|
32958
|
+
allowInteraction: false,
|
|
32959
|
+
nextTrigger: "button"
|
|
32960
|
+
},
|
|
32961
|
+
{
|
|
32962
|
+
id: "efficiency-legend",
|
|
32963
|
+
title: "Understanding Efficiency Colors",
|
|
32964
|
+
description: "Green (80-100%): Optimal performance\nYellow (70-79%): Needs attention\nRed (<70%): Critical issues\nFlashing red (<50%): Immediate action required",
|
|
32965
|
+
target: '[class*="efficiency"], [class*="legend"], [class*="indicator"]',
|
|
32966
|
+
position: "auto",
|
|
32967
|
+
spotlight: true,
|
|
32968
|
+
allowInteraction: false,
|
|
32969
|
+
nextTrigger: "button"
|
|
32970
|
+
},
|
|
32971
|
+
{
|
|
32972
|
+
id: "click-workspace",
|
|
32973
|
+
title: "Dive Into a Workspace",
|
|
32974
|
+
description: "Click on any video stream to explore detailed performance metrics for that production line.",
|
|
32975
|
+
target: '.workspace-card, [class*="workspace-item"], [class*="workspace-card"], [class*="video-card"]',
|
|
32976
|
+
actionTarget: '.workspace-card, [class*="workspace-item"], [class*="workspace-card"], [class*="video-card"]',
|
|
32977
|
+
position: "auto",
|
|
32978
|
+
action: "click",
|
|
32979
|
+
spotlight: true,
|
|
32980
|
+
allowInteraction: true,
|
|
32981
|
+
nextTrigger: "action",
|
|
32982
|
+
waitForPath: "/workspace"
|
|
32983
|
+
},
|
|
32984
|
+
{
|
|
32985
|
+
id: "workspace-metrics",
|
|
32986
|
+
title: "Real-Time Performance Dashboard",
|
|
32987
|
+
description: "Excellent! Here you can monitor OEE (Overall Equipment Effectiveness), production rates, quality metrics, and identify bottlenecks in real-time.",
|
|
32988
|
+
target: '[class*="metrics"], [class*="dashboard-content"], [class*="workspace-detail"], main',
|
|
32989
|
+
position: "auto",
|
|
32990
|
+
spotlight: true,
|
|
32991
|
+
allowInteraction: false,
|
|
32992
|
+
nextTrigger: "button"
|
|
32993
|
+
},
|
|
32994
|
+
{
|
|
32995
|
+
id: "navigate-back",
|
|
32996
|
+
title: "Navigate Back Home",
|
|
32997
|
+
description: "Click on the Home tab in the sidebar to return to the main dashboard.",
|
|
32998
|
+
target: '[data-nav-item="home"], [href="/"], nav a:has-text("Home"), nav button:has-text("Home"), tab:has-text("Home")',
|
|
32999
|
+
actionTarget: '[data-nav-item="home"], [href="/"], nav a:has-text("Home"), nav button:has-text("Home"), tab:has-text("Home")',
|
|
33000
|
+
position: "right",
|
|
33001
|
+
action: "click",
|
|
33002
|
+
spotlight: true,
|
|
33003
|
+
allowInteraction: true,
|
|
33004
|
+
nextTrigger: "action",
|
|
33005
|
+
waitForPath: "/"
|
|
33006
|
+
},
|
|
33007
|
+
{
|
|
33008
|
+
id: "complete",
|
|
33009
|
+
title: "\u{1F389} Tour Complete!",
|
|
33010
|
+
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.",
|
|
33011
|
+
position: "center",
|
|
33012
|
+
nextTrigger: "button"
|
|
33013
|
+
}
|
|
33014
|
+
];
|
|
33015
|
+
var InteractiveOnboardingTour = ({
|
|
33016
|
+
isOpen,
|
|
33017
|
+
onComplete,
|
|
33018
|
+
onSkip
|
|
33019
|
+
}) => {
|
|
33020
|
+
const [currentStep, setCurrentStep] = React19.useState(0);
|
|
33021
|
+
const [isTransitioning, setIsTransitioning] = React19.useState(false);
|
|
33022
|
+
const router$1 = router.useRouter();
|
|
33023
|
+
const step = onboardingSteps[currentStep];
|
|
33024
|
+
const [targetElement, setTargetElement] = React19.useState(null);
|
|
33025
|
+
const [tooltipPosition, setTooltipPosition] = React19.useState({});
|
|
33026
|
+
const observerRef = React19.useRef(null);
|
|
33027
|
+
const handleNext = React19.useCallback(() => {
|
|
33028
|
+
if (isTransitioning) return;
|
|
33029
|
+
setIsTransitioning(true);
|
|
33030
|
+
setTimeout(() => {
|
|
33031
|
+
if (currentStep < onboardingSteps.length - 1) {
|
|
33032
|
+
setCurrentStep(currentStep + 1);
|
|
33033
|
+
} else {
|
|
33034
|
+
onComplete();
|
|
33035
|
+
}
|
|
33036
|
+
setIsTransitioning(false);
|
|
33037
|
+
}, 200);
|
|
33038
|
+
}, [currentStep, isTransitioning, onComplete]);
|
|
33039
|
+
const handlePrevious = React19.useCallback(() => {
|
|
33040
|
+
if (currentStep > 0 && !isTransitioning) {
|
|
33041
|
+
setIsTransitioning(true);
|
|
33042
|
+
setTimeout(() => {
|
|
33043
|
+
setCurrentStep(currentStep - 1);
|
|
33044
|
+
setIsTransitioning(false);
|
|
33045
|
+
}, 200);
|
|
33046
|
+
}
|
|
33047
|
+
}, [currentStep, isTransitioning]);
|
|
33048
|
+
React19.useEffect(() => {
|
|
33049
|
+
if (!step?.target || !isOpen) {
|
|
33050
|
+
setTargetElement(null);
|
|
33051
|
+
return;
|
|
33052
|
+
}
|
|
33053
|
+
const findElement = () => {
|
|
33054
|
+
const selectors = step.target.split(",").map((s) => s.trim());
|
|
33055
|
+
for (const selector of selectors) {
|
|
33056
|
+
try {
|
|
33057
|
+
const element2 = document.querySelector(selector);
|
|
33058
|
+
if (element2) {
|
|
33059
|
+
setTargetElement(element2);
|
|
33060
|
+
return element2;
|
|
33061
|
+
}
|
|
33062
|
+
} catch (e) {
|
|
33063
|
+
}
|
|
33064
|
+
}
|
|
33065
|
+
return null;
|
|
33066
|
+
};
|
|
33067
|
+
const element = findElement();
|
|
33068
|
+
if (!element && step.target) {
|
|
33069
|
+
observerRef.current = new MutationObserver(() => {
|
|
33070
|
+
const found = findElement();
|
|
33071
|
+
if (found) {
|
|
33072
|
+
observerRef.current?.disconnect();
|
|
33073
|
+
}
|
|
33074
|
+
});
|
|
33075
|
+
observerRef.current.observe(document.body, {
|
|
33076
|
+
childList: true,
|
|
33077
|
+
subtree: true
|
|
33078
|
+
});
|
|
33079
|
+
}
|
|
33080
|
+
return () => {
|
|
33081
|
+
observerRef.current?.disconnect();
|
|
33082
|
+
};
|
|
33083
|
+
}, [step, isOpen, router$1.pathname]);
|
|
33084
|
+
React19.useEffect(() => {
|
|
33085
|
+
if (!targetElement || step?.position === "center") {
|
|
33086
|
+
setTooltipPosition({
|
|
33087
|
+
position: "fixed",
|
|
33088
|
+
top: "50%",
|
|
33089
|
+
left: "50%",
|
|
33090
|
+
transform: "translate(-50%, -50%)",
|
|
33091
|
+
zIndex: 10002
|
|
33092
|
+
});
|
|
33093
|
+
return;
|
|
33094
|
+
}
|
|
33095
|
+
const updatePosition = () => {
|
|
33096
|
+
const rect = targetElement.getBoundingClientRect();
|
|
33097
|
+
const tooltipWidth = 420;
|
|
33098
|
+
const tooltipHeight = 200;
|
|
33099
|
+
const offset = 16;
|
|
33100
|
+
const viewportWidth = window.innerWidth;
|
|
33101
|
+
const viewportHeight = window.innerHeight;
|
|
33102
|
+
let position = {
|
|
33103
|
+
position: "fixed",
|
|
33104
|
+
zIndex: 10002,
|
|
33105
|
+
maxWidth: "420px"
|
|
33106
|
+
};
|
|
33107
|
+
const spaceTop = rect.top;
|
|
33108
|
+
const spaceBottom = viewportHeight - rect.bottom;
|
|
33109
|
+
const spaceLeft = rect.left;
|
|
33110
|
+
const spaceRight = viewportWidth - rect.right;
|
|
33111
|
+
if (step.position === "auto") {
|
|
33112
|
+
if (spaceBottom > tooltipHeight + offset && rect.left + tooltipWidth / 2 < viewportWidth) {
|
|
33113
|
+
position.top = rect.bottom + offset;
|
|
33114
|
+
position.left = Math.max(offset, Math.min(rect.left + rect.width / 2 - tooltipWidth / 2, viewportWidth - tooltipWidth - offset));
|
|
33115
|
+
} else if (spaceTop > tooltipHeight + offset) {
|
|
33116
|
+
position.bottom = viewportHeight - rect.top + offset;
|
|
33117
|
+
position.left = Math.max(offset, Math.min(rect.left + rect.width / 2 - tooltipWidth / 2, viewportWidth - tooltipWidth - offset));
|
|
33118
|
+
} else if (spaceRight > tooltipWidth + offset) {
|
|
33119
|
+
position.left = rect.right + offset;
|
|
33120
|
+
position.top = Math.max(offset, Math.min(rect.top + rect.height / 2 - tooltipHeight / 2, viewportHeight - tooltipHeight - offset));
|
|
33121
|
+
} else if (spaceLeft > tooltipWidth + offset) {
|
|
33122
|
+
position.right = viewportWidth - rect.left + offset;
|
|
33123
|
+
position.top = Math.max(offset, Math.min(rect.top + rect.height / 2 - tooltipHeight / 2, viewportHeight - tooltipHeight - offset));
|
|
33124
|
+
} else {
|
|
33125
|
+
position.top = "50%";
|
|
33126
|
+
position.left = "50%";
|
|
33127
|
+
position.transform = "translate(-50%, -50%)";
|
|
33128
|
+
}
|
|
33129
|
+
} else {
|
|
33130
|
+
switch (step.position) {
|
|
33131
|
+
case "top":
|
|
33132
|
+
position.bottom = viewportHeight - rect.top + offset;
|
|
33133
|
+
position.left = rect.left + rect.width / 2 - tooltipWidth / 2;
|
|
33134
|
+
break;
|
|
33135
|
+
case "bottom":
|
|
33136
|
+
position.top = rect.bottom + offset;
|
|
33137
|
+
position.left = rect.left + rect.width / 2 - tooltipWidth / 2;
|
|
33138
|
+
break;
|
|
33139
|
+
case "left":
|
|
33140
|
+
position.right = viewportWidth - rect.left + offset;
|
|
33141
|
+
position.top = rect.top + rect.height / 2 - tooltipHeight / 2;
|
|
33142
|
+
break;
|
|
33143
|
+
case "right":
|
|
33144
|
+
position.left = rect.right + offset;
|
|
33145
|
+
position.top = rect.top + rect.height / 2 - tooltipHeight / 2;
|
|
33146
|
+
break;
|
|
33147
|
+
}
|
|
33148
|
+
}
|
|
33149
|
+
setTooltipPosition(position);
|
|
33150
|
+
};
|
|
33151
|
+
updatePosition();
|
|
33152
|
+
window.addEventListener("resize", updatePosition);
|
|
33153
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
33154
|
+
return () => {
|
|
33155
|
+
window.removeEventListener("resize", updatePosition);
|
|
33156
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
33157
|
+
};
|
|
33158
|
+
}, [targetElement, step]);
|
|
33159
|
+
React19.useEffect(() => {
|
|
33160
|
+
if (!step || step.nextTrigger !== "action" || !isOpen) return;
|
|
33161
|
+
let actionTimeout;
|
|
33162
|
+
const handleAction = (e) => {
|
|
33163
|
+
if (!step.actionTarget) return;
|
|
33164
|
+
const target = e.target;
|
|
33165
|
+
const selectors = step.actionTarget.split(",").map((s) => s.trim());
|
|
33166
|
+
for (const selector of selectors) {
|
|
33167
|
+
try {
|
|
33168
|
+
if (target.matches(selector) || target.closest(selector)) {
|
|
33169
|
+
if (step.waitForPath) {
|
|
33170
|
+
actionTimeout = setTimeout(() => {
|
|
33171
|
+
console.log("No navigation detected, progressing to next step");
|
|
33172
|
+
handleNext();
|
|
33173
|
+
}, 2e3);
|
|
33174
|
+
} else {
|
|
33175
|
+
setTimeout(() => handleNext(), 300);
|
|
33176
|
+
}
|
|
33177
|
+
return;
|
|
33178
|
+
}
|
|
33179
|
+
} catch {
|
|
33180
|
+
}
|
|
33181
|
+
}
|
|
33182
|
+
};
|
|
33183
|
+
if (step.action === "click") {
|
|
33184
|
+
document.addEventListener("click", handleAction, true);
|
|
33185
|
+
return () => {
|
|
33186
|
+
document.removeEventListener("click", handleAction, true);
|
|
33187
|
+
if (actionTimeout) clearTimeout(actionTimeout);
|
|
33188
|
+
};
|
|
33189
|
+
}
|
|
33190
|
+
}, [step, isOpen, handleNext]);
|
|
33191
|
+
React19.useEffect(() => {
|
|
33192
|
+
if (!step?.waitForPath || step.nextTrigger !== "action") return;
|
|
33193
|
+
const checkPath = (url) => {
|
|
33194
|
+
const currentPath = url || router$1.asPath || router$1.pathname;
|
|
33195
|
+
if (step.waitForPath === "/workspace" && (currentPath.includes("/workspace") || currentPath.includes("workspace-") || router$1.query?.workspace)) {
|
|
33196
|
+
setTimeout(() => handleNext(), 800);
|
|
33197
|
+
return true;
|
|
33198
|
+
}
|
|
33199
|
+
if (step.waitForPath && (currentPath === step.waitForPath || currentPath.includes(step.waitForPath))) {
|
|
33200
|
+
setTimeout(() => handleNext(), 800);
|
|
33201
|
+
return true;
|
|
33202
|
+
}
|
|
33203
|
+
return false;
|
|
33204
|
+
};
|
|
33205
|
+
if (checkPath()) return;
|
|
33206
|
+
const handleRouteChange = (url) => {
|
|
33207
|
+
checkPath(url);
|
|
33208
|
+
};
|
|
33209
|
+
router$1.events.on("routeChangeComplete", handleRouteChange);
|
|
33210
|
+
router$1.events.on("routeChangeStart", handleRouteChange);
|
|
33211
|
+
return () => {
|
|
33212
|
+
router$1.events.off("routeChangeComplete", handleRouteChange);
|
|
33213
|
+
router$1.events.off("routeChangeStart", handleRouteChange);
|
|
33214
|
+
};
|
|
33215
|
+
}, [step, router$1, handleNext]);
|
|
33216
|
+
if (!isOpen || !step) return null;
|
|
33217
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33218
|
+
!step.allowInteraction && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33219
|
+
motion.div,
|
|
33220
|
+
{
|
|
33221
|
+
initial: { opacity: 0 },
|
|
33222
|
+
animate: { opacity: 1 },
|
|
33223
|
+
exit: { opacity: 0 },
|
|
33224
|
+
className: "fixed inset-0 bg-black/20",
|
|
33225
|
+
style: { zIndex: 1e4 },
|
|
33226
|
+
onClick: (e) => e.stopPropagation()
|
|
33227
|
+
}
|
|
33228
|
+
),
|
|
33229
|
+
step.spotlight && targetElement && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33230
|
+
Spotlight,
|
|
33231
|
+
{
|
|
33232
|
+
element: targetElement,
|
|
33233
|
+
allowInteraction: step.allowInteraction,
|
|
33234
|
+
showPulse: step.action === "click"
|
|
33235
|
+
}
|
|
33236
|
+
),
|
|
33237
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33238
|
+
motion.div,
|
|
33239
|
+
{
|
|
33240
|
+
initial: { opacity: 0, scale: 0.95, y: 10 },
|
|
33241
|
+
animate: { opacity: 1, scale: 1, y: 0 },
|
|
33242
|
+
exit: { opacity: 0, scale: 0.95, y: 10 },
|
|
33243
|
+
transition: { duration: 0.2, ease: "easeOut" },
|
|
33244
|
+
style: tooltipPosition,
|
|
33245
|
+
className: "pointer-events-auto",
|
|
33246
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white dark:bg-gray-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 overflow-hidden", children: [
|
|
33247
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
33248
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
33249
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: [...Array(onboardingSteps.length)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
33250
|
+
motion.div,
|
|
33251
|
+
{
|
|
33252
|
+
initial: false,
|
|
33253
|
+
animate: {
|
|
33254
|
+
width: i === currentStep ? 24 : 6,
|
|
33255
|
+
backgroundColor: i === currentStep ? "#3B82F6" : i < currentStep ? "#93C5FD" : "#E5E7EB"
|
|
33256
|
+
},
|
|
33257
|
+
className: "h-1.5 rounded-full transition-all duration-300"
|
|
33258
|
+
},
|
|
33259
|
+
i
|
|
33260
|
+
)) }),
|
|
33261
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600 dark:text-gray-400", children: [
|
|
33262
|
+
currentStep + 1,
|
|
33263
|
+
" of ",
|
|
33264
|
+
onboardingSteps.length
|
|
33265
|
+
] })
|
|
33266
|
+
] }),
|
|
33267
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33268
|
+
"button",
|
|
33269
|
+
{
|
|
33270
|
+
onClick: onSkip,
|
|
33271
|
+
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",
|
|
33272
|
+
"aria-label": "Close tour",
|
|
33273
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
|
|
33274
|
+
}
|
|
33275
|
+
)
|
|
33276
|
+
] }) }),
|
|
33277
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-5", children: [
|
|
33278
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-semibold text-gray-900 dark:text-white mb-3", children: step.title }),
|
|
33279
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 dark:text-gray-300 leading-relaxed whitespace-pre-line", children: step.description }),
|
|
33280
|
+
step.action === "click" && step.nextTrigger === "action" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33281
|
+
motion.div,
|
|
33282
|
+
{
|
|
33283
|
+
initial: { opacity: 0, y: 5 },
|
|
33284
|
+
animate: { opacity: 1, y: 0 },
|
|
33285
|
+
transition: { delay: 0.3 },
|
|
33286
|
+
className: "mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-xl border border-blue-200 dark:border-blue-800",
|
|
33287
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-blue-700 dark:text-blue-300 flex items-center gap-2", children: [
|
|
33288
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MousePointer, { className: "w-4 h-4 animate-pulse" }),
|
|
33289
|
+
"Click the highlighted element to continue"
|
|
33290
|
+
] })
|
|
33291
|
+
}
|
|
33292
|
+
)
|
|
33293
|
+
] }),
|
|
33294
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
33295
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33296
|
+
"button",
|
|
33297
|
+
{
|
|
33298
|
+
onClick: onSkip,
|
|
33299
|
+
className: "text-sm font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors",
|
|
33300
|
+
children: "Skip tour"
|
|
33301
|
+
}
|
|
33302
|
+
),
|
|
33303
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
33304
|
+
currentStep > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33305
|
+
"button",
|
|
33306
|
+
{
|
|
33307
|
+
onClick: handlePrevious,
|
|
33308
|
+
disabled: isTransitioning,
|
|
33309
|
+
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",
|
|
33310
|
+
children: "Previous"
|
|
33311
|
+
}
|
|
33312
|
+
),
|
|
33313
|
+
step.nextTrigger === "button" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33314
|
+
"button",
|
|
33315
|
+
{
|
|
33316
|
+
onClick: handleNext,
|
|
33317
|
+
disabled: isTransitioning,
|
|
33318
|
+
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`,
|
|
33319
|
+
children: currentStep === onboardingSteps.length - 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33320
|
+
"Complete Tour",
|
|
33321
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" })
|
|
33322
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33323
|
+
"Next",
|
|
33324
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-4 h-4" })
|
|
33325
|
+
] })
|
|
33326
|
+
}
|
|
33327
|
+
)
|
|
33328
|
+
] })
|
|
33329
|
+
] })
|
|
33330
|
+
] })
|
|
33331
|
+
},
|
|
33332
|
+
step.id
|
|
33333
|
+
)
|
|
33334
|
+
] });
|
|
33335
|
+
if (typeof window !== "undefined") {
|
|
33336
|
+
return reactDom.createPortal(
|
|
33337
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: isOpen && content }),
|
|
33338
|
+
document.body
|
|
33339
|
+
);
|
|
33340
|
+
}
|
|
33341
|
+
return null;
|
|
33342
|
+
};
|
|
33343
|
+
var Spotlight = ({ element, allowInteraction, showPulse }) => {
|
|
33344
|
+
const [rect, setRect] = React19.useState(null);
|
|
33345
|
+
React19.useEffect(() => {
|
|
33346
|
+
const updateRect = () => {
|
|
33347
|
+
setRect(element.getBoundingClientRect());
|
|
33348
|
+
};
|
|
33349
|
+
updateRect();
|
|
33350
|
+
window.addEventListener("resize", updateRect);
|
|
33351
|
+
window.addEventListener("scroll", updateRect, true);
|
|
33352
|
+
const observer = new ResizeObserver(updateRect);
|
|
33353
|
+
observer.observe(element);
|
|
33354
|
+
return () => {
|
|
33355
|
+
window.removeEventListener("resize", updateRect);
|
|
33356
|
+
window.removeEventListener("scroll", updateRect, true);
|
|
33357
|
+
observer.disconnect();
|
|
33358
|
+
};
|
|
33359
|
+
}, [element]);
|
|
33360
|
+
if (!rect) return null;
|
|
33361
|
+
const padding = 8;
|
|
33362
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33363
|
+
!allowInteraction && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
33364
|
+
"svg",
|
|
33365
|
+
{
|
|
33366
|
+
className: "fixed inset-0",
|
|
33367
|
+
style: { zIndex: 10001, pointerEvents: "auto" },
|
|
33368
|
+
children: [
|
|
33369
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("mask", { id: "spotlight-mask", children: [
|
|
33370
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", fill: "white" }),
|
|
33371
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33372
|
+
"rect",
|
|
33373
|
+
{
|
|
33374
|
+
x: rect.left - padding,
|
|
33375
|
+
y: rect.top - padding,
|
|
33376
|
+
width: rect.width + padding * 2,
|
|
33377
|
+
height: rect.height + padding * 2,
|
|
33378
|
+
rx: "8",
|
|
33379
|
+
fill: "black"
|
|
33380
|
+
}
|
|
33381
|
+
)
|
|
33382
|
+
] }) }),
|
|
33383
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33384
|
+
"rect",
|
|
33385
|
+
{
|
|
33386
|
+
x: "0",
|
|
33387
|
+
y: "0",
|
|
33388
|
+
width: "100%",
|
|
33389
|
+
height: "100%",
|
|
33390
|
+
fill: "black",
|
|
33391
|
+
fillOpacity: "0.2",
|
|
33392
|
+
mask: "url(#spotlight-mask)"
|
|
33393
|
+
}
|
|
33394
|
+
)
|
|
33395
|
+
]
|
|
33396
|
+
}
|
|
33397
|
+
),
|
|
33398
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
33399
|
+
motion.div,
|
|
33400
|
+
{
|
|
33401
|
+
className: "fixed pointer-events-none",
|
|
33402
|
+
style: {
|
|
33403
|
+
left: rect.left - padding,
|
|
33404
|
+
top: rect.top - padding,
|
|
33405
|
+
width: rect.width + padding * 2,
|
|
33406
|
+
height: rect.height + padding * 2,
|
|
33407
|
+
zIndex: 10001
|
|
33408
|
+
},
|
|
33409
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
33410
|
+
animate: { opacity: 1, scale: 1 },
|
|
33411
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
33412
|
+
transition: { duration: 0.3, ease: "easeOut" },
|
|
33413
|
+
children: [
|
|
33414
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg ring-3 ring-blue-500 shadow-lg shadow-blue-500/30" }),
|
|
33415
|
+
showPulse && allowInteraction && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33416
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg ring-4 ring-blue-400 ring-opacity-75 animate-pulse" }),
|
|
33417
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-blue-500/5 animate-pulse" })
|
|
33418
|
+
] })
|
|
33419
|
+
]
|
|
33420
|
+
}
|
|
33421
|
+
)
|
|
33422
|
+
] });
|
|
33423
|
+
};
|
|
33424
|
+
var FirstTimeLoginHandler = ({
|
|
33425
|
+
children,
|
|
33426
|
+
onboardingComponent: OnboardingComponent = SimpleOnboardingPopup,
|
|
33427
|
+
// Default to simple popup
|
|
33428
|
+
enableAutoDetection = true
|
|
33429
|
+
}) => {
|
|
33430
|
+
const {
|
|
33431
|
+
needsOnboarding,
|
|
33432
|
+
completeFirstLogin,
|
|
33433
|
+
user
|
|
33434
|
+
} = useFirstTimeLogin();
|
|
33435
|
+
const {
|
|
33436
|
+
showOnboarding: showTour,
|
|
33437
|
+
setShowOnboarding: setShowTour,
|
|
33438
|
+
completeOnboarding
|
|
33439
|
+
} = useAuth();
|
|
33440
|
+
const [showOnboarding, setShowOnboarding] = React19.useState(false);
|
|
33441
|
+
const [isCompleting, setIsCompleting] = React19.useState(false);
|
|
33442
|
+
const [hasChecked, setHasChecked] = React19.useState(false);
|
|
33443
|
+
React19.useEffect(() => {
|
|
33444
|
+
if (!hasChecked && user && enableAutoDetection) {
|
|
33445
|
+
setHasChecked(true);
|
|
33446
|
+
if (needsOnboarding) {
|
|
33447
|
+
console.log("[FirstTimeLoginHandler] First-time login detected, showing onboarding tour");
|
|
33448
|
+
setShowTour(true);
|
|
33449
|
+
}
|
|
33450
|
+
}
|
|
33451
|
+
}, [user, needsOnboarding, enableAutoDetection, hasChecked, setShowTour]);
|
|
33452
|
+
const handleOnboardingComplete = async () => {
|
|
33453
|
+
setIsCompleting(true);
|
|
33454
|
+
try {
|
|
33455
|
+
const success = await completeFirstLogin();
|
|
33456
|
+
if (success) {
|
|
33457
|
+
console.log("[FirstTimeLoginHandler] First login marked as completed");
|
|
33458
|
+
setShowOnboarding(false);
|
|
33459
|
+
} else {
|
|
33460
|
+
console.error("[FirstTimeLoginHandler] Failed to mark first login as completed");
|
|
33461
|
+
setShowOnboarding(false);
|
|
33462
|
+
}
|
|
33463
|
+
} catch (error) {
|
|
33464
|
+
console.error("[FirstTimeLoginHandler] Error completing first login:", error);
|
|
33465
|
+
setShowOnboarding(false);
|
|
33466
|
+
} finally {
|
|
33467
|
+
setIsCompleting(false);
|
|
33468
|
+
}
|
|
33469
|
+
};
|
|
33470
|
+
const handleTourComplete = async () => {
|
|
33471
|
+
await completeOnboarding();
|
|
33472
|
+
};
|
|
33473
|
+
const handleTourSkip = () => {
|
|
33474
|
+
setShowTour(false);
|
|
33475
|
+
completeFirstLogin();
|
|
33476
|
+
};
|
|
33477
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33478
|
+
children,
|
|
33479
|
+
showTour && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33480
|
+
InteractiveOnboardingTour,
|
|
33481
|
+
{
|
|
33482
|
+
isOpen: showTour,
|
|
33483
|
+
onComplete: handleTourComplete,
|
|
33484
|
+
onSkip: handleTourSkip
|
|
33485
|
+
}
|
|
33486
|
+
),
|
|
33487
|
+
showOnboarding && OnboardingComponent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33488
|
+
OnboardingComponent,
|
|
33489
|
+
{
|
|
33490
|
+
onComplete: handleOnboardingComplete,
|
|
33491
|
+
isCompleting
|
|
33492
|
+
}
|
|
33493
|
+
)
|
|
33494
|
+
] });
|
|
33495
|
+
};
|
|
33496
|
+
var FirstTimeLoginDebug = () => {
|
|
33497
|
+
const {
|
|
33498
|
+
isFirstTimeLogin,
|
|
33499
|
+
hasCompletedFirstLogin,
|
|
33500
|
+
needsOnboarding,
|
|
33501
|
+
completeFirstLogin,
|
|
33502
|
+
user
|
|
33503
|
+
} = useFirstTimeLogin();
|
|
33504
|
+
if (!user) return null;
|
|
33505
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
33506
|
+
position: "fixed",
|
|
33507
|
+
top: 10,
|
|
33508
|
+
right: 10,
|
|
33509
|
+
background: "#f0f0f0",
|
|
33510
|
+
padding: "10px",
|
|
33511
|
+
border: "1px solid #ccc",
|
|
33512
|
+
fontSize: "12px",
|
|
33513
|
+
zIndex: 9999
|
|
33514
|
+
}, children: [
|
|
33515
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { children: "First-Time Login Debug" }),
|
|
33516
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
33517
|
+
"User ID: ",
|
|
33518
|
+
user.id
|
|
33519
|
+
] }),
|
|
33520
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
33521
|
+
"Email: ",
|
|
33522
|
+
user.email
|
|
33523
|
+
] }),
|
|
33524
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
33525
|
+
"Is First Time: ",
|
|
33526
|
+
isFirstTimeLogin ? "\u2705" : "\u274C"
|
|
33527
|
+
] }),
|
|
33528
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
33529
|
+
"Completed: ",
|
|
33530
|
+
hasCompletedFirstLogin ? "\u2705" : "\u274C"
|
|
33531
|
+
] }),
|
|
33532
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
33533
|
+
"Needs Onboarding: ",
|
|
33534
|
+
needsOnboarding ? "\u2705" : "\u274C"
|
|
33535
|
+
] }),
|
|
33536
|
+
needsOnboarding && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33537
|
+
"button",
|
|
33538
|
+
{
|
|
33539
|
+
onClick: completeFirstLogin,
|
|
33540
|
+
style: { marginTop: "5px", padding: "5px" },
|
|
33541
|
+
children: "Mark as Completed"
|
|
33542
|
+
}
|
|
33543
|
+
)
|
|
33544
|
+
] });
|
|
33545
|
+
};
|
|
33546
|
+
var onboardingSteps2 = [
|
|
33547
|
+
{
|
|
33548
|
+
id: "welcome",
|
|
33549
|
+
title: "Welcome to Optifye.ai",
|
|
33550
|
+
description: "Let's take a quick tour to help you get started with our intelligent manufacturing monitoring platform.",
|
|
33551
|
+
position: "center"
|
|
33552
|
+
},
|
|
33553
|
+
{
|
|
33554
|
+
id: "live-streams",
|
|
33555
|
+
title: "Live Video Streams",
|
|
33556
|
+
description: "These are live video streams of your manufacturing lines. Monitor your production in real-time from anywhere.",
|
|
33557
|
+
target: ".workspace-grid",
|
|
33558
|
+
position: "top",
|
|
33559
|
+
highlightTarget: true
|
|
33560
|
+
},
|
|
33561
|
+
{
|
|
33562
|
+
id: "efficiency-indicator",
|
|
33563
|
+
title: "Efficiency Indicators",
|
|
33564
|
+
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.",
|
|
33565
|
+
target: ".efficiency-indicator",
|
|
33566
|
+
position: "bottom",
|
|
33567
|
+
highlightTarget: true
|
|
33568
|
+
},
|
|
33569
|
+
{
|
|
33570
|
+
id: "click-workspace",
|
|
33571
|
+
title: "Explore a Workspace",
|
|
33572
|
+
description: "Click on any video stream to dive deeper into that workspace's performance metrics.",
|
|
33573
|
+
target: ".workspace-item",
|
|
33574
|
+
position: "center",
|
|
33575
|
+
highlightTarget: true,
|
|
33576
|
+
waitForNavigation: "/workspace/"
|
|
33577
|
+
},
|
|
33578
|
+
{
|
|
33579
|
+
id: "workspace-metrics",
|
|
33580
|
+
title: "Real-Time Efficiency Metrics",
|
|
33581
|
+
description: "Great! These are the real-time efficiency metrics for the workspace you just selected. Track OEE, performance, and quality in real-time.",
|
|
33582
|
+
target: ".metrics-dashboard",
|
|
33583
|
+
position: "top",
|
|
33584
|
+
highlightTarget: true
|
|
33585
|
+
},
|
|
33586
|
+
{
|
|
33587
|
+
id: "clips-navigation",
|
|
33588
|
+
title: "Explore Clips",
|
|
33589
|
+
description: 'Now, click on "Clips" in the sidebar to see how you can diagnose issues and understand root causes.',
|
|
33590
|
+
target: '[data-nav-item="clips"]',
|
|
33591
|
+
position: "right",
|
|
33592
|
+
highlightTarget: true,
|
|
33593
|
+
waitForNavigation: "/clips"
|
|
33594
|
+
},
|
|
33595
|
+
{
|
|
33596
|
+
id: "clips-explanation",
|
|
33597
|
+
title: "Clips for Root Cause Analysis",
|
|
33598
|
+
description: "Clips help you understand and diagnose the root cause of efficiency issues. Review recorded incidents, analyze patterns, and improve your processes.",
|
|
33599
|
+
target: ".clips-container",
|
|
33600
|
+
position: "top",
|
|
33601
|
+
highlightTarget: true
|
|
33602
|
+
},
|
|
33603
|
+
{
|
|
33604
|
+
id: "complete",
|
|
33605
|
+
title: "You're All Set!",
|
|
33606
|
+
description: "You've completed the tour! Start exploring Optifye.ai to optimize your manufacturing operations.",
|
|
33607
|
+
position: "center"
|
|
33608
|
+
}
|
|
33609
|
+
];
|
|
33610
|
+
var OnboardingTour = ({
|
|
33611
|
+
isOpen,
|
|
33612
|
+
onComplete,
|
|
33613
|
+
onSkip
|
|
33614
|
+
}) => {
|
|
33615
|
+
const [currentStep, setCurrentStep] = React19.useState(0);
|
|
33616
|
+
const [isWaitingForNavigation, setIsWaitingForNavigation] = React19.useState(false);
|
|
33617
|
+
const router$1 = router.useRouter();
|
|
33618
|
+
const step = onboardingSteps2[currentStep];
|
|
33619
|
+
React19.useEffect(() => {
|
|
33620
|
+
if (!step?.waitForNavigation || !isWaitingForNavigation) return;
|
|
33621
|
+
const handleRouteChange = (url) => {
|
|
33622
|
+
if (step.waitForNavigation && url.includes(step.waitForNavigation)) {
|
|
33623
|
+
setIsWaitingForNavigation(false);
|
|
33624
|
+
handleNext();
|
|
33625
|
+
}
|
|
33626
|
+
};
|
|
33627
|
+
router$1.events.on("routeChangeComplete", handleRouteChange);
|
|
33628
|
+
return () => {
|
|
33629
|
+
router$1.events.off("routeChangeComplete", handleRouteChange);
|
|
33630
|
+
};
|
|
33631
|
+
}, [step, isWaitingForNavigation, router$1]);
|
|
33632
|
+
const handleNext = React19.useCallback(() => {
|
|
33633
|
+
const nextStep = onboardingSteps2[currentStep + 1];
|
|
33634
|
+
if (step?.waitForNavigation && !isWaitingForNavigation) {
|
|
33635
|
+
setIsWaitingForNavigation(true);
|
|
33636
|
+
return;
|
|
33637
|
+
}
|
|
33638
|
+
if (currentStep < onboardingSteps2.length - 1) {
|
|
33639
|
+
setCurrentStep(currentStep + 1);
|
|
33640
|
+
if (nextStep?.target) {
|
|
33641
|
+
setTimeout(() => {
|
|
33642
|
+
const element = document.querySelector(nextStep.target);
|
|
33643
|
+
if (element) {
|
|
33644
|
+
element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
33645
|
+
}
|
|
33646
|
+
}, 100);
|
|
33647
|
+
}
|
|
33648
|
+
} else {
|
|
33649
|
+
onComplete();
|
|
33650
|
+
}
|
|
33651
|
+
}, [currentStep, step, isWaitingForNavigation, onComplete]);
|
|
33652
|
+
const handlePrevious = React19.useCallback(() => {
|
|
33653
|
+
if (currentStep > 0) {
|
|
33654
|
+
setCurrentStep(currentStep - 1);
|
|
33655
|
+
setIsWaitingForNavigation(false);
|
|
33656
|
+
}
|
|
33657
|
+
}, [currentStep]);
|
|
33658
|
+
const getPositionStyles = React19.useCallback((position) => {
|
|
33659
|
+
const baseStyles = {
|
|
33660
|
+
position: "fixed",
|
|
33661
|
+
zIndex: 1e4
|
|
33662
|
+
};
|
|
33663
|
+
if (step?.target && position !== "center") {
|
|
33664
|
+
const element = document.querySelector(step.target);
|
|
33665
|
+
if (element) {
|
|
33666
|
+
const rect = element.getBoundingClientRect();
|
|
33667
|
+
const tooltipWidth = 400;
|
|
33668
|
+
const tooltipHeight = 200;
|
|
33669
|
+
const offset = 20;
|
|
33670
|
+
switch (position) {
|
|
33671
|
+
case "top":
|
|
33672
|
+
return {
|
|
33673
|
+
...baseStyles,
|
|
33674
|
+
left: `${rect.left + rect.width / 2 - tooltipWidth / 2}px`,
|
|
33675
|
+
bottom: `${window.innerHeight - rect.top + offset}px`
|
|
33676
|
+
};
|
|
33677
|
+
case "bottom":
|
|
33678
|
+
return {
|
|
33679
|
+
...baseStyles,
|
|
33680
|
+
left: `${rect.left + rect.width / 2 - tooltipWidth / 2}px`,
|
|
33681
|
+
top: `${rect.bottom + offset}px`
|
|
33682
|
+
};
|
|
33683
|
+
case "left":
|
|
33684
|
+
return {
|
|
33685
|
+
...baseStyles,
|
|
33686
|
+
right: `${window.innerWidth - rect.left + offset}px`,
|
|
33687
|
+
top: `${rect.top + rect.height / 2 - tooltipHeight / 2}px`
|
|
33688
|
+
};
|
|
33689
|
+
case "right":
|
|
33690
|
+
return {
|
|
33691
|
+
...baseStyles,
|
|
33692
|
+
left: `${rect.right + offset}px`,
|
|
33693
|
+
top: `${rect.top + rect.height / 2 - tooltipHeight / 2}px`
|
|
33694
|
+
};
|
|
33695
|
+
}
|
|
33696
|
+
}
|
|
33697
|
+
}
|
|
33698
|
+
return {
|
|
33699
|
+
...baseStyles,
|
|
33700
|
+
top: "50%",
|
|
33701
|
+
left: "50%",
|
|
33702
|
+
transform: "translate(-50%, -50%)"
|
|
33703
|
+
};
|
|
33704
|
+
}, [step]);
|
|
33705
|
+
if (!isOpen || !step) return null;
|
|
33706
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33707
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33708
|
+
motion.div,
|
|
33709
|
+
{
|
|
33710
|
+
initial: { opacity: 0 },
|
|
33711
|
+
animate: { opacity: 1 },
|
|
33712
|
+
exit: { opacity: 0 },
|
|
33713
|
+
className: "fixed inset-0 bg-black/60 backdrop-blur-sm z-[9998]",
|
|
33714
|
+
onClick: (e) => e.stopPropagation()
|
|
33715
|
+
}
|
|
33716
|
+
),
|
|
33717
|
+
step.highlightTarget && step.target && /* @__PURE__ */ jsxRuntime.jsx(HighlightOverlay, { target: step.target }),
|
|
33718
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33719
|
+
motion.div,
|
|
33720
|
+
{
|
|
33721
|
+
initial: { opacity: 0, scale: 0.9 },
|
|
33722
|
+
animate: { opacity: 1, scale: 1 },
|
|
33723
|
+
exit: { opacity: 0, scale: 0.9 },
|
|
33724
|
+
transition: { duration: 0.3, ease: "easeOut" },
|
|
33725
|
+
style: getPositionStyles(step.position),
|
|
33726
|
+
className: "w-[400px] max-w-[90vw]",
|
|
33727
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white dark:bg-gray-800 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
|
|
33728
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between", children: [
|
|
33729
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
33730
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: [...Array(onboardingSteps2.length)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
33731
|
+
"div",
|
|
33732
|
+
{
|
|
33733
|
+
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`
|
|
33734
|
+
},
|
|
33735
|
+
i
|
|
33736
|
+
)) }),
|
|
33737
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
33738
|
+
currentStep + 1,
|
|
33739
|
+
" / ",
|
|
33740
|
+
onboardingSteps2.length
|
|
33741
|
+
] })
|
|
33742
|
+
] }),
|
|
33743
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33744
|
+
"button",
|
|
33745
|
+
{
|
|
33746
|
+
onClick: onSkip,
|
|
33747
|
+
className: "text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors",
|
|
33748
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
|
|
33749
|
+
}
|
|
33750
|
+
)
|
|
33751
|
+
] }),
|
|
33752
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-5", children: [
|
|
33753
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: step.title }),
|
|
33754
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 dark:text-gray-300 leading-relaxed", children: step.description }),
|
|
33755
|
+
isWaitingForNavigation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-blue-700 dark:text-blue-300 flex items-center gap-2", children: [
|
|
33756
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-2 h-2 bg-blue-600 rounded-full animate-pulse" }),
|
|
33757
|
+
"Waiting for you to click..."
|
|
33758
|
+
] }) })
|
|
33759
|
+
] }),
|
|
33760
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-gray-50 dark:bg-gray-900/50 flex items-center justify-between", children: [
|
|
33761
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33762
|
+
"button",
|
|
33763
|
+
{
|
|
33764
|
+
onClick: onSkip,
|
|
33765
|
+
className: "text-sm text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors",
|
|
33766
|
+
children: "Skip tour"
|
|
33767
|
+
}
|
|
33768
|
+
),
|
|
33769
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
33770
|
+
currentStep > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
33771
|
+
"button",
|
|
33772
|
+
{
|
|
33773
|
+
onClick: handlePrevious,
|
|
33774
|
+
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",
|
|
33775
|
+
children: "Previous"
|
|
33776
|
+
}
|
|
33777
|
+
),
|
|
33778
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33779
|
+
"button",
|
|
33780
|
+
{
|
|
33781
|
+
onClick: handleNext,
|
|
33782
|
+
disabled: isWaitingForNavigation,
|
|
33783
|
+
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"}`,
|
|
33784
|
+
children: currentStep === onboardingSteps2.length - 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33785
|
+
"Complete",
|
|
33786
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4" })
|
|
33787
|
+
] }) : isWaitingForNavigation ? "Waiting..." : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33788
|
+
"Next",
|
|
33789
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
|
|
33790
|
+
] })
|
|
33791
|
+
}
|
|
33792
|
+
)
|
|
33793
|
+
] })
|
|
33794
|
+
] })
|
|
33795
|
+
] })
|
|
33796
|
+
},
|
|
33797
|
+
step.id
|
|
33798
|
+
)
|
|
33799
|
+
] }) });
|
|
33800
|
+
};
|
|
33801
|
+
var HighlightOverlay = ({ target }) => {
|
|
33802
|
+
const [rect, setRect] = React19.useState(null);
|
|
33803
|
+
React19.useEffect(() => {
|
|
33804
|
+
const element = document.querySelector(target);
|
|
33805
|
+
if (element) {
|
|
33806
|
+
setRect(element.getBoundingClientRect());
|
|
33807
|
+
}
|
|
33808
|
+
}, [target]);
|
|
33809
|
+
if (!rect) return null;
|
|
33810
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
33811
|
+
motion.div,
|
|
33812
|
+
{
|
|
33813
|
+
initial: { opacity: 0 },
|
|
33814
|
+
animate: { opacity: 1 },
|
|
33815
|
+
exit: { opacity: 0 },
|
|
33816
|
+
className: "fixed z-[9999] pointer-events-none",
|
|
33817
|
+
style: {
|
|
33818
|
+
left: rect.left - 8,
|
|
33819
|
+
top: rect.top - 8,
|
|
33820
|
+
width: rect.width + 16,
|
|
33821
|
+
height: rect.height + 16
|
|
33822
|
+
},
|
|
33823
|
+
children: [
|
|
33824
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg ring-4 ring-blue-500/50 ring-offset-4 ring-offset-transparent animate-pulse" }),
|
|
33825
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-lg bg-blue-500/10" })
|
|
33826
|
+
]
|
|
33827
|
+
}
|
|
33828
|
+
);
|
|
33829
|
+
};
|
|
33830
|
+
var OnboardingDemo = () => {
|
|
33831
|
+
const [showTour, setShowTour] = React19.useState(false);
|
|
33832
|
+
const handleComplete = () => {
|
|
33833
|
+
setShowTour(false);
|
|
33834
|
+
console.log("Onboarding tour completed");
|
|
33835
|
+
};
|
|
33836
|
+
const handleSkip = () => {
|
|
33837
|
+
setShowTour(false);
|
|
33838
|
+
console.log("Onboarding tour skipped");
|
|
33839
|
+
};
|
|
33840
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
33841
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
33842
|
+
"button",
|
|
33843
|
+
{
|
|
33844
|
+
onClick: () => setShowTour(true),
|
|
33845
|
+
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",
|
|
33846
|
+
title: "Start Onboarding Tour",
|
|
33847
|
+
children: [
|
|
33848
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "w-4 h-4" }),
|
|
33849
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Start Tour" })
|
|
33850
|
+
]
|
|
33851
|
+
}
|
|
33852
|
+
),
|
|
33853
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
33854
|
+
InteractiveOnboardingTour,
|
|
33855
|
+
{
|
|
33856
|
+
isOpen: showTour,
|
|
33857
|
+
onComplete: handleComplete,
|
|
33858
|
+
onSkip: handleSkip
|
|
33859
|
+
}
|
|
33860
|
+
)
|
|
33861
|
+
] });
|
|
33862
|
+
};
|
|
31239
33863
|
var ThreadSidebar = ({
|
|
31240
33864
|
activeThreadId,
|
|
31241
33865
|
onSelectThread,
|
|
@@ -34442,6 +37066,15 @@ var QualityOverview = React19.memo(({ lineInfo }) => {
|
|
|
34442
37066
|
] });
|
|
34443
37067
|
});
|
|
34444
37068
|
QualityOverview.displayName = "QualityOverview";
|
|
37069
|
+
var getSupervisorName = (lineId) => {
|
|
37070
|
+
const supervisorMapping = {
|
|
37071
|
+
// Add more mappings as needed
|
|
37072
|
+
"default": "Vivaan Baid",
|
|
37073
|
+
"factory": "Vivaan Baid"
|
|
37074
|
+
// You can add specific line IDs here when you have real data
|
|
37075
|
+
};
|
|
37076
|
+
return supervisorMapping[lineId || "default"] || "Vivaan Baid";
|
|
37077
|
+
};
|
|
34445
37078
|
var KPIDetailView = ({
|
|
34446
37079
|
lineId,
|
|
34447
37080
|
date: urlDate,
|
|
@@ -34931,9 +37564,15 @@ var KPIDetailView = ({
|
|
|
34931
37564
|
"aria-label": "Navigate back to previous page"
|
|
34932
37565
|
}
|
|
34933
37566
|
) }),
|
|
34934
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center mt-2 sm:mt-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-
|
|
34935
|
-
/* @__PURE__ */ jsxRuntime.
|
|
34936
|
-
|
|
37567
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex justify-center mt-2 sm:mt-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
37568
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 sm:gap-3", children: [
|
|
37569
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
37570
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
37571
|
+
] }),
|
|
37572
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-gray-600 font-medium", children: [
|
|
37573
|
+
"Supervisor: ",
|
|
37574
|
+
getSupervisorName(lineInfo?.line_id)
|
|
37575
|
+
] })
|
|
34937
37576
|
] }) })
|
|
34938
37577
|
] }),
|
|
34939
37578
|
(activeTab !== "monthly_history" || urlDate || urlShift) && metrics2 && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2 sm:gap-3 md:gap-4", children: [
|
|
@@ -35141,8 +37780,11 @@ var LineCard = ({ line, onClick }) => {
|
|
|
35141
37780
|
onClick,
|
|
35142
37781
|
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",
|
|
35143
37782
|
children: [
|
|
35144
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-
|
|
35145
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37783
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
37784
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
37785
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg sm:text-xl font-semibold text-gray-900 truncate", children: line.line_name }),
|
|
37786
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600 mt-1", children: "Supervisor: Vivaan" })
|
|
37787
|
+
] }),
|
|
35146
37788
|
kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.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: [
|
|
35147
37789
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
|
|
35148
37790
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
|
|
@@ -35844,9 +38486,6 @@ var ProfileView = () => {
|
|
|
35844
38486
|
id: user.id,
|
|
35845
38487
|
email: user.email,
|
|
35846
38488
|
full_name: profileData.full_name,
|
|
35847
|
-
company: profileData.company,
|
|
35848
|
-
phone: profileData.phone,
|
|
35849
|
-
timezone: profileData.timezone,
|
|
35850
38489
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
35851
38490
|
});
|
|
35852
38491
|
if (error2) throw error2;
|
|
@@ -35995,55 +38634,6 @@ var ProfileView = () => {
|
|
|
35995
38634
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900", children: profileData.email }),
|
|
35996
38635
|
profileData.email_verified && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserCheck, { className: "h-4 w-4 text-green-500" })
|
|
35997
38636
|
] })
|
|
35998
|
-
] }),
|
|
35999
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
36000
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Company" }),
|
|
36001
|
-
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
36002
|
-
"input",
|
|
36003
|
-
{
|
|
36004
|
-
type: "text",
|
|
36005
|
-
value: profileData.company || "",
|
|
36006
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, company: e.target.value })),
|
|
36007
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
36008
|
-
}
|
|
36009
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900", children: profileData.company || "Not set" })
|
|
36010
|
-
] }),
|
|
36011
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
36012
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Role" }),
|
|
36013
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900", children: profileData.role || "Not set" })
|
|
36014
|
-
] }),
|
|
36015
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
36016
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Phone" }),
|
|
36017
|
-
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
36018
|
-
"input",
|
|
36019
|
-
{
|
|
36020
|
-
type: "tel",
|
|
36021
|
-
value: profileData.phone || "",
|
|
36022
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, phone: e.target.value })),
|
|
36023
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
36024
|
-
}
|
|
36025
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900", children: profileData.phone || "Not set" })
|
|
36026
|
-
] }),
|
|
36027
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
36028
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Timezone" }),
|
|
36029
|
-
isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36030
|
-
"select",
|
|
36031
|
-
{
|
|
36032
|
-
value: profileData.timezone || "",
|
|
36033
|
-
onChange: (e) => setProfileData((prev) => ({ ...prev, timezone: e.target.value })),
|
|
36034
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
36035
|
-
children: [
|
|
36036
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select timezone" }),
|
|
36037
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "UTC", children: "UTC" }),
|
|
36038
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "America/New_York", children: "Eastern Time" }),
|
|
36039
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "America/Chicago", children: "Central Time" }),
|
|
36040
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "America/Denver", children: "Mountain Time" }),
|
|
36041
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "America/Los_Angeles", children: "Pacific Time" }),
|
|
36042
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "Asia/Kolkata", children: "India Standard Time" }),
|
|
36043
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "Europe/London", children: "GMT" })
|
|
36044
|
-
]
|
|
36045
|
-
}
|
|
36046
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900", children: profileData.timezone || "Not set" })
|
|
36047
38637
|
] })
|
|
36048
38638
|
] }),
|
|
36049
38639
|
isEditing && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 flex gap-3", children: /* @__PURE__ */ jsxRuntime.jsx(Button2, { variant: "outline", onClick: handleSaveProfile, disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -38626,10 +41216,6 @@ var TargetsView = ({
|
|
|
38626
41216
|
}, [actionIds]);
|
|
38627
41217
|
const handleSaveLine = React19.useCallback(async (lineId) => {
|
|
38628
41218
|
console.log(`[handleSaveLine] Attempting to save line: ${lineId}`);
|
|
38629
|
-
if (!canSaveTargets) {
|
|
38630
|
-
sonner.toast.error("You do not have permission to save targets. Contact your administrator.");
|
|
38631
|
-
return;
|
|
38632
|
-
}
|
|
38633
41219
|
const hardcodedUserId = "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
38634
41220
|
const currentEffectiveUserId = hardcodedUserId;
|
|
38635
41221
|
console.log(`[handleSaveLine] effectiveUserId: ${currentEffectiveUserId}`);
|
|
@@ -38906,6 +41492,13 @@ var WorkspaceDetailView = ({
|
|
|
38906
41492
|
const [selectedMonth, setSelectedMonth] = React19.useState(today.getMonth());
|
|
38907
41493
|
const [selectedYear, setSelectedYear] = React19.useState(today.getFullYear());
|
|
38908
41494
|
const [selectedShift, setSelectedShift] = React19.useState("day");
|
|
41495
|
+
React19.useEffect(() => {
|
|
41496
|
+
if (parsedShiftId === 1) {
|
|
41497
|
+
setSelectedShift("night");
|
|
41498
|
+
} else if (parsedShiftId === 0) {
|
|
41499
|
+
setSelectedShift("day");
|
|
41500
|
+
}
|
|
41501
|
+
}, [parsedShiftId]);
|
|
38909
41502
|
const isHistoricView = Boolean(date && parsedShiftId !== void 0);
|
|
38910
41503
|
const initialTab = getInitialTab(sourceType, defaultTab, fromMonthly, date);
|
|
38911
41504
|
const [activeTab, setActiveTab] = React19.useState(initialTab);
|
|
@@ -39155,11 +41748,18 @@ var WorkspaceDetailView = ({
|
|
|
39155
41748
|
}
|
|
39156
41749
|
return;
|
|
39157
41750
|
}
|
|
41751
|
+
if (activeTab === "monthly_history") {
|
|
41752
|
+
if (onNavigate) {
|
|
41753
|
+
onNavigate("/");
|
|
41754
|
+
}
|
|
41755
|
+
return;
|
|
41756
|
+
}
|
|
39158
41757
|
if (date || shift) {
|
|
39159
41758
|
setActiveTab("monthly_history");
|
|
39160
41759
|
if (onNavigate) {
|
|
39161
41760
|
const params = new URLSearchParams();
|
|
39162
41761
|
params.set("fromMonthly", "true");
|
|
41762
|
+
params.set("shift", selectedShift === "night" ? "1" : "0");
|
|
39163
41763
|
if (effectiveLineId) {
|
|
39164
41764
|
params.set("lineId", effectiveLineId);
|
|
39165
41765
|
}
|
|
@@ -39171,6 +41771,7 @@ var WorkspaceDetailView = ({
|
|
|
39171
41771
|
if (onNavigate) {
|
|
39172
41772
|
const params = new URLSearchParams();
|
|
39173
41773
|
params.set("fromMonthly", "true");
|
|
41774
|
+
params.set("shift", selectedShift === "night" ? "1" : "0");
|
|
39174
41775
|
if (effectiveLineId) {
|
|
39175
41776
|
params.set("lineId", effectiveLineId);
|
|
39176
41777
|
}
|
|
@@ -39269,7 +41870,7 @@ var WorkspaceDetailView = ({
|
|
|
39269
41870
|
BackButtonMinimal,
|
|
39270
41871
|
{
|
|
39271
41872
|
onClick: handleBackNavigation,
|
|
39272
|
-
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",
|
|
41873
|
+
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",
|
|
39273
41874
|
size: "sm",
|
|
39274
41875
|
"aria-label": "Navigate back to previous page"
|
|
39275
41876
|
}
|
|
@@ -39295,7 +41896,7 @@ var WorkspaceDetailView = ({
|
|
|
39295
41896
|
BackButtonMinimal,
|
|
39296
41897
|
{
|
|
39297
41898
|
onClick: handleBackNavigation,
|
|
39298
|
-
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",
|
|
41899
|
+
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",
|
|
39299
41900
|
size: "default",
|
|
39300
41901
|
"aria-label": "Navigate back to previous page"
|
|
39301
41902
|
}
|
|
@@ -39683,6 +42284,7 @@ var WorkspaceDetailView = ({
|
|
|
39683
42284
|
const params = new URLSearchParams();
|
|
39684
42285
|
params.set("date", selectedDate);
|
|
39685
42286
|
params.set("shift", selectedShift === "day" ? "0" : "1");
|
|
42287
|
+
params.set("fromMonthly", "true");
|
|
39686
42288
|
if (effectiveLineId) {
|
|
39687
42289
|
params.set("lineId", effectiveLineId);
|
|
39688
42290
|
}
|
|
@@ -40166,6 +42768,168 @@ var WorkspaceHealthView_default = withAuth(WorkspaceHealthView, {
|
|
|
40166
42768
|
requireAuth: true
|
|
40167
42769
|
});
|
|
40168
42770
|
var AuthenticatedWorkspaceHealthView = WorkspaceHealthView;
|
|
42771
|
+
var SupervisorManagementView = ({
|
|
42772
|
+
onNavigate,
|
|
42773
|
+
onBack,
|
|
42774
|
+
className = ""
|
|
42775
|
+
}) => {
|
|
42776
|
+
const supabase = useSupabaseClient();
|
|
42777
|
+
const entityConfig = useEntityConfig();
|
|
42778
|
+
const companyId = entityConfig?.companyId;
|
|
42779
|
+
const [data, setData] = React19.useState({
|
|
42780
|
+
assignments: [],
|
|
42781
|
+
allSupervisors: [],
|
|
42782
|
+
loading: true,
|
|
42783
|
+
error: void 0
|
|
42784
|
+
});
|
|
42785
|
+
const [savingAssignments, setSavingAssignments] = React19.useState(/* @__PURE__ */ new Set());
|
|
42786
|
+
const loadData = React19.useCallback(async () => {
|
|
42787
|
+
try {
|
|
42788
|
+
setData((prev) => ({ ...prev, loading: true, error: void 0 }));
|
|
42789
|
+
if (!companyId) {
|
|
42790
|
+
throw new Error("Company ID is not configured");
|
|
42791
|
+
}
|
|
42792
|
+
if (!supabase) {
|
|
42793
|
+
throw new Error("Supabase client is not available");
|
|
42794
|
+
}
|
|
42795
|
+
console.log(`[SupervisorManagementView] Loading data for companyId: ${companyId}`);
|
|
42796
|
+
const supervisorService = createSupervisorService(supabase);
|
|
42797
|
+
const realData = await supervisorService.getSupervisorManagementData(companyId);
|
|
42798
|
+
console.log(`[SupervisorManagementView] Received data:`, {
|
|
42799
|
+
assignments: realData.assignments.length,
|
|
42800
|
+
allSupervisors: realData.allSupervisors.length,
|
|
42801
|
+
firstAssignmentAvailableSupervisors: realData.assignments[0]?.availableSupervisors?.length || 0,
|
|
42802
|
+
firstAssignmentAvailableSupervisorIds: realData.assignments[0]?.availableSupervisors?.map((s) => s.id) || []
|
|
42803
|
+
});
|
|
42804
|
+
setData(realData);
|
|
42805
|
+
} catch (error) {
|
|
42806
|
+
console.error("Error loading supervisor management data:", error);
|
|
42807
|
+
setData((prev) => ({
|
|
42808
|
+
...prev,
|
|
42809
|
+
loading: false,
|
|
42810
|
+
error: error instanceof Error ? error.message : "Failed to load supervisor management data. Please try again."
|
|
42811
|
+
}));
|
|
42812
|
+
}
|
|
42813
|
+
}, [supabase, companyId]);
|
|
42814
|
+
React19.useEffect(() => {
|
|
42815
|
+
loadData();
|
|
42816
|
+
}, [loadData]);
|
|
42817
|
+
const handleSupervisorChange = React19.useCallback(async (lineId, supervisor) => {
|
|
42818
|
+
try {
|
|
42819
|
+
setSavingAssignments((prev) => /* @__PURE__ */ new Set([...prev, lineId]));
|
|
42820
|
+
if (!supabase) {
|
|
42821
|
+
throw new Error("Supabase client is not available");
|
|
42822
|
+
}
|
|
42823
|
+
const supervisorService = createSupervisorService(supabase);
|
|
42824
|
+
const success = await supervisorService.assignSupervisorToLine(lineId, supervisor?.id);
|
|
42825
|
+
if (!success) {
|
|
42826
|
+
throw new Error("Failed to update supervisor assignment");
|
|
42827
|
+
}
|
|
42828
|
+
setData((prev) => ({
|
|
42829
|
+
...prev,
|
|
42830
|
+
assignments: prev.assignments.map(
|
|
42831
|
+
(assignment) => assignment.lineId === lineId ? { ...assignment, currentSupervisor: supervisor || void 0 } : assignment
|
|
42832
|
+
)
|
|
42833
|
+
}));
|
|
42834
|
+
const lineName = data.assignments.find((a) => a.lineId === lineId)?.lineName || "Line";
|
|
42835
|
+
const message = supervisor ? `${supervisor.name} assigned to ${lineName}` : `Supervisor removed from ${lineName}`;
|
|
42836
|
+
sonner.toast.success(message);
|
|
42837
|
+
} catch (error) {
|
|
42838
|
+
console.error("Error updating supervisor assignment:", error);
|
|
42839
|
+
sonner.toast.error("Failed to update supervisor assignment. Please try again.");
|
|
42840
|
+
} finally {
|
|
42841
|
+
setSavingAssignments((prev) => {
|
|
42842
|
+
const newSet = new Set(prev);
|
|
42843
|
+
newSet.delete(lineId);
|
|
42844
|
+
return newSet;
|
|
42845
|
+
});
|
|
42846
|
+
}
|
|
42847
|
+
}, [data.assignments, supabase]);
|
|
42848
|
+
const handleRefresh = React19.useCallback(() => {
|
|
42849
|
+
loadData();
|
|
42850
|
+
}, [loadData]);
|
|
42851
|
+
const handleBack = React19.useCallback(() => {
|
|
42852
|
+
if (onBack) {
|
|
42853
|
+
onBack();
|
|
42854
|
+
} else if (onNavigate) {
|
|
42855
|
+
onNavigate("/");
|
|
42856
|
+
}
|
|
42857
|
+
}, [onBack, onNavigate]);
|
|
42858
|
+
if (data.loading) {
|
|
42859
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("min-h-screen bg-slate-50", className), children: /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: "Loading supervisor management..." }) });
|
|
42860
|
+
}
|
|
42861
|
+
if (data.error) {
|
|
42862
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("min-h-screen bg-slate-50", className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42863
|
+
EmptyStateMessage,
|
|
42864
|
+
{
|
|
42865
|
+
iconType: lucideReact.AlertCircle,
|
|
42866
|
+
title: "Failed to Load Data",
|
|
42867
|
+
message: data.error,
|
|
42868
|
+
actionButton: {
|
|
42869
|
+
text: "Try Again",
|
|
42870
|
+
onClick: handleRefresh,
|
|
42871
|
+
className: "bg-blue-600 hover:bg-blue-700"
|
|
42872
|
+
}
|
|
42873
|
+
}
|
|
42874
|
+
) }) });
|
|
42875
|
+
}
|
|
42876
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: clsx("min-h-screen bg-slate-50", className), children: [
|
|
42877
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center relative", children: [
|
|
42878
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:absolute sm:left-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42879
|
+
BackButtonMinimal,
|
|
42880
|
+
{
|
|
42881
|
+
onClick: handleBack,
|
|
42882
|
+
text: "Back",
|
|
42883
|
+
size: "default",
|
|
42884
|
+
"aria-label": "Navigate back to previous page"
|
|
42885
|
+
}
|
|
42886
|
+
) }),
|
|
42887
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center mt-2 sm:mt-0", children: [
|
|
42888
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg sm:text-xl md:text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Supervisor Management" }),
|
|
42889
|
+
/* @__PURE__ */ jsxRuntime.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" })
|
|
42890
|
+
] }),
|
|
42891
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block absolute right-0 w-24", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42892
|
+
"button",
|
|
42893
|
+
{
|
|
42894
|
+
onClick: handleRefresh,
|
|
42895
|
+
className: "p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors",
|
|
42896
|
+
"aria-label": "Refresh data",
|
|
42897
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-5 w-5" })
|
|
42898
|
+
}
|
|
42899
|
+
) }) })
|
|
42900
|
+
] }) }) }),
|
|
42901
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[400px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42902
|
+
EmptyStateMessage,
|
|
42903
|
+
{
|
|
42904
|
+
iconType: lucideReact.Building2,
|
|
42905
|
+
title: "No Lines Found",
|
|
42906
|
+
message: "There are no production lines to manage supervisor assignments for."
|
|
42907
|
+
}
|
|
42908
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
|
|
42909
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
42910
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Production Line" }),
|
|
42911
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assigned Supervisor" })
|
|
42912
|
+
] }) }),
|
|
42913
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: data.assignments.map((assignment) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "hover:bg-gray-50", children: [
|
|
42914
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
42915
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Building2, { className: "h-4 w-4 text-gray-400 mr-2" }),
|
|
42916
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-900", children: assignment.lineName })
|
|
42917
|
+
] }) }),
|
|
42918
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xs", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42919
|
+
SupervisorDropdown,
|
|
42920
|
+
{
|
|
42921
|
+
selectedSupervisor: assignment.currentSupervisor,
|
|
42922
|
+
availableSupervisors: assignment.availableSupervisors,
|
|
42923
|
+
onSelect: (supervisor) => handleSupervisorChange(assignment.lineId, supervisor),
|
|
42924
|
+
disabled: savingAssignments.has(assignment.lineId),
|
|
42925
|
+
placeholder: "Select supervisor..."
|
|
42926
|
+
}
|
|
42927
|
+
) }) })
|
|
42928
|
+
] }, assignment.lineId)) })
|
|
42929
|
+
] }) }) }) }) })
|
|
42930
|
+
] });
|
|
42931
|
+
};
|
|
42932
|
+
var SupervisorManagementView_default = SupervisorManagementView;
|
|
40169
42933
|
var S3Service = class {
|
|
40170
42934
|
constructor(config) {
|
|
40171
42935
|
this.s3Client = null;
|
|
@@ -40648,6 +43412,7 @@ exports.CardHeader = CardHeader2;
|
|
|
40648
43412
|
exports.CardTitle = CardTitle2;
|
|
40649
43413
|
exports.CompactWorkspaceHealthCard = CompactWorkspaceHealthCard;
|
|
40650
43414
|
exports.CongratulationsOverlay = CongratulationsOverlay;
|
|
43415
|
+
exports.CroppedVideoPlayer = CroppedVideoPlayer;
|
|
40651
43416
|
exports.CycleTimeChart = CycleTimeChart;
|
|
40652
43417
|
exports.CycleTimeOverTimeChart = CycleTimeOverTimeChart;
|
|
40653
43418
|
exports.DEFAULT_ANALYTICS_CONFIG = DEFAULT_ANALYTICS_CONFIG;
|
|
@@ -40674,6 +43439,8 @@ exports.DetailedHealthStatus = DetailedHealthStatus;
|
|
|
40674
43439
|
exports.EmptyStateMessage = EmptyStateMessage;
|
|
40675
43440
|
exports.EncouragementOverlay = EncouragementOverlay;
|
|
40676
43441
|
exports.FactoryView = FactoryView_default;
|
|
43442
|
+
exports.FirstTimeLoginDebug = FirstTimeLoginDebug;
|
|
43443
|
+
exports.FirstTimeLoginHandler = FirstTimeLoginHandler;
|
|
40677
43444
|
exports.GaugeChart = GaugeChart;
|
|
40678
43445
|
exports.GridComponentsPlaceholder = GridComponentsPlaceholder;
|
|
40679
43446
|
exports.HamburgerButton = HamburgerButton;
|
|
@@ -40685,6 +43452,7 @@ exports.HomeView = HomeView_default;
|
|
|
40685
43452
|
exports.HourlyOutputChart = HourlyOutputChart2;
|
|
40686
43453
|
exports.ISTTimer = ISTTimer_default;
|
|
40687
43454
|
exports.InlineEditableText = InlineEditableText;
|
|
43455
|
+
exports.InteractiveOnboardingTour = InteractiveOnboardingTour;
|
|
40688
43456
|
exports.KPICard = KPICard;
|
|
40689
43457
|
exports.KPIDetailView = KPIDetailView_default;
|
|
40690
43458
|
exports.KPIGrid = KPIGrid;
|
|
@@ -40703,6 +43471,7 @@ exports.LineMonthlyPdfGenerator = LineMonthlyPdfGenerator;
|
|
|
40703
43471
|
exports.LinePdfExportButton = LinePdfExportButton;
|
|
40704
43472
|
exports.LinePdfGenerator = LinePdfGenerator;
|
|
40705
43473
|
exports.LineWhatsAppShareButton = LineWhatsAppShareButton;
|
|
43474
|
+
exports.LinesService = LinesService;
|
|
40706
43475
|
exports.LiveTimer = LiveTimer;
|
|
40707
43476
|
exports.LoadingInline = LoadingInline;
|
|
40708
43477
|
exports.LoadingOverlay = LoadingOverlay_default;
|
|
@@ -40713,7 +43482,10 @@ exports.LoginPage = LoginPage;
|
|
|
40713
43482
|
exports.LoginView = LoginView_default;
|
|
40714
43483
|
exports.MainLayout = MainLayout;
|
|
40715
43484
|
exports.MetricCard = MetricCard_default;
|
|
43485
|
+
exports.MinimalOnboardingPopup = MinimalOnboardingPopup;
|
|
40716
43486
|
exports.NoWorkspaceData = NoWorkspaceData;
|
|
43487
|
+
exports.OnboardingDemo = OnboardingDemo;
|
|
43488
|
+
exports.OnboardingTour = OnboardingTour;
|
|
40717
43489
|
exports.OptifyeAgentClient = OptifyeAgentClient;
|
|
40718
43490
|
exports.OptifyeLogoLoader = OptifyeLogoLoader_default;
|
|
40719
43491
|
exports.OutputProgressChart = OutputProgressChart;
|
|
@@ -40744,11 +43516,15 @@ exports.SelectValue = SelectValue;
|
|
|
40744
43516
|
exports.ShiftDisplay = ShiftDisplay_default;
|
|
40745
43517
|
exports.ShiftsView = ShiftsView_default;
|
|
40746
43518
|
exports.SideNavBar = SideNavBar;
|
|
43519
|
+
exports.SimpleOnboardingPopup = SimpleOnboardingPopup;
|
|
40747
43520
|
exports.SingleVideoStream = SingleVideoStream_default;
|
|
40748
43521
|
exports.Skeleton = Skeleton;
|
|
40749
43522
|
exports.SubscriptionManager = SubscriptionManager;
|
|
40750
43523
|
exports.SubscriptionManagerProvider = SubscriptionManagerProvider;
|
|
40751
43524
|
exports.SupabaseProvider = SupabaseProvider;
|
|
43525
|
+
exports.SupervisorDropdown = SupervisorDropdown_default;
|
|
43526
|
+
exports.SupervisorManagementView = SupervisorManagementView_default;
|
|
43527
|
+
exports.SupervisorService = SupervisorService;
|
|
40752
43528
|
exports.TargetWorkspaceGrid = TargetWorkspaceGrid;
|
|
40753
43529
|
exports.TargetsView = TargetsView_default;
|
|
40754
43530
|
exports.ThreadSidebar = ThreadSidebar;
|
|
@@ -40756,6 +43532,7 @@ exports.TicketHistory = TicketHistory_default;
|
|
|
40756
43532
|
exports.TicketHistoryService = TicketHistoryService;
|
|
40757
43533
|
exports.TimeDisplay = TimeDisplay_default;
|
|
40758
43534
|
exports.TimePickerDropdown = TimePickerDropdown;
|
|
43535
|
+
exports.UserService = UserService;
|
|
40759
43536
|
exports.VideoCard = VideoCard;
|
|
40760
43537
|
exports.VideoGridView = VideoGridView;
|
|
40761
43538
|
exports.VideoPlayer = VideoPlayer;
|
|
@@ -40789,9 +43566,12 @@ exports.clearS3VideoCache = clearS3VideoCache;
|
|
|
40789
43566
|
exports.clearS3VideoFromCache = clearS3VideoFromCache;
|
|
40790
43567
|
exports.clearWorkspaceDisplayNamesCache = clearWorkspaceDisplayNamesCache;
|
|
40791
43568
|
exports.cn = cn;
|
|
43569
|
+
exports.createLinesService = createLinesService;
|
|
40792
43570
|
exports.createStreamProxyHandler = createStreamProxyHandler;
|
|
40793
43571
|
exports.createSupabaseClient = createSupabaseClient;
|
|
43572
|
+
exports.createSupervisorService = createSupervisorService;
|
|
40794
43573
|
exports.createThrottledReload = createThrottledReload;
|
|
43574
|
+
exports.createUserService = createUserService;
|
|
40795
43575
|
exports.dashboardService = dashboardService;
|
|
40796
43576
|
exports.deleteThread = deleteThread;
|
|
40797
43577
|
exports.forceRefreshWorkspaceDisplayNames = forceRefreshWorkspaceDisplayNames;
|
|
@@ -40851,6 +43631,7 @@ exports.isValidWorkspaceDetailedMetricsPayload = isValidWorkspaceDetailedMetrics
|
|
|
40851
43631
|
exports.isValidWorkspaceMetricsPayload = isValidWorkspaceMetricsPayload;
|
|
40852
43632
|
exports.isWorkspaceDisplayNamesLoaded = isWorkspaceDisplayNamesLoaded;
|
|
40853
43633
|
exports.isWorkspaceDisplayNamesLoading = isWorkspaceDisplayNamesLoading;
|
|
43634
|
+
exports.linesService = linesService;
|
|
40854
43635
|
exports.mergeWithDefaultConfig = mergeWithDefaultConfig;
|
|
40855
43636
|
exports.migrateLegacyConfiguration = migrateLegacyConfiguration;
|
|
40856
43637
|
exports.optifyeAgentClient = optifyeAgentClient;
|
|
@@ -40869,6 +43650,7 @@ exports.resetFailedUrl = resetFailedUrl;
|
|
|
40869
43650
|
exports.resetSubscriptionManager = resetSubscriptionManager;
|
|
40870
43651
|
exports.s3VideoPreloader = s3VideoPreloader;
|
|
40871
43652
|
exports.shuffleArray = shuffleArray;
|
|
43653
|
+
exports.simulateApiDelay = simulateApiDelay;
|
|
40872
43654
|
exports.skuService = skuService;
|
|
40873
43655
|
exports.startCoreSessionRecording = startCoreSessionRecording;
|
|
40874
43656
|
exports.stopCoreSessionRecording = stopCoreSessionRecording;
|
|
@@ -40879,6 +43661,7 @@ exports.toUrlFriendlyName = toUrlFriendlyName;
|
|
|
40879
43661
|
exports.trackCoreEvent = trackCoreEvent;
|
|
40880
43662
|
exports.trackCorePageView = trackCorePageView;
|
|
40881
43663
|
exports.updateThreadTitle = updateThreadTitle;
|
|
43664
|
+
exports.useAccessControl = useAccessControl;
|
|
40882
43665
|
exports.useActiveBreaks = useActiveBreaks;
|
|
40883
43666
|
exports.useAllWorkspaceMetrics = useAllWorkspaceMetrics;
|
|
40884
43667
|
exports.useAnalyticsConfig = useAnalyticsConfig;
|
|
@@ -40886,6 +43669,8 @@ exports.useAudioService = useAudioService;
|
|
|
40886
43669
|
exports.useAuth = useAuth;
|
|
40887
43670
|
exports.useAuthConfig = useAuthConfig;
|
|
40888
43671
|
exports.useCanSaveTargets = useCanSaveTargets;
|
|
43672
|
+
exports.useClipTypes = useClipTypes;
|
|
43673
|
+
exports.useClipTypesWithCounts = useClipTypesWithCounts;
|
|
40889
43674
|
exports.useComponentOverride = useComponentOverride;
|
|
40890
43675
|
exports.useCustomConfig = useCustomConfig;
|
|
40891
43676
|
exports.useDashboardConfig = useDashboardConfig;
|
|
@@ -40941,9 +43726,11 @@ exports.useWorkspaceHealthById = useWorkspaceHealthById;
|
|
|
40941
43726
|
exports.useWorkspaceMetrics = useWorkspaceMetrics;
|
|
40942
43727
|
exports.useWorkspaceNavigation = useWorkspaceNavigation;
|
|
40943
43728
|
exports.useWorkspaceOperators = useWorkspaceOperators;
|
|
43729
|
+
exports.userService = userService;
|
|
40944
43730
|
exports.videoPrefetchManager = videoPrefetchManager;
|
|
40945
43731
|
exports.videoPreloader = videoPreloader;
|
|
40946
43732
|
exports.whatsappService = whatsappService;
|
|
43733
|
+
exports.withAccessControl = withAccessControl;
|
|
40947
43734
|
exports.withAuth = withAuth;
|
|
40948
43735
|
exports.withRegistry = withRegistry;
|
|
40949
43736
|
exports.workspaceHealthService = workspaceHealthService;
|