@optifye/dashboard-core 4.2.5 → 4.2.7

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.js CHANGED
@@ -3,10 +3,10 @@
3
3
  var React14 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var router = require('next/router');
6
- var dateFns = require('date-fns');
7
6
  var dateFnsTz = require('date-fns-tz');
8
- var supabaseJs = require('@supabase/supabase-js');
7
+ var dateFns = require('date-fns');
9
8
  var mixpanel = require('mixpanel-browser');
9
+ var supabaseJs = require('@supabase/supabase-js');
10
10
  var useSWR = require('swr');
11
11
  var Hls2 = require('hls.js');
12
12
  var motionUtils = require('motion-utils');
@@ -299,173 +299,6 @@ function useCustomConfig() {
299
299
  const { customConfig } = useDashboardConfig();
300
300
  return customConfig ?? {};
301
301
  }
302
- var AuthContext = React14.createContext({
303
- session: null,
304
- user: null,
305
- loading: true,
306
- error: null,
307
- signOut: async () => {
308
- }
309
- });
310
- var useAuth = () => React14.useContext(AuthContext);
311
- var AuthProvider = ({ children }) => {
312
- const supabase = useSupabase();
313
- const { authConfig } = useDashboardConfig();
314
- const [session, setSession] = React14.useState(null);
315
- const [user, setUser] = React14.useState(null);
316
- const [loading, setLoading] = React14.useState(true);
317
- const [error, setError] = React14.useState(null);
318
- const router$1 = router.useRouter();
319
- const userProfileTable = authConfig?.userProfileTable;
320
- const roleColumn = authConfig?.roleColumn || "role";
321
- const fetchUserDetails = React14.useCallback(async (supabaseUser) => {
322
- if (!supabaseUser) return null;
323
- const basicUser = {
324
- id: supabaseUser.id,
325
- email: supabaseUser.email
326
- };
327
- if (!userProfileTable || !supabase) return basicUser;
328
- try {
329
- const timeoutPromise = new Promise(
330
- (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
331
- );
332
- const fetchPromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
333
- const { data: profile, error: profileError } = await Promise.race([
334
- fetchPromise,
335
- timeoutPromise
336
- ]);
337
- if (profileError) {
338
- if (profileError.message.includes("does not exist") || profileError.message.includes("No rows found") || profileError.code === "PGRST116") {
339
- console.log("User profile table not found or user not in table, using basic auth info");
340
- return basicUser;
341
- }
342
- console.error("Error fetching user profile:", profileError);
343
- return basicUser;
344
- }
345
- const roleValue = profile ? profile[roleColumn] : void 0;
346
- return { ...basicUser, role: roleValue };
347
- } catch (err) {
348
- console.error("Error fetching user profile:", err);
349
- return basicUser;
350
- }
351
- }, [supabase, userProfileTable, roleColumn]);
352
- React14.useEffect(() => {
353
- if (!supabase) return;
354
- let mounted = true;
355
- const safetyTimeout = setTimeout(() => {
356
- if (mounted) {
357
- console.warn("Auth initialization taking too long, forcing loading to false");
358
- setLoading(false);
359
- }
360
- }, 1e4);
361
- const initializeAuth = async () => {
362
- try {
363
- const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
364
- if (!mounted) return;
365
- if (sessionError) {
366
- setError(sessionError);
367
- setLoading(false);
368
- clearTimeout(safetyTimeout);
369
- return;
370
- }
371
- setSession(initialSession);
372
- if (initialSession?.user) {
373
- try {
374
- const userDetails = await fetchUserDetails(initialSession.user);
375
- if (mounted) setUser(userDetails);
376
- } catch (err) {
377
- console.error("Error fetching user details during init:", err);
378
- if (mounted) {
379
- setUser({
380
- id: initialSession.user.id,
381
- email: initialSession.user.email
382
- });
383
- }
384
- }
385
- }
386
- } catch (err) {
387
- if (mounted) setError(err instanceof Error ? err : new Error("Failed to initialize auth"));
388
- } finally {
389
- if (mounted) {
390
- setLoading(false);
391
- clearTimeout(safetyTimeout);
392
- }
393
- }
394
- };
395
- initializeAuth();
396
- const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
397
- if (!mounted) return;
398
- setSession(currentSession);
399
- setUser(null);
400
- setLoading(true);
401
- if (currentSession?.user) {
402
- try {
403
- const userDetails = await fetchUserDetails(currentSession.user);
404
- if (mounted) setUser(userDetails);
405
- } catch (err) {
406
- console.error("Error fetching user details on auth state change:", err);
407
- if (mounted) {
408
- setUser({
409
- id: currentSession.user.id,
410
- email: currentSession.user.email
411
- });
412
- }
413
- }
414
- }
415
- if (mounted) setLoading(false);
416
- });
417
- return () => {
418
- mounted = false;
419
- clearTimeout(safetyTimeout);
420
- subscription?.unsubscribe();
421
- };
422
- }, [supabase, fetchUserDetails]);
423
- const signOut = async () => {
424
- if (!supabase) return;
425
- setLoading(true);
426
- const { error: signOutError } = await supabase.auth.signOut();
427
- if (signOutError) setError(signOutError);
428
- const logoutRedirectPath = authConfig?.defaultLogoutRedirect || "/login";
429
- if (router$1 && router$1.pathname !== logoutRedirectPath && !router$1.pathname.startsWith(logoutRedirectPath)) {
430
- router$1.replace(logoutRedirectPath);
431
- }
432
- };
433
- return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value: { session, user, loading, error, signOut }, children });
434
- };
435
- var defaultContextValue = {
436
- components: {},
437
- hooks: {},
438
- pages: {}
439
- };
440
- var DashboardOverridesContext = React14.createContext(defaultContextValue);
441
- var DashboardOverridesProvider = ({
442
- overrides = defaultContextValue,
443
- children
444
- }) => {
445
- const normalizedOverrides = React14.useMemo(() => {
446
- return {
447
- components: overrides.components || {},
448
- hooks: overrides.hooks || {},
449
- pages: overrides.pages || {}
450
- };
451
- }, [overrides]);
452
- return /* @__PURE__ */ jsxRuntime.jsx(DashboardOverridesContext.Provider, { value: normalizedOverrides, children });
453
- };
454
- function useComponentOverride(key, Default) {
455
- const { components = {} } = React14.useContext(DashboardOverridesContext);
456
- return components[key] ?? Default;
457
- }
458
- function useHookOverride(key, Default) {
459
- const { hooks = {} } = React14.useContext(DashboardOverridesContext);
460
- return hooks[key] ?? Default;
461
- }
462
- function usePageOverride(key, Default) {
463
- const { pages = {} } = React14.useContext(DashboardOverridesContext);
464
- return pages[key] ?? Default;
465
- }
466
- function useOverrides() {
467
- return React14.useContext(DashboardOverridesContext);
468
- }
469
302
 
470
303
  // src/lib/internal/supabaseClientInstance.ts
471
304
  var supabaseInstance = null;
@@ -480,21 +313,31 @@ var _getSupabaseInstance = () => {
480
313
  }
481
314
  return supabaseInstance;
482
315
  };
483
- var SupabaseContext = React14.createContext(void 0);
484
- var SupabaseProvider = ({ client, children }) => {
485
- _setSupabaseInstance(client);
486
- React14.useEffect(() => {
487
- _setSupabaseInstance(client);
488
- }, [client]);
489
- const contextValue = React14.useMemo(() => ({ supabase: client }), [client]);
490
- return /* @__PURE__ */ jsxRuntime.jsx(SupabaseContext.Provider, { value: contextValue, children });
316
+
317
+ // src/lib/services/actionService.ts
318
+ var getTable = (dbConfig, tableName) => {
319
+ const defaults2 = DEFAULT_DATABASE_CONFIG.tables;
320
+ const userValue = dbConfig?.tables?.[tableName];
321
+ return userValue ?? defaults2[tableName];
491
322
  };
492
- var useSupabase = () => {
493
- const context = React14.useContext(SupabaseContext);
494
- if (context === void 0) {
495
- throw new Error("useSupabase must be used within a SupabaseProvider.");
323
+ var actionService = {
324
+ async getActionsByName(actionNames, companyIdInput) {
325
+ const supabase = _getSupabaseInstance();
326
+ const config = _getDashboardConfigInstance();
327
+ const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
328
+ const entityConfig = config.entityConfig;
329
+ const actionsTable = getTable(dbConfig, "actions");
330
+ const targetCompanyId = companyIdInput ?? entityConfig?.companyId;
331
+ if (!targetCompanyId) {
332
+ throw new Error("Company ID must be provided either via entityConfig.companyId or as an argument to getActionsByName.");
333
+ }
334
+ const { data, error } = await supabase.from(actionsTable).select("id, action_name, company_id").eq("company_id", targetCompanyId).in("action_name", actionNames);
335
+ if (error) {
336
+ console.error(`Error fetching actions from table ${actionsTable}:`, error);
337
+ throw error;
338
+ }
339
+ return data || [];
496
340
  }
497
- return context.supabase;
498
341
  };
499
342
  var getOperationalDate = (timezone = "Asia/Kolkata", date = /* @__PURE__ */ new Date(), shiftStartTime = "06:00") => {
500
343
  const zonedDate = dateFnsTz.toZonedTime(date, timezone);
@@ -517,301 +360,57 @@ function getCurrentTimeInZone(timezone, formatString) {
517
360
  return now2;
518
361
  }
519
362
 
520
- // src/lib/utils/database.ts
521
- var getCompanyMetricsTableName = (companyId, prefix = "workspace_performance") => {
522
- if (!companyId) return `${prefix}_unknown_company`;
523
- return `${prefix}_${companyId.replace(/-/g, "_")}`;
363
+ // src/lib/utils/shifts.ts
364
+ var DEFAULT_DAY_SHIFT_START = "06:00";
365
+ var DEFAULT_NIGHT_SHIFT_START = "18:00";
366
+ var DEFAULT_TRANSITION_MINUTES = 15;
367
+ var parseTimeToMinutes = (timeString) => {
368
+ if (!timeString || !/^[0-2]\d:[0-5]\d$/.test(timeString)) {
369
+ return NaN;
370
+ }
371
+ const [hours, minutes] = timeString.split(":").map(Number);
372
+ return hours * 60 + minutes;
524
373
  };
525
- var getMetricsTablePrefix = (companyId) => {
526
- return "performance_metrics";
374
+ var getCurrentShift = (timezone, shiftConfig, now2 = /* @__PURE__ */ new Date()) => {
375
+ const dayShiftStartStr = shiftConfig?.dayShift?.startTime || DEFAULT_DAY_SHIFT_START;
376
+ const nightShiftStartStr = shiftConfig?.nightShift?.startTime || DEFAULT_NIGHT_SHIFT_START;
377
+ const dayShiftId = shiftConfig?.dayShift?.id ?? 0;
378
+ const nightShiftId = shiftConfig?.nightShift?.id ?? 1;
379
+ const dayShiftStartMinutes = parseTimeToMinutes(dayShiftStartStr);
380
+ const nightShiftStartMinutes = parseTimeToMinutes(nightShiftStartStr);
381
+ const effectiveDayStart = !isNaN(dayShiftStartMinutes) ? dayShiftStartMinutes : parseTimeToMinutes(DEFAULT_DAY_SHIFT_START);
382
+ const effectiveNightStart = !isNaN(nightShiftStartMinutes) ? nightShiftStartMinutes : parseTimeToMinutes(DEFAULT_NIGHT_SHIFT_START);
383
+ const zonedNow = dateFnsTz.toZonedTime(now2, timezone);
384
+ const currentHour = zonedNow.getHours();
385
+ const currentMinutes = zonedNow.getMinutes();
386
+ const currentTotalMinutes = currentHour * 60 + currentMinutes;
387
+ const operationalDate = getOperationalDate(timezone, zonedNow, dayShiftStartStr);
388
+ let determinedShiftId;
389
+ if (effectiveDayStart < effectiveNightStart) {
390
+ if (currentTotalMinutes >= effectiveDayStart && currentTotalMinutes < effectiveNightStart) {
391
+ determinedShiftId = dayShiftId;
392
+ } else {
393
+ determinedShiftId = nightShiftId;
394
+ }
395
+ } else {
396
+ if (currentTotalMinutes >= effectiveDayStart || currentTotalMinutes < effectiveNightStart) {
397
+ determinedShiftId = dayShiftId;
398
+ } else {
399
+ determinedShiftId = nightShiftId;
400
+ }
401
+ }
402
+ return { shiftId: determinedShiftId, date: operationalDate };
527
403
  };
528
-
529
- // src/lib/hooks/useMetrics.ts
530
- var DEFAULT_COMPANY_ID = "default-company-id";
531
- var useWorkspaceMetrics = (workspaceId) => {
532
- const supabase = useSupabase();
533
- const entityConfig = useEntityConfig();
534
- useDatabaseConfig();
535
- const dateTimeConfig = useDateTimeConfig();
536
- const [workspaceMetrics, setWorkspaceMetrics] = React14.useState(null);
537
- const [isLoading, setIsLoading] = React14.useState(true);
538
- const [error, setError] = React14.useState(null);
539
- const fetchWorkspaceMetrics = React14.useCallback(async () => {
540
- try {
541
- const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
542
- const { data, error: fetchError } = await supabase.from("overview_workspace_metrics").select("*").eq("workspace_id", workspaceId).eq("date", operationalDate).single();
543
- if (fetchError) throw fetchError;
544
- setWorkspaceMetrics(data);
545
- } catch (err) {
546
- setError({ message: err.message, code: err.code });
547
- console.error("Error fetching workspace metrics:", err);
548
- } finally {
549
- setIsLoading(false);
550
- }
551
- }, [supabase, workspaceId, dateTimeConfig.defaultTimezone]);
552
- React14.useEffect(() => {
553
- let channels = [];
554
- const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
555
- const setupSubscriptions = () => {
556
- const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
557
- const metricsTablePrefix = getMetricsTablePrefix();
558
- const metricsTable = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
559
- const metricsChannel = supabase.channel("workspace-metrics").on(
560
- "postgres_changes",
561
- {
562
- event: "*",
563
- schema: "public",
564
- table: metricsTable,
565
- filter: `workspace_id=eq.${workspaceId} and date=eq.${operationalDate}`
566
- },
567
- async (payload) => {
568
- console.log(`Received ${metricsTablePrefix} update:`, payload);
569
- await fetchWorkspaceMetrics();
570
- }
571
- ).subscribe((status) => {
572
- console.log(`${metricsTablePrefix} subscription status:`, status);
573
- });
574
- const overviewChannel = supabase.channel("workspace-overview-metrics").on(
575
- "postgres_changes",
576
- {
577
- event: "*",
578
- schema: "public",
579
- table: "overview_workspace_metrics",
580
- filter: `workspace_id=eq.${workspaceId} and date=eq.${operationalDate}`
581
- },
582
- async (payload) => {
583
- console.log("Received overview metrics update:", payload);
584
- await fetchWorkspaceMetrics();
585
- }
586
- ).subscribe((status) => {
587
- console.log("Overview metrics subscription status:", status);
588
- });
589
- channels = [metricsChannel, overviewChannel];
590
- };
591
- fetchWorkspaceMetrics();
592
- setupSubscriptions();
593
- return () => {
594
- channels.forEach((channel) => {
595
- console.log("Cleaning up channel subscription");
596
- supabase.removeChannel(channel);
597
- });
598
- };
599
- }, [supabase, workspaceId, fetchWorkspaceMetrics, entityConfig.companyId, dateTimeConfig.defaultTimezone]);
600
- return { workspaceMetrics, isLoading, error, refetch: fetchWorkspaceMetrics };
601
- };
602
- var useLineMetrics = (lineId) => {
603
- const supabase = useSupabase();
604
- const dateTimeConfig = useDateTimeConfig();
605
- const [lineMetrics, setLineMetrics] = React14.useState(null);
606
- const [isLoading, setIsLoading] = React14.useState(true);
607
- const [error, setError] = React14.useState(null);
608
- const fetchLineMetrics = React14.useCallback(async () => {
609
- try {
610
- const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
611
- const { data, error: fetchError } = await supabase.from("overview_line_metrics").select("*").eq("line_id", lineId).eq("date", operationalDate).single();
612
- if (fetchError) throw fetchError;
613
- setLineMetrics(data);
614
- } catch (err) {
615
- setError({ message: err.message, code: err.code });
616
- console.error("Error fetching line metrics:", err);
617
- } finally {
618
- setIsLoading(false);
619
- }
620
- }, [supabase, lineId, dateTimeConfig.defaultTimezone]);
621
- React14.useEffect(() => {
622
- let channels = [];
623
- const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
624
- const setupSubscriptions = () => {
625
- const lineMetricsChannel = supabase.channel("line-base-metrics").on(
626
- "postgres_changes",
627
- {
628
- event: "*",
629
- schema: "public",
630
- table: "line_metrics",
631
- filter: `line_id=eq.${lineId} and date=eq.${operationalDate}`
632
- },
633
- async (payload) => {
634
- console.log("Received line metrics update:", payload);
635
- await fetchLineMetrics();
636
- }
637
- ).subscribe((status) => {
638
- console.log("Line metrics subscription status:", status);
639
- });
640
- const overviewChannel = supabase.channel("line-overview-metrics").on(
641
- "postgres_changes",
642
- {
643
- event: "*",
644
- schema: "public",
645
- table: "overview_line_metrics",
646
- filter: `line_id=eq.${lineId} and date=eq.${operationalDate}`
647
- },
648
- async (payload) => {
649
- console.log("Received line overview update:", payload);
650
- await fetchLineMetrics();
651
- }
652
- ).subscribe((status) => {
653
- console.log("Line overview subscription status:", status);
654
- });
655
- channels = [lineMetricsChannel, overviewChannel];
656
- };
657
- fetchLineMetrics();
658
- setupSubscriptions();
659
- return () => {
660
- channels.forEach((channel) => {
661
- console.log("Cleaning up channel subscription");
662
- supabase.removeChannel(channel);
663
- });
664
- };
665
- }, [supabase, lineId, fetchLineMetrics, dateTimeConfig.defaultTimezone]);
666
- return { lineMetrics, isLoading, error, refetch: fetchLineMetrics };
667
- };
668
- var useMetrics = (tableName, options) => {
669
- const supabase = useSupabase();
670
- const entityConfig = useEntityConfig();
671
- const [data, setData] = React14.useState([]);
672
- const [isLoading, setIsLoading] = React14.useState(true);
673
- const [error, setError] = React14.useState(null);
674
- const channelRef = React14.useRef(null);
675
- React14.useEffect(() => {
676
- const fetchData = async () => {
677
- try {
678
- setIsLoading(true);
679
- setError(null);
680
- let actualTableName = tableName;
681
- if (tableName === "metrics") {
682
- const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
683
- const metricsTablePrefix = getMetricsTablePrefix(companyId);
684
- actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
685
- }
686
- let query = supabase.from(actualTableName).select("*");
687
- if (options?.filter) {
688
- Object.entries(options.filter).forEach(([key, value]) => {
689
- query = query.eq(key, value);
690
- });
691
- }
692
- const { data: result, error: fetchError } = await query;
693
- if (fetchError) throw fetchError;
694
- setData(result);
695
- } catch (err) {
696
- console.error(`Error fetching data from ${tableName}:`, err);
697
- setError({ message: err.message, code: err.code || "FETCH_ERROR" });
698
- } finally {
699
- setIsLoading(false);
700
- }
701
- };
702
- const setupSubscription = () => {
703
- if (!options?.realtime) return;
704
- let actualTableName = tableName;
705
- if (tableName === "metrics") {
706
- const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
707
- const metricsTablePrefix = getMetricsTablePrefix();
708
- actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
709
- }
710
- const filter2 = {};
711
- if (options?.filter) {
712
- Object.entries(options.filter).forEach(([key, value]) => {
713
- filter2[`${key}=eq.${value}`] = value;
714
- });
715
- }
716
- channelRef.current = supabase.channel(`${tableName}-changes`).on(
717
- "postgres_changes",
718
- {
719
- event: "*",
720
- schema: "public",
721
- table: actualTableName,
722
- filter: Object.keys(filter2).length > 0 ? Object.keys(filter2).join(",") : void 0
723
- },
724
- () => {
725
- fetchData();
726
- }
727
- ).subscribe();
728
- };
729
- fetchData();
730
- setupSubscription();
731
- return () => {
732
- if (channelRef.current) {
733
- supabase.removeChannel(channelRef.current);
734
- }
735
- };
736
- }, [supabase, tableName, options, entityConfig.companyId]);
737
- const refetch = async () => {
738
- setIsLoading(true);
739
- try {
740
- let actualTableName = tableName;
741
- if (tableName === "metrics") {
742
- const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
743
- const metricsTablePrefix = getMetricsTablePrefix(companyId);
744
- actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
745
- }
746
- let query = supabase.from(actualTableName).select("*");
747
- if (options?.filter) {
748
- Object.entries(options.filter).forEach(([key, value]) => {
749
- query = query.eq(key, value);
750
- });
751
- }
752
- const { data: result, error: fetchError } = await query;
753
- if (fetchError) throw fetchError;
754
- setData(result);
755
- setError(null);
756
- } catch (err) {
757
- console.error(`Error refetching data from ${tableName}:`, err);
758
- setError({ message: err.message, code: err.code || "FETCH_ERROR" });
759
- } finally {
760
- setIsLoading(false);
761
- }
762
- };
763
- return { data, isLoading, error, refetch };
764
- };
765
- var DEFAULT_DAY_SHIFT_START = "06:00";
766
- var DEFAULT_NIGHT_SHIFT_START = "18:00";
767
- var DEFAULT_TRANSITION_MINUTES = 15;
768
- var parseTimeToMinutes = (timeString) => {
769
- if (!timeString || !/^[0-2]\d:[0-5]\d$/.test(timeString)) {
770
- return NaN;
771
- }
772
- const [hours, minutes] = timeString.split(":").map(Number);
773
- return hours * 60 + minutes;
774
- };
775
- var getCurrentShift = (timezone, shiftConfig, now2 = /* @__PURE__ */ new Date()) => {
404
+ var isTransitionPeriod = (timezone, shiftConfig, now2 = /* @__PURE__ */ new Date()) => {
776
405
  const dayShiftStartStr = shiftConfig?.dayShift?.startTime || DEFAULT_DAY_SHIFT_START;
777
406
  const nightShiftStartStr = shiftConfig?.nightShift?.startTime || DEFAULT_NIGHT_SHIFT_START;
778
- const dayShiftId = shiftConfig?.dayShift?.id ?? 0;
779
- const nightShiftId = shiftConfig?.nightShift?.id ?? 1;
407
+ const transitionMinutes = shiftConfig?.transitionPeriodMinutes ?? DEFAULT_TRANSITION_MINUTES;
780
408
  const dayShiftStartMinutes = parseTimeToMinutes(dayShiftStartStr);
781
409
  const nightShiftStartMinutes = parseTimeToMinutes(nightShiftStartStr);
782
- const effectiveDayStart = !isNaN(dayShiftStartMinutes) ? dayShiftStartMinutes : parseTimeToMinutes(DEFAULT_DAY_SHIFT_START);
783
- const effectiveNightStart = !isNaN(nightShiftStartMinutes) ? nightShiftStartMinutes : parseTimeToMinutes(DEFAULT_NIGHT_SHIFT_START);
784
- const zonedNow = dateFnsTz.toZonedTime(now2, timezone);
785
- const currentHour = zonedNow.getHours();
786
- const currentMinutes = zonedNow.getMinutes();
787
- const currentTotalMinutes = currentHour * 60 + currentMinutes;
788
- const operationalDate = getOperationalDate(timezone, zonedNow, dayShiftStartStr);
789
- let determinedShiftId;
790
- if (effectiveDayStart < effectiveNightStart) {
791
- if (currentTotalMinutes >= effectiveDayStart && currentTotalMinutes < effectiveNightStart) {
792
- determinedShiftId = dayShiftId;
793
- } else {
794
- determinedShiftId = nightShiftId;
795
- }
796
- } else {
797
- if (currentTotalMinutes >= effectiveDayStart || currentTotalMinutes < effectiveNightStart) {
798
- determinedShiftId = dayShiftId;
799
- } else {
800
- determinedShiftId = nightShiftId;
801
- }
802
- }
803
- return { shiftId: determinedShiftId, date: operationalDate };
804
- };
805
- var isTransitionPeriod = (timezone, shiftConfig, now2 = /* @__PURE__ */ new Date()) => {
806
- const dayShiftStartStr = shiftConfig?.dayShift?.startTime || DEFAULT_DAY_SHIFT_START;
807
- const nightShiftStartStr = shiftConfig?.nightShift?.startTime || DEFAULT_NIGHT_SHIFT_START;
808
- const transitionMinutes = shiftConfig?.transitionPeriodMinutes ?? DEFAULT_TRANSITION_MINUTES;
809
- const dayShiftStartMinutes = parseTimeToMinutes(dayShiftStartStr);
810
- const nightShiftStartMinutes = parseTimeToMinutes(nightShiftStartStr);
811
- if (isNaN(dayShiftStartMinutes) || isNaN(nightShiftStartMinutes)) {
812
- return false;
813
- }
814
- const transitionTimes = [dayShiftStartMinutes, nightShiftStartMinutes];
410
+ if (isNaN(dayShiftStartMinutes) || isNaN(nightShiftStartMinutes)) {
411
+ return false;
412
+ }
413
+ const transitionTimes = [dayShiftStartMinutes, nightShiftStartMinutes];
815
414
  const zonedNow = dateFnsTz.toZonedTime(now2, timezone);
816
415
  const currentHour = zonedNow.getHours();
817
416
  const currentMinutes = zonedNow.getMinutes();
@@ -837,748 +436,246 @@ var isTransitionPeriod = (timezone, shiftConfig, now2 = /* @__PURE__ */ new Date
837
436
  });
838
437
  };
839
438
 
840
- // src/lib/hooks/useWorkspaceDetailedMetrics.ts
841
- var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
842
- const entityConfig = useEntityConfig();
843
- const databaseConfig = useDatabaseConfig();
844
- const dateTimeConfig = useDateTimeConfig();
845
- const shiftConfig = useShiftConfig();
846
- const workspaceConfig = useWorkspaceConfig();
847
- const supabase = useSupabase();
848
- const [metrics2, setMetrics] = React14.useState(null);
849
- const [isLoading, setIsLoading] = React14.useState(true);
850
- const [error, setError] = React14.useState(null);
851
- const updateQueueRef = React14.useRef(false);
852
- const isFetchingRef = React14.useRef(false);
853
- const timeoutRef = React14.useRef(null);
854
- const channelRef = React14.useRef(null);
855
- const schema = databaseConfig.schema ?? "public";
856
- const companyId = entityConfig.companyId || "";
857
- const metricsTablePrefix = getMetricsTablePrefix();
858
- const metricsTable = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
859
- const defaultTimezone = dateTimeConfig.defaultTimezone;
860
- const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
861
- const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
862
- const fetchMetrics = React14.useCallback(async () => {
863
- if (!workspaceId || isFetchingRef.current) return;
864
- try {
865
- isFetchingRef.current = true;
866
- const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
867
- const queryDate = date || currentShift.date;
868
- const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
869
- console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
870
- console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
871
- console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
872
- const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
873
- if (fetchError) throw fetchError;
874
- if (!data && !date && shiftId === void 0) {
875
- console.log("[useWorkspaceDetailedMetrics] No data found for current date/shift, attempting to find most recent data...");
876
- const { data: recentData, error: recentError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).order("date", { ascending: false }).order("shift_id", { ascending: false }).limit(1).maybeSingle();
877
- if (recentError) throw recentError;
878
- if (recentData) {
879
- console.log(`[useWorkspaceDetailedMetrics] Found fallback data from date: ${recentData.date}, shift: ${recentData.shift_id}`);
880
- const outputDifference2 = (recentData.total_output || 0) - (recentData.ideal_output || 0);
881
- const workspaceMatch2 = recentData.workspace_name?.match(/WS(\d+)/);
882
- const workspaceNumber2 = workspaceMatch2 ? parseInt(workspaceMatch2[1]) : 0;
883
- const specialWsStart2 = workspaceConfig.specialWorkspaces?.startId ?? 19;
884
- const specialWsEnd2 = workspaceConfig.specialWorkspaces?.endId ?? 34;
885
- const isSpecialWorkspace2 = workspaceNumber2 >= specialWsStart2 && workspaceNumber2 <= specialWsEnd2;
886
- const outputHourly2 = recentData.output_hourly || {};
887
- const hasOutputHourlyData2 = outputHourly2 && typeof outputHourly2 === "object" && Object.keys(outputHourly2).length > 0;
888
- let hourlyActionCounts2 = [];
889
- if (hasOutputHourlyData2) {
890
- console.log("Using new output_hourly column for workspace (fallback):", recentData.workspace_name);
891
- console.log("Raw output_hourly data (fallback):", outputHourly2);
892
- const isNightShift = recentData.shift_id === 1;
893
- const shiftStart = recentData.shift_start || (isNightShift ? "22:00" : "06:00");
894
- const shiftEnd = recentData.shift_end || (isNightShift ? "06:00" : "14:00");
895
- const startHour = parseInt(shiftStart.split(":")[0]);
896
- let expectedHours = [];
897
- if (isNightShift) {
898
- for (let i = 0; i < 9; i++) {
899
- expectedHours.push((startHour + i) % 24);
900
- }
901
- } else {
902
- for (let i = 0; i < 9; i++) {
903
- expectedHours.push((startHour + i) % 24);
904
- }
905
- }
906
- console.log("Expected shift hours (fallback):", expectedHours);
907
- console.log("Available data hours (fallback):", Object.keys(outputHourly2));
908
- hourlyActionCounts2 = expectedHours.map((expectedHour) => {
909
- let hourData = outputHourly2[expectedHour.toString()];
910
- if (!hourData && isNightShift) {
911
- for (const [storedHour, data2] of Object.entries(outputHourly2)) {
912
- if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
913
- if (storedHour === "18" && expectedHour === 1) {
914
- hourData = data2;
915
- console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour} (fallback)`);
916
- break;
917
- }
918
- }
919
- }
920
- }
921
- return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
922
- });
923
- console.log("Final hourly action counts (fallback):", hourlyActionCounts2);
924
- } else {
925
- console.log("Using output_array fallback for workspace (fallback):", recentData.workspace_name);
926
- const minuteByMinuteArray = recentData.output_array || [];
927
- if (isSpecialWorkspace2) {
928
- const last40Readings = minuteByMinuteArray.slice(Math.max(0, minuteByMinuteArray.length - 40));
929
- hourlyActionCounts2 = last40Readings;
930
- } else {
931
- for (let i = 0; i < minuteByMinuteArray.length; i += 60) {
932
- const hourSlice = minuteByMinuteArray.slice(i, Math.min(i + 60, minuteByMinuteArray.length));
933
- const hourlySum = hourSlice.reduce((sum, count) => sum + count, 0);
934
- hourlyActionCounts2.push(hourlySum);
935
- }
936
- }
937
- }
938
- const transformedData2 = {
939
- workspace_id: recentData.workspace_id,
940
- workspace_name: recentData.workspace_name,
941
- line_id: recentData.line_id,
942
- line_name: recentData.line_name || "Line 1",
943
- company_id: recentData.company_id || companyId,
944
- company_name: recentData.company_name || "Nahar Group",
945
- date: recentData.date,
946
- shift_id: recentData.shift_id,
947
- action_name: recentData.action_name || "",
948
- shift_start: recentData.shift_start || "06:00",
949
- shift_end: recentData.shift_end || "14:00",
950
- shift_type: recentData.shift_type || (recentData.shift_id === 0 ? "Day" : "Night"),
951
- pph_threshold: recentData.pph_threshold || 0,
952
- target_output: recentData.total_day_output || 0,
953
- avg_pph: recentData.avg_pph || 0,
954
- avg_cycle_time: recentData.avg_cycle_time || 0,
955
- ideal_cycle_time: recentData.ideal_cycle_time || 0,
956
- avg_efficiency: recentData.efficiency || 0,
957
- total_actions: recentData.total_output || 0,
958
- hourly_action_counts: hourlyActionCounts2,
959
- // Now uses the NEW logic with fallback
960
- workspace_rank: recentData.workspace_rank || 0,
961
- total_workspaces: recentData.total_workspaces || workspaceConfig.totalWorkspaces || 42,
962
- ideal_output_until_now: recentData.ideal_output || 0,
963
- output_difference: outputDifference2,
964
- idle_time: recentData.idle_time || 0,
965
- idle_time_hourly: recentData.idle_time_hourly || void 0,
966
- ...recentData.compliance_efficiency !== void 0 && { compliance_efficiency: recentData.compliance_efficiency },
967
- ...recentData.sop_check !== void 0 && { sop_check: recentData.sop_check }
968
- };
969
- setMetrics(transformedData2);
970
- setIsLoading(false);
971
- updateQueueRef.current = false;
972
- isFetchingRef.current = false;
973
- return;
974
- } else {
975
- console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
976
- }
977
- }
978
- if (!data) {
979
- console.warn("[useWorkspaceDetailedMetrics] No detailed metrics found for workspace:", workspaceId);
980
- setMetrics(null);
981
- setIsLoading(false);
982
- updateQueueRef.current = false;
983
- isFetchingRef.current = false;
984
- return;
439
+ // src/lib/utils/database.ts
440
+ var getCompanyMetricsTableName = (companyId, prefix = "workspace_performance") => {
441
+ if (!companyId) return `${prefix}_unknown_company`;
442
+ return `${prefix}_${companyId.replace(/-/g, "_")}`;
443
+ };
444
+ var getMetricsTablePrefix = (companyId) => {
445
+ return "performance_metrics";
446
+ };
447
+
448
+ // src/lib/services/dashboardService.ts
449
+ var getTable2 = (dbConfig, tableName) => {
450
+ const defaults2 = DEFAULT_DATABASE_CONFIG.tables;
451
+ const userValue = dbConfig?.tables?.[tableName];
452
+ return userValue ?? defaults2[tableName];
453
+ };
454
+ var dashboardService = {
455
+ // Example for getLineInfo:
456
+ async getLineInfo(lineIdInput) {
457
+ const supabase = _getSupabaseInstance();
458
+ const config = _getDashboardConfigInstance();
459
+ const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
460
+ const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
461
+ const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
462
+ const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
463
+ const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
464
+ const linesTable = getTable2(dbConfig, "lines");
465
+ const lineMetricsTable = getTable2(dbConfig, "lineMetrics");
466
+ const companyId = entityConfig.companyId;
467
+ const metricsTablePrefixStr = getMetricsTablePrefix();
468
+ const metricsTable = `${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
469
+ const defaultLineId = entityConfig.defaultLineId;
470
+ const secondaryLineId = entityConfig.secondaryLineId;
471
+ const factoryViewId = entityConfig.factoryViewId ?? "factory";
472
+ const defaultTimezone = dateTimeConfig.defaultTimezone;
473
+ const { shiftId, date } = getCurrentShift(defaultTimezone, shiftConfig);
474
+ const lineId = lineIdInput;
475
+ if (lineId === factoryViewId) {
476
+ if (!defaultLineId || !secondaryLineId || !companyId) {
477
+ throw new Error("Factory View requires defaultLineId, secondaryLineId, and companyId to be configured.");
985
478
  }
986
- const outputDifference = (data.total_output || 0) - (data.ideal_output || 0);
987
- const workspaceMatch = data.workspace_name?.match(/WS(\d+)/);
988
- const workspaceNumber = workspaceMatch ? parseInt(workspaceMatch[1]) : 0;
989
- const specialWsStart = workspaceConfig.specialWorkspaces?.startId ?? 19;
990
- const specialWsEnd = workspaceConfig.specialWorkspaces?.endId ?? 34;
991
- const isSpecialWorkspace = workspaceNumber >= specialWsStart && workspaceNumber <= specialWsEnd;
992
- const outputHourly = data.output_hourly || {};
993
- console.log("[DEBUG] Raw data.output_hourly:", data.output_hourly);
994
- console.log("[DEBUG] outputHourly after || {}:", outputHourly);
995
- console.log("[DEBUG] typeof outputHourly:", typeof outputHourly);
996
- console.log("[DEBUG] Object.keys(outputHourly):", Object.keys(outputHourly));
997
- console.log("[DEBUG] Object.keys(outputHourly).length:", Object.keys(outputHourly).length);
998
- const hasOutputHourlyData = outputHourly && typeof outputHourly === "object" && Object.keys(outputHourly).length > 0;
999
- console.log("[DEBUG] hasOutputHourlyData:", hasOutputHourlyData);
1000
- let hourlyActionCounts = [];
1001
- if (hasOutputHourlyData) {
1002
- console.log("\u2705 Using new output_hourly column for workspace:", data.workspace_name);
1003
- console.log("Raw output_hourly data:", JSON.stringify(outputHourly));
1004
- const isNightShift = data.shift_id === 1;
1005
- const shiftStart = data.shift_start || (isNightShift ? "22:00" : "06:00");
1006
- const shiftEnd = data.shift_end || (isNightShift ? "06:00" : "14:00");
1007
- const startHour = parseInt(shiftStart.split(":")[0]);
1008
- let expectedHours = [];
1009
- if (isNightShift) {
1010
- for (let i = 0; i < 9; i++) {
1011
- expectedHours.push((startHour + i) % 24);
1012
- }
1013
- } else {
1014
- for (let i = 0; i < 9; i++) {
1015
- expectedHours.push((startHour + i) % 24);
1016
- }
1017
- }
1018
- console.log("Expected shift hours:", expectedHours);
1019
- console.log("Available data hours:", Object.keys(outputHourly));
1020
- hourlyActionCounts = expectedHours.map((expectedHour) => {
1021
- let hourData = outputHourly[expectedHour.toString()];
1022
- if (!hourData && isNightShift) {
1023
- for (const [storedHour, data2] of Object.entries(outputHourly)) {
1024
- if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
1025
- if (storedHour === "18" && expectedHour === 1) {
1026
- hourData = data2;
1027
- console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour}`);
1028
- break;
1029
- }
1030
- }
1031
- }
1032
- }
1033
- return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
1034
- });
1035
- console.log("Final hourly action counts:", hourlyActionCounts);
1036
- } else {
1037
- console.log("\u274C Using output_array fallback for workspace:", data.workspace_name);
1038
- console.log("[DEBUG] Fallback reason - hasOutputHourlyData is false");
1039
- console.log("[DEBUG] data.output_hourly was:", data.output_hourly);
1040
- const minuteByMinuteArray = data.output_array || [];
1041
- if (isSpecialWorkspace) {
1042
- const last40Readings = minuteByMinuteArray.slice(Math.max(0, minuteByMinuteArray.length - 40));
1043
- hourlyActionCounts = last40Readings;
1044
- } else {
1045
- for (let i = 0; i < minuteByMinuteArray.length; i += 60) {
1046
- const hourSlice = minuteByMinuteArray.slice(i, Math.min(i + 60, minuteByMinuteArray.length));
1047
- const hourlySum = hourSlice.reduce((sum, count) => sum + count, 0);
1048
- hourlyActionCounts.push(hourlySum);
1049
- }
479
+ const { data: lineData2, error: lineError2 } = await supabase.from(linesTable).select(`
480
+ id,
481
+ line_name,
482
+ factory_id,
483
+ factories!lines_factory_id_fkey(factory_name),
484
+ company_id,
485
+ companies!lines_company_id_fkey(company_name:name)
486
+ `).eq("id", defaultLineId).maybeSingle();
487
+ if (lineError2) throw lineError2;
488
+ if (!lineData2) throw new Error(`Configured default line (${defaultLineId}) not found`);
489
+ const { data: metricsData, error: metricsError } = await supabase.from(lineMetricsTable).select("*").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
490
+ if (metricsError) throw metricsError;
491
+ const { data: performanceData2, error: performanceError2 } = await supabase.from(metricsTable).select("efficiency").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
492
+ if (performanceError2) throw performanceError2;
493
+ const underperformingCount2 = performanceData2?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
494
+ const totalValidWorkspaces2 = performanceData2?.filter((w) => w.efficiency >= 10).length || 0;
495
+ const combinedMetrics = (metricsData || []).reduce((acc, m) => {
496
+ acc.avg_efficiency += m.efficiency ?? 0;
497
+ acc.current_output += m.current_output ?? 0;
498
+ acc.ideal_output += m.ideal_output ?? m.line_threshold ?? 0;
499
+ return acc;
500
+ }, { avg_efficiency: 0, current_output: 0, ideal_output: 0 });
501
+ metricsData?.length || 1;
502
+ return {
503
+ line_id: factoryViewId,
504
+ // Use configured factory view ID
505
+ line_name: "Factory View",
506
+ // Consider making this configurable?
507
+ company_id: lineData2.company_id,
508
+ company_name: lineData2.companies?.[0]?.company_name ?? "",
509
+ factory_id: lineData2.factory_id,
510
+ factory_name: lineData2.factories?.[0]?.factory_name ?? "",
511
+ shift_id: shiftId,
512
+ date,
513
+ metrics: {
514
+ avg_efficiency: (performanceData2?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces2 || 1),
515
+ // Use performance data for avg efficiency
516
+ avg_cycle_time: 0,
517
+ // Needs calculation logic if required for factory view
518
+ current_output: combinedMetrics.current_output,
519
+ ideal_output: combinedMetrics.ideal_output,
520
+ total_workspaces: 44,
521
+ // SRC ALIGNMENT: Use hardcoded 44 for factory view total_workspaces
522
+ underperforming_workspaces: underperformingCount2,
523
+ underperforming_workspace_names: [],
524
+ // Populate if needed
525
+ underperforming_workspace_uuids: [],
526
+ // Populate if needed
527
+ output_array: [],
528
+ // Combine if needed
529
+ line_threshold: combinedMetrics.ideal_output,
530
+ threshold_pph: 0,
531
+ // Needs calculation logic if required
532
+ shift_start: shiftConfig.dayShift?.startTime || "06:00",
533
+ // Use config
534
+ shift_end: shiftConfig.dayShift?.endTime || "18:00",
535
+ // Use config
536
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
537
+ poorest_performing_workspaces: []
538
+ // Populate if needed
1050
539
  }
1051
- console.log("Final hourly action counts:", hourlyActionCounts);
1052
- }
1053
- const transformedData = {
1054
- workspace_id: data.workspace_id,
1055
- workspace_name: data.workspace_name,
1056
- line_id: data.line_id,
1057
- line_name: data.line_name || "Line 1",
1058
- company_id: data.company_id || companyId,
1059
- company_name: data.company_name || "Nahar Group",
1060
- date: data.date,
1061
- shift_id: data.shift_id,
1062
- action_name: data.action_name || "",
1063
- shift_start: data.shift_start || "06:00",
1064
- shift_end: data.shift_end || "14:00",
1065
- shift_type: data.shift_type || (data.shift_id === 0 ? "Day" : "Night"),
1066
- pph_threshold: data.pph_threshold || 0,
1067
- target_output: data.total_day_output || 0,
1068
- avg_pph: data.avg_pph || 0,
1069
- avg_cycle_time: data.avg_cycle_time || 0,
1070
- ideal_cycle_time: data.ideal_cycle_time || 0,
1071
- avg_efficiency: data.efficiency || 0,
1072
- total_actions: data.total_output || 0,
1073
- hourly_action_counts: hourlyActionCounts,
1074
- workspace_rank: data.workspace_rank || 0,
1075
- total_workspaces: data.total_workspaces || workspaceConfig.totalWorkspaces || 42,
1076
- ideal_output_until_now: data.ideal_output || 0,
1077
- output_difference: outputDifference,
1078
- idle_time: data.idle_time || 0,
1079
- // Add idle_time from performance_metrics table
1080
- idle_time_hourly: data.idle_time_hourly || void 0,
1081
- // Add idle_time_hourly from performance_metrics table
1082
- ...data.compliance_efficiency !== void 0 && { compliance_efficiency: data.compliance_efficiency },
1083
- ...data.sop_check !== void 0 && { sop_check: data.sop_check }
1084
540
  };
1085
- setMetrics(transformedData);
541
+ }
542
+ if (!companyId) {
543
+ throw new Error("Company ID must be configured for individual line requests.");
544
+ }
545
+ const { data: lineData, error: lineError } = await supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineId).maybeSingle();
546
+ if (lineError) throw lineError;
547
+ if (!lineData) throw new Error(`Line with ID ${lineId} not found`);
548
+ let metricsFromDb = null;
549
+ try {
550
+ const { data: fetchedMetrics, error } = await supabase.from(lineMetricsTable).select("*").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date).maybeSingle();
551
+ if (error) throw error;
552
+ metricsFromDb = fetchedMetrics;
1086
553
  } catch (err) {
1087
- console.error("Error fetching workspace metrics:", err);
1088
- setError({ message: err.message, code: err.code });
1089
- } finally {
1090
- isFetchingRef.current = false;
1091
- updateQueueRef.current = false;
1092
- setIsLoading(false);
554
+ console.error(`Error fetching line metrics for ${lineId}:`, err);
1093
555
  }
1094
- }, [supabase, workspaceId, date, shiftId, metricsTable, defaultTimezone, shiftConfig, workspaceConfig, companyId]);
1095
- const queueUpdate = React14.useCallback(() => {
1096
- if (!workspaceId || updateQueueRef.current) return;
1097
- updateQueueRef.current = true;
1098
- if (timeoutRef.current) {
1099
- clearTimeout(timeoutRef.current);
556
+ const { data: performanceData, error: performanceError } = await supabase.from(metricsTable).select("efficiency").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date);
557
+ if (performanceError) throw performanceError;
558
+ const underperformingCount = performanceData?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
559
+ const totalValidWorkspaces = performanceData?.filter((w) => w.efficiency >= 10).length || 0;
560
+ const avgEfficiencyFromPerf = (performanceData?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces || 1);
561
+ const useFallbackMetrics = !metricsFromDb;
562
+ let metricsForReturn;
563
+ if (useFallbackMetrics) {
564
+ metricsForReturn = {
565
+ avg_efficiency: 0,
566
+ avg_cycle_time: 0,
567
+ current_output: 0,
568
+ ideal_output: 0,
569
+ total_workspaces: 42,
570
+ underperforming_workspaces: underperformingCount,
571
+ underperforming_workspace_names: [],
572
+ underperforming_workspace_uuids: [],
573
+ output_array: [],
574
+ line_threshold: 0,
575
+ threshold_pph: 0,
576
+ shift_start: "06:00",
577
+ shift_end: "14:00",
578
+ last_updated: (/* @__PURE__ */ new Date()).toISOString(),
579
+ poorest_performing_workspaces: []
580
+ };
581
+ } else {
582
+ metricsForReturn = {
583
+ avg_efficiency: metricsFromDb.efficiency ?? avgEfficiencyFromPerf,
584
+ avg_cycle_time: metricsFromDb.avg_cycle_time || 0,
585
+ current_output: metricsFromDb.current_output || 0,
586
+ ideal_output: metricsFromDb.ideal_output || metricsFromDb.line_threshold || 0,
587
+ total_workspaces: metricsFromDb.total_workspaces ?? workspaceConfig.totalWorkspaces ?? 42,
588
+ underperforming_workspaces: underperformingCount,
589
+ underperforming_workspace_names: metricsFromDb.underperforming_workspace_names || [],
590
+ underperforming_workspace_uuids: metricsFromDb.underperforming_workspace_uuids || [],
591
+ output_array: metricsFromDb.output_array || [],
592
+ line_threshold: metricsFromDb.line_threshold || 0,
593
+ threshold_pph: metricsFromDb.threshold_pph || 0,
594
+ shift_start: metricsFromDb.shift_start || shiftConfig.dayShift?.startTime || "06:00",
595
+ shift_end: metricsFromDb.shift_end || shiftConfig.dayShift?.endTime || "18:00",
596
+ last_updated: metricsFromDb.last_updated || (/* @__PURE__ */ new Date()).toISOString(),
597
+ poorest_performing_workspaces: metricsFromDb.poorest_performing_workspaces || []
598
+ };
599
+ if (metricsFromDb.efficiency === null || metricsFromDb.efficiency === void 0) {
600
+ metricsForReturn.avg_efficiency = avgEfficiencyFromPerf;
601
+ }
1100
602
  }
1101
- timeoutRef.current = setTimeout(() => {
1102
- fetchMetrics();
1103
- }, 500);
1104
- }, [fetchMetrics, workspaceId]);
1105
- const setupSubscription = React14.useCallback(() => {
1106
- if (!workspaceId) return;
1107
- if (channelRef.current) {
1108
- supabase.removeChannel(channelRef.current);
603
+ return {
604
+ line_id: lineData.id,
605
+ line_name: lineData.line_name,
606
+ company_id: lineData.company_id,
607
+ company_name: lineData.companies?.[0]?.company_name ?? "",
608
+ factory_id: lineData.factory_id,
609
+ factory_name: lineData.factories?.[0]?.factory_name ?? "",
610
+ shift_id: shiftId,
611
+ date,
612
+ metrics: metricsForReturn
613
+ };
614
+ },
615
+ async getWorkspacesData(lineIdInput, dateProp, shiftProp) {
616
+ const supabase = _getSupabaseInstance();
617
+ const config = _getDashboardConfigInstance();
618
+ const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
619
+ const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
620
+ const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
621
+ const companyId = entityConfig.companyId;
622
+ if (!companyId) {
623
+ throw new Error("Company ID must be configured for workspace data requests.");
1109
624
  }
1110
- channelRef.current = supabase.channel("workspace-detailed-metrics").on(
1111
- "postgres_changes",
1112
- {
1113
- event: "*",
1114
- schema,
1115
- table: metricsTable,
1116
- filter: `workspace_id=eq.${workspaceId}`
1117
- },
1118
- async (payload) => {
1119
- console.log(`Received ${metricsTablePrefix} update:`, payload);
1120
- await fetchMetrics();
625
+ const metricsTablePrefixStr = getMetricsTablePrefix();
626
+ const metricsTable = `${metricsTablePrefixStr}_${companyId.replace(/-/g, "_")}`;
627
+ const defaultLineId = entityConfig.defaultLineId;
628
+ const secondaryLineId = entityConfig.secondaryLineId;
629
+ const factoryViewId = entityConfig.factoryViewId ?? "factory";
630
+ const defaultTimezone = dateTimeConfig.defaultTimezone;
631
+ const currentShiftResult = getCurrentShift(defaultTimezone, shiftConfig);
632
+ const queryDate = dateProp || getOperationalDate(defaultTimezone);
633
+ const queryShiftId = shiftProp ?? currentShiftResult.shiftId;
634
+ const lineId = lineIdInput;
635
+ let query = supabase.from(metricsTable).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("shift_id", queryShiftId).eq("date", queryDate);
636
+ if (!lineId || lineId === factoryViewId) {
637
+ if (!defaultLineId || !secondaryLineId) {
638
+ throw new Error("Factory View requires defaultLineId and secondaryLineId to be configured for workspace data.");
1121
639
  }
1122
- ).subscribe((status) => {
1123
- console.log(`Workspace detailed metrics subscription status:`, status);
1124
- });
1125
- }, [supabase, workspaceId, fetchMetrics, schema, metricsTable, metricsTablePrefix]);
1126
- React14.useEffect(() => {
1127
- if (!workspaceId) {
1128
- setIsLoading(false);
1129
- return;
640
+ query = query.in("line_id", [defaultLineId, secondaryLineId]);
641
+ } else {
642
+ query = query.eq("line_id", lineId);
1130
643
  }
1131
- const channels = [];
1132
- const operationalDate = date || getOperationalDate();
1133
- const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
1134
- const queryShiftId = shiftId ?? currentShift.shiftId;
1135
- const metricsChannel = supabase.channel(`workspace-metrics-${workspaceId}`).on(
1136
- "postgres_changes",
1137
- {
1138
- event: "*",
1139
- schema,
1140
- table: metricsTable,
1141
- filter: `workspace_id=eq.${workspaceId}`
1142
- },
1143
- async (payload) => {
1144
- const payloadData = payload.new;
1145
- console.log(`Received ${metricsTablePrefix} update:`, {
1146
- payload,
1147
- payloadDate: payloadData?.date,
1148
- payloadShift: payloadData?.shift_id,
1149
- currentDate: operationalDate,
1150
- currentShift: queryShiftId,
1151
- matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
1152
- });
1153
- if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
1154
- queueUpdate();
1155
- }
1156
- }
1157
- ).subscribe((status) => {
1158
- console.log(`${metricsTablePrefix} subscription status:`, status);
1159
- });
1160
- const workspaceMetricsChannel = supabase.channel(`workspace-metrics-${workspaceId}`).on(
1161
- "postgres_changes",
1162
- {
1163
- event: "*",
1164
- schema,
1165
- table: workspaceMetricsBaseTable,
1166
- filter: `workspace_id=eq.${workspaceId}`
1167
- },
1168
- async (payload) => {
1169
- const payloadData = payload.new;
1170
- console.log("Received workspace_metrics update:", {
1171
- payload,
1172
- payloadDate: payloadData?.date,
1173
- payloadShift: payloadData?.shift_id,
1174
- currentDate: operationalDate,
1175
- currentShift: queryShiftId,
1176
- matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
1177
- });
1178
- if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
1179
- queueUpdate();
1180
- }
1181
- }
1182
- ).subscribe((status) => {
1183
- console.log(`Workspace metrics subscription status:`, status);
1184
- });
1185
- const workspaceActionsChannel = supabase.channel(`workspace-actions-${workspaceId}`).on(
1186
- "postgres_changes",
1187
- {
1188
- event: "*",
1189
- schema,
1190
- table: workspaceActionsTable,
1191
- filter: `workspace_id=eq.${workspaceId}`
1192
- },
1193
- async (payload) => {
1194
- const payloadData = payload.new;
1195
- console.log("Received workspace_actions update:", {
1196
- payload,
1197
- payloadDate: payloadData?.date,
1198
- payloadShift: payloadData?.shift_id,
1199
- currentDate: operationalDate,
1200
- currentShift: queryShiftId,
1201
- matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
1202
- });
1203
- if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
1204
- queueUpdate();
1205
- }
1206
- }
1207
- ).subscribe((status) => {
1208
- console.log(`Workspace actions subscription status:`, status);
1209
- });
1210
- channels.push(metricsChannel, workspaceMetricsChannel, workspaceActionsChannel);
1211
- fetchMetrics();
1212
- setupSubscription();
1213
- return () => {
1214
- if (timeoutRef.current) {
1215
- clearTimeout(timeoutRef.current);
1216
- }
1217
- channels.forEach((channel) => {
1218
- console.log("Cleaning up channel subscription");
1219
- supabase.removeChannel(channel);
1220
- });
1221
- if (channelRef.current) {
1222
- supabase.removeChannel(channelRef.current);
1223
- }
1224
- };
1225
- }, [supabase, workspaceId, date, shiftId, fetchMetrics, queueUpdate, setupSubscription, metricsTable, workspaceMetricsBaseTable, workspaceActionsTable, defaultTimezone, shiftConfig, schema, metricsTablePrefix]);
1226
- return {
1227
- metrics: metrics2,
1228
- isLoading,
1229
- error,
1230
- refetch: fetchMetrics
1231
- };
1232
- };
1233
- var useLineWorkspaceMetrics = (lineId, options) => {
1234
- const entityConfig = useEntityConfig();
1235
- const databaseConfig = useDatabaseConfig();
1236
- const dateTimeConfig = useDateTimeConfig();
1237
- const shiftConfig = useShiftConfig();
1238
- const supabase = useSupabase();
1239
- const [workspaces, setWorkspaces] = React14.useState([]);
1240
- const [loading, setLoading] = React14.useState(true);
1241
- const [error, setError] = React14.useState(null);
1242
- const [initialized, setInitialized] = React14.useState(false);
1243
- const queryShiftId = React14.useMemo(() => {
1244
- const currentShift = getCurrentShift(
1245
- dateTimeConfig.defaultTimezone || "Asia/Kolkata",
1246
- shiftConfig
1247
- );
1248
- return options?.initialShiftId !== void 0 ? options.initialShiftId : currentShift.shiftId;
1249
- }, [options?.initialShiftId, dateTimeConfig.defaultTimezone, shiftConfig]);
1250
- const queryDate = React14.useMemo(() => {
1251
- return options?.initialDate || getOperationalDate(dateTimeConfig.defaultTimezone);
1252
- }, [options?.initialDate, dateTimeConfig.defaultTimezone]);
1253
- const metricsTable = React14.useMemo(() => {
1254
- const companyId = entityConfig.companyId;
1255
- if (!companyId) return "";
1256
- const metricsTablePrefix = getMetricsTablePrefix();
1257
- return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
1258
- }, [entityConfig.companyId]);
1259
- const schema = databaseConfig.schema ?? "public";
1260
- const fetchWorkspaceMetrics = React14.useCallback(async () => {
1261
- if (!lineId) return;
1262
- if (!initialized) {
1263
- setLoading(true);
1264
- }
1265
- setError(null);
1266
- try {
1267
- console.log("Fetching workspace metrics with params:", {
1268
- lineId,
1269
- queryDate,
1270
- queryShiftId,
1271
- metricsTable
1272
- });
1273
- const { data, error: fetchError } = await supabase.from(metricsTable).select(`
1274
- workspace_name,
1275
- total_output,
1276
- avg_pph,
1277
- efficiency,
1278
- workspace_id,
1279
- avg_cycle_time,
1280
- performance_score,
1281
- trend_score,
1282
- line_id,
1283
- total_day_output
1284
- `).eq("date", queryDate).eq("shift_id", queryShiftId).eq("line_id", lineId).order("workspace_name", { ascending: true });
1285
- if (fetchError) throw fetchError;
1286
- const transformedData = (data || []).map((item) => ({
1287
- company_id: entityConfig.companyId || "unknown",
1288
- line_id: item.line_id,
1289
- shift_id: queryShiftId,
1290
- date: queryDate,
1291
- workspace_uuid: item.workspace_id,
1292
- workspace_name: item.workspace_name,
1293
- action_count: item.total_output || 0,
1294
- pph: item.avg_pph || 0,
1295
- performance_score: item.performance_score || 0,
1296
- avg_cycle_time: item.avg_cycle_time || 0,
1297
- trend: item.trend_score === 1 ? 2 : 0,
1298
- predicted_output: 0,
1299
- efficiency: item.efficiency || 0,
1300
- action_threshold: item.total_day_output || 0
1301
- }));
1302
- setWorkspaces(transformedData);
1303
- setInitialized(true);
1304
- } catch (err) {
1305
- console.error("Error fetching workspace metrics:", err);
1306
- setError({ message: err.message, code: err.code || "FETCH_ERROR" });
1307
- } finally {
1308
- setLoading(false);
1309
- }
1310
- }, [lineId, queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
1311
- React14.useEffect(() => {
1312
- if (!initialized) {
1313
- fetchWorkspaceMetrics();
644
+ const { data, error } = await query;
645
+ if (error) {
646
+ console.error("Error in getWorkspacesData:", error);
647
+ throw error;
1314
648
  }
1315
- const setupSubscription = () => {
1316
- if (!lineId) return null;
1317
- const filter2 = `line_id=eq.${lineId} AND date=eq.${queryDate} AND shift_id=eq.${queryShiftId}`;
1318
- console.log("Setting up subscription with filter:", filter2);
1319
- const channel2 = supabase.channel(`line-workspace-metrics-${Date.now()}`).on(
1320
- "postgres_changes",
1321
- {
1322
- event: "*",
1323
- schema,
1324
- table: metricsTable,
1325
- filter: filter2
1326
- },
1327
- async (payload) => {
1328
- console.log("Workspace metrics update received:", payload);
1329
- await fetchWorkspaceMetrics();
1330
- }
1331
- ).subscribe();
1332
- return channel2;
1333
- };
1334
- const channel = setupSubscription();
1335
- return () => {
1336
- if (channel) {
1337
- supabase.removeChannel(channel);
1338
- }
1339
- };
1340
- }, [lineId, queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema]);
1341
- React14.useEffect(() => {
1342
- setInitialized(false);
1343
- }, [lineId, queryDate, queryShiftId]);
1344
- const refreshWorkspaces = fetchWorkspaceMetrics;
1345
- return React14.useMemo(
1346
- () => ({ workspaces, loading, error, refreshWorkspaces }),
1347
- [workspaces, loading, error, refreshWorkspaces]
1348
- );
1349
- };
1350
-
1351
- // src/lib/services/dashboardService.ts
1352
- var getTable = (dbConfig, tableName) => {
1353
- const defaults2 = DEFAULT_DATABASE_CONFIG.tables;
1354
- const userValue = dbConfig?.tables?.[tableName];
1355
- return userValue ?? defaults2[tableName];
1356
- };
1357
- var dashboardService = {
1358
- // Example for getLineInfo:
1359
- async getLineInfo(lineIdInput) {
649
+ return (data || []).map((item) => ({
650
+ company_id: item.company_id,
651
+ line_id: item.line_id,
652
+ shift_id: item.shift_id,
653
+ date: item.date,
654
+ workspace_uuid: item.workspace_id,
655
+ workspace_name: item.workspace_name,
656
+ action_count: item.total_output || 0,
657
+ pph: item.avg_pph || 0,
658
+ performance_score: item.performance_score || 0,
659
+ avg_cycle_time: item.avg_cycle_time || 0,
660
+ trend: item.trend_score === 1 ? 2 : item.trend_score === 0 ? 0 : 1,
661
+ predicted_output: item.ideal_output || 0,
662
+ efficiency: item.efficiency || 0,
663
+ action_threshold: item.total_day_output || 0
664
+ }));
665
+ },
666
+ async getWorkspaceDetailedMetrics(workspaceUuid, dateProp, shiftIdProp) {
1360
667
  const supabase = _getSupabaseInstance();
1361
668
  const config = _getDashboardConfigInstance();
1362
- const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
1363
669
  const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1364
670
  const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
1365
671
  const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
1366
672
  const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
1367
- const linesTable = getTable(dbConfig, "lines");
1368
- const lineMetricsTable = getTable(dbConfig, "lineMetrics");
1369
673
  const companyId = entityConfig.companyId;
674
+ if (!companyId) {
675
+ throw new Error("Company ID must be configured for detailed workspace metrics.");
676
+ }
1370
677
  const metricsTablePrefixStr = getMetricsTablePrefix();
1371
- const metricsTable = `${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
1372
- const defaultLineId = entityConfig.defaultLineId;
1373
- const secondaryLineId = entityConfig.secondaryLineId;
1374
- const factoryViewId = entityConfig.factoryViewId ?? "factory";
1375
- const defaultTimezone = dateTimeConfig.defaultTimezone;
1376
- const { shiftId, date } = getCurrentShift(defaultTimezone, shiftConfig);
1377
- const lineId = lineIdInput;
1378
- if (lineId === factoryViewId) {
1379
- if (!defaultLineId || !secondaryLineId || !companyId) {
1380
- throw new Error("Factory View requires defaultLineId, secondaryLineId, and companyId to be configured.");
1381
- }
1382
- const { data: lineData2, error: lineError2 } = await supabase.from(linesTable).select(`
1383
- id,
1384
- line_name,
1385
- factory_id,
1386
- factories!lines_factory_id_fkey(factory_name),
1387
- company_id,
1388
- companies!lines_company_id_fkey(company_name:name)
1389
- `).eq("id", defaultLineId).maybeSingle();
1390
- if (lineError2) throw lineError2;
1391
- if (!lineData2) throw new Error(`Configured default line (${defaultLineId}) not found`);
1392
- const { data: metricsData, error: metricsError } = await supabase.from(lineMetricsTable).select("*").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
1393
- if (metricsError) throw metricsError;
1394
- const { data: performanceData2, error: performanceError2 } = await supabase.from(metricsTable).select("efficiency").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
1395
- if (performanceError2) throw performanceError2;
1396
- const underperformingCount2 = performanceData2?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
1397
- const totalValidWorkspaces2 = performanceData2?.filter((w) => w.efficiency >= 10).length || 0;
1398
- const combinedMetrics = (metricsData || []).reduce((acc, m) => {
1399
- acc.avg_efficiency += m.efficiency ?? 0;
1400
- acc.current_output += m.current_output ?? 0;
1401
- acc.ideal_output += m.ideal_output ?? m.line_threshold ?? 0;
1402
- return acc;
1403
- }, { avg_efficiency: 0, current_output: 0, ideal_output: 0 });
1404
- metricsData?.length || 1;
1405
- return {
1406
- line_id: factoryViewId,
1407
- // Use configured factory view ID
1408
- line_name: "Factory View",
1409
- // Consider making this configurable?
1410
- company_id: lineData2.company_id,
1411
- company_name: lineData2.companies?.[0]?.company_name ?? "",
1412
- factory_id: lineData2.factory_id,
1413
- factory_name: lineData2.factories?.[0]?.factory_name ?? "",
1414
- shift_id: shiftId,
1415
- date,
1416
- metrics: {
1417
- avg_efficiency: (performanceData2?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces2 || 1),
1418
- // Use performance data for avg efficiency
1419
- avg_cycle_time: 0,
1420
- // Needs calculation logic if required for factory view
1421
- current_output: combinedMetrics.current_output,
1422
- ideal_output: combinedMetrics.ideal_output,
1423
- total_workspaces: 44,
1424
- // SRC ALIGNMENT: Use hardcoded 44 for factory view total_workspaces
1425
- underperforming_workspaces: underperformingCount2,
1426
- underperforming_workspace_names: [],
1427
- // Populate if needed
1428
- underperforming_workspace_uuids: [],
1429
- // Populate if needed
1430
- output_array: [],
1431
- // Combine if needed
1432
- line_threshold: combinedMetrics.ideal_output,
1433
- threshold_pph: 0,
1434
- // Needs calculation logic if required
1435
- shift_start: shiftConfig.dayShift?.startTime || "06:00",
1436
- // Use config
1437
- shift_end: shiftConfig.dayShift?.endTime || "18:00",
1438
- // Use config
1439
- last_updated: (/* @__PURE__ */ new Date()).toISOString(),
1440
- poorest_performing_workspaces: []
1441
- // Populate if needed
1442
- }
1443
- };
1444
- }
1445
- if (!companyId) {
1446
- throw new Error("Company ID must be configured for individual line requests.");
1447
- }
1448
- const { data: lineData, error: lineError } = await supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineId).maybeSingle();
1449
- if (lineError) throw lineError;
1450
- if (!lineData) throw new Error(`Line with ID ${lineId} not found`);
1451
- let metricsFromDb = null;
1452
- try {
1453
- const { data: fetchedMetrics, error } = await supabase.from(lineMetricsTable).select("*").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date).maybeSingle();
1454
- if (error) throw error;
1455
- metricsFromDb = fetchedMetrics;
1456
- } catch (err) {
1457
- console.error(`Error fetching line metrics for ${lineId}:`, err);
1458
- }
1459
- const { data: performanceData, error: performanceError } = await supabase.from(metricsTable).select("efficiency").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date);
1460
- if (performanceError) throw performanceError;
1461
- const underperformingCount = performanceData?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
1462
- const totalValidWorkspaces = performanceData?.filter((w) => w.efficiency >= 10).length || 0;
1463
- const avgEfficiencyFromPerf = (performanceData?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces || 1);
1464
- const useFallbackMetrics = !metricsFromDb;
1465
- let metricsForReturn;
1466
- if (useFallbackMetrics) {
1467
- metricsForReturn = {
1468
- avg_efficiency: 0,
1469
- avg_cycle_time: 0,
1470
- current_output: 0,
1471
- ideal_output: 0,
1472
- total_workspaces: 42,
1473
- underperforming_workspaces: underperformingCount,
1474
- underperforming_workspace_names: [],
1475
- underperforming_workspace_uuids: [],
1476
- output_array: [],
1477
- line_threshold: 0,
1478
- threshold_pph: 0,
1479
- shift_start: "06:00",
1480
- shift_end: "14:00",
1481
- last_updated: (/* @__PURE__ */ new Date()).toISOString(),
1482
- poorest_performing_workspaces: []
1483
- };
1484
- } else {
1485
- metricsForReturn = {
1486
- avg_efficiency: metricsFromDb.efficiency ?? avgEfficiencyFromPerf,
1487
- avg_cycle_time: metricsFromDb.avg_cycle_time || 0,
1488
- current_output: metricsFromDb.current_output || 0,
1489
- ideal_output: metricsFromDb.ideal_output || metricsFromDb.line_threshold || 0,
1490
- total_workspaces: metricsFromDb.total_workspaces ?? workspaceConfig.totalWorkspaces ?? 42,
1491
- underperforming_workspaces: underperformingCount,
1492
- underperforming_workspace_names: metricsFromDb.underperforming_workspace_names || [],
1493
- underperforming_workspace_uuids: metricsFromDb.underperforming_workspace_uuids || [],
1494
- output_array: metricsFromDb.output_array || [],
1495
- line_threshold: metricsFromDb.line_threshold || 0,
1496
- threshold_pph: metricsFromDb.threshold_pph || 0,
1497
- shift_start: metricsFromDb.shift_start || shiftConfig.dayShift?.startTime || "06:00",
1498
- shift_end: metricsFromDb.shift_end || shiftConfig.dayShift?.endTime || "18:00",
1499
- last_updated: metricsFromDb.last_updated || (/* @__PURE__ */ new Date()).toISOString(),
1500
- poorest_performing_workspaces: metricsFromDb.poorest_performing_workspaces || []
1501
- };
1502
- if (metricsFromDb.efficiency === null || metricsFromDb.efficiency === void 0) {
1503
- metricsForReturn.avg_efficiency = avgEfficiencyFromPerf;
1504
- }
1505
- }
1506
- return {
1507
- line_id: lineData.id,
1508
- line_name: lineData.line_name,
1509
- company_id: lineData.company_id,
1510
- company_name: lineData.companies?.[0]?.company_name ?? "",
1511
- factory_id: lineData.factory_id,
1512
- factory_name: lineData.factories?.[0]?.factory_name ?? "",
1513
- shift_id: shiftId,
1514
- date,
1515
- metrics: metricsForReturn
1516
- };
1517
- },
1518
- async getWorkspacesData(lineIdInput, dateProp, shiftProp) {
1519
- const supabase = _getSupabaseInstance();
1520
- const config = _getDashboardConfigInstance();
1521
- const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1522
- const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
1523
- const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
1524
- const companyId = entityConfig.companyId;
1525
- if (!companyId) {
1526
- throw new Error("Company ID must be configured for workspace data requests.");
1527
- }
1528
- const metricsTablePrefixStr = getMetricsTablePrefix();
1529
- const metricsTable = `${metricsTablePrefixStr}_${companyId.replace(/-/g, "_")}`;
1530
- const defaultLineId = entityConfig.defaultLineId;
1531
- const secondaryLineId = entityConfig.secondaryLineId;
1532
- const factoryViewId = entityConfig.factoryViewId ?? "factory";
1533
- const defaultTimezone = dateTimeConfig.defaultTimezone;
1534
- const currentShiftResult = getCurrentShift(defaultTimezone, shiftConfig);
1535
- const queryDate = dateProp || getOperationalDate(defaultTimezone);
1536
- const queryShiftId = shiftProp ?? currentShiftResult.shiftId;
1537
- const lineId = lineIdInput;
1538
- let query = supabase.from(metricsTable).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("shift_id", queryShiftId).eq("date", queryDate);
1539
- if (!lineId || lineId === factoryViewId) {
1540
- if (!defaultLineId || !secondaryLineId) {
1541
- throw new Error("Factory View requires defaultLineId and secondaryLineId to be configured for workspace data.");
1542
- }
1543
- query = query.in("line_id", [defaultLineId, secondaryLineId]);
1544
- } else {
1545
- query = query.eq("line_id", lineId);
1546
- }
1547
- const { data, error } = await query;
1548
- if (error) {
1549
- console.error("Error in getWorkspacesData:", error);
1550
- throw error;
1551
- }
1552
- return (data || []).map((item) => ({
1553
- company_id: item.company_id,
1554
- line_id: item.line_id,
1555
- shift_id: item.shift_id,
1556
- date: item.date,
1557
- workspace_uuid: item.workspace_id,
1558
- workspace_name: item.workspace_name,
1559
- action_count: item.total_output || 0,
1560
- pph: item.avg_pph || 0,
1561
- performance_score: item.performance_score || 0,
1562
- avg_cycle_time: item.avg_cycle_time || 0,
1563
- trend: item.trend_score === 1 ? 2 : item.trend_score === 0 ? 0 : 1,
1564
- predicted_output: item.ideal_output || 0,
1565
- efficiency: item.efficiency || 0,
1566
- action_threshold: item.total_day_output || 0
1567
- }));
1568
- },
1569
- async getWorkspaceDetailedMetrics(workspaceUuid, dateProp, shiftIdProp) {
1570
- const supabase = _getSupabaseInstance();
1571
- const config = _getDashboardConfigInstance();
1572
- const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1573
- const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
1574
- const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
1575
- const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
1576
- const companyId = entityConfig.companyId;
1577
- if (!companyId) {
1578
- throw new Error("Company ID must be configured for detailed workspace metrics.");
1579
- }
1580
- const metricsTablePrefixStr = getMetricsTablePrefix();
1581
- const metricsTable = `${metricsTablePrefixStr}_${companyId.replace(/-/g, "_")}`;
678
+ const metricsTable = `${metricsTablePrefixStr}_${companyId.replace(/-/g, "_")}`;
1582
679
  const defaultTimezone = dateTimeConfig.defaultTimezone;
1583
680
  const currentShiftResult = getCurrentShift(defaultTimezone, shiftConfig);
1584
681
  const queryDate = dateProp || getOperationalDate(defaultTimezone);
@@ -1718,7 +815,7 @@ var dashboardService = {
1718
815
  const supabase = _getSupabaseInstance();
1719
816
  const config = _getDashboardConfigInstance();
1720
817
  const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
1721
- const linesTable = getTable(dbConfig, "lines");
818
+ const linesTable = getTable2(dbConfig, "lines");
1722
819
  const companyId = config.entityConfig?.companyId;
1723
820
  try {
1724
821
  let query = supabase.from(linesTable).select(`
@@ -1760,8 +857,8 @@ var dashboardService = {
1760
857
  const shiftConfig = config.shiftConfig ?? DEFAULT_SHIFT_CONFIG;
1761
858
  const dateTimeConfig = config.dateTimeConfig ?? DEFAULT_DATE_TIME_CONFIG;
1762
859
  const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
1763
- const linesTable = getTable(dbConfig, "lines");
1764
- const lineMetricsTable = getTable(dbConfig, "lineMetrics");
860
+ const linesTable = getTable2(dbConfig, "lines");
861
+ const lineMetricsTable = getTable2(dbConfig, "lineMetrics");
1765
862
  const companyId = entityConfig.companyId;
1766
863
  const metricsTablePrefixStr = getMetricsTablePrefix();
1767
864
  const metricsTable = `${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
@@ -1912,7 +1009,7 @@ var dashboardService = {
1912
1009
  const formattedStartDate = formatDate(startDate);
1913
1010
  const formattedEndDate = formatDate(endDate);
1914
1011
  try {
1915
- const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, ideal_output, avg_pph, pph_threshold, workspace_rank").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1012
+ const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, idle_time, ideal_output, avg_pph, pph_threshold, workspace_rank").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1916
1013
  if (error) throw error;
1917
1014
  if (!data) return [];
1918
1015
  const transformedData = data.map((item) => ({
@@ -1925,7 +1022,8 @@ var dashboardService = {
1925
1022
  ideal_output: item.ideal_output || 0,
1926
1023
  avg_pph: item.avg_pph || 0,
1927
1024
  pph_threshold: item.pph_threshold || 0,
1928
- workspace_rank: item.workspace_rank || 0
1025
+ workspace_rank: item.workspace_rank || 0,
1026
+ idle_time: item.idle_time || 0
1929
1027
  }));
1930
1028
  return transformedData;
1931
1029
  } catch (err) {
@@ -1938,7 +1036,7 @@ var dashboardService = {
1938
1036
  const config = _getDashboardConfigInstance();
1939
1037
  const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
1940
1038
  const entityConfig = config.entityConfig ?? DEFAULT_ENTITY_CONFIG;
1941
- const lineMetricsTable = getTable(dbConfig, "lineMetrics");
1039
+ const lineMetricsTable = getTable2(dbConfig, "lineMetrics");
1942
1040
  const defaultLineId = entityConfig.defaultLineId;
1943
1041
  const secondaryLineId = entityConfig.secondaryLineId;
1944
1042
  const factoryViewId = entityConfig.factoryViewId ?? "factory";
@@ -2025,162 +1123,35 @@ var dashboardService = {
2025
1123
  }
2026
1124
  };
2027
1125
 
2028
- // src/lib/hooks/useHistoricWorkspaceMetrics.ts
2029
- var useHistoricWorkspaceMetrics = (workspaceId, date, inputShiftId) => {
2030
- useSupabase();
2031
- const [metrics2, setMetrics] = React14.useState(null);
2032
- const [isLoading, setIsLoading] = React14.useState(true);
2033
- const [error, setError] = React14.useState(null);
2034
- const fetchAndAnimateMetrics = React14.useCallback(async () => {
2035
- if (!workspaceId || !date || inputShiftId === void 0) {
2036
- setMetrics(null);
2037
- setIsLoading(false);
2038
- setError(null);
2039
- return;
2040
- }
2041
- setIsLoading(true);
2042
- setError(null);
2043
- try {
2044
- const fetchedData = await dashboardService.getWorkspaceDetailedMetrics(
2045
- workspaceId,
2046
- date,
2047
- inputShiftId
2048
- );
2049
- if (!fetchedData) {
2050
- console.warn("No historic data found for workspace:", workspaceId, date, inputShiftId);
2051
- setMetrics(null);
2052
- setIsLoading(false);
2053
- return;
2054
- }
2055
- const initialHourlyCounts = fetchedData.hourly_action_counts && Array.isArray(fetchedData.hourly_action_counts) ? new Array(fetchedData.hourly_action_counts.length).fill(0) : [];
2056
- setMetrics({
2057
- ...fetchedData,
2058
- hourly_action_counts: initialHourlyCounts
2059
- });
2060
- setIsLoading(false);
2061
- if (fetchedData.hourly_action_counts && fetchedData.hourly_action_counts.length > 0) {
2062
- const totalSteps = 60;
2063
- let currentStep = 0;
2064
- let animationIntervalId = setInterval(() => {
2065
- currentStep++;
2066
- const progress6 = currentStep / totalSteps;
2067
- setMetrics((prevMetrics) => {
2068
- const currentHourlyData = (fetchedData.hourly_action_counts || []).map(
2069
- (target) => Math.round(target * progress6)
2070
- );
2071
- return {
2072
- ...fetchedData,
2073
- // Base with all other correct data from the latest fetch
2074
- hourly_action_counts: currentHourlyData
2075
- };
2076
- });
2077
- if (currentStep >= totalSteps) {
2078
- if (animationIntervalId) clearInterval(animationIntervalId);
2079
- setMetrics(fetchedData);
2080
- }
2081
- }, 1e3 / 60);
2082
- } else {
2083
- setMetrics(fetchedData);
2084
- }
2085
- } catch (err) {
2086
- console.error("Error fetching historic workspace metrics:", err);
2087
- setError({ message: err.message, code: err.code || "FETCH_ERROR" });
2088
- setIsLoading(false);
2089
- setMetrics(null);
2090
- }
2091
- }, [workspaceId, date, inputShiftId]);
2092
- React14.useEffect(() => {
2093
- fetchAndAnimateMetrics();
2094
- }, [fetchAndAnimateMetrics]);
2095
- const refetch = React14.useCallback(async () => {
2096
- if (!workspaceId || !date || inputShiftId === void 0) {
2097
- setError(null);
2098
- return;
2099
- }
2100
- setIsLoading(true);
2101
- setError(null);
2102
- try {
2103
- const fetchedData = await dashboardService.getWorkspaceDetailedMetrics(
2104
- workspaceId,
2105
- date,
2106
- inputShiftId
2107
- );
2108
- if (!fetchedData) {
2109
- setMetrics(null);
2110
- return;
2111
- }
2112
- setMetrics(fetchedData);
2113
- } catch (err) {
2114
- console.error("Error re-fetching historic workspace metrics:", err);
2115
- setError({ message: err.message, code: err.code || "FETCH_ERROR" });
2116
- setMetrics(null);
2117
- } finally {
2118
- setIsLoading(false);
2119
- }
2120
- }, [workspaceId, date, inputShiftId]);
2121
- return {
2122
- metrics: metrics2,
2123
- isLoading,
2124
- error,
2125
- refetch
2126
- };
2127
- };
2128
-
2129
- // src/lib/services/actionService.ts
2130
- var getTable2 = (dbConfig, tableName) => {
2131
- const defaults2 = DEFAULT_DATABASE_CONFIG.tables;
2132
- const userValue = dbConfig?.tables?.[tableName];
2133
- return userValue ?? defaults2[tableName];
2134
- };
2135
- var actionService = {
2136
- async getActionsByName(actionNames, companyIdInput) {
2137
- const supabase = _getSupabaseInstance();
2138
- const config = _getDashboardConfigInstance();
2139
- const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
2140
- const entityConfig = config.entityConfig;
2141
- const actionsTable = getTable2(dbConfig, "actions");
2142
- const targetCompanyId = companyIdInput ?? entityConfig?.companyId;
2143
- if (!targetCompanyId) {
2144
- throw new Error("Company ID must be provided either via entityConfig.companyId or as an argument to getActionsByName.");
2145
- }
2146
- const { data, error } = await supabase.from(actionsTable).select("id, action_name, company_id").eq("company_id", targetCompanyId).in("action_name", actionNames);
2147
- if (error) {
2148
- console.error(`Error fetching actions from table ${actionsTable}:`, error);
2149
- throw error;
2150
- }
2151
- return data || [];
2152
- }
2153
- };
2154
-
2155
- // src/lib/services/realtimeService.ts
2156
- function isValidLineInfoPayload(payload) {
2157
- return payload && typeof payload === "object" && "line_id" in payload;
2158
- }
2159
- function isValidWorkspaceMetricsPayload(payload) {
2160
- return payload && typeof payload === "object" && "workspace_uuid" in payload;
2161
- }
2162
- function isValidWorkspaceDetailedMetricsPayload(payload) {
2163
- return payload && typeof payload === "object" && "workspace_id" in payload && "hourly_action_counts" in payload;
2164
- }
2165
- var realtimeService = {
2166
- subscribeToLineInfo(lineId, shiftId, date, onDataUpdate) {
2167
- const supabase = _getSupabaseInstance();
2168
- const config = _getDashboardConfigInstance();
2169
- const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
2170
- const schema = dbConfig.schema ?? "public";
2171
- const channelName = `line_info_${lineId}_${shiftId}_${date}`;
2172
- const TABLE_NAME = "line_info";
2173
- const channel = supabase.channel(channelName);
2174
- channel.on(
2175
- "postgres_changes",
2176
- { event: "*", schema, table: TABLE_NAME, filter: `line_id=eq.${lineId}` },
2177
- (payload) => {
2178
- const record = payload.new ?? payload.old;
2179
- if (record && isValidLineInfoPayload(record)) {
2180
- if ((!record.shift_id || record.shift_id === shiftId) && (!record.date || record.date === date)) {
2181
- onDataUpdate(record);
2182
- }
2183
- }
1126
+ // src/lib/services/realtimeService.ts
1127
+ function isValidLineInfoPayload(payload) {
1128
+ return payload && typeof payload === "object" && "line_id" in payload;
1129
+ }
1130
+ function isValidWorkspaceMetricsPayload(payload) {
1131
+ return payload && typeof payload === "object" && "workspace_uuid" in payload;
1132
+ }
1133
+ function isValidWorkspaceDetailedMetricsPayload(payload) {
1134
+ return payload && typeof payload === "object" && "workspace_id" in payload && "hourly_action_counts" in payload;
1135
+ }
1136
+ var realtimeService = {
1137
+ subscribeToLineInfo(lineId, shiftId, date, onDataUpdate) {
1138
+ const supabase = _getSupabaseInstance();
1139
+ const config = _getDashboardConfigInstance();
1140
+ const dbConfig = config.databaseConfig ?? DEFAULT_DATABASE_CONFIG;
1141
+ const schema = dbConfig.schema ?? "public";
1142
+ const channelName = `line_info_${lineId}_${shiftId}_${date}`;
1143
+ const TABLE_NAME = "line_info";
1144
+ const channel = supabase.channel(channelName);
1145
+ channel.on(
1146
+ "postgres_changes",
1147
+ { event: "*", schema, table: TABLE_NAME, filter: `line_id=eq.${lineId}` },
1148
+ (payload) => {
1149
+ const record = payload.new ?? payload.old;
1150
+ if (record && isValidLineInfoPayload(record)) {
1151
+ if ((!record.shift_id || record.shift_id === shiftId) && (!record.date || record.date === date)) {
1152
+ onDataUpdate(record);
1153
+ }
1154
+ }
2184
1155
  }
2185
1156
  ).subscribe((status, err) => {
2186
1157
  if (err) console.error(`[RealtimeService] Line info subscription error for ${channelName}:`, err);
@@ -2853,6 +1824,7 @@ var authRateLimitService = {
2853
1824
  clearAllRateLimits
2854
1825
  };
2855
1826
  var isMixpanelInitialized = false;
1827
+ var currentUserProperties;
2856
1828
  var initializeCoreMixpanel = (token, debug, trackPageView) => {
2857
1829
  if (!token) {
2858
1830
  console.warn("Mixpanel token not provided for initialization. Mixpanel will not be enabled.");
@@ -2878,7 +1850,13 @@ var trackCorePageView = (pageName, properties) => {
2878
1850
  };
2879
1851
  var trackCoreEvent = (eventName, properties) => {
2880
1852
  if (!isMixpanelInitialized) return;
2881
- mixpanel__default.default.track(eventName, properties);
1853
+ const mergedProps = {
1854
+ // Precedence order: explicit properties passed by caller should override
1855
+ // automatically appended user properties to avoid accidental overwrites.
1856
+ ...currentUserProperties || {},
1857
+ ...properties || {}
1858
+ };
1859
+ mixpanel__default.default.track(eventName, mergedProps);
2882
1860
  };
2883
1861
  var identifyCoreUser = (userId, userProperties) => {
2884
1862
  if (!isMixpanelInitialized) return;
@@ -2886,6 +1864,7 @@ var identifyCoreUser = (userId, userProperties) => {
2886
1864
  if (userProperties) {
2887
1865
  mixpanel__default.default.people.set(userProperties);
2888
1866
  }
1867
+ currentUserProperties = { ...userProperties };
2889
1868
  };
2890
1869
  var resetCoreMixpanel = () => {
2891
1870
  if (!isMixpanelInitialized) return;
@@ -2953,150 +1932,1194 @@ var SSEChatClient = class {
2953
1932
  } catch (textError) {
2954
1933
  }
2955
1934
  }
2956
- console.error("[SSEClient] Error response:", errorMessage);
2957
- throw new Error(errorMessage);
2958
- }
2959
- const contentType = response.headers.get("content-type");
2960
- if (!contentType?.includes("text/event-stream")) {
2961
- console.warn("[SSEClient] Unexpected content-type:", contentType);
1935
+ console.error("[SSEClient] Error response:", errorMessage);
1936
+ throw new Error(errorMessage);
1937
+ }
1938
+ const contentType = response.headers.get("content-type");
1939
+ if (!contentType?.includes("text/event-stream")) {
1940
+ console.warn("[SSEClient] Unexpected content-type:", contentType);
1941
+ }
1942
+ try {
1943
+ await this.handleSSEStream(response, callbacks);
1944
+ } finally {
1945
+ this.controllers.delete(connectionId);
1946
+ }
1947
+ }
1948
+ async handleSSEStream(response, callbacks) {
1949
+ if (!response.body) {
1950
+ console.error("[SSEClient] Response body is null");
1951
+ throw new Error("No response body available for streaming");
1952
+ }
1953
+ const reader = response.body.getReader();
1954
+ const decoder = new TextDecoder();
1955
+ let buffer = "";
1956
+ try {
1957
+ console.log("[SSEClient] Starting to read stream...");
1958
+ while (true) {
1959
+ const { done, value } = await reader.read();
1960
+ if (done) {
1961
+ console.log("[SSEClient] Stream ended");
1962
+ break;
1963
+ }
1964
+ buffer += decoder.decode(value, { stream: true });
1965
+ const lines = buffer.split("\n");
1966
+ buffer = lines.pop() || "";
1967
+ for (let i = 0; i < lines.length; i++) {
1968
+ const line = lines[i].trim();
1969
+ if (!line) continue;
1970
+ console.log("[SSEClient] Processing line:", line);
1971
+ if (line.startsWith("event:")) {
1972
+ const event = line.slice(6).trim();
1973
+ console.log("[SSEClient] Event type:", event);
1974
+ const nextLine = lines[i + 1];
1975
+ if (nextLine?.startsWith("data:")) {
1976
+ const dataStr = nextLine.slice(5).trim();
1977
+ console.log("[SSEClient] Event data:", dataStr);
1978
+ try {
1979
+ const data = JSON.parse(dataStr);
1980
+ switch (event) {
1981
+ case "thread":
1982
+ callbacks.onThreadCreated?.(data.thread_id);
1983
+ break;
1984
+ case "message":
1985
+ callbacks.onMessage?.(data.text);
1986
+ break;
1987
+ case "reasoning":
1988
+ callbacks.onReasoning?.(data.text);
1989
+ break;
1990
+ case "complete":
1991
+ callbacks.onComplete?.(data.message_id);
1992
+ break;
1993
+ case "error":
1994
+ callbacks.onError?.(data.error);
1995
+ break;
1996
+ }
1997
+ } catch (e) {
1998
+ console.error("[SSEClient] Failed to parse data:", dataStr, e);
1999
+ }
2000
+ i++;
2001
+ }
2002
+ }
2003
+ }
2004
+ }
2005
+ } finally {
2006
+ reader.releaseLock();
2007
+ }
2008
+ }
2009
+ abort(threadId) {
2010
+ if (threadId) {
2011
+ for (const [id3, controller] of this.controllers.entries()) {
2012
+ if (id3.startsWith(threadId)) {
2013
+ controller.abort();
2014
+ this.controllers.delete(id3);
2015
+ }
2016
+ }
2017
+ } else {
2018
+ for (const [id3, controller] of this.controllers.entries()) {
2019
+ controller.abort();
2020
+ }
2021
+ this.controllers.clear();
2022
+ }
2023
+ }
2024
+ };
2025
+
2026
+ // src/lib/services/chatService.ts
2027
+ async function getUserThreads(userId, limit = 20) {
2028
+ const supabase = _getSupabaseInstance();
2029
+ const { data, error } = await supabase.schema("ai").from("chat_threads").select("*").eq("user_id", userId).order("updated_at", { ascending: false }).limit(limit);
2030
+ if (error) throw error;
2031
+ return data;
2032
+ }
2033
+ async function getUserThreadsPaginated(userId, page = 1, pageSize = 20) {
2034
+ const supabase = _getSupabaseInstance();
2035
+ const from = (page - 1) * pageSize;
2036
+ const to = from + pageSize - 1;
2037
+ const { data, error, count } = await supabase.schema("ai").from("chat_threads").select("*", { count: "exact" }).eq("user_id", userId).order("updated_at", { ascending: false }).range(from, to);
2038
+ if (error) throw error;
2039
+ return {
2040
+ threads: data,
2041
+ totalCount: count || 0,
2042
+ totalPages: Math.ceil((count || 0) / pageSize),
2043
+ currentPage: page
2044
+ };
2045
+ }
2046
+ async function getThreadMessages(threadId, limit = 50, beforePosition) {
2047
+ const supabase = _getSupabaseInstance();
2048
+ let query = supabase.schema("ai").from("chat_messages").select("*").eq("thread_id", threadId).order("position", { ascending: true });
2049
+ if (beforePosition !== void 0) {
2050
+ query = query.lt("position", beforePosition);
2051
+ }
2052
+ const { data, error } = await query.limit(limit);
2053
+ if (error) throw error;
2054
+ return data;
2055
+ }
2056
+ async function getAllThreadMessages(threadId) {
2057
+ const supabase = _getSupabaseInstance();
2058
+ const { data, error } = await supabase.schema("ai").from("chat_messages").select("*").eq("thread_id", threadId).order("position", { ascending: true });
2059
+ if (error) throw error;
2060
+ return data;
2061
+ }
2062
+ async function updateThreadTitle(threadId, newTitle) {
2063
+ const supabase = _getSupabaseInstance();
2064
+ const { data, error } = await supabase.schema("ai").from("chat_threads").update({
2065
+ title: newTitle,
2066
+ auto_title: false,
2067
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
2068
+ }).eq("id", threadId).select().single();
2069
+ if (error) throw error;
2070
+ return data;
2071
+ }
2072
+ async function deleteThread(threadId) {
2073
+ const supabase = _getSupabaseInstance();
2074
+ const { error } = await supabase.schema("ai").from("chat_threads").delete().eq("id", threadId);
2075
+ if (error) throw error;
2076
+ }
2077
+ var AuthContext = React14.createContext({
2078
+ session: null,
2079
+ user: null,
2080
+ loading: true,
2081
+ error: null,
2082
+ signOut: async () => {
2083
+ }
2084
+ });
2085
+ var useAuth = () => React14.useContext(AuthContext);
2086
+ var AuthProvider = ({ children }) => {
2087
+ const supabase = useSupabase();
2088
+ const { authConfig } = useDashboardConfig();
2089
+ const [session, setSession] = React14.useState(null);
2090
+ const [user, setUser] = React14.useState(null);
2091
+ const [loading, setLoading] = React14.useState(true);
2092
+ const [error, setError] = React14.useState(null);
2093
+ const router$1 = router.useRouter();
2094
+ const userProfileTable = authConfig?.userProfileTable;
2095
+ const roleColumn = authConfig?.roleColumn || "role";
2096
+ const fetchUserDetails = React14.useCallback(async (supabaseUser) => {
2097
+ if (!supabaseUser) return null;
2098
+ const basicUser = {
2099
+ id: supabaseUser.id,
2100
+ email: supabaseUser.email
2101
+ };
2102
+ if (!userProfileTable || !supabase) return basicUser;
2103
+ try {
2104
+ const timeoutPromise = new Promise(
2105
+ (_, reject) => setTimeout(() => reject(new Error("Profile fetch timeout")), 5e3)
2106
+ );
2107
+ const fetchPromise = supabase.from(userProfileTable).select(roleColumn).eq("id", supabaseUser.id).single();
2108
+ const { data: profile, error: profileError } = await Promise.race([
2109
+ fetchPromise,
2110
+ timeoutPromise
2111
+ ]);
2112
+ if (profileError) {
2113
+ if (profileError.message.includes("does not exist") || profileError.message.includes("No rows found") || profileError.code === "PGRST116") {
2114
+ console.log("User profile table not found or user not in table, using basic auth info");
2115
+ return basicUser;
2116
+ }
2117
+ console.error("Error fetching user profile:", profileError);
2118
+ return basicUser;
2119
+ }
2120
+ const roleValue = profile ? profile[roleColumn] : void 0;
2121
+ return { ...basicUser, role: roleValue };
2122
+ } catch (err) {
2123
+ console.error("Error fetching user profile:", err);
2124
+ return basicUser;
2125
+ }
2126
+ }, [supabase, userProfileTable, roleColumn]);
2127
+ React14.useEffect(() => {
2128
+ if (!supabase) return;
2129
+ let mounted = true;
2130
+ const safetyTimeout = setTimeout(() => {
2131
+ if (mounted) {
2132
+ console.warn("Auth initialization taking too long, forcing loading to false");
2133
+ setLoading(false);
2134
+ }
2135
+ }, 1e4);
2136
+ const initializeAuth = async () => {
2137
+ try {
2138
+ const { data: { session: initialSession }, error: sessionError } = await supabase.auth.getSession();
2139
+ if (!mounted) return;
2140
+ if (sessionError) {
2141
+ setError(sessionError);
2142
+ setLoading(false);
2143
+ clearTimeout(safetyTimeout);
2144
+ return;
2145
+ }
2146
+ setSession(initialSession);
2147
+ setLoading(false);
2148
+ if (initialSession?.user) {
2149
+ try {
2150
+ const userDetails = await fetchUserDetails(initialSession.user);
2151
+ if (mounted) {
2152
+ setUser(userDetails);
2153
+ if (userDetails) {
2154
+ identifyCoreUser(userDetails.id, {
2155
+ email: userDetails.email,
2156
+ name: userDetails.email,
2157
+ // using email as the display name for now
2158
+ role: userDetails.role
2159
+ });
2160
+ }
2161
+ }
2162
+ } catch (err) {
2163
+ console.error("Error fetching user details during init:", err);
2164
+ if (mounted) {
2165
+ setUser({
2166
+ id: initialSession.user.id,
2167
+ email: initialSession.user.email
2168
+ });
2169
+ }
2170
+ }
2171
+ }
2172
+ } catch (err) {
2173
+ if (mounted) setError(err instanceof Error ? err : new Error("Failed to initialize auth"));
2174
+ } finally {
2175
+ if (mounted) {
2176
+ setLoading(false);
2177
+ clearTimeout(safetyTimeout);
2178
+ }
2179
+ }
2180
+ };
2181
+ initializeAuth();
2182
+ const { data: { subscription } } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
2183
+ if (!mounted) return;
2184
+ setSession(currentSession);
2185
+ setUser(null);
2186
+ setLoading(false);
2187
+ if (currentSession?.user) {
2188
+ try {
2189
+ const userDetails = await fetchUserDetails(currentSession.user);
2190
+ if (mounted) {
2191
+ setUser(userDetails);
2192
+ if (userDetails) {
2193
+ identifyCoreUser(userDetails.id, {
2194
+ email: userDetails.email,
2195
+ name: userDetails.email,
2196
+ role: userDetails.role
2197
+ });
2198
+ }
2199
+ }
2200
+ } catch (err) {
2201
+ console.error("Error fetching user details on auth state change:", err);
2202
+ if (mounted) {
2203
+ setUser({
2204
+ id: currentSession.user.id,
2205
+ email: currentSession.user.email
2206
+ });
2207
+ }
2208
+ }
2209
+ }
2210
+ if (mounted) setLoading(false);
2211
+ });
2212
+ return () => {
2213
+ mounted = false;
2214
+ clearTimeout(safetyTimeout);
2215
+ subscription?.unsubscribe();
2216
+ };
2217
+ }, [supabase, fetchUserDetails]);
2218
+ const signOut = async () => {
2219
+ if (!supabase) return;
2220
+ setLoading(true);
2221
+ const { error: signOutError } = await supabase.auth.signOut();
2222
+ if (signOutError) setError(signOutError);
2223
+ const logoutRedirectPath = authConfig?.defaultLogoutRedirect || "/login";
2224
+ if (router$1 && router$1.pathname !== logoutRedirectPath && !router$1.pathname.startsWith(logoutRedirectPath)) {
2225
+ router$1.replace(logoutRedirectPath);
2226
+ }
2227
+ };
2228
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value: { session, user, loading, error, signOut }, children });
2229
+ };
2230
+ var defaultContextValue = {
2231
+ components: {},
2232
+ hooks: {},
2233
+ pages: {}
2234
+ };
2235
+ var DashboardOverridesContext = React14.createContext(defaultContextValue);
2236
+ var DashboardOverridesProvider = ({
2237
+ overrides = defaultContextValue,
2238
+ children
2239
+ }) => {
2240
+ const normalizedOverrides = React14.useMemo(() => {
2241
+ return {
2242
+ components: overrides.components || {},
2243
+ hooks: overrides.hooks || {},
2244
+ pages: overrides.pages || {}
2245
+ };
2246
+ }, [overrides]);
2247
+ return /* @__PURE__ */ jsxRuntime.jsx(DashboardOverridesContext.Provider, { value: normalizedOverrides, children });
2248
+ };
2249
+ function useComponentOverride(key, Default) {
2250
+ const { components = {} } = React14.useContext(DashboardOverridesContext);
2251
+ return components[key] ?? Default;
2252
+ }
2253
+ function useHookOverride(key, Default) {
2254
+ const { hooks = {} } = React14.useContext(DashboardOverridesContext);
2255
+ return hooks[key] ?? Default;
2256
+ }
2257
+ function usePageOverride(key, Default) {
2258
+ const { pages = {} } = React14.useContext(DashboardOverridesContext);
2259
+ return pages[key] ?? Default;
2260
+ }
2261
+ function useOverrides() {
2262
+ return React14.useContext(DashboardOverridesContext);
2263
+ }
2264
+ var SupabaseContext = React14.createContext(void 0);
2265
+ var SupabaseProvider = ({ client, children }) => {
2266
+ _setSupabaseInstance(client);
2267
+ React14.useEffect(() => {
2268
+ _setSupabaseInstance(client);
2269
+ }, [client]);
2270
+ const contextValue = React14.useMemo(() => ({ supabase: client }), [client]);
2271
+ return /* @__PURE__ */ jsxRuntime.jsx(SupabaseContext.Provider, { value: contextValue, children });
2272
+ };
2273
+ var useSupabase = () => {
2274
+ const context = React14.useContext(SupabaseContext);
2275
+ if (context === void 0) {
2276
+ throw new Error("useSupabase must be used within a SupabaseProvider.");
2277
+ }
2278
+ return context.supabase;
2279
+ };
2280
+ var DEFAULT_COMPANY_ID = "default-company-id";
2281
+ var useWorkspaceMetrics = (workspaceId) => {
2282
+ const supabase = useSupabase();
2283
+ const entityConfig = useEntityConfig();
2284
+ useDatabaseConfig();
2285
+ const dateTimeConfig = useDateTimeConfig();
2286
+ const [workspaceMetrics, setWorkspaceMetrics] = React14.useState(null);
2287
+ const [isLoading, setIsLoading] = React14.useState(true);
2288
+ const [error, setError] = React14.useState(null);
2289
+ const fetchWorkspaceMetrics = React14.useCallback(async () => {
2290
+ try {
2291
+ const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
2292
+ const { data, error: fetchError } = await supabase.from("overview_workspace_metrics").select("*").eq("workspace_id", workspaceId).eq("date", operationalDate).single();
2293
+ if (fetchError) throw fetchError;
2294
+ setWorkspaceMetrics(data);
2295
+ } catch (err) {
2296
+ setError({ message: err.message, code: err.code });
2297
+ console.error("Error fetching workspace metrics:", err);
2298
+ } finally {
2299
+ setIsLoading(false);
2300
+ }
2301
+ }, [supabase, workspaceId, dateTimeConfig.defaultTimezone]);
2302
+ React14.useEffect(() => {
2303
+ let channels = [];
2304
+ const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
2305
+ const setupSubscriptions = () => {
2306
+ const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
2307
+ const metricsTablePrefix = getMetricsTablePrefix();
2308
+ const metricsTable = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2309
+ const metricsChannel = supabase.channel("workspace-metrics").on(
2310
+ "postgres_changes",
2311
+ {
2312
+ event: "*",
2313
+ schema: "public",
2314
+ table: metricsTable,
2315
+ filter: `workspace_id=eq.${workspaceId} and date=eq.${operationalDate}`
2316
+ },
2317
+ async (payload) => {
2318
+ console.log(`Received ${metricsTablePrefix} update:`, payload);
2319
+ await fetchWorkspaceMetrics();
2320
+ }
2321
+ ).subscribe((status) => {
2322
+ console.log(`${metricsTablePrefix} subscription status:`, status);
2323
+ });
2324
+ const overviewChannel = supabase.channel("workspace-overview-metrics").on(
2325
+ "postgres_changes",
2326
+ {
2327
+ event: "*",
2328
+ schema: "public",
2329
+ table: "overview_workspace_metrics",
2330
+ filter: `workspace_id=eq.${workspaceId} and date=eq.${operationalDate}`
2331
+ },
2332
+ async (payload) => {
2333
+ console.log("Received overview metrics update:", payload);
2334
+ await fetchWorkspaceMetrics();
2335
+ }
2336
+ ).subscribe((status) => {
2337
+ console.log("Overview metrics subscription status:", status);
2338
+ });
2339
+ channels = [metricsChannel, overviewChannel];
2340
+ };
2341
+ fetchWorkspaceMetrics();
2342
+ setupSubscriptions();
2343
+ return () => {
2344
+ channels.forEach((channel) => {
2345
+ console.log("Cleaning up channel subscription");
2346
+ supabase.removeChannel(channel);
2347
+ });
2348
+ };
2349
+ }, [supabase, workspaceId, fetchWorkspaceMetrics, entityConfig.companyId, dateTimeConfig.defaultTimezone]);
2350
+ return { workspaceMetrics, isLoading, error, refetch: fetchWorkspaceMetrics };
2351
+ };
2352
+ var useLineMetrics = (lineId) => {
2353
+ const supabase = useSupabase();
2354
+ const dateTimeConfig = useDateTimeConfig();
2355
+ const [lineMetrics, setLineMetrics] = React14.useState(null);
2356
+ const [isLoading, setIsLoading] = React14.useState(true);
2357
+ const [error, setError] = React14.useState(null);
2358
+ const fetchLineMetrics = React14.useCallback(async () => {
2359
+ try {
2360
+ const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
2361
+ const { data, error: fetchError } = await supabase.from("overview_line_metrics").select("*").eq("line_id", lineId).eq("date", operationalDate).single();
2362
+ if (fetchError) throw fetchError;
2363
+ setLineMetrics(data);
2364
+ } catch (err) {
2365
+ setError({ message: err.message, code: err.code });
2366
+ console.error("Error fetching line metrics:", err);
2367
+ } finally {
2368
+ setIsLoading(false);
2369
+ }
2370
+ }, [supabase, lineId, dateTimeConfig.defaultTimezone]);
2371
+ React14.useEffect(() => {
2372
+ let channels = [];
2373
+ const operationalDate = getOperationalDate(dateTimeConfig.defaultTimezone);
2374
+ const setupSubscriptions = () => {
2375
+ const lineMetricsChannel = supabase.channel("line-base-metrics").on(
2376
+ "postgres_changes",
2377
+ {
2378
+ event: "*",
2379
+ schema: "public",
2380
+ table: "line_metrics",
2381
+ filter: `line_id=eq.${lineId} and date=eq.${operationalDate}`
2382
+ },
2383
+ async (payload) => {
2384
+ console.log("Received line metrics update:", payload);
2385
+ await fetchLineMetrics();
2386
+ }
2387
+ ).subscribe((status) => {
2388
+ console.log("Line metrics subscription status:", status);
2389
+ });
2390
+ const overviewChannel = supabase.channel("line-overview-metrics").on(
2391
+ "postgres_changes",
2392
+ {
2393
+ event: "*",
2394
+ schema: "public",
2395
+ table: "overview_line_metrics",
2396
+ filter: `line_id=eq.${lineId} and date=eq.${operationalDate}`
2397
+ },
2398
+ async (payload) => {
2399
+ console.log("Received line overview update:", payload);
2400
+ await fetchLineMetrics();
2401
+ }
2402
+ ).subscribe((status) => {
2403
+ console.log("Line overview subscription status:", status);
2404
+ });
2405
+ channels = [lineMetricsChannel, overviewChannel];
2406
+ };
2407
+ fetchLineMetrics();
2408
+ setupSubscriptions();
2409
+ return () => {
2410
+ channels.forEach((channel) => {
2411
+ console.log("Cleaning up channel subscription");
2412
+ supabase.removeChannel(channel);
2413
+ });
2414
+ };
2415
+ }, [supabase, lineId, fetchLineMetrics, dateTimeConfig.defaultTimezone]);
2416
+ return { lineMetrics, isLoading, error, refetch: fetchLineMetrics };
2417
+ };
2418
+ var useMetrics = (tableName, options) => {
2419
+ const supabase = useSupabase();
2420
+ const entityConfig = useEntityConfig();
2421
+ const [data, setData] = React14.useState([]);
2422
+ const [isLoading, setIsLoading] = React14.useState(true);
2423
+ const [error, setError] = React14.useState(null);
2424
+ const channelRef = React14.useRef(null);
2425
+ React14.useEffect(() => {
2426
+ const fetchData = async () => {
2427
+ try {
2428
+ setIsLoading(true);
2429
+ setError(null);
2430
+ let actualTableName = tableName;
2431
+ if (tableName === "metrics") {
2432
+ const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
2433
+ const metricsTablePrefix = getMetricsTablePrefix(companyId);
2434
+ actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2435
+ }
2436
+ let query = supabase.from(actualTableName).select("*");
2437
+ if (options?.filter) {
2438
+ Object.entries(options.filter).forEach(([key, value]) => {
2439
+ query = query.eq(key, value);
2440
+ });
2441
+ }
2442
+ const { data: result, error: fetchError } = await query;
2443
+ if (fetchError) throw fetchError;
2444
+ setData(result);
2445
+ } catch (err) {
2446
+ console.error(`Error fetching data from ${tableName}:`, err);
2447
+ setError({ message: err.message, code: err.code || "FETCH_ERROR" });
2448
+ } finally {
2449
+ setIsLoading(false);
2450
+ }
2451
+ };
2452
+ const setupSubscription = () => {
2453
+ if (!options?.realtime) return;
2454
+ let actualTableName = tableName;
2455
+ if (tableName === "metrics") {
2456
+ const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
2457
+ const metricsTablePrefix = getMetricsTablePrefix();
2458
+ actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2459
+ }
2460
+ const filter2 = {};
2461
+ if (options?.filter) {
2462
+ Object.entries(options.filter).forEach(([key, value]) => {
2463
+ filter2[`${key}=eq.${value}`] = value;
2464
+ });
2465
+ }
2466
+ channelRef.current = supabase.channel(`${tableName}-changes`).on(
2467
+ "postgres_changes",
2468
+ {
2469
+ event: "*",
2470
+ schema: "public",
2471
+ table: actualTableName,
2472
+ filter: Object.keys(filter2).length > 0 ? Object.keys(filter2).join(",") : void 0
2473
+ },
2474
+ () => {
2475
+ fetchData();
2476
+ }
2477
+ ).subscribe();
2478
+ };
2479
+ fetchData();
2480
+ setupSubscription();
2481
+ return () => {
2482
+ if (channelRef.current) {
2483
+ supabase.removeChannel(channelRef.current);
2484
+ }
2485
+ };
2486
+ }, [supabase, tableName, options, entityConfig.companyId]);
2487
+ const refetch = async () => {
2488
+ setIsLoading(true);
2489
+ try {
2490
+ let actualTableName = tableName;
2491
+ if (tableName === "metrics") {
2492
+ const companyId = entityConfig.companyId || DEFAULT_COMPANY_ID;
2493
+ const metricsTablePrefix = getMetricsTablePrefix(companyId);
2494
+ actualTableName = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2495
+ }
2496
+ let query = supabase.from(actualTableName).select("*");
2497
+ if (options?.filter) {
2498
+ Object.entries(options.filter).forEach(([key, value]) => {
2499
+ query = query.eq(key, value);
2500
+ });
2501
+ }
2502
+ const { data: result, error: fetchError } = await query;
2503
+ if (fetchError) throw fetchError;
2504
+ setData(result);
2505
+ setError(null);
2506
+ } catch (err) {
2507
+ console.error(`Error refetching data from ${tableName}:`, err);
2508
+ setError({ message: err.message, code: err.code || "FETCH_ERROR" });
2509
+ } finally {
2510
+ setIsLoading(false);
2511
+ }
2512
+ };
2513
+ return { data, isLoading, error, refetch };
2514
+ };
2515
+ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2516
+ const entityConfig = useEntityConfig();
2517
+ const databaseConfig = useDatabaseConfig();
2518
+ const dateTimeConfig = useDateTimeConfig();
2519
+ const shiftConfig = useShiftConfig();
2520
+ const workspaceConfig = useWorkspaceConfig();
2521
+ const supabase = useSupabase();
2522
+ const [metrics2, setMetrics] = React14.useState(null);
2523
+ const [isLoading, setIsLoading] = React14.useState(true);
2524
+ const [error, setError] = React14.useState(null);
2525
+ const updateQueueRef = React14.useRef(false);
2526
+ const isFetchingRef = React14.useRef(false);
2527
+ const timeoutRef = React14.useRef(null);
2528
+ const channelRef = React14.useRef(null);
2529
+ const schema = databaseConfig.schema ?? "public";
2530
+ const companyId = entityConfig.companyId || "";
2531
+ const metricsTablePrefix = getMetricsTablePrefix();
2532
+ const metricsTable = `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2533
+ const defaultTimezone = dateTimeConfig.defaultTimezone;
2534
+ const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
2535
+ const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
2536
+ const fetchMetrics = React14.useCallback(async () => {
2537
+ if (!workspaceId || isFetchingRef.current) return;
2538
+ try {
2539
+ isFetchingRef.current = true;
2540
+ const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
2541
+ const queryDate = date || currentShift.date;
2542
+ const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
2543
+ console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
2544
+ console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
2545
+ console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
2546
+ const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
2547
+ if (fetchError) throw fetchError;
2548
+ if (!data && !date && shiftId === void 0) {
2549
+ console.log("[useWorkspaceDetailedMetrics] No data found for current date/shift, attempting to find most recent data...");
2550
+ const { data: recentData, error: recentError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).order("date", { ascending: false }).order("shift_id", { ascending: false }).limit(1).maybeSingle();
2551
+ if (recentError) throw recentError;
2552
+ if (recentData) {
2553
+ console.log(`[useWorkspaceDetailedMetrics] Found fallback data from date: ${recentData.date}, shift: ${recentData.shift_id}`);
2554
+ const outputDifference2 = (recentData.total_output || 0) - (recentData.ideal_output || 0);
2555
+ const workspaceMatch2 = recentData.workspace_name?.match(/WS(\d+)/);
2556
+ const workspaceNumber2 = workspaceMatch2 ? parseInt(workspaceMatch2[1]) : 0;
2557
+ const specialWsStart2 = workspaceConfig.specialWorkspaces?.startId ?? 19;
2558
+ const specialWsEnd2 = workspaceConfig.specialWorkspaces?.endId ?? 34;
2559
+ const isSpecialWorkspace2 = workspaceNumber2 >= specialWsStart2 && workspaceNumber2 <= specialWsEnd2;
2560
+ const outputHourly2 = recentData.output_hourly || {};
2561
+ const hasOutputHourlyData2 = outputHourly2 && typeof outputHourly2 === "object" && Object.keys(outputHourly2).length > 0;
2562
+ let hourlyActionCounts2 = [];
2563
+ if (hasOutputHourlyData2) {
2564
+ console.log("Using new output_hourly column for workspace (fallback):", recentData.workspace_name);
2565
+ console.log("Raw output_hourly data (fallback):", outputHourly2);
2566
+ const isNightShift = recentData.shift_id === 1;
2567
+ const shiftStart = recentData.shift_start || (isNightShift ? "22:00" : "06:00");
2568
+ const shiftEnd = recentData.shift_end || (isNightShift ? "06:00" : "14:00");
2569
+ const startHour = parseInt(shiftStart.split(":")[0]);
2570
+ let expectedHours = [];
2571
+ if (isNightShift) {
2572
+ for (let i = 0; i < 9; i++) {
2573
+ expectedHours.push((startHour + i) % 24);
2574
+ }
2575
+ } else {
2576
+ for (let i = 0; i < 9; i++) {
2577
+ expectedHours.push((startHour + i) % 24);
2578
+ }
2579
+ }
2580
+ console.log("Expected shift hours (fallback):", expectedHours);
2581
+ console.log("Available data hours (fallback):", Object.keys(outputHourly2));
2582
+ hourlyActionCounts2 = expectedHours.map((expectedHour) => {
2583
+ let hourData = outputHourly2[expectedHour.toString()];
2584
+ if (!hourData && isNightShift) {
2585
+ for (const [storedHour, data2] of Object.entries(outputHourly2)) {
2586
+ if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
2587
+ if (storedHour === "18" && expectedHour === 1) {
2588
+ hourData = data2;
2589
+ console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour} (fallback)`);
2590
+ break;
2591
+ }
2592
+ }
2593
+ }
2594
+ }
2595
+ return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
2596
+ });
2597
+ console.log("Final hourly action counts (fallback):", hourlyActionCounts2);
2598
+ } else {
2599
+ console.log("Using output_array fallback for workspace (fallback):", recentData.workspace_name);
2600
+ const minuteByMinuteArray = recentData.output_array || [];
2601
+ if (isSpecialWorkspace2) {
2602
+ const last40Readings = minuteByMinuteArray.slice(Math.max(0, minuteByMinuteArray.length - 40));
2603
+ hourlyActionCounts2 = last40Readings;
2604
+ } else {
2605
+ for (let i = 0; i < minuteByMinuteArray.length; i += 60) {
2606
+ const hourSlice = minuteByMinuteArray.slice(i, Math.min(i + 60, minuteByMinuteArray.length));
2607
+ const hourlySum = hourSlice.reduce((sum, count) => sum + count, 0);
2608
+ hourlyActionCounts2.push(hourlySum);
2609
+ }
2610
+ }
2611
+ }
2612
+ const transformedData2 = {
2613
+ workspace_id: recentData.workspace_id,
2614
+ workspace_name: recentData.workspace_name,
2615
+ line_id: recentData.line_id,
2616
+ line_name: recentData.line_name || "Line 1",
2617
+ company_id: recentData.company_id || companyId,
2618
+ company_name: recentData.company_name || "Nahar Group",
2619
+ date: recentData.date,
2620
+ shift_id: recentData.shift_id,
2621
+ action_name: recentData.action_name || "",
2622
+ shift_start: recentData.shift_start || "06:00",
2623
+ shift_end: recentData.shift_end || "14:00",
2624
+ shift_type: recentData.shift_type || (recentData.shift_id === 0 ? "Day" : "Night"),
2625
+ pph_threshold: recentData.pph_threshold || 0,
2626
+ target_output: recentData.total_day_output || 0,
2627
+ avg_pph: recentData.avg_pph || 0,
2628
+ avg_cycle_time: recentData.avg_cycle_time || 0,
2629
+ ideal_cycle_time: recentData.ideal_cycle_time || 0,
2630
+ avg_efficiency: recentData.efficiency || 0,
2631
+ total_actions: recentData.total_output || 0,
2632
+ hourly_action_counts: hourlyActionCounts2,
2633
+ // Now uses the NEW logic with fallback
2634
+ workspace_rank: recentData.workspace_rank || 0,
2635
+ total_workspaces: recentData.total_workspaces || workspaceConfig.totalWorkspaces || 42,
2636
+ ideal_output_until_now: recentData.ideal_output || 0,
2637
+ output_difference: outputDifference2,
2638
+ idle_time: recentData.idle_time || 0,
2639
+ idle_time_hourly: recentData.idle_time_hourly || void 0,
2640
+ ...recentData.compliance_efficiency !== void 0 && { compliance_efficiency: recentData.compliance_efficiency },
2641
+ ...recentData.sop_check !== void 0 && { sop_check: recentData.sop_check }
2642
+ };
2643
+ setMetrics(transformedData2);
2644
+ setIsLoading(false);
2645
+ updateQueueRef.current = false;
2646
+ isFetchingRef.current = false;
2647
+ return;
2648
+ } else {
2649
+ console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
2650
+ }
2651
+ }
2652
+ if (!data) {
2653
+ console.warn("[useWorkspaceDetailedMetrics] No detailed metrics found for workspace:", workspaceId);
2654
+ setMetrics(null);
2655
+ setIsLoading(false);
2656
+ updateQueueRef.current = false;
2657
+ isFetchingRef.current = false;
2658
+ return;
2659
+ }
2660
+ const outputDifference = (data.total_output || 0) - (data.ideal_output || 0);
2661
+ const workspaceMatch = data.workspace_name?.match(/WS(\d+)/);
2662
+ const workspaceNumber = workspaceMatch ? parseInt(workspaceMatch[1]) : 0;
2663
+ const specialWsStart = workspaceConfig.specialWorkspaces?.startId ?? 19;
2664
+ const specialWsEnd = workspaceConfig.specialWorkspaces?.endId ?? 34;
2665
+ const isSpecialWorkspace = workspaceNumber >= specialWsStart && workspaceNumber <= specialWsEnd;
2666
+ const outputHourly = data.output_hourly || {};
2667
+ console.log("[DEBUG] Raw data.output_hourly:", data.output_hourly);
2668
+ console.log("[DEBUG] outputHourly after || {}:", outputHourly);
2669
+ console.log("[DEBUG] typeof outputHourly:", typeof outputHourly);
2670
+ console.log("[DEBUG] Object.keys(outputHourly):", Object.keys(outputHourly));
2671
+ console.log("[DEBUG] Object.keys(outputHourly).length:", Object.keys(outputHourly).length);
2672
+ const hasOutputHourlyData = outputHourly && typeof outputHourly === "object" && Object.keys(outputHourly).length > 0;
2673
+ console.log("[DEBUG] hasOutputHourlyData:", hasOutputHourlyData);
2674
+ let hourlyActionCounts = [];
2675
+ if (hasOutputHourlyData) {
2676
+ console.log("\u2705 Using new output_hourly column for workspace:", data.workspace_name);
2677
+ console.log("Raw output_hourly data:", JSON.stringify(outputHourly));
2678
+ const isNightShift = data.shift_id === 1;
2679
+ const shiftStart = data.shift_start || (isNightShift ? "22:00" : "06:00");
2680
+ const shiftEnd = data.shift_end || (isNightShift ? "06:00" : "14:00");
2681
+ const startHour = parseInt(shiftStart.split(":")[0]);
2682
+ let expectedHours = [];
2683
+ if (isNightShift) {
2684
+ for (let i = 0; i < 9; i++) {
2685
+ expectedHours.push((startHour + i) % 24);
2686
+ }
2687
+ } else {
2688
+ for (let i = 0; i < 9; i++) {
2689
+ expectedHours.push((startHour + i) % 24);
2690
+ }
2691
+ }
2692
+ console.log("Expected shift hours:", expectedHours);
2693
+ console.log("Available data hours:", Object.keys(outputHourly));
2694
+ hourlyActionCounts = expectedHours.map((expectedHour) => {
2695
+ let hourData = outputHourly[expectedHour.toString()];
2696
+ if (!hourData && isNightShift) {
2697
+ for (const [storedHour, data2] of Object.entries(outputHourly)) {
2698
+ if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
2699
+ if (storedHour === "18" && expectedHour === 1) {
2700
+ hourData = data2;
2701
+ console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour}`);
2702
+ break;
2703
+ }
2704
+ }
2705
+ }
2706
+ }
2707
+ return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
2708
+ });
2709
+ console.log("Final hourly action counts:", hourlyActionCounts);
2710
+ } else {
2711
+ console.log("\u274C Using output_array fallback for workspace:", data.workspace_name);
2712
+ console.log("[DEBUG] Fallback reason - hasOutputHourlyData is false");
2713
+ console.log("[DEBUG] data.output_hourly was:", data.output_hourly);
2714
+ const minuteByMinuteArray = data.output_array || [];
2715
+ if (isSpecialWorkspace) {
2716
+ const last40Readings = minuteByMinuteArray.slice(Math.max(0, minuteByMinuteArray.length - 40));
2717
+ hourlyActionCounts = last40Readings;
2718
+ } else {
2719
+ for (let i = 0; i < minuteByMinuteArray.length; i += 60) {
2720
+ const hourSlice = minuteByMinuteArray.slice(i, Math.min(i + 60, minuteByMinuteArray.length));
2721
+ const hourlySum = hourSlice.reduce((sum, count) => sum + count, 0);
2722
+ hourlyActionCounts.push(hourlySum);
2723
+ }
2724
+ }
2725
+ console.log("Final hourly action counts:", hourlyActionCounts);
2726
+ }
2727
+ const transformedData = {
2728
+ workspace_id: data.workspace_id,
2729
+ workspace_name: data.workspace_name,
2730
+ line_id: data.line_id,
2731
+ line_name: data.line_name || "Line 1",
2732
+ company_id: data.company_id || companyId,
2733
+ company_name: data.company_name || "Nahar Group",
2734
+ date: data.date,
2735
+ shift_id: data.shift_id,
2736
+ action_name: data.action_name || "",
2737
+ shift_start: data.shift_start || "06:00",
2738
+ shift_end: data.shift_end || "14:00",
2739
+ shift_type: data.shift_type || (data.shift_id === 0 ? "Day" : "Night"),
2740
+ pph_threshold: data.pph_threshold || 0,
2741
+ target_output: data.total_day_output || 0,
2742
+ avg_pph: data.avg_pph || 0,
2743
+ avg_cycle_time: data.avg_cycle_time || 0,
2744
+ ideal_cycle_time: data.ideal_cycle_time || 0,
2745
+ avg_efficiency: data.efficiency || 0,
2746
+ total_actions: data.total_output || 0,
2747
+ hourly_action_counts: hourlyActionCounts,
2748
+ workspace_rank: data.workspace_rank || 0,
2749
+ total_workspaces: data.total_workspaces || workspaceConfig.totalWorkspaces || 42,
2750
+ ideal_output_until_now: data.ideal_output || 0,
2751
+ output_difference: outputDifference,
2752
+ idle_time: data.idle_time || 0,
2753
+ // Add idle_time from performance_metrics table
2754
+ idle_time_hourly: data.idle_time_hourly || void 0,
2755
+ // Add idle_time_hourly from performance_metrics table
2756
+ ...data.compliance_efficiency !== void 0 && { compliance_efficiency: data.compliance_efficiency },
2757
+ ...data.sop_check !== void 0 && { sop_check: data.sop_check }
2758
+ };
2759
+ setMetrics(transformedData);
2760
+ } catch (err) {
2761
+ console.error("Error fetching workspace metrics:", err);
2762
+ setError({ message: err.message, code: err.code });
2763
+ } finally {
2764
+ isFetchingRef.current = false;
2765
+ updateQueueRef.current = false;
2766
+ setIsLoading(false);
2767
+ }
2768
+ }, [supabase, workspaceId, date, shiftId, metricsTable, defaultTimezone, shiftConfig, workspaceConfig, companyId]);
2769
+ const queueUpdate = React14.useCallback(() => {
2770
+ if (!workspaceId || updateQueueRef.current) return;
2771
+ updateQueueRef.current = true;
2772
+ if (timeoutRef.current) {
2773
+ clearTimeout(timeoutRef.current);
2774
+ }
2775
+ timeoutRef.current = setTimeout(() => {
2776
+ fetchMetrics();
2777
+ }, 500);
2778
+ }, [fetchMetrics, workspaceId]);
2779
+ const setupSubscription = React14.useCallback(() => {
2780
+ if (!workspaceId) return;
2781
+ if (channelRef.current) {
2782
+ supabase.removeChannel(channelRef.current);
2783
+ }
2784
+ channelRef.current = supabase.channel("workspace-detailed-metrics").on(
2785
+ "postgres_changes",
2786
+ {
2787
+ event: "*",
2788
+ schema,
2789
+ table: metricsTable,
2790
+ filter: `workspace_id=eq.${workspaceId}`
2791
+ },
2792
+ async (payload) => {
2793
+ console.log(`Received ${metricsTablePrefix} update:`, payload);
2794
+ await fetchMetrics();
2795
+ }
2796
+ ).subscribe((status) => {
2797
+ console.log(`Workspace detailed metrics subscription status:`, status);
2798
+ });
2799
+ }, [supabase, workspaceId, fetchMetrics, schema, metricsTable, metricsTablePrefix]);
2800
+ React14.useEffect(() => {
2801
+ if (!workspaceId) {
2802
+ setIsLoading(false);
2803
+ return;
2804
+ }
2805
+ const channels = [];
2806
+ const operationalDate = date || getOperationalDate();
2807
+ const currentShift = getCurrentShift(defaultTimezone, shiftConfig);
2808
+ const queryShiftId = shiftId ?? currentShift.shiftId;
2809
+ const metricsChannel = supabase.channel(`workspace-metrics-${workspaceId}`).on(
2810
+ "postgres_changes",
2811
+ {
2812
+ event: "*",
2813
+ schema,
2814
+ table: metricsTable,
2815
+ filter: `workspace_id=eq.${workspaceId}`
2816
+ },
2817
+ async (payload) => {
2818
+ const payloadData = payload.new;
2819
+ console.log(`Received ${metricsTablePrefix} update:`, {
2820
+ payload,
2821
+ payloadDate: payloadData?.date,
2822
+ payloadShift: payloadData?.shift_id,
2823
+ currentDate: operationalDate,
2824
+ currentShift: queryShiftId,
2825
+ matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2826
+ });
2827
+ if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
2828
+ queueUpdate();
2829
+ }
2830
+ }
2831
+ ).subscribe((status) => {
2832
+ console.log(`${metricsTablePrefix} subscription status:`, status);
2833
+ });
2834
+ const workspaceMetricsChannel = supabase.channel(`workspace-metrics-${workspaceId}`).on(
2835
+ "postgres_changes",
2836
+ {
2837
+ event: "*",
2838
+ schema,
2839
+ table: workspaceMetricsBaseTable,
2840
+ filter: `workspace_id=eq.${workspaceId}`
2841
+ },
2842
+ async (payload) => {
2843
+ const payloadData = payload.new;
2844
+ console.log("Received workspace_metrics update:", {
2845
+ payload,
2846
+ payloadDate: payloadData?.date,
2847
+ payloadShift: payloadData?.shift_id,
2848
+ currentDate: operationalDate,
2849
+ currentShift: queryShiftId,
2850
+ matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2851
+ });
2852
+ if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
2853
+ queueUpdate();
2854
+ }
2855
+ }
2856
+ ).subscribe((status) => {
2857
+ console.log(`Workspace metrics subscription status:`, status);
2858
+ });
2859
+ const workspaceActionsChannel = supabase.channel(`workspace-actions-${workspaceId}`).on(
2860
+ "postgres_changes",
2861
+ {
2862
+ event: "*",
2863
+ schema,
2864
+ table: workspaceActionsTable,
2865
+ filter: `workspace_id=eq.${workspaceId}`
2866
+ },
2867
+ async (payload) => {
2868
+ const payloadData = payload.new;
2869
+ console.log("Received workspace_actions update:", {
2870
+ payload,
2871
+ payloadDate: payloadData?.date,
2872
+ payloadShift: payloadData?.shift_id,
2873
+ currentDate: operationalDate,
2874
+ currentShift: queryShiftId,
2875
+ matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2876
+ });
2877
+ if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
2878
+ queueUpdate();
2879
+ }
2880
+ }
2881
+ ).subscribe((status) => {
2882
+ console.log(`Workspace actions subscription status:`, status);
2883
+ });
2884
+ channels.push(metricsChannel, workspaceMetricsChannel, workspaceActionsChannel);
2885
+ fetchMetrics();
2886
+ setupSubscription();
2887
+ return () => {
2888
+ if (timeoutRef.current) {
2889
+ clearTimeout(timeoutRef.current);
2890
+ }
2891
+ channels.forEach((channel) => {
2892
+ console.log("Cleaning up channel subscription");
2893
+ supabase.removeChannel(channel);
2894
+ });
2895
+ if (channelRef.current) {
2896
+ supabase.removeChannel(channelRef.current);
2897
+ }
2898
+ };
2899
+ }, [supabase, workspaceId, date, shiftId, fetchMetrics, queueUpdate, setupSubscription, metricsTable, workspaceMetricsBaseTable, workspaceActionsTable, defaultTimezone, shiftConfig, schema, metricsTablePrefix]);
2900
+ return {
2901
+ metrics: metrics2,
2902
+ isLoading,
2903
+ error,
2904
+ refetch: fetchMetrics
2905
+ };
2906
+ };
2907
+ var useLineWorkspaceMetrics = (lineId, options) => {
2908
+ const entityConfig = useEntityConfig();
2909
+ const databaseConfig = useDatabaseConfig();
2910
+ const dateTimeConfig = useDateTimeConfig();
2911
+ const shiftConfig = useShiftConfig();
2912
+ const supabase = useSupabase();
2913
+ const [workspaces, setWorkspaces] = React14.useState([]);
2914
+ const [loading, setLoading] = React14.useState(true);
2915
+ const [error, setError] = React14.useState(null);
2916
+ const [initialized, setInitialized] = React14.useState(false);
2917
+ const queryShiftId = React14.useMemo(() => {
2918
+ const currentShift = getCurrentShift(
2919
+ dateTimeConfig.defaultTimezone || "Asia/Kolkata",
2920
+ shiftConfig
2921
+ );
2922
+ return options?.initialShiftId !== void 0 ? options.initialShiftId : currentShift.shiftId;
2923
+ }, [options?.initialShiftId, dateTimeConfig.defaultTimezone, shiftConfig]);
2924
+ const queryDate = React14.useMemo(() => {
2925
+ return options?.initialDate || getOperationalDate(dateTimeConfig.defaultTimezone);
2926
+ }, [options?.initialDate, dateTimeConfig.defaultTimezone]);
2927
+ const metricsTable = React14.useMemo(() => {
2928
+ const companyId = entityConfig.companyId;
2929
+ if (!companyId) return "";
2930
+ const metricsTablePrefix = getMetricsTablePrefix();
2931
+ return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
2932
+ }, [entityConfig.companyId]);
2933
+ const schema = databaseConfig.schema ?? "public";
2934
+ const fetchWorkspaceMetrics = React14.useCallback(async () => {
2935
+ if (!lineId) return;
2936
+ if (!initialized) {
2937
+ setLoading(true);
2938
+ }
2939
+ setError(null);
2940
+ try {
2941
+ console.log("Fetching workspace metrics with params:", {
2942
+ lineId,
2943
+ queryDate,
2944
+ queryShiftId,
2945
+ metricsTable
2946
+ });
2947
+ const { data, error: fetchError } = await supabase.from(metricsTable).select(`
2948
+ workspace_name,
2949
+ total_output,
2950
+ avg_pph,
2951
+ efficiency,
2952
+ workspace_id,
2953
+ avg_cycle_time,
2954
+ performance_score,
2955
+ trend_score,
2956
+ line_id,
2957
+ total_day_output
2958
+ `).eq("date", queryDate).eq("shift_id", queryShiftId).eq("line_id", lineId).order("workspace_name", { ascending: true });
2959
+ if (fetchError) throw fetchError;
2960
+ const transformedData = (data || []).map((item) => ({
2961
+ company_id: entityConfig.companyId || "unknown",
2962
+ line_id: item.line_id,
2963
+ shift_id: queryShiftId,
2964
+ date: queryDate,
2965
+ workspace_uuid: item.workspace_id,
2966
+ workspace_name: item.workspace_name,
2967
+ action_count: item.total_output || 0,
2968
+ pph: item.avg_pph || 0,
2969
+ performance_score: item.performance_score || 0,
2970
+ avg_cycle_time: item.avg_cycle_time || 0,
2971
+ trend: item.trend_score === 1 ? 2 : 0,
2972
+ predicted_output: 0,
2973
+ efficiency: item.efficiency || 0,
2974
+ action_threshold: item.total_day_output || 0
2975
+ }));
2976
+ setWorkspaces(transformedData);
2977
+ setInitialized(true);
2978
+ } catch (err) {
2979
+ console.error("Error fetching workspace metrics:", err);
2980
+ setError({ message: err.message, code: err.code || "FETCH_ERROR" });
2981
+ } finally {
2982
+ setLoading(false);
2983
+ }
2984
+ }, [lineId, queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
2985
+ React14.useEffect(() => {
2986
+ if (!initialized) {
2987
+ fetchWorkspaceMetrics();
2988
+ }
2989
+ const setupSubscription = () => {
2990
+ if (!lineId) return null;
2991
+ const filter2 = `line_id=eq.${lineId} AND date=eq.${queryDate} AND shift_id=eq.${queryShiftId}`;
2992
+ console.log("Setting up subscription with filter:", filter2);
2993
+ const channel2 = supabase.channel(`line-workspace-metrics-${Date.now()}`).on(
2994
+ "postgres_changes",
2995
+ {
2996
+ event: "*",
2997
+ schema,
2998
+ table: metricsTable,
2999
+ filter: filter2
3000
+ },
3001
+ async (payload) => {
3002
+ console.log("Workspace metrics update received:", payload);
3003
+ await fetchWorkspaceMetrics();
3004
+ }
3005
+ ).subscribe();
3006
+ return channel2;
3007
+ };
3008
+ const channel = setupSubscription();
3009
+ return () => {
3010
+ if (channel) {
3011
+ supabase.removeChannel(channel);
3012
+ }
3013
+ };
3014
+ }, [lineId, queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema]);
3015
+ React14.useEffect(() => {
3016
+ setInitialized(false);
3017
+ }, [lineId, queryDate, queryShiftId]);
3018
+ const refreshWorkspaces = fetchWorkspaceMetrics;
3019
+ return React14.useMemo(
3020
+ () => ({ workspaces, loading, error, refreshWorkspaces }),
3021
+ [workspaces, loading, error, refreshWorkspaces]
3022
+ );
3023
+ };
3024
+ var useHistoricWorkspaceMetrics = (workspaceId, date, inputShiftId) => {
3025
+ useSupabase();
3026
+ const [metrics2, setMetrics] = React14.useState(null);
3027
+ const [isLoading, setIsLoading] = React14.useState(true);
3028
+ const [error, setError] = React14.useState(null);
3029
+ const fetchAndAnimateMetrics = React14.useCallback(async () => {
3030
+ if (!workspaceId || !date || inputShiftId === void 0) {
3031
+ setMetrics(null);
3032
+ setIsLoading(false);
3033
+ setError(null);
3034
+ return;
2962
3035
  }
3036
+ setIsLoading(true);
3037
+ setError(null);
2963
3038
  try {
2964
- await this.handleSSEStream(response, callbacks);
2965
- } finally {
2966
- this.controllers.delete(connectionId);
3039
+ const fetchedData = await dashboardService.getWorkspaceDetailedMetrics(
3040
+ workspaceId,
3041
+ date,
3042
+ inputShiftId
3043
+ );
3044
+ if (!fetchedData) {
3045
+ console.warn("No historic data found for workspace:", workspaceId, date, inputShiftId);
3046
+ setMetrics(null);
3047
+ setIsLoading(false);
3048
+ return;
3049
+ }
3050
+ const initialHourlyCounts = fetchedData.hourly_action_counts && Array.isArray(fetchedData.hourly_action_counts) ? new Array(fetchedData.hourly_action_counts.length).fill(0) : [];
3051
+ setMetrics({
3052
+ ...fetchedData,
3053
+ hourly_action_counts: initialHourlyCounts
3054
+ });
3055
+ setIsLoading(false);
3056
+ if (fetchedData.hourly_action_counts && fetchedData.hourly_action_counts.length > 0) {
3057
+ const totalSteps = 60;
3058
+ let currentStep = 0;
3059
+ let animationIntervalId = setInterval(() => {
3060
+ currentStep++;
3061
+ const progress6 = currentStep / totalSteps;
3062
+ setMetrics((prevMetrics) => {
3063
+ const currentHourlyData = (fetchedData.hourly_action_counts || []).map(
3064
+ (target) => Math.round(target * progress6)
3065
+ );
3066
+ return {
3067
+ ...fetchedData,
3068
+ // Base with all other correct data from the latest fetch
3069
+ hourly_action_counts: currentHourlyData
3070
+ };
3071
+ });
3072
+ if (currentStep >= totalSteps) {
3073
+ if (animationIntervalId) clearInterval(animationIntervalId);
3074
+ setMetrics(fetchedData);
3075
+ }
3076
+ }, 1e3 / 60);
3077
+ } else {
3078
+ setMetrics(fetchedData);
3079
+ }
3080
+ } catch (err) {
3081
+ console.error("Error fetching historic workspace metrics:", err);
3082
+ setError({ message: err.message, code: err.code || "FETCH_ERROR" });
3083
+ setIsLoading(false);
3084
+ setMetrics(null);
2967
3085
  }
2968
- }
2969
- async handleSSEStream(response, callbacks) {
2970
- if (!response.body) {
2971
- console.error("[SSEClient] Response body is null");
2972
- throw new Error("No response body available for streaming");
3086
+ }, [workspaceId, date, inputShiftId]);
3087
+ React14.useEffect(() => {
3088
+ fetchAndAnimateMetrics();
3089
+ }, [fetchAndAnimateMetrics]);
3090
+ const refetch = React14.useCallback(async () => {
3091
+ if (!workspaceId || !date || inputShiftId === void 0) {
3092
+ setError(null);
3093
+ return;
2973
3094
  }
2974
- const reader = response.body.getReader();
2975
- const decoder = new TextDecoder();
2976
- let buffer = "";
3095
+ setIsLoading(true);
3096
+ setError(null);
2977
3097
  try {
2978
- console.log("[SSEClient] Starting to read stream...");
2979
- while (true) {
2980
- const { done, value } = await reader.read();
2981
- if (done) {
2982
- console.log("[SSEClient] Stream ended");
2983
- break;
2984
- }
2985
- buffer += decoder.decode(value, { stream: true });
2986
- const lines = buffer.split("\n");
2987
- buffer = lines.pop() || "";
2988
- for (let i = 0; i < lines.length; i++) {
2989
- const line = lines[i].trim();
2990
- if (!line) continue;
2991
- console.log("[SSEClient] Processing line:", line);
2992
- if (line.startsWith("event:")) {
2993
- const event = line.slice(6).trim();
2994
- console.log("[SSEClient] Event type:", event);
2995
- const nextLine = lines[i + 1];
2996
- if (nextLine?.startsWith("data:")) {
2997
- const dataStr = nextLine.slice(5).trim();
2998
- console.log("[SSEClient] Event data:", dataStr);
2999
- try {
3000
- const data = JSON.parse(dataStr);
3001
- switch (event) {
3002
- case "thread":
3003
- callbacks.onThreadCreated?.(data.thread_id);
3004
- break;
3005
- case "message":
3006
- callbacks.onMessage?.(data.text);
3007
- break;
3008
- case "reasoning":
3009
- callbacks.onReasoning?.(data.text);
3010
- break;
3011
- case "complete":
3012
- callbacks.onComplete?.(data.message_id);
3013
- break;
3014
- case "error":
3015
- callbacks.onError?.(data.error);
3016
- break;
3017
- }
3018
- } catch (e) {
3019
- console.error("[SSEClient] Failed to parse data:", dataStr, e);
3020
- }
3021
- i++;
3022
- }
3023
- }
3024
- }
3098
+ const fetchedData = await dashboardService.getWorkspaceDetailedMetrics(
3099
+ workspaceId,
3100
+ date,
3101
+ inputShiftId
3102
+ );
3103
+ if (!fetchedData) {
3104
+ setMetrics(null);
3105
+ return;
3025
3106
  }
3107
+ setMetrics(fetchedData);
3108
+ } catch (err) {
3109
+ console.error("Error re-fetching historic workspace metrics:", err);
3110
+ setError({ message: err.message, code: err.code || "FETCH_ERROR" });
3111
+ setMetrics(null);
3026
3112
  } finally {
3027
- reader.releaseLock();
3028
- }
3029
- }
3030
- abort(threadId) {
3031
- if (threadId) {
3032
- for (const [id3, controller] of this.controllers.entries()) {
3033
- if (id3.startsWith(threadId)) {
3034
- controller.abort();
3035
- this.controllers.delete(id3);
3036
- }
3037
- }
3038
- } else {
3039
- for (const [id3, controller] of this.controllers.entries()) {
3040
- controller.abort();
3041
- }
3042
- this.controllers.clear();
3113
+ setIsLoading(false);
3043
3114
  }
3044
- }
3045
- };
3046
-
3047
- // src/lib/services/chatService.ts
3048
- async function getUserThreads(userId, limit = 20) {
3049
- const supabase = _getSupabaseInstance();
3050
- const { data, error } = await supabase.schema("ai").from("chat_threads").select("*").eq("user_id", userId).order("updated_at", { ascending: false }).limit(limit);
3051
- if (error) throw error;
3052
- return data;
3053
- }
3054
- async function getUserThreadsPaginated(userId, page = 1, pageSize = 20) {
3055
- const supabase = _getSupabaseInstance();
3056
- const from = (page - 1) * pageSize;
3057
- const to = from + pageSize - 1;
3058
- const { data, error, count } = await supabase.schema("ai").from("chat_threads").select("*", { count: "exact" }).eq("user_id", userId).order("updated_at", { ascending: false }).range(from, to);
3059
- if (error) throw error;
3115
+ }, [workspaceId, date, inputShiftId]);
3060
3116
  return {
3061
- threads: data,
3062
- totalCount: count || 0,
3063
- totalPages: Math.ceil((count || 0) / pageSize),
3064
- currentPage: page
3117
+ metrics: metrics2,
3118
+ isLoading,
3119
+ error,
3120
+ refetch
3065
3121
  };
3066
- }
3067
- async function getThreadMessages(threadId, limit = 50, beforePosition) {
3068
- const supabase = _getSupabaseInstance();
3069
- let query = supabase.schema("ai").from("chat_messages").select("*").eq("thread_id", threadId).order("position", { ascending: true });
3070
- if (beforePosition !== void 0) {
3071
- query = query.lt("position", beforePosition);
3072
- }
3073
- const { data, error } = await query.limit(limit);
3074
- if (error) throw error;
3075
- return data;
3076
- }
3077
- async function getAllThreadMessages(threadId) {
3078
- const supabase = _getSupabaseInstance();
3079
- const { data, error } = await supabase.schema("ai").from("chat_messages").select("*").eq("thread_id", threadId).order("position", { ascending: true });
3080
- if (error) throw error;
3081
- return data;
3082
- }
3083
- async function updateThreadTitle(threadId, newTitle) {
3084
- const supabase = _getSupabaseInstance();
3085
- const { data, error } = await supabase.schema("ai").from("chat_threads").update({
3086
- title: newTitle,
3087
- auto_title: false,
3088
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
3089
- }).eq("id", threadId).select().single();
3090
- if (error) throw error;
3091
- return data;
3092
- }
3093
- async function deleteThread(threadId) {
3094
- const supabase = _getSupabaseInstance();
3095
- const { error } = await supabase.schema("ai").from("chat_threads").delete().eq("id", threadId);
3096
- if (error) throw error;
3097
- }
3098
-
3099
- // src/lib/hooks/useLineDetailedMetrics.ts
3122
+ };
3100
3123
  var useLineDetailedMetrics = (lineIdFromProp) => {
3101
3124
  const entityConfig = useEntityConfig();
3102
3125
  const databaseConfig = useDatabaseConfig();
@@ -19663,7 +19686,8 @@ var WorkspaceHistoryCalendar = ({
19663
19686
  pph: 0,
19664
19687
  pphThreshold: 0,
19665
19688
  idealOutput: 0,
19666
- rank: 0
19689
+ rank: 0,
19690
+ idleTime: 0
19667
19691
  },
19668
19692
  nightShift: {
19669
19693
  efficiency: 0,
@@ -19672,7 +19696,8 @@ var WorkspaceHistoryCalendar = ({
19672
19696
  pph: 0,
19673
19697
  pphThreshold: 0,
19674
19698
  idealOutput: 0,
19675
- rank: 0
19699
+ rank: 0,
19700
+ idleTime: 0
19676
19701
  }
19677
19702
  });
19678
19703
  }
@@ -19701,7 +19726,8 @@ var WorkspaceHistoryCalendar = ({
19701
19726
  avgEfficiency: Math.round(validShifts.reduce((sum, shift) => sum + shift.efficiency, 0) / validShifts.length),
19702
19727
  avgCycleTime: Math.round(validShifts.reduce((sum, shift) => sum + shift.cycleTime, 0) / validShifts.length),
19703
19728
  badDaysCount: badShiftsCount,
19704
- totalDays: validShifts.length
19729
+ totalDays: validShifts.length,
19730
+ avgIdleTime: Math.round(validShifts.reduce((sum, shift) => sum + (shift.idleTime || 0), 0) / validShifts.length)
19705
19731
  };
19706
19732
  }, [data, month, year, configuredTimezone]);
19707
19733
  const handleDayClick = React14.useCallback((day, shift) => {
@@ -19872,6 +19898,10 @@ var WorkspaceHistoryCalendar = ({
19872
19898
  monthlyMetrics.avgCycleTime,
19873
19899
  "s"
19874
19900
  ] })
19901
+ ] }),
19902
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [
19903
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-600 mb-1", children: "Avg Idle Time" }),
19904
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-semibold text-gray-900", children: formatIdleTime(monthlyMetrics.avgIdleTime) })
19875
19905
  ] })
19876
19906
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-8 text-gray-500 bg-gray-50 rounded-lg border border-gray-200", children: [
19877
19907
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-12 h-12 text-gray-400 mx-auto mb-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" }) }),
@@ -21367,6 +21397,7 @@ var BottlenecksContent = ({
21367
21397
  workspaceId,
21368
21398
  workspaceName,
21369
21399
  date,
21400
+ shift,
21370
21401
  className
21371
21402
  }) => {
21372
21403
  const dashboardConfig = useDashboardConfig();
@@ -21422,6 +21453,8 @@ var BottlenecksContent = ({
21422
21453
  const videos = await s3ClipsService.fetchClips({
21423
21454
  workspaceId,
21424
21455
  date: operationalDate,
21456
+ shift: shift?.toString(),
21457
+ // Pass the shift parameter
21425
21458
  mode: "full",
21426
21459
  includeCycleTime: true,
21427
21460
  includeMetadata: true,
@@ -21457,7 +21490,7 @@ var BottlenecksContent = ({
21457
21490
  } finally {
21458
21491
  setIsLoading(false);
21459
21492
  }
21460
- }, [workspaceId, date, s3ClipsService, timestampStart, timestampEnd]);
21493
+ }, [workspaceId, date, s3ClipsService, timestampStart, timestampEnd, shift]);
21461
21494
  React14.useEffect(() => {
21462
21495
  if (s3ClipsService) {
21463
21496
  fetchClips();
@@ -29986,8 +30019,8 @@ var WorkspaceDetailView = ({
29986
30019
  if (!dayEntry) {
29987
30020
  dayEntry = {
29988
30021
  date: dateObj,
29989
- dayShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0 },
29990
- nightShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0 }
30022
+ dayShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 },
30023
+ nightShift: { efficiency: 0, output: 0, cycleTime: 0, pph: 0, pphThreshold: 0, idealOutput: 0, rank: 0, idleTime: 0 }
29991
30024
  };
29992
30025
  dayDataMap.set(dateKey, dayEntry);
29993
30026
  }
@@ -29999,6 +30032,7 @@ var WorkspaceDetailView = ({
29999
30032
  shiftTarget.pphThreshold = metric.pph_threshold || 0;
30000
30033
  shiftTarget.idealOutput = metric.ideal_output || 0;
30001
30034
  shiftTarget.rank = metric.workspace_rank || 0;
30035
+ shiftTarget.idleTime = metric.idle_time || 0;
30002
30036
  });
30003
30037
  const processedData = Array.from(dayDataMap.values());
30004
30038
  console.log(`[handleMonthlyDataLoaded] Transformed data for calendar:`, {
@@ -30490,6 +30524,7 @@ var WorkspaceDetailView = ({
30490
30524
  workspaceId,
30491
30525
  workspaceName: formattedWorkspaceName,
30492
30526
  date,
30527
+ shift,
30493
30528
  className: "h-[calc(100vh-10rem)]"
30494
30529
  }
30495
30530
  )