@optifye/dashboard-core 6.3.4 → 6.4.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.d.mts +137 -51
- package/dist/index.d.ts +137 -51
- package/dist/index.js +896 -1071
- package/dist/index.mjs +904 -1079
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -14,9 +14,9 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
|
|
|
14
14
|
import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
|
|
15
15
|
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
|
|
16
16
|
import { Slot } from '@radix-ui/react-slot';
|
|
17
|
-
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2,
|
|
17
|
+
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, ArrowLeft, Calendar, Save, Minus, ArrowDown, ArrowUp, ArrowLeftIcon, Settings2, CheckCircle2, Search, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, LogOut, Package, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
|
|
18
18
|
import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
|
|
19
|
-
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, Bars3Icon, ArrowLeftIcon, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
19
|
+
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, Bars3Icon, ArrowLeftIcon as ArrowLeftIcon$1, CheckCircleIcon, ChatBubbleLeftRightIcon, XCircleIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
|
20
20
|
import html2canvas from 'html2canvas';
|
|
21
21
|
import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
|
|
22
22
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
@@ -394,11 +394,11 @@ var actionService = {
|
|
|
394
394
|
};
|
|
395
395
|
function createMemoizedFunction(fn, getCacheKey) {
|
|
396
396
|
const cache = /* @__PURE__ */ new Map();
|
|
397
|
-
const
|
|
397
|
+
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
398
398
|
return (...args) => {
|
|
399
399
|
const key = getCacheKey(...args);
|
|
400
400
|
const cached = cache.get(key);
|
|
401
|
-
if (cached && Date.now() - cached.timestamp <
|
|
401
|
+
if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
|
|
402
402
|
return cached.result;
|
|
403
403
|
}
|
|
404
404
|
const result = fn(...args);
|
|
@@ -2449,199 +2449,6 @@ async function deleteThread(threadId) {
|
|
|
2449
2449
|
if (error) throw error;
|
|
2450
2450
|
}
|
|
2451
2451
|
|
|
2452
|
-
// src/lib/services/cacheService.ts
|
|
2453
|
-
var CacheService = class {
|
|
2454
|
-
constructor() {
|
|
2455
|
-
this.memoryCache = /* @__PURE__ */ new Map();
|
|
2456
|
-
this.DEFAULT_DURATION = 5 * 60 * 1e3;
|
|
2457
|
-
}
|
|
2458
|
-
// 5 minutes
|
|
2459
|
-
/**
|
|
2460
|
-
* Generate a cache key from multiple parts
|
|
2461
|
-
*/
|
|
2462
|
-
generateKey(...parts) {
|
|
2463
|
-
return parts.filter((p) => p !== void 0 && p !== null).join(":");
|
|
2464
|
-
}
|
|
2465
|
-
/**
|
|
2466
|
-
* Get item from cache
|
|
2467
|
-
*/
|
|
2468
|
-
get(key, options) {
|
|
2469
|
-
const storage = options?.storage || "memory";
|
|
2470
|
-
try {
|
|
2471
|
-
let cacheItem = null;
|
|
2472
|
-
if (storage === "memory") {
|
|
2473
|
-
cacheItem = this.memoryCache.get(key) || null;
|
|
2474
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2475
|
-
const stored = window[storage].getItem(key);
|
|
2476
|
-
if (stored) {
|
|
2477
|
-
cacheItem = JSON.parse(stored);
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
if (!cacheItem) {
|
|
2481
|
-
return null;
|
|
2482
|
-
}
|
|
2483
|
-
if (Date.now() > cacheItem.expiresAt) {
|
|
2484
|
-
this.delete(key, options);
|
|
2485
|
-
return null;
|
|
2486
|
-
}
|
|
2487
|
-
return cacheItem.data;
|
|
2488
|
-
} catch (error) {
|
|
2489
|
-
console.error(`Error getting cache item ${key}:`, error);
|
|
2490
|
-
return null;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
/**
|
|
2494
|
-
* Set item in cache
|
|
2495
|
-
*/
|
|
2496
|
-
set(key, data, options) {
|
|
2497
|
-
const storage = options?.storage || "memory";
|
|
2498
|
-
const duration = options?.duration || this.DEFAULT_DURATION;
|
|
2499
|
-
const cacheItem = {
|
|
2500
|
-
data,
|
|
2501
|
-
timestamp: Date.now(),
|
|
2502
|
-
expiresAt: Date.now() + duration
|
|
2503
|
-
};
|
|
2504
|
-
try {
|
|
2505
|
-
if (storage === "memory") {
|
|
2506
|
-
this.memoryCache.set(key, cacheItem);
|
|
2507
|
-
if (this.memoryCache.size > 100) {
|
|
2508
|
-
const firstKey = this.memoryCache.keys().next().value;
|
|
2509
|
-
if (firstKey) {
|
|
2510
|
-
this.memoryCache.delete(firstKey);
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2514
|
-
window[storage].setItem(key, JSON.stringify(cacheItem));
|
|
2515
|
-
}
|
|
2516
|
-
} catch (error) {
|
|
2517
|
-
console.error(`Error setting cache item ${key}:`, error);
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
/**
|
|
2521
|
-
* Delete item from cache
|
|
2522
|
-
*/
|
|
2523
|
-
delete(key, options) {
|
|
2524
|
-
const storage = options?.storage || "memory";
|
|
2525
|
-
try {
|
|
2526
|
-
if (storage === "memory") {
|
|
2527
|
-
this.memoryCache.delete(key);
|
|
2528
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2529
|
-
window[storage].removeItem(key);
|
|
2530
|
-
}
|
|
2531
|
-
} catch (error) {
|
|
2532
|
-
console.error(`Error deleting cache item ${key}:`, error);
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
/**
|
|
2536
|
-
* Clear all items from cache
|
|
2537
|
-
*/
|
|
2538
|
-
clear(options) {
|
|
2539
|
-
const storage = options?.storage || "memory";
|
|
2540
|
-
try {
|
|
2541
|
-
if (storage === "memory") {
|
|
2542
|
-
this.memoryCache.clear();
|
|
2543
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2544
|
-
const keys = Object.keys(window[storage]);
|
|
2545
|
-
keys.forEach((key) => {
|
|
2546
|
-
try {
|
|
2547
|
-
const item = window[storage].getItem(key);
|
|
2548
|
-
if (item) {
|
|
2549
|
-
const parsed = JSON.parse(item);
|
|
2550
|
-
if (parsed.timestamp && parsed.expiresAt && parsed.data !== void 0) {
|
|
2551
|
-
window[storage].removeItem(key);
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
} catch {
|
|
2555
|
-
}
|
|
2556
|
-
});
|
|
2557
|
-
}
|
|
2558
|
-
} catch (error) {
|
|
2559
|
-
console.error("Error clearing cache:", error);
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
/**
|
|
2563
|
-
* Get or set item in cache with a factory function
|
|
2564
|
-
*/
|
|
2565
|
-
async getOrSet(key, factory, options) {
|
|
2566
|
-
const cached = this.get(key, options);
|
|
2567
|
-
if (cached !== null) {
|
|
2568
|
-
return cached;
|
|
2569
|
-
}
|
|
2570
|
-
const data = await factory();
|
|
2571
|
-
this.set(key, data, options);
|
|
2572
|
-
return data;
|
|
2573
|
-
}
|
|
2574
|
-
/**
|
|
2575
|
-
* Invalidate cache entries matching a pattern
|
|
2576
|
-
*/
|
|
2577
|
-
invalidatePattern(pattern, options) {
|
|
2578
|
-
const storage = options?.storage || "memory";
|
|
2579
|
-
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
2580
|
-
try {
|
|
2581
|
-
if (storage === "memory") {
|
|
2582
|
-
const keysToDelete = [];
|
|
2583
|
-
this.memoryCache.forEach((_, key) => {
|
|
2584
|
-
if (regex.test(key)) {
|
|
2585
|
-
keysToDelete.push(key);
|
|
2586
|
-
}
|
|
2587
|
-
});
|
|
2588
|
-
keysToDelete.forEach((key) => this.memoryCache.delete(key));
|
|
2589
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2590
|
-
const keys = Object.keys(window[storage]);
|
|
2591
|
-
keys.forEach((key) => {
|
|
2592
|
-
if (regex.test(key)) {
|
|
2593
|
-
window[storage].removeItem(key);
|
|
2594
|
-
}
|
|
2595
|
-
});
|
|
2596
|
-
}
|
|
2597
|
-
} catch (error) {
|
|
2598
|
-
console.error("Error invalidating cache pattern:", error);
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
/**
|
|
2602
|
-
* Clean up expired items
|
|
2603
|
-
*/
|
|
2604
|
-
cleanup(options) {
|
|
2605
|
-
const storage = options?.storage || "memory";
|
|
2606
|
-
const now2 = Date.now();
|
|
2607
|
-
try {
|
|
2608
|
-
if (storage === "memory") {
|
|
2609
|
-
const keysToDelete = [];
|
|
2610
|
-
this.memoryCache.forEach((item, key) => {
|
|
2611
|
-
if (now2 > item.expiresAt) {
|
|
2612
|
-
keysToDelete.push(key);
|
|
2613
|
-
}
|
|
2614
|
-
});
|
|
2615
|
-
keysToDelete.forEach((key) => this.memoryCache.delete(key));
|
|
2616
|
-
} else if (storage === "localStorage" || storage === "sessionStorage") {
|
|
2617
|
-
const keys = Object.keys(window[storage]);
|
|
2618
|
-
keys.forEach((key) => {
|
|
2619
|
-
try {
|
|
2620
|
-
const item = window[storage].getItem(key);
|
|
2621
|
-
if (item) {
|
|
2622
|
-
const parsed = JSON.parse(item);
|
|
2623
|
-
if (parsed.expiresAt && now2 > parsed.expiresAt) {
|
|
2624
|
-
window[storage].removeItem(key);
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
} catch {
|
|
2628
|
-
}
|
|
2629
|
-
});
|
|
2630
|
-
}
|
|
2631
|
-
} catch (error) {
|
|
2632
|
-
console.error("Error cleaning up cache:", error);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
};
|
|
2636
|
-
var cacheService = new CacheService();
|
|
2637
|
-
if (typeof window !== "undefined") {
|
|
2638
|
-
setInterval(() => {
|
|
2639
|
-
cacheService.cleanup({ storage: "localStorage" });
|
|
2640
|
-
cacheService.cleanup({ storage: "sessionStorage" });
|
|
2641
|
-
cacheService.cleanup({ storage: "memory" });
|
|
2642
|
-
}, 60 * 1e3);
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
2452
|
// src/lib/services/subscriptionManager.ts
|
|
2646
2453
|
var SubscriptionManager = class {
|
|
2647
2454
|
constructor(supabase) {
|
|
@@ -3970,6 +3777,13 @@ var S3ClipsService = class {
|
|
|
3970
3777
|
constructor(config) {
|
|
3971
3778
|
// Request deduplication cache
|
|
3972
3779
|
this.requestCache = new RequestDeduplicationCache();
|
|
3780
|
+
// Flag to prevent metadata fetching during index building
|
|
3781
|
+
this.isIndexBuilding = false;
|
|
3782
|
+
// Flag to prevent metadata fetching during entire prefetch operation
|
|
3783
|
+
this.isPrefetching = false;
|
|
3784
|
+
// Global safeguard: limit concurrent metadata fetches to prevent flooding
|
|
3785
|
+
this.currentMetadataFetches = 0;
|
|
3786
|
+
this.MAX_CONCURRENT_METADATA = 3;
|
|
3973
3787
|
this.config = config;
|
|
3974
3788
|
if (!config.s3Config) {
|
|
3975
3789
|
throw new Error("S3 configuration is required");
|
|
@@ -4117,16 +3931,36 @@ var S3ClipsService = class {
|
|
|
4117
3931
|
return null;
|
|
4118
3932
|
}
|
|
4119
3933
|
}
|
|
3934
|
+
/**
|
|
3935
|
+
* Control prefetch mode to prevent metadata fetching during background operations
|
|
3936
|
+
*/
|
|
3937
|
+
setPrefetchMode(enabled) {
|
|
3938
|
+
this.isPrefetching = enabled;
|
|
3939
|
+
console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"} - metadata fetching ${enabled ? "blocked" : "allowed"}`);
|
|
3940
|
+
}
|
|
4120
3941
|
/**
|
|
4121
3942
|
* Fetches full metadata including timestamps with deduplication
|
|
4122
3943
|
*/
|
|
4123
3944
|
async getFullMetadata(playlistUri) {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
3945
|
+
if (this.isIndexBuilding || this.isPrefetching) {
|
|
3946
|
+
console.warn(`[S3ClipsService] Skipping metadata fetch - building: ${this.isIndexBuilding}, prefetching: ${this.isPrefetching}`);
|
|
3947
|
+
return null;
|
|
3948
|
+
}
|
|
3949
|
+
if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
|
|
3950
|
+
console.warn(`[S3ClipsService] Skipping metadata - max concurrent fetches (${this.MAX_CONCURRENT_METADATA}) reached`);
|
|
3951
|
+
return null;
|
|
3952
|
+
}
|
|
3953
|
+
this.currentMetadataFetches++;
|
|
3954
|
+
try {
|
|
3955
|
+
const deduplicationKey = `full-metadata:${playlistUri}`;
|
|
3956
|
+
return await this.requestCache.deduplicate(
|
|
3957
|
+
deduplicationKey,
|
|
3958
|
+
() => this.executeGetFullMetadata(playlistUri),
|
|
3959
|
+
"FullMetadata"
|
|
3960
|
+
);
|
|
3961
|
+
} finally {
|
|
3962
|
+
this.currentMetadataFetches--;
|
|
3963
|
+
}
|
|
4130
3964
|
}
|
|
4131
3965
|
/**
|
|
4132
3966
|
* Internal implementation of full metadata fetching
|
|
@@ -4187,141 +4021,152 @@ var S3ClipsService = class {
|
|
|
4187
4021
|
* Internal implementation of clip counts fetching
|
|
4188
4022
|
*/
|
|
4189
4023
|
async executeGetClipCounts(workspaceId, date, shiftId, buildIndex) {
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
}
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
const
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4024
|
+
if (buildIndex) {
|
|
4025
|
+
this.isIndexBuilding = true;
|
|
4026
|
+
console.log(`[S3ClipsService] Starting index building - metadata fetching disabled`);
|
|
4027
|
+
}
|
|
4028
|
+
try {
|
|
4029
|
+
const basePrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/`;
|
|
4030
|
+
const counts = { total: 0 };
|
|
4031
|
+
const categoryFolders = [
|
|
4032
|
+
"idle_time",
|
|
4033
|
+
"low_value",
|
|
4034
|
+
"sop_deviation",
|
|
4035
|
+
"missing_quality_check",
|
|
4036
|
+
"best_cycle_time",
|
|
4037
|
+
"worst_cycle_time",
|
|
4038
|
+
"long_cycle_time",
|
|
4039
|
+
"cycle_completion",
|
|
4040
|
+
"bottleneck"
|
|
4041
|
+
];
|
|
4042
|
+
console.log(`[S3ClipsService] ${buildIndex ? "Building video index and counting" : "Fast counting"} clips for ${workspaceId} on ${date}, shift ${shiftId}`);
|
|
4043
|
+
const startTime = performance.now();
|
|
4044
|
+
const videoIndex = buildIndex ? {
|
|
4045
|
+
byCategory: /* @__PURE__ */ new Map(),
|
|
4046
|
+
allVideos: [],
|
|
4047
|
+
counts: {},
|
|
4048
|
+
workspaceId,
|
|
4049
|
+
date,
|
|
4050
|
+
shiftId: shiftId.toString(),
|
|
4051
|
+
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4052
|
+
_debugId: `vid_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
4053
|
+
} : null;
|
|
4054
|
+
const countPromises = categoryFolders.map(async (category) => {
|
|
4055
|
+
const categoryPrefix = `${basePrefix}${category}/videos/`;
|
|
4056
|
+
const categoryVideos = [];
|
|
4057
|
+
try {
|
|
4058
|
+
if (buildIndex) {
|
|
4059
|
+
const command = new ListObjectsV2Command({
|
|
4060
|
+
Bucket: this.config.s3Config.bucketName,
|
|
4061
|
+
Prefix: categoryPrefix,
|
|
4062
|
+
MaxKeys: 1e3
|
|
4063
|
+
});
|
|
4064
|
+
let continuationToken;
|
|
4065
|
+
do {
|
|
4066
|
+
if (continuationToken) {
|
|
4067
|
+
command.input.ContinuationToken = continuationToken;
|
|
4068
|
+
}
|
|
4069
|
+
const response = await this.s3Client.send(command);
|
|
4070
|
+
if (response.Contents) {
|
|
4071
|
+
for (const obj of response.Contents) {
|
|
4072
|
+
if (obj.Key && obj.Key.endsWith("playlist.m3u8")) {
|
|
4073
|
+
if (obj.Key.includes("missed_qchecks")) {
|
|
4074
|
+
continue;
|
|
4075
|
+
}
|
|
4076
|
+
const s3Uri = `s3://${this.config.s3Config.bucketName}/${obj.Key}`;
|
|
4077
|
+
const sopCategories = this.getSOPCategories(workspaceId);
|
|
4078
|
+
const parsedInfo = parseS3Uri(s3Uri, sopCategories);
|
|
4079
|
+
const belongsToCategory = parsedInfo && (parsedInfo.type === category || // Handle specific mismatches between folder names and parsed types
|
|
4080
|
+
category === "cycle_completion" && parsedInfo.type === "cycle_completion" || category === "sop_deviation" && parsedInfo.type === "missing_quality_check" || category === "missing_quality_check" && parsedInfo.type === "missing_quality_check" || category === "idle_time" && parsedInfo.type === "low_value" || category === "low_value" && parsedInfo.type === "low_value");
|
|
4081
|
+
if (belongsToCategory) {
|
|
4082
|
+
const videoEntry = {
|
|
4083
|
+
uri: s3Uri,
|
|
4084
|
+
category: parsedInfo.type,
|
|
4085
|
+
// Use the parsed type, not the folder name
|
|
4086
|
+
timestamp: parsedInfo.timestamp,
|
|
4087
|
+
videoId: `${workspaceId}-${parsedInfo.timestamp}`,
|
|
4088
|
+
workspaceId,
|
|
4089
|
+
date,
|
|
4090
|
+
shiftId: shiftId.toString()
|
|
4091
|
+
};
|
|
4092
|
+
categoryVideos.push(videoEntry);
|
|
4093
|
+
}
|
|
4254
4094
|
}
|
|
4255
4095
|
}
|
|
4256
4096
|
}
|
|
4097
|
+
continuationToken = response.NextContinuationToken;
|
|
4098
|
+
} while (continuationToken);
|
|
4099
|
+
categoryVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
4100
|
+
if (categoryVideos.length > 0) {
|
|
4101
|
+
console.log(`[S3ClipsService] Found ${categoryVideos.length} videos for category '${category}' (parsed types: ${[...new Set(categoryVideos.map((v) => v.category))].join(", ")})`);
|
|
4257
4102
|
}
|
|
4258
|
-
|
|
4259
|
-
}
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4103
|
+
return { category, count: categoryVideos.length, videos: categoryVideos };
|
|
4104
|
+
} else {
|
|
4105
|
+
const command = new ListObjectsV2Command({
|
|
4106
|
+
Bucket: this.config.s3Config.bucketName,
|
|
4107
|
+
Prefix: categoryPrefix,
|
|
4108
|
+
Delimiter: "/",
|
|
4109
|
+
MaxKeys: 1e3
|
|
4110
|
+
});
|
|
4111
|
+
let folderCount = 0;
|
|
4112
|
+
let continuationToken;
|
|
4113
|
+
do {
|
|
4114
|
+
if (continuationToken) {
|
|
4115
|
+
command.input.ContinuationToken = continuationToken;
|
|
4116
|
+
}
|
|
4117
|
+
const response = await this.s3Client.send(command);
|
|
4118
|
+
if (response.CommonPrefixes) {
|
|
4119
|
+
folderCount += response.CommonPrefixes.length;
|
|
4120
|
+
}
|
|
4121
|
+
continuationToken = response.NextContinuationToken;
|
|
4122
|
+
} while (continuationToken);
|
|
4123
|
+
return { category, count: folderCount, videos: [] };
|
|
4263
4124
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
}
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
|
|
4127
|
+
return { category, count: 0, videos: [] };
|
|
4128
|
+
}
|
|
4129
|
+
});
|
|
4130
|
+
const results = await Promise.all(countPromises);
|
|
4131
|
+
for (const { category, count, videos } of results) {
|
|
4132
|
+
counts[category] = count;
|
|
4133
|
+
counts.total += count;
|
|
4134
|
+
if (buildIndex && videoIndex && videos) {
|
|
4135
|
+
if (videos.length > 0) {
|
|
4136
|
+
const parsedType = videos[0].category;
|
|
4137
|
+
videoIndex.byCategory.set(parsedType, videos);
|
|
4138
|
+
console.log(`[S3ClipsService] Indexed ${videos.length} videos under parsed type '${parsedType}'`);
|
|
4139
|
+
if (category !== parsedType) {
|
|
4140
|
+
videoIndex.byCategory.set(category, videos);
|
|
4141
|
+
console.log(`[S3ClipsService] Created alias: S3 folder '${category}' -> parsed type '${parsedType}' (${videos.length} videos)`);
|
|
4281
4142
|
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
return { category, count: folderCount, videos: [] };
|
|
4143
|
+
}
|
|
4144
|
+
videoIndex.allVideos.push(...videos);
|
|
4285
4145
|
}
|
|
4286
|
-
} catch (error) {
|
|
4287
|
-
console.error(`Error ${buildIndex ? "building index for" : "counting folders for"} category ${category}:`, error);
|
|
4288
|
-
return { category, count: 0, videos: [] };
|
|
4289
4146
|
}
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
}
|
|
4147
|
+
if (buildIndex && videoIndex) {
|
|
4148
|
+
videoIndex.allVideos.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
4149
|
+
videoIndex.counts = { ...counts };
|
|
4150
|
+
}
|
|
4151
|
+
const elapsed = performance.now() - startTime;
|
|
4152
|
+
console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
|
|
4153
|
+
if (buildIndex && videoIndex) {
|
|
4154
|
+
console.log(`[S3ClipsService] Final video index summary:`);
|
|
4155
|
+
console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
|
|
4156
|
+
console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
|
|
4157
|
+
console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
|
|
4158
|
+
for (const [cat, vids] of videoIndex.byCategory.entries()) {
|
|
4159
|
+
console.log(` - '${cat}': ${vids.length} videos`);
|
|
4304
4160
|
}
|
|
4305
|
-
videoIndex
|
|
4161
|
+
return { counts, videoIndex };
|
|
4306
4162
|
}
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
const elapsed = performance.now() - startTime;
|
|
4313
|
-
console.log(`[S3ClipsService] ${buildIndex ? "Video index and counts" : "Clip counts"} completed in ${elapsed.toFixed(2)}ms - Total: ${counts.total}`);
|
|
4314
|
-
if (buildIndex && videoIndex) {
|
|
4315
|
-
console.log(`[S3ClipsService] Final video index summary:`);
|
|
4316
|
-
console.log(` - VideoIndex ID: ${videoIndex._debugId}`);
|
|
4317
|
-
console.log(` - Total videos in allVideos: ${videoIndex.allVideos.length}`);
|
|
4318
|
-
console.log(` - Categories in byCategory Map: ${Array.from(videoIndex.byCategory.keys()).join(", ")}`);
|
|
4319
|
-
for (const [cat, vids] of videoIndex.byCategory.entries()) {
|
|
4320
|
-
console.log(` - '${cat}': ${vids.length} videos`);
|
|
4163
|
+
return counts;
|
|
4164
|
+
} finally {
|
|
4165
|
+
if (buildIndex) {
|
|
4166
|
+
this.isIndexBuilding = false;
|
|
4167
|
+
console.log(`[S3ClipsService] Index building complete - metadata fetching re-enabled`);
|
|
4321
4168
|
}
|
|
4322
|
-
return { counts, videoIndex };
|
|
4323
4169
|
}
|
|
4324
|
-
return counts;
|
|
4325
4170
|
}
|
|
4326
4171
|
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
|
|
4327
4172
|
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
@@ -4415,18 +4260,18 @@ var S3ClipsService = class {
|
|
|
4415
4260
|
* Get a specific clip by index for a category with deduplication
|
|
4416
4261
|
* @deprecated Use getVideoFromIndex with a pre-built VideoIndex for better performance
|
|
4417
4262
|
*/
|
|
4418
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index) {
|
|
4419
|
-
const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
4263
|
+
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4264
|
+
const deduplicationKey = `clip-by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}:${includeCycleTime}:${includeMetadata}`;
|
|
4420
4265
|
return this.requestCache.deduplicate(
|
|
4421
4266
|
deduplicationKey,
|
|
4422
|
-
() => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index),
|
|
4267
|
+
() => this.executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime, includeMetadata),
|
|
4423
4268
|
"ClipByIndex"
|
|
4424
4269
|
);
|
|
4425
4270
|
}
|
|
4426
4271
|
/**
|
|
4427
4272
|
* Internal implementation of clip by index fetching
|
|
4428
4273
|
*/
|
|
4429
|
-
async executeGetClipByIndex(workspaceId, date, shiftId, category, index) {
|
|
4274
|
+
async executeGetClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4430
4275
|
const categoryPrefix = `sop_violations/${workspaceId}/${date}/${shiftId}/${category}/videos/`;
|
|
4431
4276
|
try {
|
|
4432
4277
|
const command = new ListObjectsV2Command({
|
|
@@ -4464,10 +4309,8 @@ var S3ClipsService = class {
|
|
|
4464
4309
|
workspaceId,
|
|
4465
4310
|
date,
|
|
4466
4311
|
shiftId.toString(),
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
true
|
|
4470
|
-
// includeMetadata
|
|
4312
|
+
includeCycleTime,
|
|
4313
|
+
includeMetadata
|
|
4471
4314
|
);
|
|
4472
4315
|
}
|
|
4473
4316
|
} catch (error) {
|
|
@@ -4538,7 +4381,7 @@ var S3ClipsService = class {
|
|
|
4538
4381
|
}
|
|
4539
4382
|
let cycleTimeSeconds = null;
|
|
4540
4383
|
let creationTimestamp = void 0;
|
|
4541
|
-
if (includeMetadata
|
|
4384
|
+
if (includeMetadata) {
|
|
4542
4385
|
const metadata = await this.getFullMetadata(uri);
|
|
4543
4386
|
if (metadata) {
|
|
4544
4387
|
if (metadata.original_task_metadata?.cycle_time) {
|
|
@@ -4546,6 +4389,8 @@ var S3ClipsService = class {
|
|
|
4546
4389
|
}
|
|
4547
4390
|
creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
|
|
4548
4391
|
}
|
|
4392
|
+
} else if (includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time" || parsedInfo.type === "cycle_completion")) {
|
|
4393
|
+
cycleTimeSeconds = null;
|
|
4549
4394
|
}
|
|
4550
4395
|
const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
|
|
4551
4396
|
const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
|
|
@@ -4617,7 +4462,8 @@ var S3ClipsService = class {
|
|
|
4617
4462
|
date,
|
|
4618
4463
|
shiftId.toString(),
|
|
4619
4464
|
includeCycleTime || false,
|
|
4620
|
-
includeMetadata ||
|
|
4465
|
+
includeMetadata || false
|
|
4466
|
+
// Never fetch metadata for timestamp filtering to prevent flooding
|
|
4621
4467
|
);
|
|
4622
4468
|
});
|
|
4623
4469
|
const videoResults = await Promise.all(videoPromises);
|
|
@@ -4688,6 +4534,7 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
4688
4534
|
}
|
|
4689
4535
|
/**
|
|
4690
4536
|
* Get or create S3 service instance for dashboard config
|
|
4537
|
+
* Public method to allow sharing the same S3Service instance across components
|
|
4691
4538
|
*/
|
|
4692
4539
|
getS3Service(dashboardConfig) {
|
|
4693
4540
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
@@ -4757,75 +4604,80 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
4757
4604
|
* Perform the actual prefetch work
|
|
4758
4605
|
*/
|
|
4759
4606
|
async performPrefetchWork(key, params, s3Service, buildIndex) {
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4607
|
+
s3Service.setPrefetchMode(true);
|
|
4608
|
+
try {
|
|
4609
|
+
const cacheKey = `clip-counts:${params.workspaceId}:${params.date}:${params.shift}`;
|
|
4610
|
+
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
4611
|
+
if (cachedResult) {
|
|
4612
|
+
console.log(`[VideoPrefetchManager] Found cached data for ${key}`);
|
|
4613
|
+
this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, cachedResult);
|
|
4614
|
+
return cachedResult;
|
|
4615
|
+
}
|
|
4616
|
+
if (buildIndex) {
|
|
4617
|
+
const countsOnly = await s3Service.getClipCounts(
|
|
4618
|
+
params.workspaceId,
|
|
4619
|
+
params.date,
|
|
4620
|
+
params.shift
|
|
4621
|
+
);
|
|
4622
|
+
if (typeof countsOnly === "object" && countsOnly !== null) {
|
|
4623
|
+
const renderReadyData = {
|
|
4624
|
+
counts: countsOnly,
|
|
4625
|
+
videoIndex: {
|
|
4626
|
+
byCategory: /* @__PURE__ */ new Map(),
|
|
4627
|
+
allVideos: [],
|
|
4628
|
+
counts: countsOnly,
|
|
4629
|
+
workspaceId: params.workspaceId,
|
|
4630
|
+
date: params.date,
|
|
4631
|
+
shiftId: params.shift,
|
|
4632
|
+
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4633
|
+
_debugId: `vid_RENDER_READY_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
4634
|
+
}
|
|
4635
|
+
};
|
|
4636
|
+
this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, renderReadyData);
|
|
4637
|
+
console.log(`[VideoPrefetchManager] Render ready, building full index for ${key}`);
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
const fullResult = await s3Service.getClipCountsCacheFirst(
|
|
4769
4641
|
params.workspaceId,
|
|
4770
4642
|
params.date,
|
|
4771
|
-
params.shift
|
|
4643
|
+
params.shift,
|
|
4644
|
+
true
|
|
4772
4645
|
);
|
|
4773
|
-
if (typeof
|
|
4774
|
-
const
|
|
4775
|
-
|
|
4646
|
+
if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
|
|
4647
|
+
const clipCountsWithIndex = fullResult;
|
|
4648
|
+
if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
|
|
4649
|
+
console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
|
|
4650
|
+
} else {
|
|
4651
|
+
console.warn(`[VideoPrefetchManager] Video index is empty, but counts available:`, clipCountsWithIndex.counts);
|
|
4652
|
+
}
|
|
4653
|
+
await smartVideoCache.setClipCounts(cacheKey, clipCountsWithIndex, 5 * 60);
|
|
4654
|
+
this.updateRequestStatus(key, "fully_indexed" /* FULLY_INDEXED */, clipCountsWithIndex);
|
|
4655
|
+
console.log(`[VideoPrefetchManager] Fully indexed: ${key} (${clipCountsWithIndex.videoIndex.allVideos.length} videos)`);
|
|
4656
|
+
return clipCountsWithIndex;
|
|
4657
|
+
} else if (fullResult && typeof fullResult === "object") {
|
|
4658
|
+
console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
|
|
4659
|
+
const countsResult = fullResult;
|
|
4660
|
+
const fallbackData = {
|
|
4661
|
+
counts: countsResult,
|
|
4776
4662
|
videoIndex: {
|
|
4777
4663
|
byCategory: /* @__PURE__ */ new Map(),
|
|
4778
4664
|
allVideos: [],
|
|
4779
|
-
counts:
|
|
4665
|
+
counts: countsResult,
|
|
4780
4666
|
workspaceId: params.workspaceId,
|
|
4781
4667
|
date: params.date,
|
|
4782
4668
|
shiftId: params.shift,
|
|
4783
4669
|
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4784
|
-
_debugId: `
|
|
4670
|
+
_debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
4785
4671
|
}
|
|
4786
4672
|
};
|
|
4787
|
-
this.updateRequestStatus(key, "render_ready" /* RENDER_READY */,
|
|
4788
|
-
|
|
4789
|
-
}
|
|
4790
|
-
}
|
|
4791
|
-
const fullResult = await s3Service.getClipCountsCacheFirst(
|
|
4792
|
-
params.workspaceId,
|
|
4793
|
-
params.date,
|
|
4794
|
-
params.shift,
|
|
4795
|
-
true
|
|
4796
|
-
);
|
|
4797
|
-
if (fullResult && typeof fullResult === "object" && "videoIndex" in fullResult) {
|
|
4798
|
-
const clipCountsWithIndex = fullResult;
|
|
4799
|
-
if (clipCountsWithIndex.videoIndex.allVideos.length > 0) {
|
|
4800
|
-
console.log(`[VideoPrefetchManager] Video index properly populated with ${clipCountsWithIndex.videoIndex.allVideos.length} videos`);
|
|
4673
|
+
this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
|
|
4674
|
+
return fallbackData;
|
|
4801
4675
|
} else {
|
|
4802
|
-
console.
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
return clipCountsWithIndex;
|
|
4808
|
-
} else if (fullResult && typeof fullResult === "object") {
|
|
4809
|
-
console.log(`[VideoPrefetchManager] Received counts only, building fallback data structure`);
|
|
4810
|
-
const countsResult = fullResult;
|
|
4811
|
-
const fallbackData = {
|
|
4812
|
-
counts: countsResult,
|
|
4813
|
-
videoIndex: {
|
|
4814
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
4815
|
-
allVideos: [],
|
|
4816
|
-
counts: countsResult,
|
|
4817
|
-
workspaceId: params.workspaceId,
|
|
4818
|
-
date: params.date,
|
|
4819
|
-
shiftId: params.shift,
|
|
4820
|
-
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4821
|
-
_debugId: `vid_FALLBACK_${Date.now()}_${Math.random().toString(36).substring(7)}`
|
|
4822
|
-
}
|
|
4823
|
-
};
|
|
4824
|
-
this.updateRequestStatus(key, "render_ready" /* RENDER_READY */, fallbackData);
|
|
4825
|
-
return fallbackData;
|
|
4826
|
-
} else {
|
|
4827
|
-
console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
|
|
4828
|
-
throw new Error("Failed to fetch clip counts from S3 service");
|
|
4676
|
+
console.error(`[VideoPrefetchManager] Received null/undefined result from S3 service`);
|
|
4677
|
+
throw new Error("Failed to fetch clip counts from S3 service");
|
|
4678
|
+
}
|
|
4679
|
+
} finally {
|
|
4680
|
+
s3Service.setPrefetchMode(false);
|
|
4829
4681
|
}
|
|
4830
4682
|
}
|
|
4831
4683
|
/**
|
|
@@ -5088,9 +4940,13 @@ var AuthProvider = ({ children }) => {
|
|
|
5088
4940
|
const [loading, setLoading] = useState(true);
|
|
5089
4941
|
const [error, setError] = useState(null);
|
|
5090
4942
|
const router = useRouter();
|
|
5091
|
-
|
|
5092
|
-
|
|
4943
|
+
authConfig?.userProfileTable;
|
|
4944
|
+
authConfig?.roleColumn || "role";
|
|
5093
4945
|
const fetchUserDetails = useCallback(async (supabaseUser) => {
|
|
4946
|
+
console.log("[fetchUserDetails] Called for user:", supabaseUser.id, {
|
|
4947
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4948
|
+
stackTrace: new Error().stack?.split("\n").slice(1, 4).join(" -> ")
|
|
4949
|
+
});
|
|
5094
4950
|
if (!supabaseUser) return null;
|
|
5095
4951
|
const basicUser = {
|
|
5096
4952
|
id: supabaseUser.id,
|
|
@@ -5099,42 +4955,34 @@ var AuthProvider = ({ children }) => {
|
|
|
5099
4955
|
if (!supabase) return basicUser;
|
|
5100
4956
|
try {
|
|
5101
4957
|
const timeoutPromise = new Promise(
|
|
5102
|
-
(_, reject) => setTimeout(() =>
|
|
4958
|
+
(_, reject) => setTimeout(() => {
|
|
4959
|
+
console.log("[fetchUserDetails] Timeout triggered after 2 seconds");
|
|
4960
|
+
reject(new Error("Profile fetch timeout"));
|
|
4961
|
+
}, 2e3)
|
|
5103
4962
|
);
|
|
5104
4963
|
const rolePromise = supabase.from("user_roles").select("role_level").eq("user_id", supabaseUser.id).single();
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
const [roleResult, profileResult] = await Promise.race([
|
|
5110
|
-
Promise.all([
|
|
5111
|
-
rolePromise,
|
|
5112
|
-
profilePromise || Promise.resolve(null)
|
|
5113
|
-
]),
|
|
5114
|
-
timeoutPromise.then(() => {
|
|
5115
|
-
throw new Error("Timeout");
|
|
5116
|
-
})
|
|
4964
|
+
const roleResult = await Promise.race([
|
|
4965
|
+
rolePromise,
|
|
4966
|
+
timeoutPromise
|
|
4967
|
+
// Fixed: removed .then() which was causing the bug
|
|
5117
4968
|
]);
|
|
5118
4969
|
let roleLevel = void 0;
|
|
5119
4970
|
if (roleResult && !roleResult.error && roleResult.data) {
|
|
5120
4971
|
roleLevel = roleResult.data.role_level;
|
|
5121
|
-
} else if (roleResult?.error) {
|
|
4972
|
+
} else if (roleResult?.error && roleResult.error.code !== "PGRST116") {
|
|
5122
4973
|
console.log("Error fetching role_level:", roleResult.error.message);
|
|
5123
4974
|
}
|
|
5124
|
-
let roleValue = void 0;
|
|
5125
|
-
if (profileResult && !profileResult.error && profileResult.data) {
|
|
5126
|
-
roleValue = profileResult.data[roleColumn];
|
|
5127
|
-
}
|
|
5128
4975
|
return {
|
|
5129
4976
|
...basicUser,
|
|
5130
|
-
role: roleValue,
|
|
5131
4977
|
role_level: roleLevel
|
|
5132
4978
|
};
|
|
5133
4979
|
} catch (err) {
|
|
5134
|
-
|
|
4980
|
+
if (err instanceof Error && err.message.includes("timeout")) {
|
|
4981
|
+
console.warn("Auth fetch timeout - using basic user info");
|
|
4982
|
+
}
|
|
5135
4983
|
return basicUser;
|
|
5136
4984
|
}
|
|
5137
|
-
}, [supabase
|
|
4985
|
+
}, [supabase]);
|
|
5138
4986
|
useEffect(() => {
|
|
5139
4987
|
if (!supabase) return;
|
|
5140
4988
|
let mounted = true;
|
|
@@ -5145,6 +4993,7 @@ var AuthProvider = ({ children }) => {
|
|
|
5145
4993
|
}
|
|
5146
4994
|
}, 1e4);
|
|
5147
4995
|
const initializeAuth = async () => {
|
|
4996
|
+
const startTime = performance.now();
|
|
5148
4997
|
try {
|
|
5149
4998
|
const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
|
|
5150
4999
|
if (!mounted) return;
|
|
@@ -5187,12 +5036,38 @@ var AuthProvider = ({ children }) => {
|
|
|
5187
5036
|
if (mounted) {
|
|
5188
5037
|
setLoading(false);
|
|
5189
5038
|
clearTimeout(safetyTimeout);
|
|
5039
|
+
const duration = performance.now() - startTime;
|
|
5040
|
+
if (duration > 3e3) {
|
|
5041
|
+
console.warn(`[Auth] Initialization took ${duration.toFixed(0)}ms - consider optimization`);
|
|
5042
|
+
} else if (process.env.NODE_ENV === "development") {
|
|
5043
|
+
console.log(`[Auth] Initialized in ${duration.toFixed(0)}ms`);
|
|
5044
|
+
}
|
|
5190
5045
|
}
|
|
5191
5046
|
}
|
|
5192
5047
|
};
|
|
5193
5048
|
initializeAuth();
|
|
5194
|
-
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (
|
|
5049
|
+
const { data: { subscription } } = supabase.auth.onAuthStateChange(async (event, currentSession) => {
|
|
5195
5050
|
if (!mounted) return;
|
|
5051
|
+
console.log("[AuthContext] Auth event fired:", {
|
|
5052
|
+
event,
|
|
5053
|
+
sessionId: currentSession?.user?.id,
|
|
5054
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5055
|
+
currentPath: typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
5056
|
+
});
|
|
5057
|
+
if (event === "TOKEN_REFRESHED") {
|
|
5058
|
+
if (session?.user?.id === currentSession?.user?.id && session?.user?.email === currentSession?.user?.email) {
|
|
5059
|
+
console.log("[AuthContext] Skipping TOKEN_REFRESHED - session unchanged");
|
|
5060
|
+
return;
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
if (event !== "TOKEN_REFRESHED" && currentSession?.user) {
|
|
5064
|
+
console.log("[AuthContext] Non-TOKEN_REFRESHED event will trigger fetchUserDetails:", event);
|
|
5065
|
+
}
|
|
5066
|
+
const sessionChanged = session?.user?.id !== currentSession?.user?.id || session?.user?.email !== currentSession?.user?.email;
|
|
5067
|
+
if (!sessionChanged && user) {
|
|
5068
|
+
console.log("[AuthContext] Session and user unchanged, skipping update");
|
|
5069
|
+
return;
|
|
5070
|
+
}
|
|
5196
5071
|
setSession(currentSession);
|
|
5197
5072
|
setUser(null);
|
|
5198
5073
|
setLoading(false);
|
|
@@ -5567,7 +5442,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5567
5442
|
const [error, setError] = useState(null);
|
|
5568
5443
|
const updateQueueRef = useRef(false);
|
|
5569
5444
|
const isFetchingRef = useRef(false);
|
|
5570
|
-
const timeoutRef = useRef(null);
|
|
5571
5445
|
const channelRef = useRef(null);
|
|
5572
5446
|
const schema = databaseConfig.schema ?? "public";
|
|
5573
5447
|
const companyId = entityConfig.companyId || "";
|
|
@@ -5576,7 +5450,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5576
5450
|
const defaultTimezone = dateTimeConfig.defaultTimezone;
|
|
5577
5451
|
const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
|
|
5578
5452
|
const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
|
|
5579
|
-
const fetchMetrics = useCallback(async (
|
|
5453
|
+
const fetchMetrics = useCallback(async () => {
|
|
5580
5454
|
if (!workspaceId || isFetchingRef.current) return;
|
|
5581
5455
|
try {
|
|
5582
5456
|
isFetchingRef.current = true;
|
|
@@ -5585,28 +5459,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5585
5459
|
const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
|
|
5586
5460
|
console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
|
|
5587
5461
|
console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
|
|
5588
|
-
const cacheKey = cacheService.generateKey(
|
|
5589
|
-
"workspace-detailed-metrics",
|
|
5590
|
-
workspaceId,
|
|
5591
|
-
queryDate,
|
|
5592
|
-
queryShiftId,
|
|
5593
|
-
companyId
|
|
5594
|
-
);
|
|
5595
|
-
if (!skipCache) {
|
|
5596
|
-
const cachedData = cacheService.get(cacheKey, {
|
|
5597
|
-
storage: "memory",
|
|
5598
|
-
duration: 5 * 60 * 1e3
|
|
5599
|
-
// 5 minutes
|
|
5600
|
-
});
|
|
5601
|
-
if (cachedData) {
|
|
5602
|
-
console.log("[useWorkspaceDetailedMetrics] Using cached data for:", cacheKey);
|
|
5603
|
-
setMetrics(cachedData);
|
|
5604
|
-
setIsLoading(false);
|
|
5605
|
-
updateQueueRef.current = false;
|
|
5606
|
-
isFetchingRef.current = false;
|
|
5607
|
-
return;
|
|
5608
|
-
}
|
|
5609
|
-
}
|
|
5610
5462
|
console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
|
|
5611
5463
|
const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
|
|
5612
5464
|
if (fetchError) throw fetchError;
|
|
@@ -5709,18 +5561,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5709
5561
|
setIsLoading(false);
|
|
5710
5562
|
updateQueueRef.current = false;
|
|
5711
5563
|
isFetchingRef.current = false;
|
|
5712
|
-
const fallbackCacheKey = cacheService.generateKey(
|
|
5713
|
-
"workspace-detailed-metrics",
|
|
5714
|
-
workspaceId,
|
|
5715
|
-
recentData.date,
|
|
5716
|
-
recentData.shift_id,
|
|
5717
|
-
companyId
|
|
5718
|
-
);
|
|
5719
|
-
cacheService.set(fallbackCacheKey, transformedData2, {
|
|
5720
|
-
storage: "memory",
|
|
5721
|
-
duration: 2 * 60 * 1e3
|
|
5722
|
-
// 2 minutes for fallback data
|
|
5723
|
-
});
|
|
5724
5564
|
return;
|
|
5725
5565
|
} else {
|
|
5726
5566
|
console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
|
|
@@ -5834,11 +5674,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5834
5674
|
...data.sop_check !== void 0 && { sop_check: data.sop_check }
|
|
5835
5675
|
};
|
|
5836
5676
|
setMetrics(transformedData);
|
|
5837
|
-
cacheService.set(cacheKey, transformedData, {
|
|
5838
|
-
storage: "memory",
|
|
5839
|
-
duration: 5 * 60 * 1e3
|
|
5840
|
-
// 5 minutes
|
|
5841
|
-
});
|
|
5842
5677
|
} catch (err) {
|
|
5843
5678
|
console.error("Error fetching workspace metrics:", err);
|
|
5844
5679
|
setError({ message: err.message, code: err.code });
|
|
@@ -5851,12 +5686,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5851
5686
|
const queueUpdate = useCallback(() => {
|
|
5852
5687
|
if (!workspaceId || updateQueueRef.current) return;
|
|
5853
5688
|
updateQueueRef.current = true;
|
|
5854
|
-
|
|
5855
|
-
clearTimeout(timeoutRef.current);
|
|
5856
|
-
}
|
|
5857
|
-
timeoutRef.current = setTimeout(() => {
|
|
5858
|
-
fetchMetrics();
|
|
5859
|
-
}, 500);
|
|
5689
|
+
fetchMetrics();
|
|
5860
5690
|
}, [fetchMetrics, workspaceId]);
|
|
5861
5691
|
const setupSubscription = useCallback(() => {
|
|
5862
5692
|
if (!workspaceId) return;
|
|
@@ -5873,7 +5703,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5873
5703
|
},
|
|
5874
5704
|
async (payload) => {
|
|
5875
5705
|
console.log(`Received ${metricsTablePrefix} update:`, payload);
|
|
5876
|
-
await fetchMetrics(
|
|
5706
|
+
await fetchMetrics();
|
|
5877
5707
|
}
|
|
5878
5708
|
).subscribe((status) => {
|
|
5879
5709
|
console.log(`Workspace detailed metrics subscription status:`, status);
|
|
@@ -5907,14 +5737,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5907
5737
|
matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
|
|
5908
5738
|
});
|
|
5909
5739
|
if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
|
|
5910
|
-
const cacheKey = cacheService.generateKey(
|
|
5911
|
-
"workspace-detailed-metrics",
|
|
5912
|
-
workspaceId,
|
|
5913
|
-
operationalDate,
|
|
5914
|
-
queryShiftId,
|
|
5915
|
-
companyId
|
|
5916
|
-
);
|
|
5917
|
-
cacheService.delete(cacheKey, { storage: "memory" });
|
|
5918
5740
|
queueUpdate();
|
|
5919
5741
|
}
|
|
5920
5742
|
}
|
|
@@ -5940,14 +5762,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5940
5762
|
matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
|
|
5941
5763
|
});
|
|
5942
5764
|
if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
|
|
5943
|
-
const cacheKey = cacheService.generateKey(
|
|
5944
|
-
"workspace-detailed-metrics",
|
|
5945
|
-
workspaceId,
|
|
5946
|
-
operationalDate,
|
|
5947
|
-
queryShiftId,
|
|
5948
|
-
companyId
|
|
5949
|
-
);
|
|
5950
|
-
cacheService.delete(cacheKey, { storage: "memory" });
|
|
5951
5765
|
queueUpdate();
|
|
5952
5766
|
}
|
|
5953
5767
|
}
|
|
@@ -5973,14 +5787,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5973
5787
|
matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
|
|
5974
5788
|
});
|
|
5975
5789
|
if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
|
|
5976
|
-
const cacheKey = cacheService.generateKey(
|
|
5977
|
-
"workspace-detailed-metrics",
|
|
5978
|
-
workspaceId,
|
|
5979
|
-
operationalDate,
|
|
5980
|
-
queryShiftId,
|
|
5981
|
-
companyId
|
|
5982
|
-
);
|
|
5983
|
-
cacheService.delete(cacheKey, { storage: "memory" });
|
|
5984
5790
|
queueUpdate();
|
|
5985
5791
|
}
|
|
5986
5792
|
}
|
|
@@ -5991,9 +5797,6 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
5991
5797
|
fetchMetrics();
|
|
5992
5798
|
setupSubscription();
|
|
5993
5799
|
return () => {
|
|
5994
|
-
if (timeoutRef.current) {
|
|
5995
|
-
clearTimeout(timeoutRef.current);
|
|
5996
|
-
}
|
|
5997
5800
|
channels.forEach((channel) => {
|
|
5998
5801
|
console.log("Cleaning up channel subscription");
|
|
5999
5802
|
supabase.removeChannel(channel);
|
|
@@ -6007,7 +5810,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
|
|
|
6007
5810
|
metrics: metrics2,
|
|
6008
5811
|
isLoading,
|
|
6009
5812
|
error,
|
|
6010
|
-
refetch: () => fetchMetrics(
|
|
5813
|
+
refetch: () => fetchMetrics()
|
|
6011
5814
|
// Force refresh without cache
|
|
6012
5815
|
};
|
|
6013
5816
|
};
|
|
@@ -6452,33 +6255,6 @@ var useLeaderboardMetrics = (lineId, topCount = 10) => {
|
|
|
6452
6255
|
refetch: fetchLeaderboardData
|
|
6453
6256
|
};
|
|
6454
6257
|
};
|
|
6455
|
-
var CACHE_KEY_PREFIX = "dashboard_metrics_cache_";
|
|
6456
|
-
var CACHE_DURATION = 5 * 60 * 1e3;
|
|
6457
|
-
var getCache = (lineId) => {
|
|
6458
|
-
if (typeof window === "undefined") return null;
|
|
6459
|
-
try {
|
|
6460
|
-
const cached = localStorage.getItem(`${CACHE_KEY_PREFIX}${lineId}`);
|
|
6461
|
-
if (!cached) return null;
|
|
6462
|
-
const parsedCache = JSON.parse(cached);
|
|
6463
|
-
if (!parsedCache.lastUpdated || Date.now() - parsedCache.lastUpdated > CACHE_DURATION) {
|
|
6464
|
-
localStorage.removeItem(`${CACHE_KEY_PREFIX}${lineId}`);
|
|
6465
|
-
return null;
|
|
6466
|
-
}
|
|
6467
|
-
return parsedCache;
|
|
6468
|
-
} catch {
|
|
6469
|
-
return null;
|
|
6470
|
-
}
|
|
6471
|
-
};
|
|
6472
|
-
var setCache = (lineId, metrics2) => {
|
|
6473
|
-
if (typeof window === "undefined") return;
|
|
6474
|
-
try {
|
|
6475
|
-
localStorage.setItem(`${CACHE_KEY_PREFIX}${lineId}`, JSON.stringify({
|
|
6476
|
-
...metrics2,
|
|
6477
|
-
lastUpdated: Date.now()
|
|
6478
|
-
}));
|
|
6479
|
-
} catch {
|
|
6480
|
-
}
|
|
6481
|
-
};
|
|
6482
6258
|
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
6483
6259
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
6484
6260
|
const entityConfig = useEntityConfig();
|
|
@@ -6489,22 +6265,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6489
6265
|
const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
|
|
6490
6266
|
const schema = databaseConfig?.schema ?? "public";
|
|
6491
6267
|
const supabase = useSupabase();
|
|
6492
|
-
const [metrics2, setMetrics] = useState(
|
|
6493
|
-
const [isLoading, setIsLoading] = useState(
|
|
6268
|
+
const [metrics2, setMetrics] = useState({ workspaceMetrics: [], lineMetrics: [] });
|
|
6269
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
6494
6270
|
const [error, setError] = useState(null);
|
|
6495
6271
|
const lineIdRef = useRef(lineId);
|
|
6496
6272
|
const isFetchingRef = useRef(false);
|
|
6497
6273
|
const updateQueueRef = useRef(false);
|
|
6498
|
-
const timeoutRef = useRef(null);
|
|
6499
6274
|
const companySpecificMetricsTable = useMemo(
|
|
6500
6275
|
() => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
|
|
6501
6276
|
[entityConfig.companyId]
|
|
6502
6277
|
);
|
|
6503
6278
|
useEffect(() => {
|
|
6504
6279
|
lineIdRef.current = lineId;
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
setIsLoading(!cachedData);
|
|
6280
|
+
setMetrics({ workspaceMetrics: [], lineMetrics: [] });
|
|
6281
|
+
setIsLoading(true);
|
|
6508
6282
|
}, [lineId]);
|
|
6509
6283
|
const fetchAllMetrics = useCallback(async () => {
|
|
6510
6284
|
const currentLineIdToUse = lineIdRef.current;
|
|
@@ -6516,9 +6290,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6516
6290
|
return;
|
|
6517
6291
|
}
|
|
6518
6292
|
isFetchingRef.current = true;
|
|
6519
|
-
|
|
6520
|
-
setIsLoading(true);
|
|
6521
|
-
}
|
|
6293
|
+
setIsLoading(true);
|
|
6522
6294
|
setError(null);
|
|
6523
6295
|
try {
|
|
6524
6296
|
const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
|
|
@@ -6543,7 +6315,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6543
6315
|
lineMetrics: []
|
|
6544
6316
|
};
|
|
6545
6317
|
setMetrics(newMetricsState2);
|
|
6546
|
-
setCache(currentLineIdToUse, newMetricsState2);
|
|
6547
6318
|
return;
|
|
6548
6319
|
}
|
|
6549
6320
|
let workspaceQuery = supabase.from(companySpecificMetricsTable).select("company_id,line_id,shift_id,date,workspace_id,workspace_name,total_output,avg_pph,performance_score,avg_cycle_time,trend_score,ideal_output,efficiency,total_day_output").eq("date", operationalDate).eq("shift_id", currentShiftDetails.shiftId).in("workspace_id", enabledWorkspaceIds);
|
|
@@ -6595,7 +6366,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6595
6366
|
lineMetrics: lineData || []
|
|
6596
6367
|
};
|
|
6597
6368
|
setMetrics(newMetricsState);
|
|
6598
|
-
setCache(currentLineIdToUse, newMetricsState);
|
|
6599
6369
|
} catch (err) {
|
|
6600
6370
|
setError({ message: err.message, code: err.code || "FETCH_ERROR" });
|
|
6601
6371
|
} finally {
|
|
@@ -6621,12 +6391,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6621
6391
|
return;
|
|
6622
6392
|
}
|
|
6623
6393
|
updateQueueRef.current = true;
|
|
6624
|
-
|
|
6625
|
-
clearTimeout(timeoutRef.current);
|
|
6626
|
-
}
|
|
6627
|
-
timeoutRef.current = setTimeout(() => {
|
|
6628
|
-
fetchAllMetrics();
|
|
6629
|
-
}, 500);
|
|
6394
|
+
fetchAllMetrics();
|
|
6630
6395
|
}, [fetchAllMetrics, supabase]);
|
|
6631
6396
|
useEffect(() => {
|
|
6632
6397
|
if (lineId && supabase) {
|
|
@@ -6671,9 +6436,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6671
6436
|
onLineMetricsUpdate?.();
|
|
6672
6437
|
});
|
|
6673
6438
|
return () => {
|
|
6674
|
-
if (timeoutRef.current) {
|
|
6675
|
-
clearTimeout(timeoutRef.current);
|
|
6676
|
-
}
|
|
6677
6439
|
channels.forEach((channel) => {
|
|
6678
6440
|
supabase?.removeChannel(channel).catch((err) => console.error("[useDashboardMetrics] Error removing channel:", err.message));
|
|
6679
6441
|
});
|
|
@@ -6702,30 +6464,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6702
6464
|
refetch: fetchAllMetrics
|
|
6703
6465
|
};
|
|
6704
6466
|
};
|
|
6705
|
-
var CACHE_KEY_PREFIX2 = "line_kpis_cache_";
|
|
6706
|
-
var CACHE_DURATION2 = 5 * 60 * 1e3;
|
|
6707
|
-
var getCache2 = (lineId) => {
|
|
6708
|
-
if (typeof window === "undefined") return null;
|
|
6709
|
-
try {
|
|
6710
|
-
const cached = localStorage.getItem(`${CACHE_KEY_PREFIX2}${lineId}`);
|
|
6711
|
-
if (!cached) return null;
|
|
6712
|
-
const parsedCache = JSON.parse(cached);
|
|
6713
|
-
if (!parsedCache.timestamp || Date.now() - parsedCache.timestamp > CACHE_DURATION2) {
|
|
6714
|
-
localStorage.removeItem(`${CACHE_KEY_PREFIX2}${lineId}`);
|
|
6715
|
-
return null;
|
|
6716
|
-
}
|
|
6717
|
-
return parsedCache.data;
|
|
6718
|
-
} catch (error) {
|
|
6719
|
-
return null;
|
|
6720
|
-
}
|
|
6721
|
-
};
|
|
6722
|
-
var setCache2 = (lineId, data) => {
|
|
6723
|
-
if (typeof window === "undefined") return;
|
|
6724
|
-
try {
|
|
6725
|
-
localStorage.setItem(`${CACHE_KEY_PREFIX2}${lineId}`, JSON.stringify({ data, timestamp: Date.now() }));
|
|
6726
|
-
} catch (error) {
|
|
6727
|
-
}
|
|
6728
|
-
};
|
|
6729
6467
|
var useLineKPIs = ({ lineId }) => {
|
|
6730
6468
|
useDashboardConfig();
|
|
6731
6469
|
const entityConfig = useEntityConfig();
|
|
@@ -6737,13 +6475,12 @@ var useLineKPIs = ({ lineId }) => {
|
|
|
6737
6475
|
const dashboardServiceInstance = useMemo(() => {
|
|
6738
6476
|
return dashboardService;
|
|
6739
6477
|
}, []);
|
|
6740
|
-
const [kpis, setKPIs] = useState(
|
|
6741
|
-
const [isLoading, setIsLoading] = useState(
|
|
6478
|
+
const [kpis, setKPIs] = useState(null);
|
|
6479
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
6742
6480
|
const [error, setError] = useState(null);
|
|
6743
6481
|
const lineIdRef = useRef(lineId);
|
|
6744
6482
|
const isFetchingRef = useRef(false);
|
|
6745
6483
|
const updateQueueRef = useRef(false);
|
|
6746
|
-
const timeoutRef = useRef(null);
|
|
6747
6484
|
const defaultTimezone = dateTimeConfig.defaultTimezone;
|
|
6748
6485
|
const schema = databaseConfig.schema ?? "public";
|
|
6749
6486
|
const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
|
|
@@ -6774,7 +6511,6 @@ var useLineKPIs = ({ lineId }) => {
|
|
|
6774
6511
|
if (lineInfo) {
|
|
6775
6512
|
const newKPIs = dashboardServiceInstance.calculateKPIs(lineInfo);
|
|
6776
6513
|
setKPIs(newKPIs);
|
|
6777
|
-
setCache2(currentLineId, newKPIs);
|
|
6778
6514
|
} else {
|
|
6779
6515
|
setKPIs(null);
|
|
6780
6516
|
}
|
|
@@ -6791,12 +6527,7 @@ var useLineKPIs = ({ lineId }) => {
|
|
|
6791
6527
|
const queueUpdate = useCallback(() => {
|
|
6792
6528
|
if (updateQueueRef.current) return;
|
|
6793
6529
|
updateQueueRef.current = true;
|
|
6794
|
-
|
|
6795
|
-
clearTimeout(timeoutRef.current);
|
|
6796
|
-
}
|
|
6797
|
-
timeoutRef.current = setTimeout(() => {
|
|
6798
|
-
fetchKPIs();
|
|
6799
|
-
}, 500);
|
|
6530
|
+
fetchKPIs();
|
|
6800
6531
|
}, [fetchKPIs]);
|
|
6801
6532
|
useEffect(() => {
|
|
6802
6533
|
const currentLineId = lineIdRef.current;
|
|
@@ -6854,9 +6585,6 @@ var useLineKPIs = ({ lineId }) => {
|
|
|
6854
6585
|
activeChannels.push(csChannel);
|
|
6855
6586
|
}
|
|
6856
6587
|
return () => {
|
|
6857
|
-
if (timeoutRef.current) {
|
|
6858
|
-
clearTimeout(timeoutRef.current);
|
|
6859
|
-
}
|
|
6860
6588
|
activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
|
|
6861
6589
|
};
|
|
6862
6590
|
}, [supabase, lineId, fetchKPIs, queueUpdate, dashboardServiceInstance, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, defaultTimezone, shiftConfig, kpis, isFactoryView]);
|
|
@@ -6885,7 +6613,6 @@ var useRealtimeLineMetrics = ({
|
|
|
6885
6613
|
const [initialized, setInitialized] = useState(false);
|
|
6886
6614
|
const lineIdRef = useRef(null);
|
|
6887
6615
|
const updateQueueRef = useRef(false);
|
|
6888
|
-
const timeoutRef = useRef(null);
|
|
6889
6616
|
const isFetchingRef = useRef(false);
|
|
6890
6617
|
const channelsRef = useRef([]);
|
|
6891
6618
|
const currentShift = useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
|
|
@@ -7098,12 +6825,7 @@ var useRealtimeLineMetrics = ({
|
|
|
7098
6825
|
const queueUpdate = useCallback(() => {
|
|
7099
6826
|
if (updateQueueRef.current) return;
|
|
7100
6827
|
updateQueueRef.current = true;
|
|
7101
|
-
|
|
7102
|
-
clearTimeout(timeoutRef.current);
|
|
7103
|
-
}
|
|
7104
|
-
timeoutRef.current = setTimeout(() => {
|
|
7105
|
-
fetchData();
|
|
7106
|
-
}, 500);
|
|
6828
|
+
fetchData();
|
|
7107
6829
|
}, [fetchData]);
|
|
7108
6830
|
const setupSubscriptions = useCallback(() => {
|
|
7109
6831
|
if (channelsRef.current.length > 0) {
|
|
@@ -7181,9 +6903,6 @@ var useRealtimeLineMetrics = ({
|
|
|
7181
6903
|
}
|
|
7182
6904
|
setupSubscriptions();
|
|
7183
6905
|
return () => {
|
|
7184
|
-
if (timeoutRef.current) {
|
|
7185
|
-
clearTimeout(timeoutRef.current);
|
|
7186
|
-
}
|
|
7187
6906
|
if (channelsRef.current.length > 0) {
|
|
7188
6907
|
channelsRef.current.forEach((channel) => {
|
|
7189
6908
|
if (process.env.NODE_ENV === "development") {
|
|
@@ -7269,16 +6988,12 @@ var useTargets = (options) => {
|
|
|
7269
6988
|
};
|
|
7270
6989
|
var DEFAULT_SHIFTS_TABLE_NAME = "shift_configurations";
|
|
7271
6990
|
var useShifts = () => {
|
|
7272
|
-
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
7273
6991
|
const { companyId } = useEntityConfig();
|
|
7274
6992
|
const { tables } = useDatabaseConfig();
|
|
6993
|
+
const supabase = useSupabase();
|
|
7275
6994
|
const [shifts, setShifts] = useState([]);
|
|
7276
6995
|
const [isLoading, setIsLoading] = useState(true);
|
|
7277
6996
|
const [error, setError] = useState(null);
|
|
7278
|
-
const supabase = useMemo(() => {
|
|
7279
|
-
if (!supabaseUrl || !supabaseKey) return null;
|
|
7280
|
-
return createClient(supabaseUrl, supabaseKey);
|
|
7281
|
-
}, [supabaseUrl, supabaseKey]);
|
|
7282
6997
|
const shiftsTable = tables?.shiftConfigurations || DEFAULT_SHIFTS_TABLE_NAME;
|
|
7283
6998
|
const fetchData = useCallback(async () => {
|
|
7284
6999
|
if (!supabase) {
|
|
@@ -7977,6 +7692,7 @@ var runtimeWorkspaceDisplayNames = {};
|
|
|
7977
7692
|
var isInitialized = false;
|
|
7978
7693
|
var isInitializing = false;
|
|
7979
7694
|
var initializedWithLineIds = [];
|
|
7695
|
+
var initializationPromise = null;
|
|
7980
7696
|
function getCurrentLineIds() {
|
|
7981
7697
|
try {
|
|
7982
7698
|
const config = _getDashboardConfigInstance();
|
|
@@ -7997,52 +7713,79 @@ function getCurrentLineIds() {
|
|
|
7997
7713
|
}
|
|
7998
7714
|
async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
7999
7715
|
console.log("\u{1F504} initializeWorkspaceDisplayNames called", { isInitialized, isInitializing, explicitLineId });
|
|
8000
|
-
if (isInitialized
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
7716
|
+
if (isInitialized) return;
|
|
7717
|
+
if (initializationPromise) {
|
|
7718
|
+
console.log("\u{1F504} Already initializing, waiting for existing initialization...");
|
|
7719
|
+
await initializationPromise;
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
initializationPromise = (async () => {
|
|
7723
|
+
isInitializing = true;
|
|
7724
|
+
try {
|
|
7725
|
+
console.log("\u{1F504} Starting Supabase workspace display names initialization...");
|
|
7726
|
+
let targetLineIds = [];
|
|
7727
|
+
if (explicitLineId) {
|
|
7728
|
+
targetLineIds = [explicitLineId];
|
|
7729
|
+
} else {
|
|
7730
|
+
targetLineIds = getCurrentLineIds();
|
|
7731
|
+
}
|
|
7732
|
+
console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
|
|
7733
|
+
runtimeWorkspaceDisplayNames = {};
|
|
7734
|
+
if (targetLineIds.length > 0) {
|
|
7735
|
+
for (const lineId of targetLineIds) {
|
|
7736
|
+
console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
|
|
7737
|
+
const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
|
|
7738
|
+
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
7739
|
+
lineDisplayNamesMap.forEach((displayName, workspaceId) => {
|
|
7740
|
+
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
7741
|
+
});
|
|
7742
|
+
console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
|
|
7743
|
+
}
|
|
7744
|
+
} else {
|
|
7745
|
+
console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
|
|
7746
|
+
const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
|
|
7747
|
+
runtimeWorkspaceDisplayNames["global"] = {};
|
|
7748
|
+
allWorkspacesMap.forEach((displayName, workspaceId) => {
|
|
7749
|
+
runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
|
|
7750
|
+
});
|
|
7751
|
+
}
|
|
7752
|
+
isInitialized = true;
|
|
7753
|
+
initializedWithLineIds = targetLineIds;
|
|
7754
|
+
console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
|
|
7755
|
+
console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
|
|
7756
|
+
} catch (error) {
|
|
7757
|
+
console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
|
|
7758
|
+
} finally {
|
|
7759
|
+
isInitializing = false;
|
|
7760
|
+
initializationPromise = null;
|
|
8009
7761
|
}
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
7762
|
+
})();
|
|
7763
|
+
await initializationPromise;
|
|
7764
|
+
}
|
|
7765
|
+
var preInitializeWorkspaceDisplayNames = async (lineId) => {
|
|
7766
|
+
console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
|
|
7767
|
+
if (isInitialized) {
|
|
7768
|
+
console.log("\u{1F504} Already initialized");
|
|
7769
|
+
if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
|
|
7770
|
+
console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
|
|
7771
|
+
try {
|
|
8015
7772
|
const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
|
|
8016
7773
|
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
8017
7774
|
lineDisplayNamesMap.forEach((displayName, workspaceId) => {
|
|
8018
7775
|
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
8019
7776
|
});
|
|
8020
|
-
console.log(`\u2705
|
|
7777
|
+
console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
|
|
7778
|
+
} catch (error) {
|
|
7779
|
+
console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
|
|
8021
7780
|
}
|
|
8022
|
-
} else {
|
|
8023
|
-
console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
|
|
8024
|
-
const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
|
|
8025
|
-
runtimeWorkspaceDisplayNames["global"] = {};
|
|
8026
|
-
allWorkspacesMap.forEach((displayName, workspaceId) => {
|
|
8027
|
-
runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
|
|
8028
|
-
});
|
|
8029
7781
|
}
|
|
8030
|
-
|
|
8031
|
-
initializedWithLineIds = targetLineIds;
|
|
8032
|
-
console.log("\u2705 Workspace display names initialized from Supabase:", runtimeWorkspaceDisplayNames);
|
|
8033
|
-
console.log("\u2705 Initialized with line IDs:", initializedWithLineIds);
|
|
8034
|
-
} catch (error) {
|
|
8035
|
-
console.error("\u274C Failed to initialize workspace display names from Supabase:", error);
|
|
8036
|
-
} finally {
|
|
8037
|
-
isInitializing = false;
|
|
7782
|
+
return;
|
|
8038
7783
|
}
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
if (isInitialized || isInitializing) {
|
|
8043
|
-
console.log("\u{1F504} Already initialized or initializing");
|
|
7784
|
+
if (initializationPromise) {
|
|
7785
|
+
console.log("\u{1F504} Already initializing, waiting for completion...");
|
|
7786
|
+
await initializationPromise;
|
|
8044
7787
|
if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
|
|
8045
|
-
console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
|
|
7788
|
+
console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
|
|
8046
7789
|
try {
|
|
8047
7790
|
const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
|
|
8048
7791
|
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
@@ -8060,7 +7803,12 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
|
|
|
8060
7803
|
};
|
|
8061
7804
|
var forceRefreshWorkspaceDisplayNames = async (lineId) => {
|
|
8062
7805
|
console.log("\u{1F504} forceRefreshWorkspaceDisplayNames called for lineId:", lineId);
|
|
7806
|
+
if (initializationPromise) {
|
|
7807
|
+
console.log("\u{1F504} Waiting for existing initialization to complete before refresh...");
|
|
7808
|
+
await initializationPromise;
|
|
7809
|
+
}
|
|
8063
7810
|
clearWorkspaceDisplayNamesCache();
|
|
7811
|
+
initializationPromise = null;
|
|
8064
7812
|
await initializeWorkspaceDisplayNames(lineId);
|
|
8065
7813
|
};
|
|
8066
7814
|
console.log("\u{1F504} Module loaded, will initialize lazily when first function is called");
|
|
@@ -8208,6 +7956,7 @@ var clearWorkspaceDisplayNamesCache = () => {
|
|
|
8208
7956
|
isInitialized = false;
|
|
8209
7957
|
isInitializing = false;
|
|
8210
7958
|
initializedWithLineIds = [];
|
|
7959
|
+
initializationPromise = null;
|
|
8211
7960
|
};
|
|
8212
7961
|
|
|
8213
7962
|
// src/lib/hooks/useWorkspaceDisplayNames.ts
|
|
@@ -8471,32 +8220,12 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8471
8220
|
return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
|
|
8472
8221
|
}, [entityConfig.companyId]);
|
|
8473
8222
|
const schema = databaseConfig.schema ?? "public";
|
|
8474
|
-
const fetchWorkspaceMetrics = useCallback(async (
|
|
8223
|
+
const fetchWorkspaceMetrics = useCallback(async () => {
|
|
8475
8224
|
if (!initialized) {
|
|
8476
8225
|
setLoading(true);
|
|
8477
8226
|
}
|
|
8478
8227
|
setError(null);
|
|
8479
8228
|
try {
|
|
8480
|
-
const cacheKey = cacheService.generateKey(
|
|
8481
|
-
"all-workspace-metrics",
|
|
8482
|
-
entityConfig.companyId,
|
|
8483
|
-
queryDate,
|
|
8484
|
-
queryShiftId
|
|
8485
|
-
);
|
|
8486
|
-
if (!skipCache && !loading) {
|
|
8487
|
-
const cachedData = cacheService.get(cacheKey, {
|
|
8488
|
-
storage: "memory",
|
|
8489
|
-
duration: 5 * 60 * 1e3
|
|
8490
|
-
// 5 minutes
|
|
8491
|
-
});
|
|
8492
|
-
if (cachedData) {
|
|
8493
|
-
console.log("[useAllWorkspaceMetrics] Using cached data for:", cacheKey);
|
|
8494
|
-
setWorkspaces(cachedData);
|
|
8495
|
-
setInitialized(true);
|
|
8496
|
-
setLoading(false);
|
|
8497
|
-
return;
|
|
8498
|
-
}
|
|
8499
|
-
}
|
|
8500
8229
|
console.log("Fetching all workspace metrics with params:", {
|
|
8501
8230
|
queryDate,
|
|
8502
8231
|
queryShiftId,
|
|
@@ -8546,11 +8275,6 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8546
8275
|
}));
|
|
8547
8276
|
setWorkspaces(transformedData);
|
|
8548
8277
|
setInitialized(true);
|
|
8549
|
-
cacheService.set(cacheKey, transformedData, {
|
|
8550
|
-
storage: "memory",
|
|
8551
|
-
duration: 5 * 60 * 1e3
|
|
8552
|
-
// 5 minutes
|
|
8553
|
-
});
|
|
8554
8278
|
} catch (err) {
|
|
8555
8279
|
console.error("Error fetching all workspace metrics:", err);
|
|
8556
8280
|
setError({ message: err.message, code: err.code || "FETCH_ERROR" });
|
|
@@ -8575,14 +8299,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8575
8299
|
},
|
|
8576
8300
|
async (payload) => {
|
|
8577
8301
|
console.log("All workspace metrics update received:", payload);
|
|
8578
|
-
|
|
8579
|
-
"all-workspace-metrics",
|
|
8580
|
-
entityConfig.companyId,
|
|
8581
|
-
queryDate,
|
|
8582
|
-
queryShiftId
|
|
8583
|
-
);
|
|
8584
|
-
cacheService.delete(cacheKey, { storage: "memory" });
|
|
8585
|
-
await fetchWorkspaceMetrics(true);
|
|
8302
|
+
await fetchWorkspaceMetrics();
|
|
8586
8303
|
}
|
|
8587
8304
|
).subscribe();
|
|
8588
8305
|
return channel2;
|
|
@@ -8597,7 +8314,7 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8597
8314
|
useEffect(() => {
|
|
8598
8315
|
setInitialized(false);
|
|
8599
8316
|
}, [queryDate, queryShiftId]);
|
|
8600
|
-
const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(
|
|
8317
|
+
const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
|
|
8601
8318
|
return useMemo(
|
|
8602
8319
|
() => ({ workspaces, loading, error, refreshWorkspaces }),
|
|
8603
8320
|
[workspaces, loading, error, refreshWorkspaces]
|
|
@@ -19880,11 +19597,13 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
19880
19597
|
requireAuth: true,
|
|
19881
19598
|
...options
|
|
19882
19599
|
};
|
|
19883
|
-
|
|
19600
|
+
const WithAuthComponent = React19.memo(function WithAuthComponent2(props) {
|
|
19884
19601
|
const { session, loading } = useAuth();
|
|
19885
19602
|
const router = useRouter();
|
|
19886
19603
|
React19.useEffect(() => {
|
|
19887
|
-
|
|
19604
|
+
if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
|
|
19605
|
+
console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
|
|
19606
|
+
}
|
|
19888
19607
|
}, [session, loading]);
|
|
19889
19608
|
React19.useEffect(() => {
|
|
19890
19609
|
if (!loading && defaultOptions.requireAuth && !session) {
|
|
@@ -19899,7 +19618,9 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
19899
19618
|
return null;
|
|
19900
19619
|
}
|
|
19901
19620
|
return /* @__PURE__ */ jsx(WrappedComponent2, { ...props });
|
|
19902
|
-
};
|
|
19621
|
+
});
|
|
19622
|
+
WithAuthComponent.displayName = `withAuth(${WrappedComponent2.displayName || WrappedComponent2.name || "Component"})`;
|
|
19623
|
+
return WithAuthComponent;
|
|
19903
19624
|
};
|
|
19904
19625
|
var LoginPage = ({
|
|
19905
19626
|
onRateLimitCheck,
|
|
@@ -26487,7 +26208,7 @@ var BottlenecksContent = ({
|
|
|
26487
26208
|
const [duration, setDuration] = useState(0);
|
|
26488
26209
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
26489
26210
|
const [activeFilter, setActiveFilter] = useState(initialFilter);
|
|
26490
|
-
const previousFilterRef = useRef(
|
|
26211
|
+
const previousFilterRef = useRef("");
|
|
26491
26212
|
const [allVideos, setAllVideos] = useState([]);
|
|
26492
26213
|
const [isLoading, setIsLoading] = useState(true);
|
|
26493
26214
|
const [isCategoryLoading, setIsCategoryLoading] = useState(false);
|
|
@@ -26537,7 +26258,7 @@ var BottlenecksContent = ({
|
|
|
26537
26258
|
console.warn("S3 configuration not found in dashboard config");
|
|
26538
26259
|
return null;
|
|
26539
26260
|
}
|
|
26540
|
-
return
|
|
26261
|
+
return videoPrefetchManager.getS3Service(dashboardConfig);
|
|
26541
26262
|
}, [dashboardConfig]);
|
|
26542
26263
|
const {
|
|
26543
26264
|
data: prefetchData,
|
|
@@ -26605,6 +26326,85 @@ var BottlenecksContent = ({
|
|
|
26605
26326
|
}, [workspaceId, date, s3ClipsService, shift, dashboardConfig, updateClipCounts, updateVideoIndex]);
|
|
26606
26327
|
const loadingCategoryRef = useRef(null);
|
|
26607
26328
|
const videoRetryCountRef = useRef(0);
|
|
26329
|
+
const loadingVideosRef = useRef(/* @__PURE__ */ new Set());
|
|
26330
|
+
const loadedVideosMapRef = useRef(/* @__PURE__ */ new Map());
|
|
26331
|
+
const ensureVideosLoaded = useCallback(async (centerIndex) => {
|
|
26332
|
+
if (!s3ClipsService || !workspaceId || !isMountedRef.current) return;
|
|
26333
|
+
const currentFilter = activeFilterRef.current;
|
|
26334
|
+
const currentVideoIndex = videoIndexRef.current;
|
|
26335
|
+
let effectiveFilter = currentFilter;
|
|
26336
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
26337
|
+
const category = sopCategories.find((cat) => cat.id === currentFilter);
|
|
26338
|
+
if (category && category.s3FolderName) {
|
|
26339
|
+
effectiveFilter = category.s3FolderName;
|
|
26340
|
+
}
|
|
26341
|
+
}
|
|
26342
|
+
const cacheKey = `${effectiveFilter}:${date}:${shift}`;
|
|
26343
|
+
if (!loadedVideosMapRef.current.has(cacheKey)) {
|
|
26344
|
+
loadedVideosMapRef.current.set(cacheKey, /* @__PURE__ */ new Set());
|
|
26345
|
+
}
|
|
26346
|
+
const loadedIndices = loadedVideosMapRef.current.get(cacheKey);
|
|
26347
|
+
const indicesToLoad = [];
|
|
26348
|
+
const rangeBefore = 1;
|
|
26349
|
+
const rangeAfter = 3;
|
|
26350
|
+
for (let i = Math.max(0, centerIndex - rangeBefore); i <= Math.min(clipCounts[effectiveFilter] - 1, centerIndex + rangeAfter); i++) {
|
|
26351
|
+
if (!loadedIndices.has(i) && !loadingVideosRef.current.has(i)) {
|
|
26352
|
+
indicesToLoad.push(i);
|
|
26353
|
+
}
|
|
26354
|
+
}
|
|
26355
|
+
if (indicesToLoad.length === 0) return;
|
|
26356
|
+
console.log(`[ensureVideosLoaded] Preloading ${indicesToLoad.length} videos around index ${centerIndex}: [${indicesToLoad.join(", ")}]`);
|
|
26357
|
+
indicesToLoad.forEach((idx) => loadingVideosRef.current.add(idx));
|
|
26358
|
+
const loadPromises = indicesToLoad.map(async (index) => {
|
|
26359
|
+
try {
|
|
26360
|
+
let video = null;
|
|
26361
|
+
if (currentVideoIndex && currentVideoIndex.byCategory && currentVideoIndex.allVideos.length > 0) {
|
|
26362
|
+
video = await s3ClipsService.getVideoFromIndex(
|
|
26363
|
+
currentVideoIndex,
|
|
26364
|
+
effectiveFilter,
|
|
26365
|
+
index,
|
|
26366
|
+
true,
|
|
26367
|
+
// includeCycleTime - OK for preloading
|
|
26368
|
+
false
|
|
26369
|
+
// includeMetadata - NO metadata during bulk preloading to prevent flooding
|
|
26370
|
+
);
|
|
26371
|
+
}
|
|
26372
|
+
if (!video) {
|
|
26373
|
+
const operationalDate = date || getOperationalDate();
|
|
26374
|
+
const shiftStr = shift?.toString() || "0";
|
|
26375
|
+
video = await s3ClipsService.getClipByIndex(
|
|
26376
|
+
workspaceId,
|
|
26377
|
+
operationalDate,
|
|
26378
|
+
shiftStr,
|
|
26379
|
+
effectiveFilter,
|
|
26380
|
+
index,
|
|
26381
|
+
true,
|
|
26382
|
+
// includeCycleTime - OK for preloading
|
|
26383
|
+
false
|
|
26384
|
+
// includeMetadata - NO metadata during bulk preloading to prevent flooding
|
|
26385
|
+
);
|
|
26386
|
+
}
|
|
26387
|
+
if (video && isMountedRef.current) {
|
|
26388
|
+
setAllVideos((prev) => {
|
|
26389
|
+
const exists = prev.some((v) => v.id === video.id);
|
|
26390
|
+
if (!exists) {
|
|
26391
|
+
return [...prev, video];
|
|
26392
|
+
}
|
|
26393
|
+
return prev;
|
|
26394
|
+
});
|
|
26395
|
+
loadedIndices.add(index);
|
|
26396
|
+
preloadVideoUrl(video.src);
|
|
26397
|
+
}
|
|
26398
|
+
} catch (error2) {
|
|
26399
|
+
console.warn(`[ensureVideosLoaded] Failed to load video at index ${index}:`, error2);
|
|
26400
|
+
} finally {
|
|
26401
|
+
loadingVideosRef.current.delete(index);
|
|
26402
|
+
}
|
|
26403
|
+
});
|
|
26404
|
+
Promise.all(loadPromises).catch((err) => {
|
|
26405
|
+
console.warn("[ensureVideosLoaded] Some videos failed to preload:", err);
|
|
26406
|
+
});
|
|
26407
|
+
}, [s3ClipsService, workspaceId, clipCounts, sopCategories, date, shift]);
|
|
26608
26408
|
const loadFirstVideoForCategory = useCallback(async (category) => {
|
|
26609
26409
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
26610
26410
|
const targetCategory = category || activeFilterRef.current;
|
|
@@ -26641,8 +26441,16 @@ var BottlenecksContent = ({
|
|
|
26641
26441
|
);
|
|
26642
26442
|
if (firstVideo) {
|
|
26643
26443
|
console.log(`[BottlenecksContent] Successfully loaded first video via direct S3 method`);
|
|
26644
|
-
setAllVideos(
|
|
26444
|
+
setAllVideos((prev) => {
|
|
26445
|
+
const exists = prev.some((v) => v.id === firstVideo.id);
|
|
26446
|
+
if (!exists) {
|
|
26447
|
+
return [...prev, firstVideo];
|
|
26448
|
+
}
|
|
26449
|
+
return prev;
|
|
26450
|
+
});
|
|
26645
26451
|
preloadVideoUrl(firstVideo.src);
|
|
26452
|
+
setIsLoading(false);
|
|
26453
|
+
setHasInitialLoad(true);
|
|
26646
26454
|
loadingCategoryRef.current = null;
|
|
26647
26455
|
setIsCategoryLoading(false);
|
|
26648
26456
|
return;
|
|
@@ -26665,7 +26473,13 @@ var BottlenecksContent = ({
|
|
|
26665
26473
|
);
|
|
26666
26474
|
if (firstVideo && isMountedRef.current) {
|
|
26667
26475
|
console.log(`[BottlenecksContent] Successfully loaded first video via video index`);
|
|
26668
|
-
setAllVideos(
|
|
26476
|
+
setAllVideos((prev) => {
|
|
26477
|
+
const exists = prev.some((v) => v.id === firstVideo.id);
|
|
26478
|
+
if (!exists) {
|
|
26479
|
+
return [...prev, firstVideo];
|
|
26480
|
+
}
|
|
26481
|
+
return prev;
|
|
26482
|
+
});
|
|
26669
26483
|
preloadVideoUrl(firstVideo.src);
|
|
26670
26484
|
setIsCategoryLoading(false);
|
|
26671
26485
|
return;
|
|
@@ -26690,15 +26504,17 @@ var BottlenecksContent = ({
|
|
|
26690
26504
|
}
|
|
26691
26505
|
}, [workspaceId, date, s3ClipsService, clipCounts, videoIndex, shift, updateClipCounts, updateVideoIndex]);
|
|
26692
26506
|
useEffect(() => {
|
|
26693
|
-
if (s3ClipsService) {
|
|
26507
|
+
if (s3ClipsService && !prefetchData) {
|
|
26694
26508
|
fetchClipCounts();
|
|
26695
26509
|
}
|
|
26696
|
-
}, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex]);
|
|
26510
|
+
}, [workspaceId, date, shift, s3ClipsService, fetchClipCounts, updateClipCounts, updateVideoIndex, prefetchData]);
|
|
26697
26511
|
useEffect(() => {
|
|
26698
26512
|
if (prefetchData) {
|
|
26699
26513
|
console.log(`[BottlenecksContent] Received prefetch update - status: ${prefetchStatus}, videos: ${prefetchData.videoIndex.allVideos.length}, ID: ${prefetchData.videoIndex._debugId || "NO_ID"}`);
|
|
26700
26514
|
updateClipCounts(prefetchData.counts);
|
|
26701
|
-
|
|
26515
|
+
if (prefetchData.videoIndex.allVideos.length > 0) {
|
|
26516
|
+
updateVideoIndex(prefetchData.videoIndex);
|
|
26517
|
+
}
|
|
26702
26518
|
if (!hasInitialLoad && prefetchData.videoIndex.allVideos.length > 0) {
|
|
26703
26519
|
setIsLoading(false);
|
|
26704
26520
|
setHasInitialLoad(true);
|
|
@@ -26706,10 +26522,45 @@ var BottlenecksContent = ({
|
|
|
26706
26522
|
}
|
|
26707
26523
|
}, [prefetchData, prefetchStatus, updateClipCounts, updateVideoIndex, hasInitialLoad]);
|
|
26708
26524
|
useEffect(() => {
|
|
26709
|
-
if (s3ClipsService &&
|
|
26710
|
-
|
|
26525
|
+
if (s3ClipsService && clipCounts[activeFilter] > 0) {
|
|
26526
|
+
const hasVideosForCurrentFilter = allVideos.some((video) => {
|
|
26527
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
26528
|
+
const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
|
|
26529
|
+
if (selectedCategory) {
|
|
26530
|
+
return video.type === selectedCategory.id;
|
|
26531
|
+
}
|
|
26532
|
+
}
|
|
26533
|
+
if (activeFilter === "all") return true;
|
|
26534
|
+
if (activeFilter === "low_value") {
|
|
26535
|
+
return video.type === "low_value";
|
|
26536
|
+
}
|
|
26537
|
+
if (activeFilter === "idle_time") {
|
|
26538
|
+
return video.type === "idle_time";
|
|
26539
|
+
}
|
|
26540
|
+
if (activeFilter === "sop_deviations") {
|
|
26541
|
+
return video.type === "missing_quality_check";
|
|
26542
|
+
}
|
|
26543
|
+
if (activeFilter === "best_cycle_time") {
|
|
26544
|
+
return video.type === "best_cycle_time";
|
|
26545
|
+
}
|
|
26546
|
+
if (activeFilter === "worst_cycle_time") {
|
|
26547
|
+
return video.type === "worst_cycle_time";
|
|
26548
|
+
}
|
|
26549
|
+
if (activeFilter === "cycle_completion") {
|
|
26550
|
+
return video.type === "cycle_completion";
|
|
26551
|
+
}
|
|
26552
|
+
if (activeFilter === "long_cycle_time") {
|
|
26553
|
+
return video.type === "long_cycle_time";
|
|
26554
|
+
}
|
|
26555
|
+
return video.type === "bottleneck" && video.severity === activeFilter;
|
|
26556
|
+
});
|
|
26557
|
+
if (!hasVideosForCurrentFilter) {
|
|
26558
|
+
loadFirstVideoForCategory(activeFilter);
|
|
26559
|
+
} else {
|
|
26560
|
+
setIsCategoryLoading(false);
|
|
26561
|
+
}
|
|
26711
26562
|
}
|
|
26712
|
-
}, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory]);
|
|
26563
|
+
}, [activeFilter, s3ClipsService, videoIndex, clipCounts, loadFirstVideoForCategory, allVideos, sopCategories]);
|
|
26713
26564
|
useEffect(() => {
|
|
26714
26565
|
if (previousFilterRef.current !== activeFilter) {
|
|
26715
26566
|
console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
|
|
@@ -26778,26 +26629,25 @@ var BottlenecksContent = ({
|
|
|
26778
26629
|
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
26779
26630
|
});
|
|
26780
26631
|
}, [activeFilter, allVideos, sopCategories]);
|
|
26781
|
-
useEffect(() => {
|
|
26782
|
-
if (filteredVideos.length === 0) return;
|
|
26783
|
-
const upcoming = [];
|
|
26784
|
-
if (currentIndex + 1 < filteredVideos.length && filteredVideos[currentIndex + 1]) {
|
|
26785
|
-
upcoming.push(filteredVideos[currentIndex + 1].src);
|
|
26786
|
-
}
|
|
26787
|
-
if (currentIndex - 1 >= 0 && currentIndex - 1 < filteredVideos.length && filteredVideos[currentIndex - 1]) {
|
|
26788
|
-
upcoming.push(filteredVideos[currentIndex - 1].src);
|
|
26789
|
-
}
|
|
26790
|
-
if (upcoming.length > 0) {
|
|
26791
|
-
preloadVideosUrl(upcoming);
|
|
26792
|
-
}
|
|
26793
|
-
}, [currentIndex, filteredVideos]);
|
|
26794
26632
|
useEffect(() => {
|
|
26795
26633
|
if (isNavigating && currentIndex < filteredVideos.length) {
|
|
26796
26634
|
setIsNavigating(false);
|
|
26797
26635
|
setError(null);
|
|
26798
26636
|
videoRetryCountRef.current = 0;
|
|
26637
|
+
ensureVideosLoaded(currentIndex);
|
|
26799
26638
|
}
|
|
26800
26639
|
}, [isNavigating, currentIndex, filteredVideos.length]);
|
|
26640
|
+
useEffect(() => {
|
|
26641
|
+
if (!isPlaying || !isMountedRef.current) return;
|
|
26642
|
+
const preloadInterval = setInterval(() => {
|
|
26643
|
+
if (isMountedRef.current) {
|
|
26644
|
+
const currentIdx = currentIndexRef.current;
|
|
26645
|
+
console.log(`[Background Preloader] Ensuring videos loaded around index ${currentIdx}`);
|
|
26646
|
+
ensureVideosLoaded(currentIdx);
|
|
26647
|
+
}
|
|
26648
|
+
}, 2e3);
|
|
26649
|
+
return () => clearInterval(preloadInterval);
|
|
26650
|
+
}, [isPlaying]);
|
|
26801
26651
|
const handleNext = useCallback(async () => {
|
|
26802
26652
|
if (!isMountedRef.current) return;
|
|
26803
26653
|
const currentIdx = currentIndexRef.current;
|
|
@@ -26818,6 +26668,7 @@ var BottlenecksContent = ({
|
|
|
26818
26668
|
}
|
|
26819
26669
|
if (nextIndex < filteredVideos.length) {
|
|
26820
26670
|
setIsNavigating(false);
|
|
26671
|
+
ensureVideosLoaded(nextIndex);
|
|
26821
26672
|
return;
|
|
26822
26673
|
}
|
|
26823
26674
|
if (isMountedRef.current) {
|
|
@@ -26835,8 +26686,8 @@ var BottlenecksContent = ({
|
|
|
26835
26686
|
nextIndex,
|
|
26836
26687
|
true,
|
|
26837
26688
|
// includeCycleTime
|
|
26838
|
-
|
|
26839
|
-
// includeMetadata
|
|
26689
|
+
false
|
|
26690
|
+
// includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
|
|
26840
26691
|
);
|
|
26841
26692
|
} else {
|
|
26842
26693
|
console.warn(`[BottlenecksContent] Video index not ready for navigation: ID: ${currentVideoIndex?._debugId || "NO_ID"}, byCategory exists = ${!!currentVideoIndex?.byCategory}, allVideos = ${currentVideoIndex?.allVideos?.length || 0}`);
|
|
@@ -26849,7 +26700,11 @@ var BottlenecksContent = ({
|
|
|
26849
26700
|
operationalDate,
|
|
26850
26701
|
shiftStr,
|
|
26851
26702
|
effectiveFilter,
|
|
26852
|
-
nextIndex
|
|
26703
|
+
nextIndex,
|
|
26704
|
+
true,
|
|
26705
|
+
// includeCycleTime - needed for main video display
|
|
26706
|
+
false
|
|
26707
|
+
// includeMetadata - DON'T fetch metadata during navigation to prevent flooding!
|
|
26853
26708
|
);
|
|
26854
26709
|
}
|
|
26855
26710
|
if (video && isMountedRef.current) {
|
|
@@ -26862,26 +26717,7 @@ var BottlenecksContent = ({
|
|
|
26862
26717
|
return prev;
|
|
26863
26718
|
});
|
|
26864
26719
|
preloadVideoUrl(video.src);
|
|
26865
|
-
|
|
26866
|
-
setTimeout(() => {
|
|
26867
|
-
const videoIndexForPreload = videoIndexRef.current;
|
|
26868
|
-
if (videoIndexForPreload && s3ClipsService && isMountedRef.current) {
|
|
26869
|
-
s3ClipsService.getVideoFromIndex(videoIndexForPreload, effectiveFilter, nextIndex + 1, true, true).then((nextVideo) => {
|
|
26870
|
-
if (nextVideo && isMountedRef.current) {
|
|
26871
|
-
setAllVideos((prev) => {
|
|
26872
|
-
if (!prev.some((v) => v.id === nextVideo.id)) {
|
|
26873
|
-
const newVideos = [...prev, nextVideo];
|
|
26874
|
-
return newVideos;
|
|
26875
|
-
}
|
|
26876
|
-
return prev;
|
|
26877
|
-
});
|
|
26878
|
-
preloadVideoUrl(nextVideo.src);
|
|
26879
|
-
}
|
|
26880
|
-
}).catch(() => {
|
|
26881
|
-
});
|
|
26882
|
-
}
|
|
26883
|
-
}, 100);
|
|
26884
|
-
}
|
|
26720
|
+
ensureVideosLoaded(nextIndex);
|
|
26885
26721
|
}
|
|
26886
26722
|
} catch (error2) {
|
|
26887
26723
|
console.error("Error loading next video:", error2);
|
|
@@ -26903,15 +26739,43 @@ var BottlenecksContent = ({
|
|
|
26903
26739
|
if (prevIndex < filteredVideos.length) {
|
|
26904
26740
|
setCurrentIndex(prevIndex);
|
|
26905
26741
|
setError(null);
|
|
26742
|
+
ensureVideosLoaded(prevIndex);
|
|
26906
26743
|
}
|
|
26907
26744
|
}
|
|
26908
26745
|
}, [filteredVideos.length]);
|
|
26746
|
+
const currentVideo = useMemo(() => {
|
|
26747
|
+
if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
|
|
26748
|
+
return null;
|
|
26749
|
+
}
|
|
26750
|
+
return filteredVideos[currentIndex];
|
|
26751
|
+
}, [filteredVideos, currentIndex]);
|
|
26909
26752
|
const handleVideoReady = useCallback((player) => {
|
|
26910
26753
|
console.log("Video.js player ready");
|
|
26911
26754
|
}, []);
|
|
26912
|
-
const handleVideoPlay = useCallback((player) => {
|
|
26755
|
+
const handleVideoPlay = useCallback(async (player) => {
|
|
26913
26756
|
setIsPlaying(true);
|
|
26914
|
-
|
|
26757
|
+
const currentIdx = currentIndexRef.current;
|
|
26758
|
+
ensureVideosLoaded(currentIdx);
|
|
26759
|
+
if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
|
|
26760
|
+
try {
|
|
26761
|
+
const originalUri = currentVideo.originalUri || currentVideo.src;
|
|
26762
|
+
if (originalUri && originalUri.includes("s3://")) {
|
|
26763
|
+
const metadata = await s3ClipsService.getFullMetadata(originalUri);
|
|
26764
|
+
if (metadata && isMountedRef.current) {
|
|
26765
|
+
setAllVideos((prev) => prev.map(
|
|
26766
|
+
(v) => v.id === currentVideo.id ? {
|
|
26767
|
+
...v,
|
|
26768
|
+
creation_timestamp: metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp,
|
|
26769
|
+
cycle_time_seconds: metadata.original_task_metadata?.cycle_time || v.cycle_time_seconds
|
|
26770
|
+
} : v
|
|
26771
|
+
));
|
|
26772
|
+
}
|
|
26773
|
+
}
|
|
26774
|
+
} catch (error2) {
|
|
26775
|
+
console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
|
|
26776
|
+
}
|
|
26777
|
+
}
|
|
26778
|
+
}, [currentVideo, s3ClipsService]);
|
|
26915
26779
|
const handleVideoPause = useCallback((player) => {
|
|
26916
26780
|
setIsPlaying(false);
|
|
26917
26781
|
}, []);
|
|
@@ -26938,13 +26802,6 @@ var BottlenecksContent = ({
|
|
|
26938
26802
|
fetchInProgressRef.current.clear();
|
|
26939
26803
|
setIsCategoryLoading(false);
|
|
26940
26804
|
setIsNavigating(false);
|
|
26941
|
-
if (s3ClipsService) {
|
|
26942
|
-
try {
|
|
26943
|
-
s3ClipsService.dispose();
|
|
26944
|
-
} catch (error2) {
|
|
26945
|
-
console.warn("[BottlenecksContent] Error disposing S3 service:", error2);
|
|
26946
|
-
}
|
|
26947
|
-
}
|
|
26948
26805
|
};
|
|
26949
26806
|
}, [s3ClipsService]);
|
|
26950
26807
|
useEffect(() => {
|
|
@@ -26985,12 +26842,6 @@ var BottlenecksContent = ({
|
|
|
26985
26842
|
counts.bottlenecks = counts.bottleneck || counts.bottlenecks || 0;
|
|
26986
26843
|
return counts;
|
|
26987
26844
|
}, [clipCounts]);
|
|
26988
|
-
const currentVideo = useMemo(() => {
|
|
26989
|
-
if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
|
|
26990
|
-
return null;
|
|
26991
|
-
}
|
|
26992
|
-
return filteredVideos[currentIndex];
|
|
26993
|
-
}, [filteredVideos, currentIndex]);
|
|
26994
26845
|
const getClipTypeLabel = (video) => {
|
|
26995
26846
|
if (!video) return "";
|
|
26996
26847
|
switch (video.type) {
|
|
@@ -27225,8 +27076,8 @@ var BottlenecksContent = ({
|
|
|
27225
27076
|
] }),
|
|
27226
27077
|
/* Priority 1: Show loading if initial load hasn't completed yet */
|
|
27227
27078
|
isLoading && !hasInitialLoad ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
|
|
27228
|
-
/* Priority 2: Show loading if category is loading
|
|
27229
|
-
isCategoryLoading ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
|
|
27079
|
+
/* Priority 2: Show loading if category is loading BUT only if no video is available */
|
|
27080
|
+
isCategoryLoading && (!filteredVideos.length || !currentVideo) ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading videos..." }) }) }) }) : (
|
|
27230
27081
|
/* Priority 3: Show loading if navigating and current video not available */
|
|
27231
27082
|
isNavigating || currentIndex >= filteredVideos.length && currentIndex < clipCounts[activeFilter] ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }) }) }) : (
|
|
27232
27083
|
/* Priority 4: Show video if we have filtered videos and current video */
|
|
@@ -31583,7 +31434,7 @@ var HelpView = ({
|
|
|
31583
31434
|
onClick: handleBackClick,
|
|
31584
31435
|
className: "flex items-center text-gray-600 hover:text-gray-900",
|
|
31585
31436
|
children: [
|
|
31586
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
31437
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
|
|
31587
31438
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
31588
31439
|
]
|
|
31589
31440
|
}
|
|
@@ -32024,9 +31875,25 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
|
32024
31875
|
return function WithWorkspaceDisplayNamesWrapper(props) {
|
|
32025
31876
|
const [isInitialized2, setIsInitialized] = useState(false);
|
|
32026
31877
|
const [error, setError] = useState(null);
|
|
31878
|
+
const [lastInitKey, setLastInitKey] = useState("");
|
|
31879
|
+
const lineIdsKey = useMemo(() => {
|
|
31880
|
+
if (!props.lineIds) return "";
|
|
31881
|
+
if (Array.isArray(props.lineIds)) {
|
|
31882
|
+
return props.lineIds.sort().join(",");
|
|
31883
|
+
}
|
|
31884
|
+
const values = Object.values(props.lineIds).filter(Boolean);
|
|
31885
|
+
return values.sort().join(",");
|
|
31886
|
+
}, [props.lineIds]);
|
|
31887
|
+
const initKey = useMemo(() => {
|
|
31888
|
+
return `${lineIdsKey}-${props.selectedLineId || ""}-${props.factoryViewId || ""}-${initializeFor}`;
|
|
31889
|
+
}, [lineIdsKey, props.selectedLineId, props.factoryViewId]);
|
|
32027
31890
|
useEffect(() => {
|
|
32028
|
-
|
|
32029
|
-
|
|
31891
|
+
if (initKey === lastInitKey && isInitialized2) {
|
|
31892
|
+
return;
|
|
31893
|
+
}
|
|
31894
|
+
if (initKey !== lastInitKey) {
|
|
31895
|
+
setError(null);
|
|
31896
|
+
}
|
|
32030
31897
|
const initializeDisplayNames = async () => {
|
|
32031
31898
|
try {
|
|
32032
31899
|
const { lineIds, selectedLineId, factoryViewId } = props;
|
|
@@ -32052,20 +31919,17 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
|
32052
31919
|
await preInitializeWorkspaceDisplayNames();
|
|
32053
31920
|
}
|
|
32054
31921
|
setIsInitialized(true);
|
|
31922
|
+
setLastInitKey(initKey);
|
|
32055
31923
|
} catch (err) {
|
|
32056
31924
|
console.error("Failed to initialize workspace display names:", err);
|
|
32057
31925
|
setError(err);
|
|
32058
31926
|
setIsInitialized(true);
|
|
31927
|
+
setLastInitKey(initKey);
|
|
32059
31928
|
}
|
|
32060
31929
|
};
|
|
32061
31930
|
initializeDisplayNames();
|
|
32062
|
-
}, [
|
|
32063
|
-
|
|
32064
|
-
props.selectedLineId,
|
|
32065
|
-
props.factoryViewId,
|
|
32066
|
-
initializeFor
|
|
32067
|
-
]);
|
|
32068
|
-
if (!isInitialized2 && showLoading) {
|
|
31931
|
+
}, [initKey]);
|
|
31932
|
+
if (!isInitialized2 && showLoading && lastInitKey === "") {
|
|
32069
31933
|
return /* @__PURE__ */ jsx(LoadingPage, { message: loadingMessage });
|
|
32070
31934
|
}
|
|
32071
31935
|
if (error && showLoading) {
|
|
@@ -32705,7 +32569,7 @@ var KPIDetailView = ({
|
|
|
32705
32569
|
onClick: handleBackClick,
|
|
32706
32570
|
className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
|
|
32707
32571
|
children: [
|
|
32708
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
32572
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
|
|
32709
32573
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
32710
32574
|
]
|
|
32711
32575
|
}
|
|
@@ -33077,7 +32941,7 @@ var KPIsOverviewView = ({
|
|
|
33077
32941
|
onClick: handleBackClick,
|
|
33078
32942
|
className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
|
|
33079
32943
|
children: [
|
|
33080
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
32944
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
|
|
33081
32945
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
33082
32946
|
]
|
|
33083
32947
|
}
|
|
@@ -33099,7 +32963,7 @@ var KPIsOverviewView = ({
|
|
|
33099
32963
|
onClick: handleBackClick,
|
|
33100
32964
|
className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
|
|
33101
32965
|
children: [
|
|
33102
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
32966
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
|
|
33103
32967
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
33104
32968
|
]
|
|
33105
32969
|
}
|
|
@@ -33124,7 +32988,7 @@ var KPIsOverviewView = ({
|
|
|
33124
32988
|
onClick: handleBackClick,
|
|
33125
32989
|
className: "absolute left-0 flex items-center text-gray-600 hover:text-gray-900 cursor-pointer",
|
|
33126
32990
|
children: [
|
|
33127
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
32991
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon$1, { className: "h-5 w-5" }),
|
|
33128
32992
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
33129
32993
|
]
|
|
33130
32994
|
}
|
|
@@ -34136,7 +34000,15 @@ var ShiftsView = ({
|
|
|
34136
34000
|
className = ""
|
|
34137
34001
|
}) => {
|
|
34138
34002
|
const supabase = useSupabase();
|
|
34139
|
-
|
|
34003
|
+
useEffect(() => {
|
|
34004
|
+
console.log("[ShiftsView] Component mounted/re-rendered", {
|
|
34005
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
34006
|
+
lineIds: lineIds.length
|
|
34007
|
+
});
|
|
34008
|
+
return () => {
|
|
34009
|
+
console.log("[ShiftsView] Component unmounting");
|
|
34010
|
+
};
|
|
34011
|
+
}, []);
|
|
34140
34012
|
const [lineConfigs, setLineConfigs] = useState(
|
|
34141
34013
|
() => lineIds.map((id3) => ({
|
|
34142
34014
|
id: id3,
|
|
@@ -34234,7 +34106,7 @@ var ShiftsView = ({
|
|
|
34234
34106
|
}
|
|
34235
34107
|
};
|
|
34236
34108
|
fetchShiftConfigs();
|
|
34237
|
-
}, [
|
|
34109
|
+
}, [lineIds, showToast]);
|
|
34238
34110
|
useCallback((lineId) => {
|
|
34239
34111
|
setLineConfigs((prev) => {
|
|
34240
34112
|
const typedPrev = prev;
|
|
@@ -34427,7 +34299,6 @@ var ShiftsView = ({
|
|
|
34427
34299
|
}));
|
|
34428
34300
|
}, []);
|
|
34429
34301
|
const handleSaveShifts = useCallback(async (lineId) => {
|
|
34430
|
-
if (!auth.user?.id && false) ;
|
|
34431
34302
|
setLineConfigs((prev) => prev.map(
|
|
34432
34303
|
(config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
|
|
34433
34304
|
));
|
|
@@ -34474,7 +34345,7 @@ var ShiftsView = ({
|
|
|
34474
34345
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
|
|
34475
34346
|
));
|
|
34476
34347
|
}
|
|
34477
|
-
}, [
|
|
34348
|
+
}, [lineConfigs, supabase, showToast]);
|
|
34478
34349
|
return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
|
|
34479
34350
|
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center relative", children: [
|
|
34480
34351
|
/* @__PURE__ */ jsxs(
|
|
@@ -34582,6 +34453,7 @@ var ShiftsView = ({
|
|
|
34582
34453
|
] })
|
|
34583
34454
|
] });
|
|
34584
34455
|
};
|
|
34456
|
+
var AuthenticatedShiftsView = withAuth(React19__default.memo(ShiftsView));
|
|
34585
34457
|
var ShiftsView_default = ShiftsView;
|
|
34586
34458
|
|
|
34587
34459
|
// src/lib/constants/actions.ts
|
|
@@ -35369,6 +35241,7 @@ var TargetsViewUI = ({
|
|
|
35369
35241
|
isLoading,
|
|
35370
35242
|
lineWorkspaces,
|
|
35371
35243
|
lineNames,
|
|
35244
|
+
dropdownStates,
|
|
35372
35245
|
savingLines,
|
|
35373
35246
|
saveSuccess,
|
|
35374
35247
|
selectedShift,
|
|
@@ -35401,7 +35274,7 @@ var TargetsViewUI = ({
|
|
|
35401
35274
|
onClick: onBack,
|
|
35402
35275
|
className: "flex items-center text-gray-600 hover:text-gray-900",
|
|
35403
35276
|
children: [
|
|
35404
|
-
/* @__PURE__ */ jsx(ArrowLeftIcon
|
|
35277
|
+
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-5 w-5" }),
|
|
35405
35278
|
/* @__PURE__ */ jsx("span", { className: "ml-2", children: "Back" })
|
|
35406
35279
|
]
|
|
35407
35280
|
}
|
|
@@ -35454,13 +35327,13 @@ var TargetsViewUI = ({
|
|
|
35454
35327
|
{
|
|
35455
35328
|
onClick: () => onToggleLineDropdown(lineId),
|
|
35456
35329
|
className: "flex items-center gap-3 text-lg font-medium transition-colors duration-200 \n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded-lg \n hover:bg-blue-50 px-3 py-2 group",
|
|
35457
|
-
"aria-expanded":
|
|
35330
|
+
"aria-expanded": dropdownStates[lineId],
|
|
35458
35331
|
"aria-controls": `line-${lineId}-content`,
|
|
35459
35332
|
children: [
|
|
35460
35333
|
/* @__PURE__ */ jsx(
|
|
35461
35334
|
ChevronDown,
|
|
35462
35335
|
{
|
|
35463
|
-
className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${
|
|
35336
|
+
className: `w-5 h-5 text-blue-500 transform transition-transform duration-200 ${dropdownStates[lineId] ? "rotate-180" : ""}`
|
|
35464
35337
|
}
|
|
35465
35338
|
),
|
|
35466
35339
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
@@ -35507,7 +35380,7 @@ var TargetsViewUI = ({
|
|
|
35507
35380
|
)
|
|
35508
35381
|
] })
|
|
35509
35382
|
] }) }),
|
|
35510
|
-
|
|
35383
|
+
dropdownStates[lineId] && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
|
|
35511
35384
|
skuEnabled && /* @__PURE__ */ jsx("div", { className: "px-6 py-4 border-b border-gray-200 bg-gray-50/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
35512
35385
|
/* @__PURE__ */ jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
|
|
35513
35386
|
/* @__PURE__ */ jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsx(
|
|
@@ -35544,68 +35417,71 @@ var TargetsViewUI = ({
|
|
|
35544
35417
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: "pieces per day" })
|
|
35545
35418
|
] })
|
|
35546
35419
|
] }) }),
|
|
35547
|
-
/* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) =>
|
|
35548
|
-
|
|
35549
|
-
|
|
35550
|
-
|
|
35551
|
-
|
|
35552
|
-
|
|
35553
|
-
/* @__PURE__ */
|
|
35554
|
-
"
|
|
35555
|
-
{
|
|
35556
|
-
|
|
35557
|
-
|
|
35558
|
-
|
|
35559
|
-
|
|
35560
|
-
|
|
35561
|
-
|
|
35562
|
-
|
|
35563
|
-
|
|
35564
|
-
|
|
35565
|
-
|
|
35566
|
-
|
|
35567
|
-
|
|
35568
|
-
|
|
35569
|
-
|
|
35570
|
-
|
|
35571
|
-
{
|
|
35572
|
-
|
|
35573
|
-
|
|
35574
|
-
|
|
35575
|
-
|
|
35576
|
-
|
|
35577
|
-
|
|
35578
|
-
|
|
35579
|
-
|
|
35580
|
-
|
|
35581
|
-
|
|
35582
|
-
|
|
35583
|
-
{
|
|
35584
|
-
|
|
35585
|
-
|
|
35586
|
-
|
|
35587
|
-
|
|
35588
|
-
|
|
35589
|
-
|
|
35590
|
-
|
|
35591
|
-
|
|
35592
|
-
|
|
35593
|
-
|
|
35594
|
-
|
|
35595
|
-
{
|
|
35596
|
-
|
|
35597
|
-
|
|
35598
|
-
|
|
35599
|
-
|
|
35600
|
-
|
|
35601
|
-
|
|
35602
|
-
|
|
35603
|
-
|
|
35604
|
-
|
|
35605
|
-
|
|
35606
|
-
|
|
35607
|
-
|
|
35608
|
-
|
|
35420
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
|
|
35421
|
+
const formattedName = formatWorkspaceName(workspace.name, lineId);
|
|
35422
|
+
return /* @__PURE__ */ jsx(
|
|
35423
|
+
"div",
|
|
35424
|
+
{
|
|
35425
|
+
className: "px-6 py-4 hover:bg-gray-50 transition-all duration-200",
|
|
35426
|
+
children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 items-center", children: [
|
|
35427
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: formattedName }) }),
|
|
35428
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsxs(
|
|
35429
|
+
"select",
|
|
35430
|
+
{
|
|
35431
|
+
value: workspace.actionType,
|
|
35432
|
+
onChange: (e) => {
|
|
35433
|
+
const newActionType = e.target.value;
|
|
35434
|
+
onActionTypeChange(lineId, workspace.id, newActionType);
|
|
35435
|
+
},
|
|
35436
|
+
className: "w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm",
|
|
35437
|
+
"aria-label": `Action type for ${formattedName}`,
|
|
35438
|
+
children: [
|
|
35439
|
+
/* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
|
|
35440
|
+
/* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
|
|
35441
|
+
]
|
|
35442
|
+
}
|
|
35443
|
+
) }),
|
|
35444
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
|
|
35445
|
+
"input",
|
|
35446
|
+
{
|
|
35447
|
+
type: "number",
|
|
35448
|
+
value: workspace.targetCycleTime === 0 ? "" : workspace.targetCycleTime,
|
|
35449
|
+
onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
|
|
35450
|
+
className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400",
|
|
35451
|
+
min: "0",
|
|
35452
|
+
step: "0.01",
|
|
35453
|
+
placeholder: "Enter cycle time"
|
|
35454
|
+
}
|
|
35455
|
+
) }),
|
|
35456
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
|
|
35457
|
+
"input",
|
|
35458
|
+
{
|
|
35459
|
+
type: "number",
|
|
35460
|
+
value: workspace.targetPPH === 0 ? "" : workspace.targetPPH,
|
|
35461
|
+
onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetPPH", Number(e.target.value) || ""),
|
|
35462
|
+
className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
|
|
35463
|
+
min: "0",
|
|
35464
|
+
step: "0.1",
|
|
35465
|
+
placeholder: "Enter PPH"
|
|
35466
|
+
}
|
|
35467
|
+
) }),
|
|
35468
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-2", children: /* @__PURE__ */ jsx(
|
|
35469
|
+
"input",
|
|
35470
|
+
{
|
|
35471
|
+
type: "number",
|
|
35472
|
+
value: workspace.targetDayOutput === 0 ? "" : workspace.targetDayOutput,
|
|
35473
|
+
onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetDayOutput", Number(e.target.value) || ""),
|
|
35474
|
+
className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400 \n placeholder:text-gray-400",
|
|
35475
|
+
min: "0",
|
|
35476
|
+
step: "1",
|
|
35477
|
+
placeholder: "Enter day output"
|
|
35478
|
+
}
|
|
35479
|
+
) })
|
|
35480
|
+
] })
|
|
35481
|
+
},
|
|
35482
|
+
workspace.id
|
|
35483
|
+
);
|
|
35484
|
+
}) })
|
|
35609
35485
|
] })
|
|
35610
35486
|
]
|
|
35611
35487
|
},
|
|
@@ -35638,7 +35514,6 @@ var TargetsView = ({
|
|
|
35638
35514
|
return lineIds.reduce((acc, lineId) => ({
|
|
35639
35515
|
...acc,
|
|
35640
35516
|
[lineId]: {
|
|
35641
|
-
isOpen: getStoredLineState2(lineId),
|
|
35642
35517
|
productId: "",
|
|
35643
35518
|
shiftStartTime: "08:00",
|
|
35644
35519
|
shiftEndTime: "19:00",
|
|
@@ -35651,6 +35526,12 @@ var TargetsView = ({
|
|
|
35651
35526
|
}
|
|
35652
35527
|
}), {});
|
|
35653
35528
|
}, [lineIds]);
|
|
35529
|
+
const [dropdownStates, setDropdownStates] = useState(() => {
|
|
35530
|
+
return lineIds.reduce((acc, lineId) => ({
|
|
35531
|
+
...acc,
|
|
35532
|
+
[lineId]: getStoredLineState2(lineId)
|
|
35533
|
+
}), {});
|
|
35534
|
+
});
|
|
35654
35535
|
const [allShiftsData, setAllShiftsData] = useState({
|
|
35655
35536
|
0: initialLineWorkspaces,
|
|
35656
35537
|
// Day shift
|
|
@@ -35668,6 +35549,8 @@ var TargetsView = ({
|
|
|
35668
35549
|
const [isBulkConfigureOpen, setIsBulkConfigureOpen] = useState(false);
|
|
35669
35550
|
const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
|
|
35670
35551
|
const [selectedShift, setSelectedShift] = useState(0);
|
|
35552
|
+
const [dbValues, setDbValues] = useState({ 0: {}, 1: {} });
|
|
35553
|
+
const [userEditedFields, setUserEditedFields] = useState(/* @__PURE__ */ new Set());
|
|
35671
35554
|
const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
|
|
35672
35555
|
const setLineWorkspaces = useCallback((updater) => {
|
|
35673
35556
|
setAllShiftsData((prev) => ({
|
|
@@ -35676,18 +35559,123 @@ var TargetsView = ({
|
|
|
35676
35559
|
}));
|
|
35677
35560
|
}, [selectedShift]);
|
|
35678
35561
|
const supabase = useSupabase();
|
|
35679
|
-
const
|
|
35680
|
-
userId || auth?.user?.id;
|
|
35562
|
+
const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
35681
35563
|
const dashboardConfig = useDashboardConfig();
|
|
35682
35564
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
35683
35565
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
35566
|
+
const loadOperatingHours = useCallback(async (lineId, shiftId) => {
|
|
35567
|
+
try {
|
|
35568
|
+
if (!supabase) return null;
|
|
35569
|
+
const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
|
|
35570
|
+
if (error) {
|
|
35571
|
+
if (error.code === "PGRST116") {
|
|
35572
|
+
console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
|
|
35573
|
+
return {
|
|
35574
|
+
startTime: "08:00",
|
|
35575
|
+
// Default values
|
|
35576
|
+
endTime: "19:00",
|
|
35577
|
+
breaks: []
|
|
35578
|
+
};
|
|
35579
|
+
} else {
|
|
35580
|
+
console.error("Error fetching operating hours:", error);
|
|
35581
|
+
return null;
|
|
35582
|
+
}
|
|
35583
|
+
}
|
|
35584
|
+
let breaks = [];
|
|
35585
|
+
if (data?.breaks) {
|
|
35586
|
+
if (Array.isArray(data.breaks)) {
|
|
35587
|
+
breaks = data.breaks.map((breakItem) => {
|
|
35588
|
+
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
35589
|
+
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
35590
|
+
const calculateDuration = (start, end) => {
|
|
35591
|
+
const [startHour, startMinute] = start.split(":").map(Number);
|
|
35592
|
+
const [endHour, endMinute] = end.split(":").map(Number);
|
|
35593
|
+
let startMinutes = startHour * 60 + startMinute;
|
|
35594
|
+
let endMinutes = endHour * 60 + endMinute;
|
|
35595
|
+
if (endMinutes < startMinutes) {
|
|
35596
|
+
endMinutes += 24 * 60;
|
|
35597
|
+
}
|
|
35598
|
+
return endMinutes - startMinutes;
|
|
35599
|
+
};
|
|
35600
|
+
return {
|
|
35601
|
+
startTime,
|
|
35602
|
+
endTime,
|
|
35603
|
+
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
35604
|
+
};
|
|
35605
|
+
});
|
|
35606
|
+
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
35607
|
+
breaks = data.breaks.breaks.map((breakItem) => {
|
|
35608
|
+
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
35609
|
+
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
35610
|
+
const calculateDuration = (start, end) => {
|
|
35611
|
+
const [startHour, startMinute] = start.split(":").map(Number);
|
|
35612
|
+
const [endHour, endMinute] = end.split(":").map(Number);
|
|
35613
|
+
let startMinutes = startHour * 60 + startMinute;
|
|
35614
|
+
let endMinutes = endHour * 60 + endMinute;
|
|
35615
|
+
if (endMinutes < startMinutes) {
|
|
35616
|
+
endMinutes += 24 * 60;
|
|
35617
|
+
}
|
|
35618
|
+
return endMinutes - startMinutes;
|
|
35619
|
+
};
|
|
35620
|
+
return {
|
|
35621
|
+
startTime,
|
|
35622
|
+
endTime,
|
|
35623
|
+
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
35624
|
+
};
|
|
35625
|
+
});
|
|
35626
|
+
} else if (typeof data.breaks === "string") {
|
|
35627
|
+
try {
|
|
35628
|
+
const parsedBreaks = JSON.parse(data.breaks);
|
|
35629
|
+
if (Array.isArray(parsedBreaks)) {
|
|
35630
|
+
breaks = parsedBreaks.map((breakItem) => ({
|
|
35631
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
35632
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
35633
|
+
duration: breakItem.duration || 0
|
|
35634
|
+
}));
|
|
35635
|
+
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
35636
|
+
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
35637
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
35638
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
35639
|
+
duration: breakItem.duration || 0
|
|
35640
|
+
}));
|
|
35641
|
+
}
|
|
35642
|
+
} catch (e) {
|
|
35643
|
+
console.error("Error parsing breaks data:", e);
|
|
35644
|
+
}
|
|
35645
|
+
}
|
|
35646
|
+
}
|
|
35647
|
+
return {
|
|
35648
|
+
startTime: data?.start_time || "08:00",
|
|
35649
|
+
endTime: data?.end_time || "19:00",
|
|
35650
|
+
breaks
|
|
35651
|
+
};
|
|
35652
|
+
} catch (e) {
|
|
35653
|
+
console.error("Exception when loading operating hours:", e);
|
|
35654
|
+
return {
|
|
35655
|
+
startTime: "08:00",
|
|
35656
|
+
endTime: "19:00",
|
|
35657
|
+
breaks: []
|
|
35658
|
+
};
|
|
35659
|
+
}
|
|
35660
|
+
}, [supabase]);
|
|
35661
|
+
useEffect(() => {
|
|
35662
|
+
console.log("[TargetsView] Component mounted/re-rendered", {
|
|
35663
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35664
|
+
lineIds: lineIds.length,
|
|
35665
|
+
effectiveUserId
|
|
35666
|
+
});
|
|
35667
|
+
return () => {
|
|
35668
|
+
console.log("[TargetsView] Component unmounting");
|
|
35669
|
+
};
|
|
35670
|
+
}, []);
|
|
35684
35671
|
useEffect(() => {
|
|
35685
35672
|
let timeoutId;
|
|
35686
35673
|
let retryCount = 0;
|
|
35687
35674
|
const MAX_RETRIES2 = 2;
|
|
35688
35675
|
const LOADING_TIMEOUT = 15e3;
|
|
35689
35676
|
const fetchInitialData = async () => {
|
|
35690
|
-
if (
|
|
35677
|
+
if (lineIds.length === 0) return;
|
|
35678
|
+
console.log("[TargetsView] Starting fetchInitialData");
|
|
35691
35679
|
setIsLoading(true);
|
|
35692
35680
|
timeoutId = setTimeout(() => {
|
|
35693
35681
|
console.error("Loading timeout reached");
|
|
@@ -35751,10 +35739,32 @@ var TargetsView = ({
|
|
|
35751
35739
|
const actionThresholds = await workspaceService.getActionThresholds(
|
|
35752
35740
|
lineId,
|
|
35753
35741
|
currentDate,
|
|
35754
|
-
|
|
35742
|
+
0
|
|
35743
|
+
// Always use day shift for initial load
|
|
35755
35744
|
);
|
|
35745
|
+
const operatingHoursData = await loadOperatingHours(lineId, 0);
|
|
35746
|
+
if (operatingHoursData) {
|
|
35747
|
+
updatedLineWorkspaces[lineId].shiftStartTime = operatingHoursData.startTime;
|
|
35748
|
+
updatedLineWorkspaces[lineId].shiftEndTime = operatingHoursData.endTime;
|
|
35749
|
+
updatedLineWorkspaces[lineId].breaks = operatingHoursData.breaks;
|
|
35750
|
+
updatedLineWorkspaces[lineId].shiftHours = calculateShiftHours2(
|
|
35751
|
+
operatingHoursData.startTime,
|
|
35752
|
+
operatingHoursData.endTime,
|
|
35753
|
+
operatingHoursData.breaks
|
|
35754
|
+
);
|
|
35755
|
+
}
|
|
35756
35756
|
const mappedWorkspaces = enabledWorkspaces.map((ws) => {
|
|
35757
35757
|
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
35758
|
+
if (!dbValues[0][lineId]) {
|
|
35759
|
+
dbValues[0][lineId] = {};
|
|
35760
|
+
}
|
|
35761
|
+
if (threshold) {
|
|
35762
|
+
dbValues[0][lineId][ws.id] = {
|
|
35763
|
+
targetPPH: threshold.pph_threshold,
|
|
35764
|
+
targetCycleTime: threshold.ideal_cycle_time,
|
|
35765
|
+
targetDayOutput: threshold.total_day_output
|
|
35766
|
+
};
|
|
35767
|
+
}
|
|
35758
35768
|
return {
|
|
35759
35769
|
id: ws.id,
|
|
35760
35770
|
name: ws.workspace_id,
|
|
@@ -35795,90 +35805,7 @@ var TargetsView = ({
|
|
|
35795
35805
|
return () => {
|
|
35796
35806
|
clearTimeout(timeoutId);
|
|
35797
35807
|
};
|
|
35798
|
-
}, [
|
|
35799
|
-
useCallback(async (shiftId) => {
|
|
35800
|
-
try {
|
|
35801
|
-
if (!supabase) return;
|
|
35802
|
-
const currentDate = getOperationalDate();
|
|
35803
|
-
const updatedLineWorkspaces = { ...lineWorkspaces };
|
|
35804
|
-
let hasUpdates = false;
|
|
35805
|
-
for (const lineId of lineIds) {
|
|
35806
|
-
const lineState = lineWorkspaces[lineId];
|
|
35807
|
-
if (!lineState || !lineState.factoryId) {
|
|
35808
|
-
console.warn(`Skipping line thresholds for ${lineId} as factoryId is not yet available.`);
|
|
35809
|
-
continue;
|
|
35810
|
-
}
|
|
35811
|
-
const currentFactoryId = lineState.factoryId;
|
|
35812
|
-
try {
|
|
35813
|
-
const { data: lineThresholdsRows, error: thresholdError } = await supabase.from("line_thresholds").select("product_code").eq("line_id", lineId).eq("date", currentDate).eq("shift_id", selectedShift).eq("factory_id", currentFactoryId);
|
|
35814
|
-
if (thresholdError) {
|
|
35815
|
-
console.error(`Error fetching line threshold for line ${lineId}, factory ${currentFactoryId}:`, thresholdError);
|
|
35816
|
-
continue;
|
|
35817
|
-
}
|
|
35818
|
-
let determinedProductId = updatedLineWorkspaces[lineId]?.productId || "";
|
|
35819
|
-
if (lineThresholdsRows && lineThresholdsRows.length > 0) {
|
|
35820
|
-
if (lineThresholdsRows.length > 1) {
|
|
35821
|
-
console.warn(
|
|
35822
|
-
`Multiple line_thresholds records found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using product_code from the first record. Rows:`,
|
|
35823
|
-
lineThresholdsRows
|
|
35824
|
-
);
|
|
35825
|
-
}
|
|
35826
|
-
determinedProductId = lineThresholdsRows[0].product_code;
|
|
35827
|
-
} else {
|
|
35828
|
-
console.log(
|
|
35829
|
-
`No line_thresholds record found for line ${lineId}, factory ${currentFactoryId}, date ${currentDate}, shift ${selectedShift}. Using existing/default product ID.`
|
|
35830
|
-
);
|
|
35831
|
-
}
|
|
35832
|
-
const { data: operatingHours, error: hoursError } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", selectedShift).maybeSingle();
|
|
35833
|
-
if (hoursError) {
|
|
35834
|
-
console.error(`Error fetching operating hours for line ${lineId}:`, hoursError);
|
|
35835
|
-
continue;
|
|
35836
|
-
}
|
|
35837
|
-
const startTime = operatingHours?.start_time || updatedLineWorkspaces[lineId]?.shiftStartTime || "08:00";
|
|
35838
|
-
const endTime = operatingHours?.end_time || updatedLineWorkspaces[lineId]?.shiftEndTime || "19:00";
|
|
35839
|
-
let breaks = [];
|
|
35840
|
-
if (operatingHours?.breaks) {
|
|
35841
|
-
if (Array.isArray(operatingHours.breaks)) {
|
|
35842
|
-
breaks = operatingHours.breaks.map((breakItem) => ({
|
|
35843
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
35844
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
35845
|
-
duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
|
|
35846
|
-
}));
|
|
35847
|
-
} else if (typeof operatingHours.breaks === "object" && operatingHours.breaks.breaks) {
|
|
35848
|
-
breaks = operatingHours.breaks.breaks.map((breakItem) => ({
|
|
35849
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
35850
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
35851
|
-
duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
|
|
35852
|
-
}));
|
|
35853
|
-
}
|
|
35854
|
-
}
|
|
35855
|
-
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
35856
|
-
const currentLineStateFromLoop = updatedLineWorkspaces[lineId];
|
|
35857
|
-
if (determinedProductId !== currentLineStateFromLoop?.productId || startTime !== currentLineStateFromLoop?.shiftStartTime || endTime !== currentLineStateFromLoop?.shiftEndTime || shiftHours !== currentLineStateFromLoop?.shiftHours || JSON.stringify(breaks) !== JSON.stringify(currentLineStateFromLoop?.breaks)) {
|
|
35858
|
-
updatedLineWorkspaces[lineId] = {
|
|
35859
|
-
...currentLineStateFromLoop || {},
|
|
35860
|
-
factoryId: currentFactoryId,
|
|
35861
|
-
// Ensure factoryId is preserved
|
|
35862
|
-
productId: determinedProductId,
|
|
35863
|
-
shiftStartTime: startTime,
|
|
35864
|
-
shiftEndTime: endTime,
|
|
35865
|
-
breaks,
|
|
35866
|
-
shiftHours: Number(shiftHours),
|
|
35867
|
-
workspaces: currentLineStateFromLoop?.workspaces || []
|
|
35868
|
-
};
|
|
35869
|
-
hasUpdates = true;
|
|
35870
|
-
}
|
|
35871
|
-
} catch (lineError) {
|
|
35872
|
-
console.error(`Error processing line ${lineId}:`, lineError);
|
|
35873
|
-
}
|
|
35874
|
-
}
|
|
35875
|
-
if (hasUpdates) {
|
|
35876
|
-
setLineWorkspaces(updatedLineWorkspaces);
|
|
35877
|
-
}
|
|
35878
|
-
} catch (error) {
|
|
35879
|
-
console.error("Error in fetchLineThresholds outer try-catch:", error);
|
|
35880
|
-
}
|
|
35881
|
-
}, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
|
|
35808
|
+
}, [lineIds, companyId, loadOperatingHours]);
|
|
35882
35809
|
const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
|
|
35883
35810
|
if (!supabase) return;
|
|
35884
35811
|
const currentDate = getOperationalDate();
|
|
@@ -35888,32 +35815,25 @@ var TargetsView = ({
|
|
|
35888
35815
|
1: JSON.parse(JSON.stringify(currentWorkspaces))
|
|
35889
35816
|
// Deep clone for night shift
|
|
35890
35817
|
};
|
|
35818
|
+
const newDbValues = { 0: {}, 1: {} };
|
|
35891
35819
|
for (const shiftId of [0, 1]) {
|
|
35892
35820
|
for (const lineId of lineIds) {
|
|
35893
35821
|
try {
|
|
35894
|
-
const
|
|
35895
|
-
if (
|
|
35896
|
-
console.
|
|
35822
|
+
const operatingHoursData = await loadOperatingHours(lineId, shiftId);
|
|
35823
|
+
if (!operatingHoursData) {
|
|
35824
|
+
console.warn(`No operating hours for line ${lineId}, shift ${shiftId} - using defaults`);
|
|
35897
35825
|
continue;
|
|
35898
35826
|
}
|
|
35899
|
-
|
|
35900
|
-
if (operatingHours?.breaks) {
|
|
35901
|
-
if (Array.isArray(operatingHours.breaks)) {
|
|
35902
|
-
breaks = operatingHours.breaks.map((breakItem) => ({
|
|
35903
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
35904
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
35905
|
-
duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
|
|
35906
|
-
}));
|
|
35907
|
-
}
|
|
35908
|
-
}
|
|
35909
|
-
const startTime = operatingHours?.start_time || "08:00";
|
|
35910
|
-
const endTime = operatingHours?.end_time || "19:00";
|
|
35827
|
+
const { startTime, endTime, breaks } = operatingHoursData;
|
|
35911
35828
|
const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
|
|
35912
35829
|
const actionThresholds = await workspaceService.getActionThresholds(
|
|
35913
35830
|
lineId,
|
|
35914
35831
|
currentDate,
|
|
35915
35832
|
shiftId
|
|
35916
35833
|
);
|
|
35834
|
+
if (!newDbValues[shiftId][lineId]) {
|
|
35835
|
+
newDbValues[shiftId][lineId] = {};
|
|
35836
|
+
}
|
|
35917
35837
|
const existingLine = newAllShiftsData[shiftId][lineId];
|
|
35918
35838
|
if (existingLine) {
|
|
35919
35839
|
newAllShiftsData[shiftId][lineId] = {
|
|
@@ -35925,6 +35845,11 @@ var TargetsView = ({
|
|
|
35925
35845
|
workspaces: existingLine.workspaces.map((ws) => {
|
|
35926
35846
|
const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
|
|
35927
35847
|
if (threshold) {
|
|
35848
|
+
newDbValues[shiftId][lineId][ws.id] = {
|
|
35849
|
+
targetPPH: threshold.pph_threshold,
|
|
35850
|
+
targetCycleTime: threshold.ideal_cycle_time,
|
|
35851
|
+
targetDayOutput: threshold.total_day_output
|
|
35852
|
+
};
|
|
35928
35853
|
return {
|
|
35929
35854
|
...ws,
|
|
35930
35855
|
targetPPH: threshold.pph_threshold,
|
|
@@ -35942,114 +35867,17 @@ var TargetsView = ({
|
|
|
35942
35867
|
}
|
|
35943
35868
|
}
|
|
35944
35869
|
setAllShiftsData(newAllShiftsData);
|
|
35945
|
-
|
|
35946
|
-
|
|
35947
|
-
try {
|
|
35948
|
-
if (!supabase) return null;
|
|
35949
|
-
const { data, error } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
|
|
35950
|
-
if (error) {
|
|
35951
|
-
if (error.code === "PGRST116") {
|
|
35952
|
-
console.log(`No operating hours found for line ${lineId}, shift ${shiftId}`);
|
|
35953
|
-
return {
|
|
35954
|
-
startTime: "08:00",
|
|
35955
|
-
// Default values
|
|
35956
|
-
endTime: "19:00",
|
|
35957
|
-
breaks: []
|
|
35958
|
-
};
|
|
35959
|
-
} else {
|
|
35960
|
-
console.error("Error fetching operating hours:", error);
|
|
35961
|
-
return null;
|
|
35962
|
-
}
|
|
35963
|
-
}
|
|
35964
|
-
let breaks = [];
|
|
35965
|
-
if (data?.breaks) {
|
|
35966
|
-
if (Array.isArray(data.breaks)) {
|
|
35967
|
-
breaks = data.breaks.map((breakItem) => {
|
|
35968
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
35969
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
35970
|
-
const calculateDuration = (start, end) => {
|
|
35971
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
35972
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
35973
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
35974
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
35975
|
-
if (endMinutes < startMinutes) {
|
|
35976
|
-
endMinutes += 24 * 60;
|
|
35977
|
-
}
|
|
35978
|
-
return endMinutes - startMinutes;
|
|
35979
|
-
};
|
|
35980
|
-
return {
|
|
35981
|
-
startTime,
|
|
35982
|
-
endTime,
|
|
35983
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
35984
|
-
};
|
|
35985
|
-
});
|
|
35986
|
-
} else if (typeof data.breaks === "object" && data.breaks.breaks) {
|
|
35987
|
-
breaks = data.breaks.breaks.map((breakItem) => {
|
|
35988
|
-
const startTime = breakItem.start || breakItem.startTime || "00:00";
|
|
35989
|
-
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
35990
|
-
const calculateDuration = (start, end) => {
|
|
35991
|
-
const [startHour, startMinute] = start.split(":").map(Number);
|
|
35992
|
-
const [endHour, endMinute] = end.split(":").map(Number);
|
|
35993
|
-
let startMinutes = startHour * 60 + startMinute;
|
|
35994
|
-
let endMinutes = endHour * 60 + endMinute;
|
|
35995
|
-
if (endMinutes < startMinutes) {
|
|
35996
|
-
endMinutes += 24 * 60;
|
|
35997
|
-
}
|
|
35998
|
-
return endMinutes - startMinutes;
|
|
35999
|
-
};
|
|
36000
|
-
return {
|
|
36001
|
-
startTime,
|
|
36002
|
-
endTime,
|
|
36003
|
-
duration: breakItem.duration || calculateDuration(startTime, endTime)
|
|
36004
|
-
};
|
|
36005
|
-
});
|
|
36006
|
-
} else if (typeof data.breaks === "string") {
|
|
36007
|
-
try {
|
|
36008
|
-
const parsedBreaks = JSON.parse(data.breaks);
|
|
36009
|
-
if (Array.isArray(parsedBreaks)) {
|
|
36010
|
-
breaks = parsedBreaks.map((breakItem) => ({
|
|
36011
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
36012
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
36013
|
-
duration: breakItem.duration || 0
|
|
36014
|
-
}));
|
|
36015
|
-
} else if (parsedBreaks.breaks && Array.isArray(parsedBreaks.breaks)) {
|
|
36016
|
-
breaks = parsedBreaks.breaks.map((breakItem) => ({
|
|
36017
|
-
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
36018
|
-
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
36019
|
-
duration: breakItem.duration || 0
|
|
36020
|
-
}));
|
|
36021
|
-
}
|
|
36022
|
-
} catch (e) {
|
|
36023
|
-
console.error("Error parsing breaks data:", e);
|
|
36024
|
-
}
|
|
36025
|
-
}
|
|
36026
|
-
}
|
|
36027
|
-
return {
|
|
36028
|
-
startTime: data?.start_time || "08:00",
|
|
36029
|
-
endTime: data?.end_time || "19:00",
|
|
36030
|
-
breaks
|
|
36031
|
-
};
|
|
36032
|
-
} catch (e) {
|
|
36033
|
-
console.error("Exception when loading operating hours:", e);
|
|
36034
|
-
return {
|
|
36035
|
-
startTime: "08:00",
|
|
36036
|
-
endTime: "19:00",
|
|
36037
|
-
breaks: []
|
|
36038
|
-
};
|
|
36039
|
-
}
|
|
36040
|
-
}, [supabase]);
|
|
35870
|
+
setDbValues(newDbValues);
|
|
35871
|
+
}, [supabase, lineIds, loadOperatingHours]);
|
|
36041
35872
|
const toggleLineDropdown = useCallback((lineId) => {
|
|
36042
|
-
|
|
36043
|
-
const newIsOpen = !prev[lineId]
|
|
35873
|
+
setDropdownStates((prev) => {
|
|
35874
|
+
const newIsOpen = !prev[lineId];
|
|
36044
35875
|
if (typeof window !== "undefined") {
|
|
36045
35876
|
localStorage.setItem(`line_${lineId}_open`, JSON.stringify(newIsOpen));
|
|
36046
35877
|
}
|
|
36047
35878
|
return {
|
|
36048
35879
|
...prev,
|
|
36049
|
-
[lineId]:
|
|
36050
|
-
...prev[lineId],
|
|
36051
|
-
isOpen: newIsOpen
|
|
36052
|
-
}
|
|
35880
|
+
[lineId]: newIsOpen
|
|
36053
35881
|
};
|
|
36054
35882
|
});
|
|
36055
35883
|
}, []);
|
|
@@ -36098,6 +35926,8 @@ var TargetsView = ({
|
|
|
36098
35926
|
}
|
|
36099
35927
|
};
|
|
36100
35928
|
const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
|
|
35929
|
+
const fieldKey = `${lineId}-${workspaceId}-${field}`;
|
|
35930
|
+
setUserEditedFields((prev) => new Set(prev).add(fieldKey));
|
|
36101
35931
|
setLineWorkspaces((prev) => {
|
|
36102
35932
|
const shiftHours = prev[lineId].shiftHours;
|
|
36103
35933
|
return {
|
|
@@ -36122,11 +35952,7 @@ var TargetsView = ({
|
|
|
36122
35952
|
} else if (field === "targetDayOutput") {
|
|
36123
35953
|
updates.targetDayOutput = value;
|
|
36124
35954
|
if (value !== "") {
|
|
36125
|
-
const
|
|
36126
|
-
const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
|
|
36127
|
-
const totalBreakHours = totalBreakMinutes / 60;
|
|
36128
|
-
const realWorkHours = shiftHours - totalBreakHours;
|
|
36129
|
-
const calculatedPPH = Math.round(value / realWorkHours);
|
|
35955
|
+
const calculatedPPH = Math.round(value / shiftHours);
|
|
36130
35956
|
updates.targetPPH = calculatedPPH;
|
|
36131
35957
|
} else {
|
|
36132
35958
|
updates.targetPPH = "";
|
|
@@ -36141,62 +35967,35 @@ var TargetsView = ({
|
|
|
36141
35967
|
};
|
|
36142
35968
|
const handleShiftChange = (shiftId) => {
|
|
36143
35969
|
setSelectedShift(shiftId);
|
|
36144
|
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
|
|
36148
|
-
|
|
36149
|
-
|
|
36150
|
-
|
|
36151
|
-
|
|
36152
|
-
|
|
36153
|
-
|
|
36154
|
-
|
|
36155
|
-
|
|
36156
|
-
|
|
36157
|
-
|
|
36158
|
-
|
|
36159
|
-
|
|
36160
|
-
|
|
36161
|
-
shiftHours: Number(shiftHours),
|
|
36162
|
-
workspaces: updatedLineWorkspaces[lineId].workspaces.map((ws) => {
|
|
36163
|
-
let updatedPPH = ws.targetPPH;
|
|
36164
|
-
if (ws.targetCycleTime !== "") {
|
|
36165
|
-
const idealPPH = calculatePPH(
|
|
36166
|
-
ws.targetCycleTime,
|
|
36167
|
-
operatingHours.breaks,
|
|
36168
|
-
Number(shiftHours)
|
|
36169
|
-
);
|
|
36170
|
-
const shouldUpdatePPH = typeof ws.targetPPH === "string" ? ws.targetPPH === "" : ws.targetPPH === 0 || !ws.targetPPH;
|
|
36171
|
-
if (shouldUpdatePPH) {
|
|
36172
|
-
updatedPPH = idealPPH;
|
|
35970
|
+
setUserEditedFields(/* @__PURE__ */ new Set());
|
|
35971
|
+
if (dbValues[shiftId] && Object.keys(dbValues[shiftId]).length > 0) {
|
|
35972
|
+
setAllShiftsData((prev) => {
|
|
35973
|
+
const updatedShiftData = { ...prev[shiftId] };
|
|
35974
|
+
for (const lineId of Object.keys(updatedShiftData)) {
|
|
35975
|
+
if (dbValues[shiftId][lineId]) {
|
|
35976
|
+
updatedShiftData[lineId] = {
|
|
35977
|
+
...updatedShiftData[lineId],
|
|
35978
|
+
workspaces: updatedShiftData[lineId].workspaces.map((ws) => {
|
|
35979
|
+
const dbValue = dbValues[shiftId][lineId][ws.id];
|
|
35980
|
+
if (dbValue) {
|
|
35981
|
+
return {
|
|
35982
|
+
...ws,
|
|
35983
|
+
targetPPH: dbValue.targetPPH,
|
|
35984
|
+
targetCycleTime: dbValue.targetCycleTime,
|
|
35985
|
+
targetDayOutput: dbValue.targetDayOutput
|
|
35986
|
+
};
|
|
36173
35987
|
}
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
operatingHours.breaks
|
|
36179
|
-
);
|
|
36180
|
-
return {
|
|
36181
|
-
...ws,
|
|
36182
|
-
targetPPH: updatedPPH,
|
|
36183
|
-
targetDayOutput: updatedDayOutput
|
|
36184
|
-
};
|
|
36185
|
-
})
|
|
36186
|
-
};
|
|
36187
|
-
hasUpdates = true;
|
|
36188
|
-
} catch (e) {
|
|
36189
|
-
console.error(`Exception when loading shift hours for line ${lineId}:`, e);
|
|
35988
|
+
return ws;
|
|
35989
|
+
})
|
|
35990
|
+
};
|
|
35991
|
+
}
|
|
36190
35992
|
}
|
|
36191
|
-
|
|
36192
|
-
if (hasUpdates) {
|
|
36193
|
-
setAllShiftsData((prev) => ({
|
|
35993
|
+
return {
|
|
36194
35994
|
...prev,
|
|
36195
|
-
[shiftId]:
|
|
36196
|
-
}
|
|
36197
|
-
}
|
|
36198
|
-
}
|
|
36199
|
-
loadShiftHours();
|
|
35995
|
+
[shiftId]: updatedShiftData
|
|
35996
|
+
};
|
|
35997
|
+
});
|
|
35998
|
+
}
|
|
36200
35999
|
};
|
|
36201
36000
|
const handleActionTypeChange = useCallback((lineId, workspaceId, newActionType) => {
|
|
36202
36001
|
if (!actionIds) return;
|
|
@@ -36286,6 +36085,31 @@ var TargetsView = ({
|
|
|
36286
36085
|
throw lineUpsertError;
|
|
36287
36086
|
}
|
|
36288
36087
|
console.log(`[handleSaveLine] Successfully upserted line_thresholds for ${lineId}`);
|
|
36088
|
+
setDbValues((prev) => ({
|
|
36089
|
+
...prev,
|
|
36090
|
+
[selectedShift]: {
|
|
36091
|
+
...prev[selectedShift],
|
|
36092
|
+
[lineId]: lineDataToSave.workspaces.reduce((acc, ws) => ({
|
|
36093
|
+
...acc,
|
|
36094
|
+
[ws.id]: {
|
|
36095
|
+
targetPPH: ws.targetPPH,
|
|
36096
|
+
targetCycleTime: ws.targetCycleTime,
|
|
36097
|
+
targetDayOutput: ws.targetDayOutput
|
|
36098
|
+
}
|
|
36099
|
+
}), {})
|
|
36100
|
+
}
|
|
36101
|
+
}));
|
|
36102
|
+
console.log(`[handleSaveLine] Updated dbValues for line ${lineId}, shift ${selectedShift}`);
|
|
36103
|
+
setUserEditedFields((prev) => {
|
|
36104
|
+
const newSet = new Set(prev);
|
|
36105
|
+
lineDataToSave.workspaces.forEach((ws) => {
|
|
36106
|
+
newSet.delete(`${lineId}-${ws.id}-targetPPH`);
|
|
36107
|
+
newSet.delete(`${lineId}-${ws.id}-targetCycleTime`);
|
|
36108
|
+
newSet.delete(`${lineId}-${ws.id}-targetDayOutput`);
|
|
36109
|
+
});
|
|
36110
|
+
return newSet;
|
|
36111
|
+
});
|
|
36112
|
+
console.log(`[handleSaveLine] Cleared user edited fields for line ${lineId}`);
|
|
36289
36113
|
setSaveSuccess((prev) => ({ ...prev, [lineId]: true }));
|
|
36290
36114
|
toast.success(`${lineNames[lineId] || lineId} targets saved successfully`);
|
|
36291
36115
|
if (onSaveChanges) onSaveChanges(lineId);
|
|
@@ -36299,7 +36123,7 @@ var TargetsView = ({
|
|
|
36299
36123
|
setSavingLines((prev) => ({ ...prev, [lineId]: false }));
|
|
36300
36124
|
console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
|
|
36301
36125
|
}
|
|
36302
|
-
}, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges]);
|
|
36126
|
+
}, [supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
|
|
36303
36127
|
const handleBulkConfigure = async (updates) => {
|
|
36304
36128
|
if (!actionIds) return;
|
|
36305
36129
|
if (updates.productId !== void 0) {
|
|
@@ -36347,6 +36171,7 @@ var TargetsView = ({
|
|
|
36347
36171
|
isLoading: isLoading || skusLoading,
|
|
36348
36172
|
lineWorkspaces,
|
|
36349
36173
|
lineNames,
|
|
36174
|
+
dropdownStates,
|
|
36350
36175
|
savingLines,
|
|
36351
36176
|
saveSuccess,
|
|
36352
36177
|
selectedShift,
|
|
@@ -36371,7 +36196,7 @@ var TargetsView = ({
|
|
|
36371
36196
|
};
|
|
36372
36197
|
var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
|
|
36373
36198
|
var TargetsView_default = TargetsViewWithDisplayNames;
|
|
36374
|
-
var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
|
|
36199
|
+
var AuthenticatedTargetsView = withAuth(React19__default.memo(TargetsViewWithDisplayNames));
|
|
36375
36200
|
|
|
36376
36201
|
// src/views/workspace-detail-view.utils.ts
|
|
36377
36202
|
var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
|
|
@@ -37842,4 +37667,4 @@ var streamProxyConfig = {
|
|
|
37842
37667
|
}
|
|
37843
37668
|
};
|
|
37844
37669
|
|
|
37845
|
-
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CongratulationsOverlay, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService,
|
|
37670
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CongratulationsOverlay, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPrefetchManager, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
|