@optifye/dashboard-core 6.12.2 → 6.12.4

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
@@ -2628,19 +2628,206 @@ function getSentry() {
2628
2628
  return null;
2629
2629
  }
2630
2630
  }
2631
+ var SENTRY_HANDLED_EVENT_WINDOW_MS = 10 * 60 * 1e3;
2632
+ var SENTRY_HANDLED_EVENT_SESSION_LIMIT = 20;
2633
+ var SENTRY_QUOTA_STORAGE_KEY = "optifye:sentry-handled-quota:v1";
2634
+ var sentryFingerprintSentAt = /* @__PURE__ */ new Map();
2635
+ var handledSentryEventCount = 0;
2636
+ var UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
2637
+ var LONG_HEX_PATTERN = /\b[0-9a-f]{24,}\b/gi;
2638
+ var EMAIL_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
2639
+ var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
2640
+ var URI_PATTERN = /\b[a-z][a-z0-9+.-]{1,31}:\/\/[^\s)]+/gi;
2641
+ var MEDIA_PATH_PATTERN = /\b[^\s)]+\.m3u8(?:\?[^\s)]*)?/gi;
2642
+ var SENSITIVE_ASSIGNMENT_PATTERN = /\b([A-Za-z0-9_.-]*(?:authorization|cookie|password|secret|token|session|email|api[_-]?key)[A-Za-z0-9_.-]*)\b\s*[:=]\s*["']?[^"'\s,;)}\]]+/gi;
2643
+ var SENSITIVE_KEY_PATTERN = /(authorization|cookie|password|secret|token|session|email|api[_-]?key|url|stream|playlist|media)/i;
2644
+ function resetSentryQuotaForTests() {
2645
+ sentryFingerprintSentAt.clear();
2646
+ handledSentryEventCount = 0;
2647
+ const storage = getBrowserSessionStorage();
2648
+ storage?.removeItem(SENTRY_QUOTA_STORAGE_KEY);
2649
+ }
2650
+ var sanitizeString = (value) => {
2651
+ return value.replace(BEARER_PATTERN, "Bearer [redacted]").replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, key) => `${key}=[redacted]`).replace(URI_PATTERN, "[url]").replace(MEDIA_PATH_PATTERN, "[media]").replace(EMAIL_PATTERN, "[email]").replace(UUID_PATTERN, ":uuid").replace(LONG_HEX_PATTERN, ":id").replace(/\?.*$/, "").replace(/\s+/g, " ").trim();
2652
+ };
2653
+ var sanitizeRoute = (value) => {
2654
+ if (typeof value !== "string" || value.trim().length === 0) return void 0;
2655
+ try {
2656
+ const parsed = new URL(value, "https://optifye.local");
2657
+ return sanitizeString(parsed.pathname);
2658
+ } catch {
2659
+ return sanitizeString(value.split("?")[0] || value);
2660
+ }
2661
+ };
2662
+ var sanitizeValue = (value, depth = 0) => {
2663
+ if (depth > 3) return "[truncated]";
2664
+ if (typeof value === "string") return sanitizeString(value);
2665
+ if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) return value;
2666
+ if (Array.isArray(value)) {
2667
+ return value.slice(0, 20).map((item) => sanitizeValue(item, depth + 1));
2668
+ }
2669
+ if (typeof value === "object") {
2670
+ const sanitized = {};
2671
+ Object.entries(value).forEach(([key, item]) => {
2672
+ sanitized[key] = SENSITIVE_KEY_PATTERN.test(key) ? "[redacted]" : sanitizeValue(item, depth + 1);
2673
+ });
2674
+ return sanitized;
2675
+ }
2676
+ return String(value);
2677
+ };
2678
+ var sanitizeStack = (stack) => {
2679
+ return stack.split("\n").map((line) => sanitizeString(line)).join("\n");
2680
+ };
2681
+ var sanitizeExtras = (extras) => {
2682
+ if (!extras) return void 0;
2683
+ return sanitizeValue(extras);
2684
+ };
2685
+ var getBrowserSessionStorage = () => {
2686
+ if (typeof window === "undefined") return null;
2687
+ try {
2688
+ return window.sessionStorage;
2689
+ } catch {
2690
+ return null;
2691
+ }
2692
+ };
2693
+ var loadSentryQuotaFromStorage = (now4) => {
2694
+ const storage = getBrowserSessionStorage();
2695
+ if (!storage) return;
2696
+ const rawState = storage.getItem(SENTRY_QUOTA_STORAGE_KEY);
2697
+ if (!rawState) return;
2698
+ try {
2699
+ const parsed = JSON.parse(rawState);
2700
+ const persistedCount = Number(parsed.handledEventCount);
2701
+ handledSentryEventCount = Number.isFinite(persistedCount) && persistedCount > 0 ? Math.floor(persistedCount) : 0;
2702
+ sentryFingerprintSentAt.clear();
2703
+ if (parsed.fingerprintSentAt && typeof parsed.fingerprintSentAt === "object") {
2704
+ Object.entries(parsed.fingerprintSentAt).forEach(([fingerprint, sentAt]) => {
2705
+ const sentAtMs = Number(sentAt);
2706
+ if (Number.isFinite(sentAtMs) && now4 - sentAtMs < SENTRY_HANDLED_EVENT_WINDOW_MS) {
2707
+ sentryFingerprintSentAt.set(fingerprint, sentAtMs);
2708
+ }
2709
+ });
2710
+ }
2711
+ } catch {
2712
+ storage.removeItem(SENTRY_QUOTA_STORAGE_KEY);
2713
+ }
2714
+ };
2715
+ var persistSentryQuotaToStorage = () => {
2716
+ const storage = getBrowserSessionStorage();
2717
+ if (!storage) return;
2718
+ try {
2719
+ storage.setItem(
2720
+ SENTRY_QUOTA_STORAGE_KEY,
2721
+ JSON.stringify({
2722
+ handledEventCount: handledSentryEventCount,
2723
+ fingerprintSentAt: Object.fromEntries(sentryFingerprintSentAt)
2724
+ })
2725
+ );
2726
+ } catch {
2727
+ }
2728
+ };
2729
+ var normalizeMessage = (error) => {
2730
+ const message = error instanceof Error ? error.message : String(error ?? "unknown");
2731
+ return sanitizeString(message.toLowerCase()).slice(0, 180) || "unknown";
2732
+ };
2733
+ var getErrorName = (error) => {
2734
+ if (error && typeof error === "object" && "name" in error) {
2735
+ const name = error.name;
2736
+ if (typeof name === "string" && name.trim()) return sanitizeString(name);
2737
+ }
2738
+ return error instanceof Error ? error.constructor.name : typeof error;
2739
+ };
2740
+ var getStatusFromError = (error) => {
2741
+ const message = error instanceof Error ? error.message : String(error ?? "");
2742
+ const statusMatch = message.match(/\((\d{3})\)/) || message.match(/http\s+(\d{3})/i) || message.match(/status:\s*(\d{3})/i);
2743
+ if (!statusMatch) return null;
2744
+ const status = Number.parseInt(statusMatch[1], 10);
2745
+ return Number.isFinite(status) ? status : null;
2746
+ };
2747
+ var hasCaptureOptionShape = (value) => {
2748
+ return "surface" in value || "route" in value || "status" in value || "severity" in value || "quotaKey" in value || "extras" in value;
2749
+ };
2750
+ var normalizeCaptureOptions = (options) => {
2751
+ if (!options) return {};
2752
+ const optionRecord = options;
2753
+ if (!hasCaptureOptionShape(optionRecord)) {
2754
+ return { extras: optionRecord };
2755
+ }
2756
+ const {
2757
+ surface,
2758
+ route,
2759
+ status,
2760
+ severity,
2761
+ quotaKey,
2762
+ extras,
2763
+ ...rest
2764
+ } = options;
2765
+ return {
2766
+ surface: typeof surface === "string" ? surface : void 0,
2767
+ route: typeof route === "string" ? route : void 0,
2768
+ status: typeof status === "number" ? status : null,
2769
+ severity: severity === "info" || severity === "warning" || severity === "error" ? severity : void 0,
2770
+ quotaKey: typeof quotaKey === "string" ? quotaKey : void 0,
2771
+ extras: {
2772
+ ...rest,
2773
+ ...extras && typeof extras === "object" && !Array.isArray(extras) ? extras : {}
2774
+ }
2775
+ };
2776
+ };
2777
+ var buildSentryFingerprint = (error, options) => {
2778
+ if (options.quotaKey) return sanitizeString(options.quotaKey);
2779
+ const surface = sanitizeString(options.surface || "frontend");
2780
+ const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url) || "unknown-route";
2781
+ const status = options.status ?? getStatusFromError(error) ?? "unknown-status";
2782
+ return [
2783
+ surface,
2784
+ route,
2785
+ String(status),
2786
+ getErrorName(error),
2787
+ normalizeMessage(error)
2788
+ ].join("|");
2789
+ };
2790
+ var shouldSendHandledSentryEvent = (fingerprint) => {
2791
+ const now4 = Date.now();
2792
+ loadSentryQuotaFromStorage(now4);
2793
+ const lastSentAt = sentryFingerprintSentAt.get(fingerprint);
2794
+ if (lastSentAt !== void 0 && now4 - lastSentAt < SENTRY_HANDLED_EVENT_WINDOW_MS) {
2795
+ return false;
2796
+ }
2797
+ if (handledSentryEventCount >= SENTRY_HANDLED_EVENT_SESSION_LIMIT) {
2798
+ return false;
2799
+ }
2800
+ sentryFingerprintSentAt.set(fingerprint, now4);
2801
+ handledSentryEventCount += 1;
2802
+ persistSentryQuotaToStorage();
2803
+ return true;
2804
+ };
2805
+ var createSanitizedSentryException = (error) => {
2806
+ if (error instanceof Error) {
2807
+ const sanitizedError = new Error(sanitizeString(error.message) || getErrorName(error) || "Error");
2808
+ sanitizedError.name = getErrorName(error) || "Error";
2809
+ if (typeof error.stack === "string") {
2810
+ sanitizedError.stack = sanitizeStack(error.stack);
2811
+ }
2812
+ return sanitizedError;
2813
+ }
2814
+ if (typeof error === "string") {
2815
+ return sanitizeString(error) || "unknown";
2816
+ }
2817
+ return sanitizeValue(error);
2818
+ };
2631
2819
  function isIgnorableFrontendError(error) {
2632
2820
  const name = error && typeof error === "object" ? error.name || "" : "";
2633
2821
  const message = error instanceof Error ? error.message : String(error ?? "");
2634
2822
  const lowerMessage = message.toLowerCase();
2635
- return name === "AbortError" || lowerMessage.includes("the operation was aborted") || lowerMessage.includes("signal is aborted") || lowerMessage.includes("resizeobserver loop");
2823
+ return name === "AbortError" || name === "NotAllowedError" || lowerMessage.includes("the operation was aborted") || lowerMessage.includes("signal is aborted") || lowerMessage.includes("resizeobserver loop") || lowerMessage.includes("play() failed") || lowerMessage.includes("play request was interrupted") || lowerMessage.includes("autoplay") || lowerMessage.includes("not allowed by the user agent") || lowerMessage.includes("recoverable hls") || lowerMessage.includes("non-fatal hls") || lowerMessage.includes("fragloaderror") || lowerMessage.includes("frag load error") || lowerMessage.includes("fragloadtimeout") || lowerMessage.includes("bufferstallederror") || lowerMessage.includes("buffer stalled") || lowerMessage.includes("media error recovered") || lowerMessage.includes("recovermediaerror");
2636
2824
  }
2637
2825
  function setSentryUserContext(user) {
2638
2826
  const sentry = getSentry();
2639
2827
  if (!sentry) return;
2640
2828
  if (user) {
2641
2829
  sentry.setUser({
2642
- id: user.id,
2643
- email: user.email
2830
+ id: user.id
2644
2831
  });
2645
2832
  sentry.setTags({
2646
2833
  company_id: user.company_id || "unknown",
@@ -2691,28 +2878,60 @@ function applyScopeExtras(scope, extras) {
2691
2878
  function captureSentryMessage(message, level = "warning", extras) {
2692
2879
  const sentry = getSentry();
2693
2880
  if (!sentry || !sentry.captureMessage) return;
2881
+ const options = normalizeCaptureOptions({ ...normalizeCaptureOptions(extras), severity: level });
2882
+ const fingerprint = buildSentryFingerprint(new Error(message), options);
2883
+ if (!shouldSendHandledSentryEvent(fingerprint)) return;
2884
+ const sanitizedExtras = sanitizeExtras({
2885
+ ...options.extras,
2886
+ sentry_quota_key: fingerprint,
2887
+ sentry_capture_policy: "quota_controlled"
2888
+ });
2889
+ const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
2890
+ const sanitizedMessage = sanitizeString(message) || "Sentry message";
2694
2891
  if (sentry.withScope) {
2695
2892
  sentry.withScope((scope) => {
2696
2893
  scope.setLevel?.(level);
2697
- applyScopeExtras(scope, extras);
2698
- sentry.captureMessage?.(message);
2894
+ scope.setFingerprint?.([fingerprint]);
2895
+ if (options.surface) scope.setTag?.("surface", sanitizeString(options.surface));
2896
+ if (route) scope.setTag?.("route", route);
2897
+ if (options.status !== void 0 && options.status !== null) scope.setTag?.("status", options.status);
2898
+ applyScopeExtras(scope, sanitizedExtras);
2899
+ sentry.captureMessage?.(sanitizedMessage);
2699
2900
  });
2700
2901
  return;
2701
2902
  }
2702
- sentry.captureMessage(message, level);
2903
+ sentry.captureMessage(sanitizedMessage, level);
2703
2904
  }
2704
2905
  function captureSentryException(error, extras) {
2906
+ if (isIgnorableFrontendError(error)) {
2907
+ return;
2908
+ }
2705
2909
  const sentry = getSentry();
2706
2910
  if (!sentry || !sentry.captureException) return;
2911
+ const options = normalizeCaptureOptions(extras);
2912
+ const fingerprint = buildSentryFingerprint(error, options);
2913
+ if (!shouldSendHandledSentryEvent(fingerprint)) return;
2914
+ const sanitizedExtras = sanitizeExtras({
2915
+ ...options.extras,
2916
+ sentry_quota_key: fingerprint,
2917
+ sentry_capture_policy: "quota_controlled"
2918
+ });
2919
+ const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
2920
+ const status = options.status ?? getStatusFromError(error);
2921
+ const sanitizedError = createSanitizedSentryException(error);
2707
2922
  if (sentry.withScope) {
2708
2923
  sentry.withScope((scope) => {
2709
- scope.setLevel?.("error");
2710
- applyScopeExtras(scope, extras);
2711
- sentry.captureException?.(error);
2924
+ scope.setLevel?.(options.severity || "error");
2925
+ scope.setFingerprint?.([fingerprint]);
2926
+ if (options.surface) scope.setTag?.("surface", sanitizeString(options.surface));
2927
+ if (route) scope.setTag?.("route", route);
2928
+ if (status !== null && status !== void 0) scope.setTag?.("status", status);
2929
+ applyScopeExtras(scope, sanitizedExtras);
2930
+ sentry.captureException?.(sanitizedError);
2712
2931
  });
2713
2932
  return;
2714
2933
  }
2715
- sentry.captureException(error);
2934
+ sentry.captureException(sanitizedError);
2716
2935
  }
2717
2936
  function captureHandledFrontendException(error, extras) {
2718
2937
  if (isIgnorableFrontendError(error)) {
@@ -2720,6 +2939,23 @@ function captureHandledFrontendException(error, extras) {
2720
2939
  }
2721
2940
  captureSentryException(error, extras);
2722
2941
  }
2942
+ function addSentryBreadcrumb(message, options = {}) {
2943
+ const sentry = getSentry();
2944
+ if (!sentry?.addBreadcrumb) return;
2945
+ const route = sanitizeRoute(options.route || options.extras?.route || options.extras?.endpoint || options.extras?.url);
2946
+ const data = sanitizeExtras({
2947
+ surface: options.surface,
2948
+ route,
2949
+ status: options.status,
2950
+ ...options.extras
2951
+ });
2952
+ sentry.addBreadcrumb({
2953
+ category: options.category || options.surface || "frontend",
2954
+ message: sanitizeString(message),
2955
+ level: options.severity || "info",
2956
+ data
2957
+ });
2958
+ }
2723
2959
 
2724
2960
  // src/lib/services/backendClient.ts
2725
2961
  var ACCESS_TOKEN_REFRESH_BUFFER_MS = 6e4;
@@ -2787,7 +3023,7 @@ var createAbortError = () => {
2787
3023
  return error;
2788
3024
  }
2789
3025
  };
2790
- var getStatusFromError = (error) => {
3026
+ var getStatusFromError2 = (error) => {
2791
3027
  const message = error instanceof Error ? error.message : String(error ?? "");
2792
3028
  const statusMatch = message.match(/\((\d{3})\)/) || message.match(/http\s+(\d{3})/i);
2793
3029
  if (!statusMatch) {
@@ -2803,7 +3039,7 @@ var isRetryableError = (error) => {
2803
3039
  if (isAbortError(error)) {
2804
3040
  return false;
2805
3041
  }
2806
- const status = getStatusFromError(error);
3042
+ const status = getStatusFromError2(error);
2807
3043
  if (status !== null) {
2808
3044
  return status >= 500 || status === 408 || status === 425 || status === 429;
2809
3045
  }
@@ -2827,6 +3063,7 @@ var fetchBackendJson = async (supabase, endpoint, options = {}) => {
2827
3063
  timeoutMs = DEFAULT_TIMEOUT_MS,
2828
3064
  retries = 0,
2829
3065
  retryDelayMs = DEFAULT_RETRY_DELAY_MS,
3066
+ sentry,
2830
3067
  signal: externalSignal,
2831
3068
  ...fetchOptions
2832
3069
  } = options;
@@ -2864,14 +3101,22 @@ var fetchBackendJson = async (supabase, endpoint, options = {}) => {
2864
3101
  } catch (error) {
2865
3102
  const wrappedError = externalSignal?.aborted || controller.signal.aborted && !isAbortError(error) ? createAbortError() : error;
2866
3103
  if (attempt >= retries || !isRetryableError(wrappedError)) {
2867
- captureHandledFrontendException(wrappedError, {
2868
- surface: "backend_client",
2869
- url,
2870
- method,
2871
- retry_attempt: attempt,
2872
- retries,
2873
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
2874
- });
3104
+ if (sentry && sentry.capture !== false) {
3105
+ captureHandledFrontendException(wrappedError, {
3106
+ surface: sentry.surface || "backend_client",
3107
+ route: sentry.route || endpoint,
3108
+ status: sentry.status ?? getStatusFromError2(wrappedError),
3109
+ severity: sentry.severity || "error",
3110
+ quotaKey: sentry.quotaKey,
3111
+ extras: {
3112
+ method,
3113
+ retry_attempt: attempt,
3114
+ retries,
3115
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
3116
+ ...sentry.extras
3117
+ }
3118
+ });
3119
+ }
2875
3120
  throw wrappedError;
2876
3121
  }
2877
3122
  await new Promise((resolve) => globalThis.setTimeout(resolve, retryDelayMs));
@@ -2957,6 +3202,7 @@ var ApiClient = class {
2957
3202
  retryDelay = 1e3,
2958
3203
  silentErrors = true,
2959
3204
  fallbackData = null,
3205
+ sentry,
2960
3206
  ...fetchOptions
2961
3207
  } = options;
2962
3208
  let lastError = null;
@@ -2990,24 +3236,36 @@ var ApiClient = class {
2990
3236
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
2991
3237
  continue;
2992
3238
  }
2993
- if (silentErrors) {
3239
+ if (sentry && sentry.capture !== false) {
2994
3240
  captureHandledFrontendException(lastError, {
3241
+ surface: sentry.surface || "api_client",
3242
+ route: sentry.route || url,
3243
+ status: sentry.status,
3244
+ severity: sentry.severity || "error",
3245
+ quotaKey: sentry.quotaKey,
3246
+ extras: {
3247
+ retries,
3248
+ silent_errors: silentErrors,
3249
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
3250
+ ...sentry.extras
3251
+ }
3252
+ });
3253
+ } else {
3254
+ addSentryBreadcrumb("API client request failed", {
2995
3255
  surface: "api_client",
2996
- url,
2997
- retries,
2998
- silent_errors: true,
2999
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
3256
+ route: url,
3257
+ severity: silentErrors ? "info" : "warning",
3258
+ extras: {
3259
+ retries,
3260
+ silent_errors: silentErrors,
3261
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
3262
+ }
3000
3263
  });
3264
+ }
3265
+ if (silentErrors) {
3001
3266
  console.warn("[ApiClient] All retries exhausted, returning fallback data");
3002
3267
  return fallbackData;
3003
3268
  } else {
3004
- captureHandledFrontendException(lastError, {
3005
- surface: "api_client",
3006
- url,
3007
- retries,
3008
- silent_errors: false,
3009
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
3010
- });
3011
3269
  throw lastError;
3012
3270
  }
3013
3271
  }
@@ -3259,8 +3517,12 @@ var AuthService = class {
3259
3517
  timeout: 1e4,
3260
3518
  // 10 seconds
3261
3519
  retries: 1,
3262
- silentErrors: false
3520
+ silentErrors: false,
3263
3521
  // We want to know about auth errors
3522
+ sentry: {
3523
+ surface: "auth_session_request",
3524
+ route: "/api/auth/session"
3525
+ }
3264
3526
  }
3265
3527
  );
3266
3528
  console.log("[AuthService] Session loaded:", {
@@ -3330,7 +3592,11 @@ var AuthService = class {
3330
3592
  timeout: 1e4,
3331
3593
  // 10 seconds
3332
3594
  retries: 1,
3333
- silentErrors: false
3595
+ silentErrors: false,
3596
+ sentry: {
3597
+ surface: "auth_permissions_request",
3598
+ route: "/api/auth/permissions"
3599
+ }
3334
3600
  }
3335
3601
  );
3336
3602
  console.log("[AuthService] Permissions loaded for role:", data.role);
@@ -5972,12 +6238,16 @@ var workspaceService = {
5972
6238
  return displayNamesMap;
5973
6239
  } catch (error) {
5974
6240
  console.error("Error fetching workspace display names:", error);
5975
- captureSentryException(error, {
6241
+ addSentryBreadcrumb("Workspace display-name fallback failed", {
5976
6242
  surface: "workspace_display_names",
5977
6243
  route: "/api/workspaces/display-names",
5978
- company_id: companyId || null,
5979
- line_id: lineId || null,
5980
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
6244
+ severity: "warning",
6245
+ extras: {
6246
+ company_id: companyId || null,
6247
+ line_id: lineId || null,
6248
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
6249
+ error_message: error instanceof Error ? error.message : String(error)
6250
+ }
5981
6251
  });
5982
6252
  throw error;
5983
6253
  }
@@ -7581,11 +7851,28 @@ var shouldReportMixpanel = (key, isError) => {
7581
7851
  };
7582
7852
  var reportMixpanelWarning = (key, message, extras) => {
7583
7853
  if (!shouldReportMixpanel(key, false)) return;
7584
- captureSentryMessage(message, "warning", { ...baseMixpanelExtras(), ...extras });
7854
+ addSentryBreadcrumb(message, {
7855
+ surface: "mixpanel",
7856
+ severity: "warning",
7857
+ extras: {
7858
+ key,
7859
+ ...baseMixpanelExtras(),
7860
+ ...extras
7861
+ }
7862
+ });
7585
7863
  };
7586
7864
  var reportMixpanelError = (key, error, extras) => {
7587
7865
  if (!shouldReportMixpanel(key, true)) return;
7588
- captureSentryException(error, { ...baseMixpanelExtras(), ...extras });
7866
+ addSentryBreadcrumb("Mixpanel operation failed", {
7867
+ surface: "mixpanel",
7868
+ severity: "warning",
7869
+ extras: {
7870
+ key,
7871
+ error_message: error instanceof Error ? error.message : String(error ?? "unknown"),
7872
+ ...baseMixpanelExtras(),
7873
+ ...extras
7874
+ }
7875
+ });
7589
7876
  };
7590
7877
  var normalizeCoreEventName = (eventName) => {
7591
7878
  const canonicalName = MIXPANEL_EVENT_NAME_ALIASES[eventName] || eventName;
@@ -9602,11 +9889,15 @@ var LinesService = class {
9602
9889
  }));
9603
9890
  } catch (error) {
9604
9891
  console.error("Error fetching lines:", error);
9605
- captureHandledFrontendException(error, {
9892
+ addSentryBreadcrumb("Lines service request failed", {
9606
9893
  surface: "lines_service",
9607
- operation: "getLinesByCompanyId",
9608
- company_id: companyId,
9609
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
9894
+ route: "/api/lines",
9895
+ severity: "warning",
9896
+ extras: {
9897
+ operation: "getLinesByCompanyId",
9898
+ company_id: companyId,
9899
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
9900
+ }
9610
9901
  });
9611
9902
  throw new Error(`Failed to fetch lines: ${error.message}`);
9612
9903
  }
@@ -9657,10 +9948,14 @@ var LinesService = class {
9657
9948
  }));
9658
9949
  } catch (error) {
9659
9950
  console.error("Error fetching all lines:", error);
9660
- captureHandledFrontendException(error, {
9951
+ addSentryBreadcrumb("Lines service request failed", {
9661
9952
  surface: "lines_service",
9662
- operation: "getAllLines",
9663
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
9953
+ route: "/api/lines",
9954
+ severity: "warning",
9955
+ extras: {
9956
+ operation: "getAllLines",
9957
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
9958
+ }
9664
9959
  });
9665
9960
  throw new Error(`Failed to fetch lines: ${error.message}`);
9666
9961
  }
@@ -9720,11 +10015,15 @@ var LinesService = class {
9720
10015
  };
9721
10016
  } catch (error) {
9722
10017
  console.error("Error fetching line:", error);
9723
- captureHandledFrontendException(error, {
10018
+ addSentryBreadcrumb("Lines service request failed", {
9724
10019
  surface: "lines_service",
9725
- operation: "getLineById",
9726
- line_id: lineId,
9727
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
10020
+ route: "/api/lines/:id",
10021
+ severity: "warning",
10022
+ extras: {
10023
+ operation: "getLineById",
10024
+ line_id: lineId,
10025
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
10026
+ }
9728
10027
  });
9729
10028
  throw new Error(`Failed to fetch line: ${error.message}`);
9730
10029
  }
@@ -13199,6 +13498,17 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId, options) => {
13199
13498
  workspaceMetricsStore.setDetailed(transformedData);
13200
13499
  } catch (err) {
13201
13500
  console.error("Error fetching workspace metrics:", err);
13501
+ captureHandledFrontendException(err, {
13502
+ surface: "workspace_detail_metrics",
13503
+ route: "/api/dashboard/workspace/:workspaceId/metrics",
13504
+ extras: {
13505
+ workspace_id: workspaceId,
13506
+ company_id: companyId,
13507
+ date,
13508
+ shift_id: shiftId,
13509
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
13510
+ }
13511
+ });
13202
13512
  setError({ message: err.message, code: err.code });
13203
13513
  } finally {
13204
13514
  isFetchingRef.current = false;
@@ -14339,6 +14649,7 @@ var transformMonitorWorkspaceMetrics = ({
14339
14649
  recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
14340
14650
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
14341
14651
  recent_flow_computed_at: item.recent_flow_computed_at ?? null,
14652
+ recent_flow_forced_zero_after_shift: item.recent_flow_forced_zero_after_shift ?? null,
14342
14653
  scheduled_break_active: item.scheduled_break_active ?? false,
14343
14654
  incoming_wip_current: item.incoming_wip_current ?? null,
14344
14655
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
@@ -21465,20 +21776,26 @@ var apiUtils = {
21465
21776
  const token = sessionResponse.data.session?.access_token;
21466
21777
  if (!token) {
21467
21778
  console.error("API Util: No authentication token available.");
21468
- captureHandledFrontendException(new Error("Authentication required."), {
21779
+ addSentryBreadcrumb("API utility request skipped without auth token", {
21469
21780
  surface: "api_utils",
21470
- endpoint: relativeEndpoint,
21471
- reason: "missing_auth_token"
21781
+ route: relativeEndpoint,
21782
+ severity: "warning",
21783
+ extras: {
21784
+ reason: "missing_auth_token"
21785
+ }
21472
21786
  });
21473
21787
  throw new Error("Authentication required.");
21474
21788
  }
21475
21789
  const baseUrl = config.apiBaseUrl;
21476
21790
  if (!baseUrl) {
21477
21791
  console.error("API Util: apiBaseUrl is not configured.");
21478
- captureHandledFrontendException(new Error("API base URL is not configured."), {
21792
+ addSentryBreadcrumb("API utility request skipped without base URL", {
21479
21793
  surface: "api_utils",
21480
- endpoint: relativeEndpoint,
21481
- reason: "missing_api_base_url"
21794
+ route: relativeEndpoint,
21795
+ severity: "warning",
21796
+ extras: {
21797
+ reason: "missing_api_base_url"
21798
+ }
21482
21799
  });
21483
21800
  throw new Error("API base URL is not configured.");
21484
21801
  }
@@ -21508,11 +21825,14 @@ var apiUtils = {
21508
21825
  return await response.json();
21509
21826
  } catch (error) {
21510
21827
  console.error(`Network or fetch error calling ${endpoint}:`, error);
21511
- captureHandledFrontendException(error, {
21828
+ addSentryBreadcrumb("API utility request failed", {
21512
21829
  surface: "api_utils",
21513
- endpoint,
21514
- method: options.method || "GET",
21515
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
21830
+ route: endpoint,
21831
+ severity: "warning",
21832
+ extras: {
21833
+ method: options.method || "GET",
21834
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
21835
+ }
21516
21836
  });
21517
21837
  if (error instanceof Error) {
21518
21838
  throw error;
@@ -36962,7 +37282,7 @@ HourlyOutputChart.displayName = "HourlyOutputChart";
36962
37282
 
36963
37283
  // src/components/dashboard/grid/videoGridMetricUtils.ts
36964
37284
  var MAP_GRID_LEGEND_LABEL = "Efficiency";
36965
- var MIXED_VIDEO_GRID_LEGEND_LABEL = "Flow Efficiency";
37285
+ var MIXED_VIDEO_GRID_LEGEND_LABEL = "Live Efficiency";
36966
37286
  var isFiniteNumber2 = (value) => typeof value === "number" && Number.isFinite(value);
36967
37287
  var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
36968
37288
  workspace.video_grid_metric_mode,
@@ -36996,6 +37316,9 @@ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
36996
37316
  if (workspace.scheduled_break_active === true) {
36997
37317
  return false;
36998
37318
  }
37319
+ if (workspace.recent_flow_forced_zero_after_shift === true) {
37320
+ return false;
37321
+ }
36999
37322
  if (!hasVideoGridRecentFlow(workspace) || !isVideoGridWipGated(workspace)) {
37000
37323
  return false;
37001
37324
  }
@@ -37241,7 +37564,7 @@ var VideoCard = React144__namespace.default.memo(({
37241
37564
  }
37242
37565
  );
37243
37566
  }, (prevProps, nextProps) => {
37244
- if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
37567
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
37245
37568
  return false;
37246
37569
  }
37247
37570
  if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
@@ -37560,6 +37883,15 @@ var VideoGridView = React144__namespace.default.memo(({
37560
37883
  }
37561
37884
  console.error(`[VideoGridView] Stream failed for workspace: ${workspaceId}`);
37562
37885
  setFailedStreams((prev) => new Set(prev).add(workspaceId));
37886
+ captureHandledFrontendException(new Error("Video stream failed after recovery attempts"), {
37887
+ surface: "video_grid_stream",
37888
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
37889
+ extras: {
37890
+ workspace_id: workspaceId,
37891
+ stream_source: isR2Stream ? "r2" : "media_config",
37892
+ has_fallback: hasFallback
37893
+ }
37894
+ });
37563
37895
  trackCoreEvent("Video Stream Error", {
37564
37896
  workspace_id: workspaceId,
37565
37897
  view_type: "video_grid",
@@ -42227,11 +42559,14 @@ var SilentErrorBoundary = class extends React144__namespace.default.Component {
42227
42559
  componentStack: errorInfo.componentStack,
42228
42560
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
42229
42561
  });
42230
- captureSentryException(error, {
42562
+ captureHandledFrontendException(error, {
42231
42563
  surface: "react_error_boundary",
42232
- component_stack: errorInfo.componentStack,
42233
- error_count: this.state.errorCount + 1,
42234
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
42564
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
42565
+ severity: "error",
42566
+ extras: {
42567
+ component_stack: errorInfo.componentStack,
42568
+ error_count: this.state.errorCount + 1
42569
+ }
42235
42570
  });
42236
42571
  this.setState((prev) => ({
42237
42572
  errorCount: prev.errorCount + 1,
@@ -44768,6 +45103,11 @@ var OVERLAY_ICON_COLOR_BY_PALETTE = {
44768
45103
  cyan: "text-cyan-300",
44769
45104
  slate: "text-slate-200"
44770
45105
  };
45106
+ var buildPlaybackCaptureError = (recoverable) => {
45107
+ return new Error(
45108
+ recoverable ? "Clip playback failed after retry exhaustion" : "Clip playback failed with non-recoverable media error"
45109
+ );
45110
+ };
44771
45111
  var BottlenecksContent = ({
44772
45112
  workspaceId,
44773
45113
  workspaceName,
@@ -45222,6 +45562,16 @@ var BottlenecksContent = ({
45222
45562
  }
45223
45563
  } catch (err) {
45224
45564
  console.error("Error loading first video for category:", err);
45565
+ captureHandledFrontendException(err, {
45566
+ surface: "clips_initial_load",
45567
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
45568
+ extras: {
45569
+ workspace_id: workspaceId,
45570
+ category: targetCategory,
45571
+ date: effectiveDateString,
45572
+ shift_id: effectiveShiftId
45573
+ }
45574
+ });
45225
45575
  if (isMountedRef.current) {
45226
45576
  setError({
45227
45577
  type: "fatal",
@@ -45759,6 +46109,15 @@ var BottlenecksContent = ({
45759
46109
  console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
45760
46110
  } catch (error2) {
45761
46111
  console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
46112
+ captureHandledFrontendException(error2, {
46113
+ surface: "clips_selected_load",
46114
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
46115
+ extras: {
46116
+ workspace_id: workspaceId,
46117
+ clip_id: clipId,
46118
+ category: categoryId
46119
+ }
46120
+ });
45762
46121
  if (isMountedRef.current) {
45763
46122
  setError({
45764
46123
  type: "fatal",
@@ -46358,6 +46717,18 @@ var BottlenecksContent = ({
46358
46717
  errorCode,
46359
46718
  errorMessage
46360
46719
  });
46720
+ captureHandledFrontendException(buildPlaybackCaptureError(false), {
46721
+ surface: "clips_video_playback",
46722
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
46723
+ extras: {
46724
+ workspace_id: workspaceId,
46725
+ category: activeFilterRef.current,
46726
+ video_id: currentVideo?.id,
46727
+ error_code: errorCode,
46728
+ player_error_message: errorMessage,
46729
+ recoverable: false
46730
+ }
46731
+ });
46361
46732
  return;
46362
46733
  }
46363
46734
  if (videoRetryCountRef.current < 3 && currentVideo) {
@@ -46401,6 +46772,19 @@ var BottlenecksContent = ({
46401
46772
  errorMessage,
46402
46773
  attempts: 3
46403
46774
  });
46775
+ captureHandledFrontendException(buildPlaybackCaptureError(true), {
46776
+ surface: "clips_video_playback",
46777
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
46778
+ extras: {
46779
+ workspace_id: workspaceId,
46780
+ category: activeFilterRef.current,
46781
+ video_id: currentVideo?.id,
46782
+ error_code: errorCode,
46783
+ player_error_message: errorMessage,
46784
+ recoverable: true,
46785
+ attempts: 3
46786
+ }
46787
+ });
46404
46788
  }
46405
46789
  }, [currentVideo, workspaceId, clearLoadingState, clearRetryTimeout, restartCurrentClipPlayback]);
46406
46790
  React144.useEffect(() => {
@@ -48732,6 +49116,14 @@ var LinePdfExportButton = ({
48732
49116
  pdf.save(`${fileName}.pdf`);
48733
49117
  } catch (error) {
48734
49118
  console.error("PDF Export Error:", error);
49119
+ captureHandledFrontendException(error, {
49120
+ surface: "line_pdf_export",
49121
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
49122
+ extras: {
49123
+ file_name: fileName,
49124
+ target_type: typeof targetElement === "string" ? "selector" : "element"
49125
+ }
49126
+ });
48735
49127
  alert("An error occurred while exporting to PDF. Please try again.");
48736
49128
  } finally {
48737
49129
  setIsExporting(false);
@@ -50554,6 +50946,16 @@ var LineMonthlyPdfGenerator = ({
50554
50946
  doc.save(fileName);
50555
50947
  } catch (error) {
50556
50948
  console.error("Line Monthly PDF generation failed:", error);
50949
+ captureHandledFrontendException(error, {
50950
+ surface: "line_monthly_pdf_generation",
50951
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
50952
+ extras: {
50953
+ line_name: lineName || null,
50954
+ selected_month: selectedMonth,
50955
+ selected_year: selectedYear,
50956
+ selected_shift_id: selectedShiftId
50957
+ }
50958
+ });
50557
50959
  } finally {
50558
50960
  setIsGenerating(false);
50559
50961
  }
@@ -51336,6 +51738,15 @@ var LinePdfGenerator = ({
51336
51738
  doc.save(fileName);
51337
51739
  } catch (error) {
51338
51740
  console.error("PDF generation failed:", error);
51741
+ captureHandledFrontendException(error, {
51742
+ surface: "line_pdf_generation",
51743
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
51744
+ extras: {
51745
+ line_id: lineInfo.line_id,
51746
+ date: lineInfo.date,
51747
+ shift_id: lineInfo.shift_id
51748
+ }
51749
+ });
51339
51750
  } finally {
51340
51751
  setIsGenerating(false);
51341
51752
  }
@@ -51408,6 +51819,14 @@ var WorkspacePdfExportButton = ({
51408
51819
  pdf.save(`${fileName}.pdf`);
51409
51820
  } catch (error) {
51410
51821
  console.error("PDF Export Error:", error);
51822
+ captureHandledFrontendException(error, {
51823
+ surface: "workspace_pdf_export",
51824
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
51825
+ extras: {
51826
+ file_name: fileName,
51827
+ target_type: typeof targetElement === "string" ? "selector" : "element"
51828
+ }
51829
+ });
51411
51830
  alert("An error occurred while exporting to PDF. Please try again.");
51412
51831
  } finally {
51413
51832
  setIsExporting(false);
@@ -53257,6 +53676,16 @@ var WorkspacePdfGenerator = ({
53257
53676
  doc.save(fileName);
53258
53677
  } catch (error) {
53259
53678
  console.error("PDF generation failed:", error);
53679
+ captureHandledFrontendException(error, {
53680
+ surface: "workspace_pdf_generation",
53681
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
53682
+ extras: {
53683
+ workspace_id: workspace.workspace_id,
53684
+ line_id: workspace.line_id,
53685
+ date: workspace.date,
53686
+ shift_id: workspace.shift_id
53687
+ }
53688
+ });
53260
53689
  } finally {
53261
53690
  setIsGenerating(false);
53262
53691
  }
@@ -53664,6 +54093,18 @@ var WorkspaceMonthlyPdfGenerator = ({
53664
54093
  doc.save(fileName);
53665
54094
  } catch (error) {
53666
54095
  console.error("Monthly PDF generation failed:", error);
54096
+ captureHandledFrontendException(error, {
54097
+ surface: "workspace_monthly_pdf_generation",
54098
+ route: typeof window !== "undefined" ? window.location.pathname : "unknown",
54099
+ extras: {
54100
+ workspace_id: workspaceId,
54101
+ workspace_name: workspaceName,
54102
+ line_name: lineName || null,
54103
+ selected_month: selectedMonth,
54104
+ selected_year: selectedYear,
54105
+ selected_shift_id: selectedShiftId
54106
+ }
54107
+ });
53667
54108
  } finally {
53668
54109
  setIsGenerating(false);
53669
54110
  }
@@ -62936,6 +63377,16 @@ var useLiveMonitorBootstrap = ({
62936
63377
  if (requestId !== activeRequestIdRef.current) {
62937
63378
  return;
62938
63379
  }
63380
+ captureHandledFrontendException(fetchError, {
63381
+ surface: "live_monitor_bootstrap",
63382
+ route: "/api/dashboard/monitor-bootstrap",
63383
+ extras: {
63384
+ company_id: resolvedCompanyId,
63385
+ line_ids: normalizedLineIds,
63386
+ force_refresh: force,
63387
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
63388
+ }
63389
+ });
62939
63390
  setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
62940
63391
  } finally {
62941
63392
  if (requestId === activeRequestIdRef.current) {
@@ -63248,11 +63699,14 @@ var NotificationService = class {
63248
63699
  }
63249
63700
  return response;
63250
63701
  } catch (error) {
63251
- captureHandledFrontendException(error, {
63702
+ addSentryBreadcrumb("Notification service request failed", {
63252
63703
  surface: "notification_service",
63253
- url,
63254
- method: options.method || "GET",
63255
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
63704
+ route: url,
63705
+ severity: "warning",
63706
+ extras: {
63707
+ method: options.method || "GET",
63708
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
63709
+ }
63256
63710
  });
63257
63711
  throw error;
63258
63712
  }
@@ -77407,11 +77861,14 @@ var TicketService = class {
77407
77861
  }
77408
77862
  return response;
77409
77863
  } catch (error) {
77410
- captureHandledFrontendException(error, {
77864
+ addSentryBreadcrumb("Ticket service request failed", {
77411
77865
  surface: "ticket_service",
77412
- url,
77413
- method: options.method || "GET",
77414
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
77866
+ route: url,
77867
+ severity: "warning",
77868
+ extras: {
77869
+ method: options.method || "GET",
77870
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown"
77871
+ }
77415
77872
  });
77416
77873
  throw error;
77417
77874
  }
@@ -83191,10 +83648,33 @@ var useOperationsOverviewRefresh = ({
83191
83648
  if (controller.signal.aborted || requestIdsRef.current[section] !== requestId || isAbortError2(error)) {
83192
83649
  return;
83193
83650
  }
83651
+ const sentryContext = {
83652
+ surface: "operations_overview_refresh",
83653
+ route: `/api/dashboard/operations-overview/${section}`,
83654
+ extras: {
83655
+ section,
83656
+ reason,
83657
+ company_id: companyId,
83658
+ line_ids: lineIds,
83659
+ start_date: startKey,
83660
+ end_date: endKey,
83661
+ trend_mode: trendMode,
83662
+ comparison_strategy: comparisonStrategy || null
83663
+ }
83664
+ };
83665
+ if (section === "improvements") {
83666
+ addSentryBreadcrumb("Operations overview optional improvements refresh failed", {
83667
+ ...sentryContext,
83668
+ severity: "warning",
83669
+ category: "operations_overview"
83670
+ });
83671
+ } else {
83672
+ captureHandledFrontendException(error, sentryContext);
83673
+ }
83194
83674
  onError(error instanceof Error ? error.message : `Failed to refresh ${section}`);
83195
83675
  }
83196
83676
  },
83197
- [companyId, enabled, lineIds.length, supabase]
83677
+ [companyId, comparisonStrategy, enabled, endKey, lineIds, startKey, supabase, trendMode]
83198
83678
  );
83199
83679
  const refreshSnapshot = React144__namespace.default.useCallback(
83200
83680
  async (reason) => {
@@ -84796,6 +85276,9 @@ exports.RegistryProvider = RegistryProvider;
84796
85276
  exports.RoleBadge = RoleBadge;
84797
85277
  exports.S3ClipsService = S3ClipsSupabaseService;
84798
85278
  exports.S3Service = S3Service;
85279
+ exports.SENTRY_HANDLED_EVENT_SESSION_LIMIT = SENTRY_HANDLED_EVENT_SESSION_LIMIT;
85280
+ exports.SENTRY_HANDLED_EVENT_WINDOW_MS = SENTRY_HANDLED_EVENT_WINDOW_MS;
85281
+ exports.SENTRY_QUOTA_STORAGE_KEY = SENTRY_QUOTA_STORAGE_KEY;
84799
85282
  exports.SKUManagementView = SKUManagementView;
84800
85283
  exports.SOPComplianceChart = SOPComplianceChart;
84801
85284
  exports.SSEChatClient = SSEChatClient;
@@ -84872,6 +85355,7 @@ exports.WorkspacePdfExportButton = WorkspacePdfExportButton;
84872
85355
  exports.WorkspacePdfGenerator = WorkspacePdfGenerator;
84873
85356
  exports.WorkspaceWhatsAppShareButton = WorkspaceWhatsAppShareButton;
84874
85357
  exports.actionService = actionService;
85358
+ exports.addSentryBreadcrumb = addSentryBreadcrumb;
84875
85359
  exports.aggregateKPIsFromLineMetricsRows = aggregateKPIsFromLineMetricsRows;
84876
85360
  exports.alertsService = alertsService;
84877
85361
  exports.apiUtils = apiUtils;
@@ -85057,6 +85541,7 @@ exports.realtimeService = realtimeService;
85057
85541
  exports.refreshWorkspaceDisplayNames = refreshWorkspaceDisplayNames;
85058
85542
  exports.resetCoreMixpanel = resetCoreMixpanel;
85059
85543
  exports.resetFailedUrl = resetFailedUrl;
85544
+ exports.resetSentryQuotaForTests = resetSentryQuotaForTests;
85060
85545
  exports.resetSubscriptionManager = resetSubscriptionManager;
85061
85546
  exports.resolveDefaultSkuId = resolveDefaultSkuId;
85062
85547
  exports.resolveLiveSkuId = resolveLiveSkuId;