@optifye/dashboard-core 6.10.5 → 6.10.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.css +31 -0
- package/dist/index.d.mts +37 -23
- package/dist/index.d.ts +37 -23
- package/dist/index.js +1209 -610
- package/dist/index.mjs +1211 -612
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -13,11 +13,11 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
|
|
|
13
13
|
import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
|
|
14
14
|
import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Filter, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, MessageSquare, Menu, Send, Copy, UserCheck, LogOut, Settings, LifeBuoy, EyeOff, UserCircle } from 'lucide-react';
|
|
15
15
|
import { toast } from 'sonner';
|
|
16
|
-
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell,
|
|
16
|
+
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
|
|
17
17
|
import { Slot } from '@radix-ui/react-slot';
|
|
18
18
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
19
19
|
import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
|
|
20
|
-
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon,
|
|
20
|
+
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, LightBulbIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, PlayCircleIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
|
|
21
21
|
import { CheckIcon } from '@heroicons/react/24/solid';
|
|
22
22
|
import html2canvas from 'html2canvas';
|
|
23
23
|
import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
|
|
@@ -3728,18 +3728,17 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
|
3728
3728
|
track_pageview: trackPageView ?? true,
|
|
3729
3729
|
persistence: "localStorage"
|
|
3730
3730
|
};
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
initOptions.record_idle_timeout_ms = sessionOpts.recordIdleTimeoutMs;
|
|
3731
|
+
const recordSessionsPercent = sessionOpts.recordSessionsPercent ?? 0;
|
|
3732
|
+
initOptions.record_sessions_percent = recordSessionsPercent;
|
|
3733
|
+
const defaultIdleTimeoutMs = 10 * 60 * 1e3;
|
|
3734
|
+
const recordIdleTimeoutMs = sessionOpts.recordIdleTimeoutMs ?? (recordSessionsPercent > 0 ? defaultIdleTimeoutMs : void 0);
|
|
3735
|
+
if (recordIdleTimeoutMs !== void 0) {
|
|
3736
|
+
initOptions.record_idle_timeout_ms = recordIdleTimeoutMs;
|
|
3738
3737
|
}
|
|
3739
3738
|
if (sessionOpts.recordHeatmapData !== void 0) {
|
|
3740
3739
|
initOptions.record_heatmap_data = sessionOpts.recordHeatmapData;
|
|
3741
3740
|
} else {
|
|
3742
|
-
initOptions.record_heatmap_data =
|
|
3741
|
+
initOptions.record_heatmap_data = false;
|
|
3743
3742
|
}
|
|
3744
3743
|
if (sessionOpts.recordCanvas !== void 0) {
|
|
3745
3744
|
initOptions.record_canvas = sessionOpts.recordCanvas;
|
|
@@ -3757,7 +3756,9 @@ var initializeCoreMixpanel = (token, debugOrOptions, trackPageViewArg) => {
|
|
|
3757
3756
|
});
|
|
3758
3757
|
mixpanel.init(token, initOptions);
|
|
3759
3758
|
isMixpanelInitialized = true;
|
|
3760
|
-
|
|
3759
|
+
if (initOptions.debug) {
|
|
3760
|
+
console.log("Mixpanel initialized in dashboard-core.");
|
|
3761
|
+
}
|
|
3761
3762
|
};
|
|
3762
3763
|
var trackCorePageView = (pageName, properties) => {
|
|
3763
3764
|
if (!isMixpanelInitialized) return;
|
|
@@ -10301,6 +10302,41 @@ var useRealtimeLineMetrics = ({
|
|
|
10301
10302
|
refreshMetrics: fetchData
|
|
10302
10303
|
}), [metrics2, lineDetails, loading, error, fetchData]);
|
|
10303
10304
|
};
|
|
10305
|
+
var useLines = () => {
|
|
10306
|
+
const supabase = useSupabase();
|
|
10307
|
+
const entityConfig = useEntityConfig();
|
|
10308
|
+
const [lines, setLines] = useState([]);
|
|
10309
|
+
const [loading, setLoading] = useState(true);
|
|
10310
|
+
const [error, setError] = useState(null);
|
|
10311
|
+
const fetchLines = useCallback(async () => {
|
|
10312
|
+
if (!supabase) {
|
|
10313
|
+
setLoading(false);
|
|
10314
|
+
return;
|
|
10315
|
+
}
|
|
10316
|
+
try {
|
|
10317
|
+
setLoading(true);
|
|
10318
|
+
let query = supabase.from("lines").select("*").eq("enable", true);
|
|
10319
|
+
if (entityConfig.companyId) {
|
|
10320
|
+
query = query.eq("company_id", entityConfig.companyId);
|
|
10321
|
+
}
|
|
10322
|
+
const { data, error: error2 } = await query;
|
|
10323
|
+
if (error2) throw error2;
|
|
10324
|
+
const sortedLines = (data || []).sort(
|
|
10325
|
+
(a, b) => (a.line_name || "").localeCompare(b.line_name || "")
|
|
10326
|
+
);
|
|
10327
|
+
setLines(sortedLines);
|
|
10328
|
+
} catch (err) {
|
|
10329
|
+
console.error("Error fetching lines:", err);
|
|
10330
|
+
setError(err);
|
|
10331
|
+
} finally {
|
|
10332
|
+
setLoading(false);
|
|
10333
|
+
}
|
|
10334
|
+
}, [supabase, entityConfig.companyId]);
|
|
10335
|
+
useEffect(() => {
|
|
10336
|
+
fetchLines();
|
|
10337
|
+
}, [fetchLines]);
|
|
10338
|
+
return { lines, loading, error, refetch: fetchLines };
|
|
10339
|
+
};
|
|
10304
10340
|
var useTargets = (options) => {
|
|
10305
10341
|
const { companyId } = useEntityConfig();
|
|
10306
10342
|
const supabase = useSupabase();
|
|
@@ -12434,41 +12470,6 @@ function useClipTypesWithCounts(workspaceId, date, shiftId, totalOutput, options
|
|
|
12434
12470
|
counts
|
|
12435
12471
|
};
|
|
12436
12472
|
}
|
|
12437
|
-
var useLines = () => {
|
|
12438
|
-
const supabase = useSupabase();
|
|
12439
|
-
const entityConfig = useEntityConfig();
|
|
12440
|
-
const [lines, setLines] = useState([]);
|
|
12441
|
-
const [loading, setLoading] = useState(true);
|
|
12442
|
-
const [error, setError] = useState(null);
|
|
12443
|
-
const fetchLines = useCallback(async () => {
|
|
12444
|
-
if (!supabase) {
|
|
12445
|
-
setLoading(false);
|
|
12446
|
-
return;
|
|
12447
|
-
}
|
|
12448
|
-
try {
|
|
12449
|
-
setLoading(true);
|
|
12450
|
-
let query = supabase.from("lines").select("*").eq("enable", true);
|
|
12451
|
-
if (entityConfig.companyId) {
|
|
12452
|
-
query = query.eq("company_id", entityConfig.companyId);
|
|
12453
|
-
}
|
|
12454
|
-
const { data, error: error2 } = await query;
|
|
12455
|
-
if (error2) throw error2;
|
|
12456
|
-
const sortedLines = (data || []).sort(
|
|
12457
|
-
(a, b) => (a.line_name || "").localeCompare(b.line_name || "")
|
|
12458
|
-
);
|
|
12459
|
-
setLines(sortedLines);
|
|
12460
|
-
} catch (err) {
|
|
12461
|
-
console.error("Error fetching lines:", err);
|
|
12462
|
-
setError(err);
|
|
12463
|
-
} finally {
|
|
12464
|
-
setLoading(false);
|
|
12465
|
-
}
|
|
12466
|
-
}, [supabase, entityConfig.companyId]);
|
|
12467
|
-
useEffect(() => {
|
|
12468
|
-
fetchLines();
|
|
12469
|
-
}, [fetchLines]);
|
|
12470
|
-
return { lines, loading, error, refetch: fetchLines };
|
|
12471
|
-
};
|
|
12472
12473
|
var MAX_RETRIES = 10;
|
|
12473
12474
|
var RETRY_DELAY = 500;
|
|
12474
12475
|
function useNavigation(customNavigate) {
|
|
@@ -25323,6 +25324,7 @@ var DEFAULT_BAR_RADIUS = [4, 4, 0, 0];
|
|
|
25323
25324
|
var BarChartComponent = ({
|
|
25324
25325
|
data,
|
|
25325
25326
|
bars,
|
|
25327
|
+
referenceLines,
|
|
25326
25328
|
xAxisDataKey = "name",
|
|
25327
25329
|
xAxisLabel,
|
|
25328
25330
|
yAxisLabel,
|
|
@@ -25406,6 +25408,22 @@ var BarChartComponent = ({
|
|
|
25406
25408
|
stroke: axisStrokeColor
|
|
25407
25409
|
}
|
|
25408
25410
|
),
|
|
25411
|
+
referenceLines?.map((line, idx) => /* @__PURE__ */ jsx(
|
|
25412
|
+
ReferenceLine,
|
|
25413
|
+
{
|
|
25414
|
+
y: line.y,
|
|
25415
|
+
stroke: line.stroke || axisStrokeColor,
|
|
25416
|
+
strokeDasharray: line.strokeDasharray || "4 4",
|
|
25417
|
+
strokeWidth: line.strokeWidth,
|
|
25418
|
+
label: line.label ? {
|
|
25419
|
+
value: line.label,
|
|
25420
|
+
position: "insideTopRight",
|
|
25421
|
+
fill: line.stroke || axisTickFillColor,
|
|
25422
|
+
fontSize: 12
|
|
25423
|
+
} : void 0
|
|
25424
|
+
},
|
|
25425
|
+
`ref-line-${idx}`
|
|
25426
|
+
)),
|
|
25409
25427
|
showTooltip && /* @__PURE__ */ jsx(Tooltip, { formatter: tooltipFormatter || defaultTooltipFormatter, cursor: { fill: "transparent" } }),
|
|
25410
25428
|
showLegend && /* @__PURE__ */ jsx(Legend, { payload: legendPayload }),
|
|
25411
25429
|
bars.map((barConfig, index) => /* @__PURE__ */ jsx(
|
|
@@ -25441,7 +25459,7 @@ var BarChartComponent = ({
|
|
|
25441
25459
|
return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
|
|
25442
25460
|
};
|
|
25443
25461
|
var BarChart = React24__default.memo(BarChartComponent, (prevProps, nextProps) => {
|
|
25444
|
-
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.layout !== nextProps.layout || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect) {
|
|
25462
|
+
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || JSON.stringify(prevProps.referenceLines || []) !== JSON.stringify(nextProps.referenceLines || []) || prevProps.layout !== nextProps.layout || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect) {
|
|
25445
25463
|
return false;
|
|
25446
25464
|
}
|
|
25447
25465
|
if (prevProps.data.length !== nextProps.data.length) {
|
|
@@ -28969,6 +28987,33 @@ var VideoControls = ({
|
|
|
28969
28987
|
}
|
|
28970
28988
|
);
|
|
28971
28989
|
};
|
|
28990
|
+
|
|
28991
|
+
// src/lib/utils/r2Detection.ts
|
|
28992
|
+
function isR2WorkerUrl(url, r2WorkerDomain) {
|
|
28993
|
+
if (!url || !r2WorkerDomain) return false;
|
|
28994
|
+
try {
|
|
28995
|
+
const workerDomain = new URL(r2WorkerDomain).hostname;
|
|
28996
|
+
return url.includes(workerDomain);
|
|
28997
|
+
} catch {
|
|
28998
|
+
return url.includes(r2WorkerDomain.replace(/https?:\/\//, ""));
|
|
28999
|
+
}
|
|
29000
|
+
}
|
|
29001
|
+
|
|
29002
|
+
// src/lib/services/hlsAuthService.ts
|
|
29003
|
+
async function getAuthTokenForHls(supabase) {
|
|
29004
|
+
try {
|
|
29005
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
29006
|
+
if (!session?.access_token) {
|
|
29007
|
+
console.warn("[HLS Auth] No active session, R2 streaming may fail");
|
|
29008
|
+
return null;
|
|
29009
|
+
}
|
|
29010
|
+
console.log("[HLS Auth] Retrieved token for HLS.js requests");
|
|
29011
|
+
return session.access_token;
|
|
29012
|
+
} catch (error) {
|
|
29013
|
+
console.error("[HLS Auth] Error getting auth token:", error);
|
|
29014
|
+
return null;
|
|
29015
|
+
}
|
|
29016
|
+
}
|
|
28972
29017
|
var ERROR_MAPPING = {
|
|
28973
29018
|
"networkError": {
|
|
28974
29019
|
code: 2,
|
|
@@ -28995,6 +29040,38 @@ var ERROR_MAPPING = {
|
|
|
28995
29040
|
canRetry: false
|
|
28996
29041
|
}
|
|
28997
29042
|
};
|
|
29043
|
+
var CLIP_ID_COMMENT_REGEX = /#\s*Clip ID:\s*([a-f0-9-]+)/i;
|
|
29044
|
+
var PLAYLIST_PROXY_REGEX = /\/api\/clips\/playlist\/([a-f0-9-]+)/i;
|
|
29045
|
+
var R2_FALLBACK_DETAILS = /* @__PURE__ */ new Set([
|
|
29046
|
+
"fragLoadError",
|
|
29047
|
+
"fragLoadHTTPError",
|
|
29048
|
+
"manifestLoadError",
|
|
29049
|
+
"levelLoadError",
|
|
29050
|
+
"keyLoadError"
|
|
29051
|
+
]);
|
|
29052
|
+
var extractClipIdFromSource = (source) => {
|
|
29053
|
+
if (!source) return null;
|
|
29054
|
+
const commentMatch = source.match(CLIP_ID_COMMENT_REGEX);
|
|
29055
|
+
if (commentMatch) return commentMatch[1];
|
|
29056
|
+
const urlMatch = source.match(PLAYLIST_PROXY_REGEX);
|
|
29057
|
+
if (urlMatch) return urlMatch[1];
|
|
29058
|
+
return null;
|
|
29059
|
+
};
|
|
29060
|
+
var ensureClipIdComment = (playlist, clipId) => {
|
|
29061
|
+
if (!playlist || playlist.includes(`# Clip ID: ${clipId}`) || CLIP_ID_COMMENT_REGEX.test(playlist)) {
|
|
29062
|
+
return playlist;
|
|
29063
|
+
}
|
|
29064
|
+
const lines = playlist.split("\n");
|
|
29065
|
+
if (lines.length === 0) return playlist;
|
|
29066
|
+
return [lines[0], `# Clip ID: ${clipId}`, ...lines.slice(1)].join("\n");
|
|
29067
|
+
};
|
|
29068
|
+
var getHlsErrorUrl = (data) => {
|
|
29069
|
+
return data?.frag?.url || data?.response?.url || data?.context?.url || data?.networkDetails?.response?.url || data?.url;
|
|
29070
|
+
};
|
|
29071
|
+
var getHlsErrorStatus = (data) => {
|
|
29072
|
+
const status = data?.response?.code ?? data?.response?.status ?? data?.networkDetails?.status ?? data?.networkDetails?.response?.status;
|
|
29073
|
+
return typeof status === "number" ? status : void 0;
|
|
29074
|
+
};
|
|
28998
29075
|
var hlsVideoPlayerStyles = `
|
|
28999
29076
|
.hls-video-player-container {
|
|
29000
29077
|
width: 100%;
|
|
@@ -29091,12 +29168,17 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29091
29168
|
onSeeked,
|
|
29092
29169
|
onClick
|
|
29093
29170
|
}, ref) => {
|
|
29171
|
+
const supabase = useSupabase();
|
|
29094
29172
|
const videoContainerRef = useRef(null);
|
|
29095
29173
|
const videoRef = useRef(null);
|
|
29096
29174
|
const hlsRef = useRef(null);
|
|
29097
29175
|
const blobUrlRef = useRef(null);
|
|
29176
|
+
const clipIdRef = useRef(null);
|
|
29177
|
+
const r2FallbackAttemptedRef = useRef(false);
|
|
29098
29178
|
const [isReady, setIsReady] = useState(false);
|
|
29099
29179
|
const [isLoading, setIsLoading] = useState(true);
|
|
29180
|
+
const [overrideSource, setOverrideSource] = useState(null);
|
|
29181
|
+
const effectiveSrc = overrideSource && overrideSource.baseSrc === src ? overrideSource.value : src;
|
|
29100
29182
|
const [showControls, setShowControls] = useState(true);
|
|
29101
29183
|
const [controlsPinned, setControlsPinned] = useState(false);
|
|
29102
29184
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
@@ -29156,10 +29238,16 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29156
29238
|
onSeeked,
|
|
29157
29239
|
onLoadingChange
|
|
29158
29240
|
]);
|
|
29241
|
+
useEffect(() => {
|
|
29242
|
+
clipIdRef.current = extractClipIdFromSource(src);
|
|
29243
|
+
r2FallbackAttemptedRef.current = false;
|
|
29244
|
+
setOverrideSource(null);
|
|
29245
|
+
}, [src]);
|
|
29159
29246
|
const stableHlsConfigRef = useRef(hlsConfig);
|
|
29160
29247
|
const stableOptionsRef = useRef(options);
|
|
29161
29248
|
const configSignatureRef = useRef("");
|
|
29162
29249
|
const [configVersion, setConfigVersion] = useState(0);
|
|
29250
|
+
const r2WorkerDomain = process.env.NEXT_PUBLIC_R2_WORKER_DOMAIN || "https://r2-stream-proxy.optifye-r2.workers.dev";
|
|
29163
29251
|
useEffect(() => {
|
|
29164
29252
|
const serialized = JSON.stringify({
|
|
29165
29253
|
hlsConfig: hlsConfig || null,
|
|
@@ -29178,6 +29266,50 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29178
29266
|
setConfigVersion((prev) => prev + 1);
|
|
29179
29267
|
}
|
|
29180
29268
|
}, [hlsConfig, options]);
|
|
29269
|
+
const attemptS3Fallback = useCallback(async (mode, reason, errorUrl) => {
|
|
29270
|
+
if (r2FallbackAttemptedRef.current) return false;
|
|
29271
|
+
const clipId = clipIdRef.current || extractClipIdFromSource(effectiveSrc);
|
|
29272
|
+
if (!clipId) {
|
|
29273
|
+
console.warn(`[HlsVideoPlayer] R2 fallback skipped - no clip ID (${reason})`);
|
|
29274
|
+
return false;
|
|
29275
|
+
}
|
|
29276
|
+
r2FallbackAttemptedRef.current = true;
|
|
29277
|
+
if (mode === "url") {
|
|
29278
|
+
const fallbackUrl = `/api/clips/playlist/${clipId}?source=s3`;
|
|
29279
|
+
console.warn(`[HlsVideoPlayer] Switching to S3 playlist URL (${reason})`, { errorUrl, clipId });
|
|
29280
|
+
setOverrideSource({ baseSrc: src, value: fallbackUrl });
|
|
29281
|
+
return true;
|
|
29282
|
+
}
|
|
29283
|
+
try {
|
|
29284
|
+
console.warn(`[HlsVideoPlayer] Fetching S3 playlist (${reason})`, { errorUrl, clipId });
|
|
29285
|
+
const response = await fetch(`/api/clips/playlist/${clipId}?source=s3`);
|
|
29286
|
+
if (!response.ok) {
|
|
29287
|
+
console.warn("[HlsVideoPlayer] S3 playlist fetch failed", { status: response.status, clipId });
|
|
29288
|
+
return false;
|
|
29289
|
+
}
|
|
29290
|
+
let playlistText = await response.text();
|
|
29291
|
+
playlistText = ensureClipIdComment(playlistText, clipId);
|
|
29292
|
+
setOverrideSource({ baseSrc: src, value: playlistText });
|
|
29293
|
+
return true;
|
|
29294
|
+
} catch (error) {
|
|
29295
|
+
console.error("[HlsVideoPlayer] S3 fallback failed", error);
|
|
29296
|
+
return false;
|
|
29297
|
+
}
|
|
29298
|
+
}, [effectiveSrc, src]);
|
|
29299
|
+
const maybeHandleR2Fallback = useCallback((data) => {
|
|
29300
|
+
const errorUrl = getHlsErrorUrl(data);
|
|
29301
|
+
if (!errorUrl || !isR2WorkerUrl(errorUrl, r2WorkerDomain)) {
|
|
29302
|
+
return false;
|
|
29303
|
+
}
|
|
29304
|
+
const status = getHlsErrorStatus(data);
|
|
29305
|
+
const details = data?.details;
|
|
29306
|
+
const shouldFallback = R2_FALLBACK_DETAILS.has(details) || typeof status === "number" && status >= 400 && status < 500;
|
|
29307
|
+
if (!shouldFallback) {
|
|
29308
|
+
return false;
|
|
29309
|
+
}
|
|
29310
|
+
attemptS3Fallback("playlist", `HLS.js ${details || data?.type || "error"}`, errorUrl);
|
|
29311
|
+
return true;
|
|
29312
|
+
}, [attemptS3Fallback, r2WorkerDomain]);
|
|
29181
29313
|
const cleanupBlobUrl = useCallback(() => {
|
|
29182
29314
|
if (blobUrlRef.current) {
|
|
29183
29315
|
URL.revokeObjectURL(blobUrlRef.current);
|
|
@@ -29218,36 +29350,56 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29218
29350
|
dispose: () => dispose()
|
|
29219
29351
|
};
|
|
29220
29352
|
}, [dispose]);
|
|
29221
|
-
const initializePlayer = useCallback(() => {
|
|
29222
|
-
if (!videoRef.current || !
|
|
29353
|
+
const initializePlayer = useCallback(async () => {
|
|
29354
|
+
if (!videoRef.current || !effectiveSrc) return;
|
|
29223
29355
|
const video = videoRef.current;
|
|
29224
29356
|
const player = playerLikeObject();
|
|
29357
|
+
let authToken = null;
|
|
29358
|
+
try {
|
|
29359
|
+
authToken = await getAuthTokenForHls(supabase);
|
|
29360
|
+
if (!authToken) {
|
|
29361
|
+
console.warn("[HLS Auth] No active session - R2 streaming may fail");
|
|
29362
|
+
}
|
|
29363
|
+
} catch (error) {
|
|
29364
|
+
console.error("[HLS Auth] Error getting auth token:", error);
|
|
29365
|
+
}
|
|
29225
29366
|
const mergedHlsConfig = {
|
|
29226
29367
|
...BASE_HLS_CONFIG,
|
|
29227
29368
|
...stableHlsConfigRef.current || {},
|
|
29228
|
-
...stableOptionsRef.current || {}
|
|
29369
|
+
...stableOptionsRef.current || {},
|
|
29370
|
+
// Modern HLS.js uses Fetch API - override fetch to add Authorization header for R2 Worker requests
|
|
29371
|
+
fetchSetup: function(context, initParams) {
|
|
29372
|
+
const url = context.url;
|
|
29373
|
+
if (isR2WorkerUrl(url, r2WorkerDomain) && authToken) {
|
|
29374
|
+
initParams.headers = {
|
|
29375
|
+
...initParams.headers,
|
|
29376
|
+
"Authorization": `Bearer ${authToken}`
|
|
29377
|
+
};
|
|
29378
|
+
}
|
|
29379
|
+
return new Request(url, initParams);
|
|
29380
|
+
}
|
|
29229
29381
|
};
|
|
29230
29382
|
cleanupBlobUrl();
|
|
29231
|
-
const isHLS =
|
|
29383
|
+
const isHLS = effectiveSrc.endsWith(".m3u8") || effectiveSrc.startsWith("#EXTM3U");
|
|
29232
29384
|
if (isHLS) {
|
|
29233
|
-
if (
|
|
29385
|
+
if (effectiveSrc.startsWith("#EXTM3U")) {
|
|
29234
29386
|
const safariMode = isSafari();
|
|
29235
29387
|
const browserName = getBrowserName();
|
|
29236
29388
|
if (safariMode) {
|
|
29237
|
-
const clipIdMatch =
|
|
29389
|
+
const clipIdMatch = effectiveSrc.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
29238
29390
|
if (clipIdMatch) {
|
|
29239
29391
|
const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
29240
29392
|
console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
|
|
29241
29393
|
video.src = proxyUrl;
|
|
29242
29394
|
} else {
|
|
29243
29395
|
console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
|
|
29244
|
-
const blob = new Blob([
|
|
29396
|
+
const blob = new Blob([effectiveSrc], { type: "application/vnd.apple.mpegurl" });
|
|
29245
29397
|
const blobUrl = URL.createObjectURL(blob);
|
|
29246
29398
|
blobUrlRef.current = blobUrl;
|
|
29247
29399
|
video.src = blobUrl;
|
|
29248
29400
|
}
|
|
29249
29401
|
} else if (Hls3.isSupported()) {
|
|
29250
|
-
const blob = new Blob([
|
|
29402
|
+
const blob = new Blob([effectiveSrc], { type: "application/vnd.apple.mpegurl" });
|
|
29251
29403
|
const blobUrl = URL.createObjectURL(blob);
|
|
29252
29404
|
blobUrlRef.current = blobUrl;
|
|
29253
29405
|
console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
|
|
@@ -29260,6 +29412,9 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29260
29412
|
});
|
|
29261
29413
|
hls.on(Events.ERROR, (event, data) => {
|
|
29262
29414
|
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
29415
|
+
if (maybeHandleR2Fallback(data)) {
|
|
29416
|
+
return;
|
|
29417
|
+
}
|
|
29263
29418
|
if (data.fatal) {
|
|
29264
29419
|
let errorInfo;
|
|
29265
29420
|
switch (data.type) {
|
|
@@ -29307,6 +29462,9 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29307
29462
|
});
|
|
29308
29463
|
hls.on(Events.ERROR, (event, data) => {
|
|
29309
29464
|
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
29465
|
+
if (maybeHandleR2Fallback(data)) {
|
|
29466
|
+
return;
|
|
29467
|
+
}
|
|
29310
29468
|
if (data.fatal) {
|
|
29311
29469
|
let errorInfo;
|
|
29312
29470
|
switch (data.type) {
|
|
@@ -29326,14 +29484,14 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29326
29484
|
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
29327
29485
|
}
|
|
29328
29486
|
});
|
|
29329
|
-
hls.loadSource(
|
|
29487
|
+
hls.loadSource(effectiveSrc);
|
|
29330
29488
|
hls.attachMedia(video);
|
|
29331
29489
|
} else {
|
|
29332
|
-
video.src =
|
|
29490
|
+
video.src = effectiveSrc;
|
|
29333
29491
|
}
|
|
29334
29492
|
}
|
|
29335
29493
|
} else {
|
|
29336
|
-
video.src =
|
|
29494
|
+
video.src = effectiveSrc;
|
|
29337
29495
|
}
|
|
29338
29496
|
const handleCanPlay = () => {
|
|
29339
29497
|
if (!hlsRef.current) {
|
|
@@ -29409,6 +29567,11 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29409
29567
|
eventCallbacksRef.current.onSeeked?.(player);
|
|
29410
29568
|
};
|
|
29411
29569
|
const handleError = () => {
|
|
29570
|
+
const currentSrc = video.currentSrc || video.src;
|
|
29571
|
+
if (isSafari() && currentSrc.includes("/api/clips/playlist/") && !currentSrc.includes("source=s3") && !r2FallbackAttemptedRef.current) {
|
|
29572
|
+
attemptS3Fallback("url", "native playback error", currentSrc);
|
|
29573
|
+
return;
|
|
29574
|
+
}
|
|
29412
29575
|
const error = video.error;
|
|
29413
29576
|
if (error) {
|
|
29414
29577
|
const errorInfo = {
|
|
@@ -29457,13 +29620,20 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29457
29620
|
video.removeEventListener("ratechange", handlePlaybackRateChange2);
|
|
29458
29621
|
};
|
|
29459
29622
|
}, [
|
|
29460
|
-
|
|
29623
|
+
effectiveSrc,
|
|
29461
29624
|
cleanupBlobUrl,
|
|
29462
29625
|
playerLikeObject,
|
|
29463
|
-
configVersion
|
|
29626
|
+
configVersion,
|
|
29627
|
+
supabase,
|
|
29628
|
+
attemptS3Fallback,
|
|
29629
|
+
maybeHandleR2Fallback,
|
|
29630
|
+
r2WorkerDomain
|
|
29464
29631
|
]);
|
|
29465
29632
|
useEffect(() => {
|
|
29466
|
-
|
|
29633
|
+
let cleanup;
|
|
29634
|
+
initializePlayer().then((cleanupFn) => {
|
|
29635
|
+
cleanup = cleanupFn;
|
|
29636
|
+
});
|
|
29467
29637
|
return () => {
|
|
29468
29638
|
cleanup?.();
|
|
29469
29639
|
if (hlsRef.current) {
|
|
@@ -29473,7 +29643,7 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29473
29643
|
cleanupBlobUrl();
|
|
29474
29644
|
setIsReady(false);
|
|
29475
29645
|
};
|
|
29476
|
-
}, [
|
|
29646
|
+
}, [effectiveSrc, initializePlayer, cleanupBlobUrl]);
|
|
29477
29647
|
useEffect(() => {
|
|
29478
29648
|
if (videoRef.current) {
|
|
29479
29649
|
if (autoplay) {
|
|
@@ -31670,6 +31840,30 @@ var FileManagerFilters = ({
|
|
|
31670
31840
|
if (node.type === "category" || node.type === "percentile-category") {
|
|
31671
31841
|
toggleExpanded(node.id);
|
|
31672
31842
|
onFilterChange(node.id);
|
|
31843
|
+
if (node.id === "fast-cycles") {
|
|
31844
|
+
trackCoreEvent("Fast Clips Clicked", {
|
|
31845
|
+
workspaceId,
|
|
31846
|
+
date,
|
|
31847
|
+
shift,
|
|
31848
|
+
count: node.count || 0,
|
|
31849
|
+
percentile: filterState.percentile
|
|
31850
|
+
});
|
|
31851
|
+
} else if (node.id === "slow-cycles") {
|
|
31852
|
+
trackCoreEvent("Slow Clips Clicked", {
|
|
31853
|
+
workspaceId,
|
|
31854
|
+
date,
|
|
31855
|
+
shift,
|
|
31856
|
+
count: node.count || 0,
|
|
31857
|
+
percentile: filterState.percentile
|
|
31858
|
+
});
|
|
31859
|
+
} else if (node.id === "cycle_completion") {
|
|
31860
|
+
trackCoreEvent("Cycle Completions Clicked", {
|
|
31861
|
+
workspaceId,
|
|
31862
|
+
date,
|
|
31863
|
+
shift,
|
|
31864
|
+
count: node.count || 0
|
|
31865
|
+
});
|
|
31866
|
+
}
|
|
31673
31867
|
if (node.id !== "idle_time" && idleLabelFilter) {
|
|
31674
31868
|
setIdleLabelFilter(null);
|
|
31675
31869
|
}
|
|
@@ -36279,7 +36473,16 @@ var STATIC_COLORS = {
|
|
|
36279
36473
|
"Operator Idle": "#8b5cf6"
|
|
36280
36474
|
// violet-500 - Low Priority/Behavioral
|
|
36281
36475
|
};
|
|
36476
|
+
var PRODUCTIVE_COLOR = "#00AB45";
|
|
36477
|
+
var IDLE_COLOR = "#e5e7eb";
|
|
36282
36478
|
var getColorForEntry = (name, index) => {
|
|
36479
|
+
const normalized = name.trim().toLowerCase();
|
|
36480
|
+
if (normalized === "productive" || normalized === "productive time") {
|
|
36481
|
+
return PRODUCTIVE_COLOR;
|
|
36482
|
+
}
|
|
36483
|
+
if (normalized === "idle" || normalized === "idle time") {
|
|
36484
|
+
return IDLE_COLOR;
|
|
36485
|
+
}
|
|
36283
36486
|
if (STATIC_COLORS[name]) {
|
|
36284
36487
|
return STATIC_COLORS[name];
|
|
36285
36488
|
}
|
|
@@ -36423,6 +36626,7 @@ var IdleTimeReasonChart = ({
|
|
|
36423
36626
|
)
|
|
36424
36627
|
] });
|
|
36425
36628
|
};
|
|
36629
|
+
var IdleTimeReasonChart_default = IdleTimeReasonChart;
|
|
36426
36630
|
var DEFAULT_PERFORMANCE_DATA = {
|
|
36427
36631
|
avg_efficiency: 0,
|
|
36428
36632
|
underperforming_workspaces: 0,
|
|
@@ -37184,11 +37388,6 @@ var LinePdfGenerator = ({
|
|
|
37184
37388
|
doc.setLineWidth(0.8);
|
|
37185
37389
|
doc.line(20, 118, 190, 118);
|
|
37186
37390
|
const hourlyOverviewStartY = 123;
|
|
37187
|
-
doc.setFontSize(18);
|
|
37188
|
-
doc.setFont("helvetica", "bold");
|
|
37189
|
-
doc.setTextColor(40, 40, 40);
|
|
37190
|
-
doc.text("Hourly Output Overview", 20, 133);
|
|
37191
|
-
doc.setTextColor(0, 0, 0);
|
|
37192
37391
|
const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
|
|
37193
37392
|
const [hours, minutes] = startTimeStr.split(":");
|
|
37194
37393
|
const startHour = parseInt(hours);
|
|
@@ -37379,23 +37578,43 @@ var LinePdfGenerator = ({
|
|
|
37379
37578
|
return Math.round(lineInfo.metrics.current_output / shiftDuration);
|
|
37380
37579
|
});
|
|
37381
37580
|
}
|
|
37382
|
-
const tableHeaderY =
|
|
37383
|
-
const tableStartY =
|
|
37581
|
+
const tableHeaderY = 146;
|
|
37582
|
+
const tableStartY = 153;
|
|
37384
37583
|
const rowSpacing = 8;
|
|
37385
37584
|
const bottomPadding = 8;
|
|
37386
37585
|
const hourlyTableHeight = hourlyTimeRanges.length * rowSpacing;
|
|
37387
37586
|
const backgroundHeight = tableStartY - hourlyOverviewStartY + hourlyTableHeight + bottomPadding;
|
|
37388
37587
|
doc.setFillColor(245, 245, 245);
|
|
37389
37588
|
doc.roundedRect(15, hourlyOverviewStartY, 180, backgroundHeight, 3, 3, "F");
|
|
37589
|
+
doc.setFontSize(18);
|
|
37590
|
+
doc.setFont("helvetica", "bold");
|
|
37591
|
+
doc.setTextColor(40, 40, 40);
|
|
37592
|
+
doc.text("Hourly Performance", 20, 133);
|
|
37593
|
+
doc.setTextColor(0, 0, 0);
|
|
37594
|
+
const gridTopY = 139;
|
|
37595
|
+
const headerBottomY = 148;
|
|
37596
|
+
const colBoundaries = [20, 70, 100, 130, 155, 190];
|
|
37597
|
+
const totalRows = hourlyTimeRanges.length;
|
|
37598
|
+
const gridBottomY = headerBottomY + totalRows * rowSpacing;
|
|
37599
|
+
const tableHeight = gridBottomY - gridTopY;
|
|
37600
|
+
doc.setFillColor(255, 255, 255);
|
|
37601
|
+
doc.roundedRect(20, gridTopY, 170, tableHeight, 2, 2, "F");
|
|
37602
|
+
doc.setDrawColor(230, 230, 230);
|
|
37603
|
+
doc.setLineWidth(0.2);
|
|
37604
|
+
doc.roundedRect(20, gridTopY, 170, tableHeight, 2, 2, "S");
|
|
37605
|
+
doc.setDrawColor(200, 200, 200);
|
|
37606
|
+
doc.setLineWidth(0.3);
|
|
37607
|
+
doc.line(20, headerBottomY, 190, headerBottomY);
|
|
37608
|
+
colBoundaries.slice(1, -1).forEach((x) => {
|
|
37609
|
+
doc.line(x, gridTopY, x, gridBottomY);
|
|
37610
|
+
});
|
|
37390
37611
|
doc.setFontSize(11);
|
|
37391
37612
|
doc.setFont("helvetica", "bold");
|
|
37392
37613
|
doc.text("Time Range", 25, tableHeaderY);
|
|
37393
|
-
doc.text("Output",
|
|
37394
|
-
doc.text("Target",
|
|
37395
|
-
doc.text("Status",
|
|
37396
|
-
doc.
|
|
37397
|
-
doc.setDrawColor(200, 200, 200);
|
|
37398
|
-
doc.line(20, 146, 190, 146);
|
|
37614
|
+
doc.text("Output", 75, tableHeaderY);
|
|
37615
|
+
doc.text("Target", 105, tableHeaderY);
|
|
37616
|
+
doc.text("Status", 135, tableHeaderY);
|
|
37617
|
+
doc.text("Remarks", 160, tableHeaderY);
|
|
37399
37618
|
doc.setFont("helvetica", "normal");
|
|
37400
37619
|
let yPos = tableStartY;
|
|
37401
37620
|
const lineDateForTable = new Date(lineInfo.date);
|
|
@@ -37417,20 +37636,25 @@ var LinePdfGenerator = ({
|
|
|
37417
37636
|
const dataCollected = !isTodayForTable || hourNumber < currentHour;
|
|
37418
37637
|
const outputStr = dataCollected ? actualOutput.toString() : "TBD";
|
|
37419
37638
|
const targetStr = targetOutputPerHour.toString();
|
|
37639
|
+
if (index < totalRows - 1) {
|
|
37640
|
+
const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
|
|
37641
|
+
doc.setDrawColor(200, 200, 200);
|
|
37642
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
37643
|
+
}
|
|
37420
37644
|
doc.text(timeRange, 25, yPos);
|
|
37421
|
-
doc.text(outputStr,
|
|
37422
|
-
doc.text(targetStr,
|
|
37645
|
+
doc.text(outputStr, 75, yPos);
|
|
37646
|
+
doc.text(targetStr, 105, yPos);
|
|
37423
37647
|
if (!dataCollected) {
|
|
37424
37648
|
doc.setTextColor(100, 100, 100);
|
|
37425
|
-
doc.text("-",
|
|
37649
|
+
doc.text("-", 135, yPos);
|
|
37426
37650
|
} else if (actualOutput >= targetOutputPerHour) {
|
|
37427
37651
|
doc.setTextColor(0, 171, 69);
|
|
37428
37652
|
doc.setFont("ZapfDingbats", "normal");
|
|
37429
|
-
doc.text("4",
|
|
37653
|
+
doc.text("4", 135, yPos);
|
|
37430
37654
|
doc.setFont("helvetica", "normal");
|
|
37431
37655
|
} else {
|
|
37432
37656
|
doc.setTextColor(227, 67, 41);
|
|
37433
|
-
doc.text("\xD7",
|
|
37657
|
+
doc.text("\xD7", 135, yPos);
|
|
37434
37658
|
}
|
|
37435
37659
|
doc.setTextColor(0, 0, 0);
|
|
37436
37660
|
yPos += rowSpacing;
|
|
@@ -37438,7 +37662,7 @@ var LinePdfGenerator = ({
|
|
|
37438
37662
|
doc.addPage();
|
|
37439
37663
|
yPos = addHeaderPage2();
|
|
37440
37664
|
const workspaceSectionStartY = yPos;
|
|
37441
|
-
const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0,
|
|
37665
|
+
const sortedWorkspaces = [...workspaceData].sort((a, b) => (a.efficiency || 0) - (b.efficiency || 0)).slice(0, 3);
|
|
37442
37666
|
const wsRowCount = sortedWorkspaces.length > 0 ? sortedWorkspaces.length : 1;
|
|
37443
37667
|
const wsTableHeight = 10 + 8 + 7 + wsRowCount * 8 + 8;
|
|
37444
37668
|
doc.setFillColor(245, 245, 245);
|
|
@@ -37446,28 +37670,45 @@ var LinePdfGenerator = ({
|
|
|
37446
37670
|
doc.setFontSize(18);
|
|
37447
37671
|
doc.setFont("helvetica", "bold");
|
|
37448
37672
|
doc.setTextColor(40, 40, 40);
|
|
37449
|
-
doc.text("Poorest Performing Workspaces", 20, yPos);
|
|
37673
|
+
doc.text("Poorest Performing Workspaces", 20, yPos + 10);
|
|
37450
37674
|
doc.setTextColor(0, 0, 0);
|
|
37451
|
-
yPos +=
|
|
37675
|
+
yPos += 20;
|
|
37676
|
+
const wsGridTopY = yPos - 5;
|
|
37677
|
+
const wsHeaderBottomY = wsGridTopY + 8;
|
|
37678
|
+
const wsColBoundaries = [20, 80, 140, 190];
|
|
37679
|
+
const wsGridBottomY = wsHeaderBottomY + wsRowCount * 8;
|
|
37680
|
+
const wsTableTotalHeight = wsGridBottomY - wsGridTopY;
|
|
37681
|
+
doc.setFillColor(255, 255, 255);
|
|
37682
|
+
doc.roundedRect(20, wsGridTopY, 170, wsTableTotalHeight, 2, 2, "F");
|
|
37683
|
+
doc.setDrawColor(230, 230, 230);
|
|
37684
|
+
doc.setLineWidth(0.2);
|
|
37685
|
+
doc.roundedRect(20, wsGridTopY, 170, wsTableTotalHeight, 2, 2, "S");
|
|
37686
|
+
doc.setDrawColor(200, 200, 200);
|
|
37687
|
+
doc.setLineWidth(0.3);
|
|
37688
|
+
doc.line(20, wsHeaderBottomY, 190, wsHeaderBottomY);
|
|
37689
|
+
wsColBoundaries.slice(1, -1).forEach((x) => {
|
|
37690
|
+
doc.line(x, wsGridTopY, x, wsGridBottomY);
|
|
37691
|
+
});
|
|
37452
37692
|
doc.setFontSize(11);
|
|
37453
37693
|
doc.setFont("helvetica", "bold");
|
|
37454
|
-
yPos
|
|
37694
|
+
yPos = wsGridTopY + 5.5;
|
|
37455
37695
|
doc.text("Workspace", 25, yPos);
|
|
37456
37696
|
doc.text("Current/Target", 85, yPos);
|
|
37457
37697
|
doc.text("Efficiency", 145, yPos);
|
|
37458
|
-
yPos += 3;
|
|
37459
|
-
doc.setLineWidth(0.3);
|
|
37460
|
-
doc.setDrawColor(200, 200, 200);
|
|
37461
|
-
doc.line(20, yPos, 190, yPos);
|
|
37462
37698
|
doc.setFont("helvetica", "normal");
|
|
37463
|
-
yPos
|
|
37699
|
+
yPos = wsHeaderBottomY + 5.5;
|
|
37464
37700
|
if (sortedWorkspaces.length === 0) {
|
|
37465
37701
|
doc.text("No workspace data available", 25, yPos);
|
|
37466
|
-
yPos +=
|
|
37702
|
+
yPos += 8;
|
|
37467
37703
|
} else {
|
|
37468
37704
|
sortedWorkspaces.forEach((ws, index) => {
|
|
37469
37705
|
const workspaceName = getWorkspaceDisplayName(ws.workspace_name, lineInfo.line_id);
|
|
37470
37706
|
const truncatedName = workspaceName.length > 25 ? workspaceName.substring(0, 22) + "..." : workspaceName;
|
|
37707
|
+
if (index < wsRowCount - 1) {
|
|
37708
|
+
const rowBottomY = wsHeaderBottomY + (index + 1) * 8;
|
|
37709
|
+
doc.setDrawColor(200, 200, 200);
|
|
37710
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
37711
|
+
}
|
|
37471
37712
|
doc.text(truncatedName, 25, yPos);
|
|
37472
37713
|
doc.text(`${ws.action_count || 0} / ${ws.action_threshold || 0}`, 85, yPos);
|
|
37473
37714
|
doc.text(`${(ws.efficiency || 0).toFixed(1)}%`, 145, yPos);
|
|
@@ -38810,18 +39051,27 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons }) => {
|
|
|
38810
39051
|
const hourlyTitleY = hasIdleTimeReason ? 198 : 188;
|
|
38811
39052
|
doc.text("Hourly Performance", 20, hourlyTitleY);
|
|
38812
39053
|
doc.setTextColor(0, 0, 0);
|
|
38813
|
-
doc.setFontSize(headerFontSize);
|
|
38814
|
-
doc.setFont("helvetica", "bold");
|
|
38815
39054
|
const baseHeaderY = hasIdleTimeReason ? 208 : 198;
|
|
38816
39055
|
const headerY = titleFontSize === 16 ? hasIdleTimeReason ? 206 : 196 : baseHeaderY;
|
|
38817
|
-
|
|
38818
|
-
|
|
38819
|
-
|
|
38820
|
-
|
|
38821
|
-
|
|
39056
|
+
const gridTopY = headerY - 5;
|
|
39057
|
+
const headerBottomY = gridTopY + 8;
|
|
39058
|
+
const colBoundaries = [20, 70, 100, 130, 155, 190];
|
|
39059
|
+
const totalRows = hourlyData.length;
|
|
39060
|
+
const gridBottomY = headerBottomY + totalRows * rowHeight;
|
|
38822
39061
|
doc.setDrawColor(200, 200, 200);
|
|
38823
|
-
|
|
38824
|
-
doc.line(20,
|
|
39062
|
+
doc.setLineWidth(0.3);
|
|
39063
|
+
doc.line(20, gridTopY, 190, gridTopY);
|
|
39064
|
+
doc.line(20, headerBottomY, 190, headerBottomY);
|
|
39065
|
+
colBoundaries.forEach((x) => {
|
|
39066
|
+
doc.line(x, gridTopY, x, gridBottomY);
|
|
39067
|
+
});
|
|
39068
|
+
doc.setFontSize(headerFontSize);
|
|
39069
|
+
doc.setFont("helvetica", "bold");
|
|
39070
|
+
doc.text("Time Range", 25, headerY);
|
|
39071
|
+
doc.text("Output", 75, headerY);
|
|
39072
|
+
doc.text("Target", 105, headerY);
|
|
39073
|
+
doc.text("Status", 135, headerY);
|
|
39074
|
+
doc.text("Remarks", 160, headerY);
|
|
38825
39075
|
doc.setFontSize(contentFontSize);
|
|
38826
39076
|
doc.setFont("helvetica", "normal");
|
|
38827
39077
|
let yPos = tableStartY;
|
|
@@ -38852,20 +39102,23 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons }) => {
|
|
|
38852
39102
|
const dataCollected = !isToday2 || hourNumber < currentHour;
|
|
38853
39103
|
const outputStr = dataCollected ? output.toString() : "TBD";
|
|
38854
39104
|
const targetStr = hourlyTarget.toString();
|
|
39105
|
+
const rowBottomY = headerBottomY + (index + 1) * rowHeight;
|
|
39106
|
+
doc.setDrawColor(200, 200, 200);
|
|
39107
|
+
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
38855
39108
|
doc.text(timeRange, 25, yPos);
|
|
38856
|
-
doc.text(outputStr,
|
|
38857
|
-
doc.text(targetStr,
|
|
39109
|
+
doc.text(outputStr, 75, yPos);
|
|
39110
|
+
doc.text(targetStr, 105, yPos);
|
|
38858
39111
|
if (!dataCollected) {
|
|
38859
39112
|
doc.setTextColor(100, 100, 100);
|
|
38860
|
-
doc.text("-",
|
|
39113
|
+
doc.text("-", 135, yPos);
|
|
38861
39114
|
} else if (output >= hourlyTarget) {
|
|
38862
39115
|
doc.setTextColor(0, 171, 69);
|
|
38863
39116
|
doc.setFont("ZapfDingbats", "normal");
|
|
38864
|
-
doc.text("4",
|
|
39117
|
+
doc.text("4", 135, yPos);
|
|
38865
39118
|
doc.setFont("helvetica", "normal");
|
|
38866
39119
|
} else {
|
|
38867
39120
|
doc.setTextColor(227, 67, 41);
|
|
38868
|
-
doc.text("\xD7",
|
|
39121
|
+
doc.text("\xD7", 135, yPos);
|
|
38869
39122
|
}
|
|
38870
39123
|
doc.setTextColor(0, 0, 0);
|
|
38871
39124
|
yPos += rowHeight;
|
|
@@ -39671,14 +39924,16 @@ var KPICard = ({
|
|
|
39671
39924
|
"uppercase tracking-wide sm:tracking-wider"
|
|
39672
39925
|
), children: title }),
|
|
39673
39926
|
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-0.5 sm:gap-2 flex-wrap", children: [
|
|
39674
|
-
/* @__PURE__ */ jsx("
|
|
39675
|
-
"
|
|
39676
|
-
|
|
39677
|
-
|
|
39678
|
-
|
|
39679
|
-
"
|
|
39680
|
-
|
|
39681
|
-
|
|
39927
|
+
isLoading ? /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 md:h-7 w-12 sm:w-16 md:w-20 bg-gray-200 dark:bg-gray-700 rounded animate-pulse" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
39928
|
+
/* @__PURE__ */ jsx("span", { className: clsx(
|
|
39929
|
+
"font-bold text-gray-900 dark:text-gray-50",
|
|
39930
|
+
"text-base sm:text-xl md:text-2xl"
|
|
39931
|
+
), children: formattedValue }),
|
|
39932
|
+
suffix && /* @__PURE__ */ jsx("span", { className: clsx(
|
|
39933
|
+
"font-medium text-gray-600 dark:text-gray-300",
|
|
39934
|
+
style.compact ? "text-base" : "text-lg"
|
|
39935
|
+
), children: suffix })
|
|
39936
|
+
] }),
|
|
39682
39937
|
!isLoading && trendInfo.shouldShowTrend && trendInfo.trendValue !== "neutral" && title !== "Output" && /* @__PURE__ */ jsx("span", { className: trendInfo.colorClass, children: /* @__PURE__ */ jsx(
|
|
39683
39938
|
trendInfo.Icon,
|
|
39684
39939
|
{
|
|
@@ -39837,6 +40092,7 @@ var KPIHeader = ({
|
|
|
39837
40092
|
};
|
|
39838
40093
|
var KPISection = memo(({
|
|
39839
40094
|
kpis,
|
|
40095
|
+
isLoading = false,
|
|
39840
40096
|
className,
|
|
39841
40097
|
layout: layout2 = "row",
|
|
39842
40098
|
gridColumns,
|
|
@@ -39845,7 +40101,8 @@ var KPISection = memo(({
|
|
|
39845
40101
|
compactCards = false,
|
|
39846
40102
|
useSrcLayout = false
|
|
39847
40103
|
}) => {
|
|
39848
|
-
const
|
|
40104
|
+
const showSkeleton = isLoading || !kpis;
|
|
40105
|
+
const outputDifference = kpis ? kpis.outputProgress.current - kpis.outputProgress.idealOutput : 0;
|
|
39849
40106
|
const outputIsOnTarget = outputDifference >= 0;
|
|
39850
40107
|
if (useSrcLayout) {
|
|
39851
40108
|
return /* @__PURE__ */ jsxs(
|
|
@@ -39862,27 +40119,30 @@ var KPISection = memo(({
|
|
|
39862
40119
|
KPICard,
|
|
39863
40120
|
{
|
|
39864
40121
|
title: "Underperforming",
|
|
39865
|
-
value: "2/3",
|
|
39866
|
-
change: 0
|
|
40122
|
+
value: showSkeleton ? "" : "2/3",
|
|
40123
|
+
change: 0,
|
|
40124
|
+
isLoading: showSkeleton
|
|
39867
40125
|
}
|
|
39868
40126
|
) }),
|
|
39869
40127
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
39870
40128
|
KPICard,
|
|
39871
40129
|
{
|
|
39872
40130
|
title: "Efficiency",
|
|
39873
|
-
value: kpis.efficiency.value,
|
|
39874
|
-
change: kpis.efficiency.change,
|
|
39875
|
-
suffix: "%"
|
|
40131
|
+
value: showSkeleton ? "" : kpis.efficiency.value,
|
|
40132
|
+
change: showSkeleton ? 0 : kpis.efficiency.change,
|
|
40133
|
+
suffix: "%",
|
|
40134
|
+
isLoading: showSkeleton
|
|
39876
40135
|
}
|
|
39877
40136
|
) }),
|
|
39878
40137
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
39879
40138
|
KPICard,
|
|
39880
40139
|
{
|
|
39881
40140
|
title: "Output Progress",
|
|
39882
|
-
value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
39883
|
-
change: kpis.outputProgress.change,
|
|
40141
|
+
value: showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
40142
|
+
change: showSkeleton ? 0 : kpis.outputProgress.change,
|
|
39884
40143
|
outputDifference,
|
|
39885
|
-
showOutputDetails:
|
|
40144
|
+
showOutputDetails: !showSkeleton,
|
|
40145
|
+
isLoading: showSkeleton
|
|
39886
40146
|
}
|
|
39887
40147
|
) })
|
|
39888
40148
|
]
|
|
@@ -39893,34 +40153,30 @@ var KPISection = memo(({
|
|
|
39893
40153
|
{
|
|
39894
40154
|
key: "underperforming",
|
|
39895
40155
|
title: "Underperforming",
|
|
39896
|
-
value: `${kpis.underperformingWorkers.current}/${kpis.underperformingWorkers.total}`,
|
|
39897
|
-
change: kpis.underperformingWorkers.change
|
|
40156
|
+
value: showSkeleton ? "" : `${kpis.underperformingWorkers.current}/${kpis.underperformingWorkers.total}`,
|
|
40157
|
+
change: showSkeleton ? 0 : kpis.underperformingWorkers.change,
|
|
40158
|
+
suffix: void 0,
|
|
40159
|
+
status: void 0
|
|
39898
40160
|
},
|
|
39899
40161
|
{
|
|
39900
40162
|
key: "efficiency",
|
|
39901
40163
|
title: "Efficiency",
|
|
39902
|
-
value: kpis.efficiency.value,
|
|
39903
|
-
change: kpis.efficiency.change,
|
|
39904
|
-
suffix: "%"
|
|
40164
|
+
value: showSkeleton ? "" : kpis.efficiency.value,
|
|
40165
|
+
change: showSkeleton ? 0 : kpis.efficiency.change,
|
|
40166
|
+
suffix: "%",
|
|
40167
|
+
status: void 0
|
|
39905
40168
|
},
|
|
39906
40169
|
{
|
|
39907
40170
|
key: "outputProgress",
|
|
39908
40171
|
title: "Output Progress",
|
|
39909
|
-
value: `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
39910
|
-
change: kpis.outputProgress.change,
|
|
39911
|
-
|
|
40172
|
+
value: showSkeleton ? "" : `${kpis.outputProgress.current}/${kpis.outputProgress.target}`,
|
|
40173
|
+
change: showSkeleton ? 0 : kpis.outputProgress.change,
|
|
40174
|
+
suffix: void 0,
|
|
40175
|
+
status: showSkeleton ? void 0 : {
|
|
39912
40176
|
tooltipText: outputIsOnTarget ? "On Target" : "Off Target",
|
|
39913
40177
|
positive: outputIsOnTarget
|
|
39914
40178
|
}
|
|
39915
40179
|
}
|
|
39916
|
-
// Only include these additional KPIs if not using the src layout
|
|
39917
|
-
// ...(kpis.qualityCompliance ? [{
|
|
39918
|
-
// key: 'qualityCompliance',
|
|
39919
|
-
// title: 'Quality Compliance',
|
|
39920
|
-
// value: kpis.qualityCompliance.value,
|
|
39921
|
-
// change: kpis.qualityCompliance.change,
|
|
39922
|
-
// suffix: '%',
|
|
39923
|
-
// }] : []),
|
|
39924
40180
|
];
|
|
39925
40181
|
return /* @__PURE__ */ jsx(
|
|
39926
40182
|
KPIGrid,
|
|
@@ -39937,7 +40193,8 @@ var KPISection = memo(({
|
|
|
39937
40193
|
change: kpi.change,
|
|
39938
40194
|
suffix: kpi.suffix,
|
|
39939
40195
|
status: kpi.status,
|
|
39940
|
-
style: { variant: cardVariant, compact: compactCards }
|
|
40196
|
+
style: { variant: cardVariant, compact: compactCards },
|
|
40197
|
+
isLoading: showSkeleton
|
|
39941
40198
|
},
|
|
39942
40199
|
kpi.key
|
|
39943
40200
|
))
|
|
@@ -39946,6 +40203,9 @@ var KPISection = memo(({
|
|
|
39946
40203
|
}, (prevProps, nextProps) => {
|
|
39947
40204
|
const prevKpis = prevProps.kpis;
|
|
39948
40205
|
const nextKpis = nextProps.kpis;
|
|
40206
|
+
if (prevProps.isLoading !== nextProps.isLoading) return false;
|
|
40207
|
+
if (!prevKpis && !nextKpis) return true;
|
|
40208
|
+
if (!prevKpis || !nextKpis) return false;
|
|
39949
40209
|
if (prevKpis === nextKpis) return true;
|
|
39950
40210
|
if (Math.abs(prevKpis.efficiency.value - nextKpis.efficiency.value) >= 0.5) return false;
|
|
39951
40211
|
if (prevKpis.underperformingWorkers.current !== nextKpis.underperformingWorkers.current || prevKpis.underperformingWorkers.total !== nextKpis.underperformingWorkers.total) return false;
|
|
@@ -41284,6 +41544,17 @@ var SideNavBar = memo(({
|
|
|
41284
41544
|
});
|
|
41285
41545
|
onMobileMenuClose?.();
|
|
41286
41546
|
}, [navigate, onMobileMenuClose]);
|
|
41547
|
+
const handleImprovementClick = useCallback(() => {
|
|
41548
|
+
navigate("/improvement-center", {
|
|
41549
|
+
trackingEvent: {
|
|
41550
|
+
name: "Improvement Center Clicked",
|
|
41551
|
+
properties: {
|
|
41552
|
+
source: "side_nav"
|
|
41553
|
+
}
|
|
41554
|
+
}
|
|
41555
|
+
});
|
|
41556
|
+
onMobileMenuClose?.();
|
|
41557
|
+
}, [navigate, onMobileMenuClose]);
|
|
41287
41558
|
const handleTargetsClick = useCallback(() => {
|
|
41288
41559
|
navigate("/targets", {
|
|
41289
41560
|
trackingEvent: {
|
|
@@ -41401,6 +41672,7 @@ var SideNavBar = memo(({
|
|
|
41401
41672
|
const homeButtonClasses = useMemo(() => getButtonClasses("/"), [getButtonClasses, pathname]);
|
|
41402
41673
|
const leaderboardButtonClasses = useMemo(() => getButtonClasses("/leaderboard"), [getButtonClasses, pathname]);
|
|
41403
41674
|
const kpisButtonClasses = useMemo(() => getButtonClasses("/kpis"), [getButtonClasses, pathname]);
|
|
41675
|
+
const improvementButtonClasses = useMemo(() => getButtonClasses("/improvement-center"), [getButtonClasses, pathname]);
|
|
41404
41676
|
const targetsButtonClasses = useMemo(() => getButtonClasses("/targets"), [getButtonClasses, pathname]);
|
|
41405
41677
|
const shiftsButtonClasses = useMemo(() => getButtonClasses("/shifts"), [getButtonClasses, pathname]);
|
|
41406
41678
|
const teamManagementButtonClasses = useMemo(() => getButtonClasses("/team-management"), [getButtonClasses, pathname]);
|
|
@@ -41468,6 +41740,22 @@ var SideNavBar = memo(({
|
|
|
41468
41740
|
]
|
|
41469
41741
|
}
|
|
41470
41742
|
),
|
|
41743
|
+
/* @__PURE__ */ jsxs(
|
|
41744
|
+
"button",
|
|
41745
|
+
{
|
|
41746
|
+
onClick: handleImprovementClick,
|
|
41747
|
+
className: improvementButtonClasses,
|
|
41748
|
+
"aria-label": "Improvement Center",
|
|
41749
|
+
tabIndex: 0,
|
|
41750
|
+
role: "tab",
|
|
41751
|
+
"aria-selected": pathname === "/improvement-center" || pathname.startsWith("/improvement-center/"),
|
|
41752
|
+
children: [
|
|
41753
|
+
/* @__PURE__ */ jsx(LightBulbIcon, { className: "w-5 h-5 mb-1" }),
|
|
41754
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight text-center", children: "Improvement" }),
|
|
41755
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px] font-semibold text-orange-500 uppercase tracking-wide bg-orange-50 px-1.5 py-0.5 rounded", children: "Beta" })
|
|
41756
|
+
]
|
|
41757
|
+
}
|
|
41758
|
+
),
|
|
41471
41759
|
/* @__PURE__ */ jsxs(
|
|
41472
41760
|
"button",
|
|
41473
41761
|
{
|
|
@@ -41668,6 +41956,19 @@ var SideNavBar = memo(({
|
|
|
41668
41956
|
]
|
|
41669
41957
|
}
|
|
41670
41958
|
),
|
|
41959
|
+
/* @__PURE__ */ jsxs(
|
|
41960
|
+
"button",
|
|
41961
|
+
{
|
|
41962
|
+
onClick: handleMobileNavClick(handleImprovementClick),
|
|
41963
|
+
className: getMobileButtonClass("/improvement-center"),
|
|
41964
|
+
"aria-label": "Improvement Center",
|
|
41965
|
+
children: [
|
|
41966
|
+
/* @__PURE__ */ jsx(LightBulbIcon, { className: getIconClass("/improvement-center") }),
|
|
41967
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-medium", children: "Improvement" }),
|
|
41968
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-[10px] font-semibold text-orange-500 uppercase tracking-wide bg-orange-50 px-1.5 py-0.5 rounded", children: "Beta" })
|
|
41969
|
+
]
|
|
41970
|
+
}
|
|
41971
|
+
),
|
|
41671
41972
|
/* @__PURE__ */ jsxs(
|
|
41672
41973
|
"button",
|
|
41673
41974
|
{
|
|
@@ -45698,7 +45999,8 @@ var TeamUsagePdfGenerator = ({
|
|
|
45698
45999
|
usageData,
|
|
45699
46000
|
daysInRange = 1,
|
|
45700
46001
|
title = "Team Usage Report",
|
|
45701
|
-
className
|
|
46002
|
+
className,
|
|
46003
|
+
iconOnly = false
|
|
45702
46004
|
}) => {
|
|
45703
46005
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
45704
46006
|
const generatePDF = async () => {
|
|
@@ -45878,10 +46180,11 @@ var TeamUsagePdfGenerator = ({
|
|
|
45878
46180
|
{
|
|
45879
46181
|
onClick: generatePDF,
|
|
45880
46182
|
disabled: isGenerating || !usageData,
|
|
45881
|
-
className: `inline-flex items-center gap-2 rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed ${className || ""}`,
|
|
46183
|
+
className: `inline-flex items-center gap-2 rounded-lg bg-gray-100 ${iconOnly ? "p-2" : "px-4 py-2"} text-sm font-medium text-gray-700 hover:bg-gray-200 active:bg-gray-300 transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${className || ""}`,
|
|
46184
|
+
"aria-label": iconOnly ? isGenerating ? "Generating PDF..." : "Export PDF" : void 0,
|
|
45882
46185
|
children: [
|
|
45883
|
-
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
|
|
45884
|
-
isGenerating ? "Generating..." : "Export PDF"
|
|
46186
|
+
/* @__PURE__ */ jsx(Download, { className: iconOnly ? "h-5 w-5" : "h-4 w-4" }),
|
|
46187
|
+
!iconOnly && (isGenerating ? "Generating..." : "Export PDF")
|
|
45885
46188
|
]
|
|
45886
46189
|
}
|
|
45887
46190
|
);
|
|
@@ -48650,10 +48953,19 @@ function HomeView({
|
|
|
48650
48953
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
48651
48954
|
const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
|
|
48652
48955
|
const [hasInitialDataLoaded, setHasInitialDataLoaded] = useState(false);
|
|
48956
|
+
const KPI_CACHE_KEY = "optifye_kpi_cache";
|
|
48653
48957
|
const dashboardConfig = useDashboardConfig();
|
|
48654
48958
|
const entityConfig = useEntityConfig();
|
|
48655
48959
|
const supabaseClient = useSupabaseClient();
|
|
48656
48960
|
const { user } = useAuth();
|
|
48961
|
+
const { lines: dbLines } = useLines();
|
|
48962
|
+
const mergedLineNames = useMemo(() => {
|
|
48963
|
+
const merged = { ...lineNames };
|
|
48964
|
+
dbLines.forEach((line) => {
|
|
48965
|
+
merged[line.id] = line.line_name;
|
|
48966
|
+
});
|
|
48967
|
+
return merged;
|
|
48968
|
+
}, [lineNames, dbLines]);
|
|
48657
48969
|
const isSupervisor = user?.role_level === "supervisor";
|
|
48658
48970
|
const hasMultipleLines = allLineIds.length > 1;
|
|
48659
48971
|
const availableLineIds = useMemo(() => {
|
|
@@ -48760,6 +49072,42 @@ function HomeView({
|
|
|
48760
49072
|
}
|
|
48761
49073
|
return buildKPIsFromLineMetricsRow(row);
|
|
48762
49074
|
}, [selectedLineId, factoryViewId, lineMetrics, metricsLoading]);
|
|
49075
|
+
const [cachedKpis, setCachedKpis] = useState(() => {
|
|
49076
|
+
if (typeof window === "undefined") return null;
|
|
49077
|
+
try {
|
|
49078
|
+
const cached = localStorage.getItem(KPI_CACHE_KEY);
|
|
49079
|
+
if (cached) {
|
|
49080
|
+
const parsed = JSON.parse(cached);
|
|
49081
|
+
return parsed[selectedLineId] || parsed[factoryViewId] || null;
|
|
49082
|
+
}
|
|
49083
|
+
} catch (e) {
|
|
49084
|
+
}
|
|
49085
|
+
return null;
|
|
49086
|
+
});
|
|
49087
|
+
useEffect(() => {
|
|
49088
|
+
if (kpis && typeof window !== "undefined") {
|
|
49089
|
+
try {
|
|
49090
|
+
const existing = localStorage.getItem(KPI_CACHE_KEY);
|
|
49091
|
+
const cache = existing ? JSON.parse(existing) : {};
|
|
49092
|
+
cache[selectedLineId] = kpis;
|
|
49093
|
+
localStorage.setItem(KPI_CACHE_KEY, JSON.stringify(cache));
|
|
49094
|
+
setCachedKpis(kpis);
|
|
49095
|
+
} catch (e) {
|
|
49096
|
+
}
|
|
49097
|
+
}
|
|
49098
|
+
}, [kpis, selectedLineId, KPI_CACHE_KEY]);
|
|
49099
|
+
useEffect(() => {
|
|
49100
|
+
if (typeof window === "undefined") return;
|
|
49101
|
+
try {
|
|
49102
|
+
const cached = localStorage.getItem(KPI_CACHE_KEY);
|
|
49103
|
+
if (cached) {
|
|
49104
|
+
const parsed = JSON.parse(cached);
|
|
49105
|
+
setCachedKpis(parsed[selectedLineId] || null);
|
|
49106
|
+
}
|
|
49107
|
+
} catch (e) {
|
|
49108
|
+
}
|
|
49109
|
+
}, [selectedLineId, KPI_CACHE_KEY]);
|
|
49110
|
+
const displayKpis = kpis || cachedKpis;
|
|
48763
49111
|
const {
|
|
48764
49112
|
activeBreaks: allActiveBreaks,
|
|
48765
49113
|
isLoading: breaksLoading,
|
|
@@ -49061,16 +49409,16 @@ function HomeView({
|
|
|
49061
49409
|
// Use stable string representation instead of spreading array
|
|
49062
49410
|
JSON.stringify(workspaceMetrics.map((w) => `${w.workspace_uuid}-${Math.round(w.efficiency)}-${w.trend}`))
|
|
49063
49411
|
]);
|
|
49064
|
-
const memoizedKPIs = useMemo(() =>
|
|
49412
|
+
const memoizedKPIs = useMemo(() => displayKpis, [
|
|
49065
49413
|
// Only update reference when values change by at least 1%
|
|
49066
|
-
|
|
49067
|
-
|
|
49068
|
-
|
|
49069
|
-
|
|
49070
|
-
|
|
49071
|
-
|
|
49414
|
+
displayKpis?.efficiency?.value ? Math.round(displayKpis.efficiency.value) : null,
|
|
49415
|
+
displayKpis?.underperformingWorkers?.current,
|
|
49416
|
+
displayKpis?.underperformingWorkers?.total,
|
|
49417
|
+
displayKpis?.outputProgress?.current,
|
|
49418
|
+
displayKpis?.outputProgress?.target,
|
|
49419
|
+
displayKpis?.avgCycleTime?.value ? Math.round(displayKpis.avgCycleTime.value * 10) / 10 : null,
|
|
49072
49420
|
// Round to 1 decimal
|
|
49073
|
-
|
|
49421
|
+
displayKpis?.qualityCompliance?.value ? Math.round(displayKpis.qualityCompliance.value) : null
|
|
49074
49422
|
]);
|
|
49075
49423
|
useEffect(() => {
|
|
49076
49424
|
setIsHydrated(true);
|
|
@@ -49088,7 +49436,7 @@ function HomeView({
|
|
|
49088
49436
|
trackCoreEvent("Home Line Filter Changed", {
|
|
49089
49437
|
previous_line_id: selectedLineId,
|
|
49090
49438
|
new_line_id: value,
|
|
49091
|
-
line_name:
|
|
49439
|
+
line_name: mergedLineNames[value] || (value === factoryViewId ? "All Lines" : `Line ${value.substring(0, 4)}`)
|
|
49092
49440
|
});
|
|
49093
49441
|
try {
|
|
49094
49442
|
sessionStorage.setItem(LINE_FILTER_STORAGE_KEY, value);
|
|
@@ -49122,9 +49470,9 @@ function HomeView({
|
|
|
49122
49470
|
}
|
|
49123
49471
|
return /* @__PURE__ */ jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
|
|
49124
49472
|
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 sm:h-9 text-xs sm:text-sm px-2 sm:px-3", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a line" }) }),
|
|
49125
|
-
/* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children:
|
|
49473
|
+
/* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: mergedLineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
|
|
49126
49474
|
] });
|
|
49127
|
-
}, [availableLineIds, handleLineChange, selectedLineId,
|
|
49475
|
+
}, [availableLineIds, handleLineChange, selectedLineId, mergedLineNames, factoryViewId, allLineIds.length]);
|
|
49128
49476
|
const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized;
|
|
49129
49477
|
const isDataLoading = metricsLoading;
|
|
49130
49478
|
if (isInitialLoading) {
|
|
@@ -49153,7 +49501,7 @@ function HomeView({
|
|
|
49153
49501
|
lineTitle,
|
|
49154
49502
|
lineId: selectedLineId === factoryViewId ? allLineIds[0] : selectedLineId,
|
|
49155
49503
|
className: "w-full",
|
|
49156
|
-
headerControls:
|
|
49504
|
+
headerControls: /* @__PURE__ */ jsx(KPISection2, { kpis: memoizedKPIs, isLoading: !memoizedKPIs, className: "w-full sm:w-auto" })
|
|
49157
49505
|
}
|
|
49158
49506
|
) }) }),
|
|
49159
49507
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
|
|
@@ -56442,35 +56790,62 @@ var TeamManagementView = ({
|
|
|
56442
56790
|
) }) });
|
|
56443
56791
|
}
|
|
56444
56792
|
return /* @__PURE__ */ jsxs("div", { className: cn("min-h-screen bg-slate-50", className), children: [
|
|
56445
|
-
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm", children: /* @__PURE__ */
|
|
56446
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
56447
|
-
|
|
56448
|
-
/* @__PURE__ */ jsx("
|
|
56449
|
-
/* @__PURE__ */
|
|
56450
|
-
|
|
56451
|
-
|
|
56452
|
-
|
|
56453
|
-
|
|
56454
|
-
|
|
56455
|
-
|
|
56456
|
-
|
|
56457
|
-
|
|
56458
|
-
|
|
56459
|
-
|
|
56460
|
-
|
|
56461
|
-
|
|
56462
|
-
|
|
56463
|
-
|
|
56464
|
-
|
|
56465
|
-
|
|
56466
|
-
|
|
56467
|
-
|
|
56468
|
-
|
|
56469
|
-
|
|
56470
|
-
|
|
56471
|
-
|
|
56793
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "px-3 sm:px-6 lg:px-8 py-3 sm:py-4", children: [
|
|
56794
|
+
/* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
56795
|
+
/* @__PURE__ */ jsx(BackButtonMinimal, { onClick: handleBack, text: "Back" }),
|
|
56796
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-900 truncate px-2", children: pageTitle }) }),
|
|
56797
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
56798
|
+
canViewUsageStats && /* @__PURE__ */ jsx(
|
|
56799
|
+
TeamUsagePdfGenerator,
|
|
56800
|
+
{
|
|
56801
|
+
users,
|
|
56802
|
+
usageData,
|
|
56803
|
+
daysInRange: usageDateRange.daysElapsed,
|
|
56804
|
+
title: "Team Usage Report",
|
|
56805
|
+
iconOnly: true
|
|
56806
|
+
}
|
|
56807
|
+
),
|
|
56808
|
+
canAddUsers && /* @__PURE__ */ jsx(
|
|
56809
|
+
"button",
|
|
56810
|
+
{
|
|
56811
|
+
onClick: () => setIsAddUserDialogOpen(true),
|
|
56812
|
+
className: "p-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 active:bg-blue-800 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
|
|
56813
|
+
"aria-label": "Add User",
|
|
56814
|
+
children: /* @__PURE__ */ jsx(UserPlus, { className: "w-5 h-5" })
|
|
56815
|
+
}
|
|
56816
|
+
)
|
|
56817
|
+
] })
|
|
56818
|
+
] }) }),
|
|
56819
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden sm:flex items-center justify-between", children: [
|
|
56820
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx(BackButtonMinimal, { onClick: handleBack, text: "Back" }) }),
|
|
56821
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
56822
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: pageTitle }),
|
|
56823
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4", children: pageDescription })
|
|
56824
|
+
] }),
|
|
56825
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
56826
|
+
canViewUsageStats && /* @__PURE__ */ jsx(
|
|
56827
|
+
TeamUsagePdfGenerator,
|
|
56828
|
+
{
|
|
56829
|
+
users,
|
|
56830
|
+
usageData,
|
|
56831
|
+
daysInRange: usageDateRange.daysElapsed,
|
|
56832
|
+
title: "Team Usage Report"
|
|
56833
|
+
}
|
|
56834
|
+
),
|
|
56835
|
+
canAddUsers && /* @__PURE__ */ jsxs(
|
|
56836
|
+
"button",
|
|
56837
|
+
{
|
|
56838
|
+
onClick: () => setIsAddUserDialogOpen(true),
|
|
56839
|
+
className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
|
|
56840
|
+
children: [
|
|
56841
|
+
/* @__PURE__ */ jsx(UserPlus, { className: "w-4 h-4" }),
|
|
56842
|
+
/* @__PURE__ */ jsx("span", { children: "Add User" })
|
|
56843
|
+
]
|
|
56844
|
+
}
|
|
56845
|
+
)
|
|
56846
|
+
] })
|
|
56472
56847
|
] })
|
|
56473
|
-
] }) })
|
|
56848
|
+
] }) }),
|
|
56474
56849
|
/* @__PURE__ */ jsx("main", { className: "px-4 sm:px-6 lg:px-8 py-6", children: /* @__PURE__ */ jsx(
|
|
56475
56850
|
UserManagementTable,
|
|
56476
56851
|
{
|
|
@@ -57408,135 +57783,258 @@ Please ensure:
|
|
|
57408
57783
|
}
|
|
57409
57784
|
var AuthenticatedTicketsView = withAuth(React24__default.memo(TicketsView));
|
|
57410
57785
|
var TicketsView_default = TicketsView;
|
|
57411
|
-
|
|
57412
|
-
|
|
57413
|
-
|
|
57414
|
-
|
|
57415
|
-
|
|
57416
|
-
|
|
57417
|
-
|
|
57418
|
-
|
|
57419
|
-
evidence: {
|
|
57420
|
-
type: "video",
|
|
57421
|
-
label: "Operator Cycle Recording",
|
|
57422
|
-
videoUrls: [
|
|
57423
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4",
|
|
57424
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4",
|
|
57425
|
-
// Mock duplicate for demo
|
|
57426
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4"
|
|
57427
|
-
// Mock duplicate for demo
|
|
57428
|
-
]
|
|
57429
|
-
},
|
|
57430
|
-
impact: "Potential 50% efficiency gain or standard adjustment needed.",
|
|
57431
|
-
date: "Today",
|
|
57432
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
57433
|
-
},
|
|
57434
|
-
{
|
|
57435
|
-
id: "rec-2",
|
|
57436
|
-
type: "efficiency",
|
|
57437
|
-
title: "Consistent Low Efficiency Period",
|
|
57438
|
-
location: "Line 2",
|
|
57439
|
-
line: "Line 2",
|
|
57440
|
-
description: "Efficiency from 7pm - 8pm is consistently lower than standard. This pattern has been observed for the last 3 days.",
|
|
57441
|
-
evidence: {
|
|
57442
|
-
type: "chart",
|
|
57443
|
-
label: "7pm - 8pm Efficiency Trend",
|
|
57444
|
-
chartData: [
|
|
57445
|
-
{ label: "7:00", value: 45, standard: 85 },
|
|
57446
|
-
{ label: "7:15", value: 42, standard: 85 },
|
|
57447
|
-
{ label: "7:30", value: 40, standard: 85 },
|
|
57448
|
-
{ label: "7:45", value: 48, standard: 85 },
|
|
57449
|
-
{ label: "8:00", value: 50, standard: 85 }
|
|
57450
|
-
]
|
|
57451
|
-
},
|
|
57452
|
-
impact: "Loss of approx. 40 units per hour during shift changeover.",
|
|
57453
|
-
date: "Today",
|
|
57454
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
57455
|
-
},
|
|
57456
|
-
{
|
|
57457
|
-
id: "rec-3",
|
|
57458
|
-
type: "downtime",
|
|
57459
|
-
title: "Recurring Micro-stoppages",
|
|
57460
|
-
location: "Unit 3: Conveyor",
|
|
57461
|
-
line: "Line 1",
|
|
57462
|
-
description: "Frequent micro-stoppages detected every 15 minutes. This suggests a potential jam or sensor issue.",
|
|
57463
|
-
evidence: {
|
|
57464
|
-
type: "chart",
|
|
57465
|
-
label: "Downtime Events",
|
|
57466
|
-
chartData: [
|
|
57467
|
-
{ label: "9:00", value: 2, standard: 0 },
|
|
57468
|
-
{ label: "9:15", value: 3, standard: 0 },
|
|
57469
|
-
{ label: "9:30", value: 1, standard: 0 },
|
|
57470
|
-
{ label: "9:45", value: 4, standard: 0 },
|
|
57471
|
-
{ label: "10:00", value: 2, standard: 0 }
|
|
57472
|
-
]
|
|
57473
|
-
},
|
|
57474
|
-
impact: "Cumulative downtime of 45 mins per shift.",
|
|
57475
|
-
date: "Yesterday",
|
|
57476
|
-
timestamp: new Date(Date.now() - 864e5).toISOString()
|
|
57477
|
-
},
|
|
57478
|
-
{
|
|
57479
|
-
id: "rec-4",
|
|
57480
|
-
type: "efficiency",
|
|
57481
|
-
title: "High Idle Time Detected",
|
|
57482
|
-
location: "Packaging Station",
|
|
57483
|
-
line: "Line 2",
|
|
57484
|
-
description: "Operator idle time exceeds 20% of shift duration. Consider rebalancing workload.",
|
|
57485
|
-
evidence: {
|
|
57486
|
-
type: "video",
|
|
57487
|
-
label: "Idle Time Observation",
|
|
57488
|
-
videoUrls: [
|
|
57489
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4"
|
|
57490
|
-
]
|
|
57491
|
-
},
|
|
57492
|
-
impact: "Potential to reassign 1 FTE to other tasks.",
|
|
57493
|
-
date: "Yesterday",
|
|
57494
|
-
timestamp: new Date(Date.now() - 864e5).toISOString()
|
|
57495
|
-
},
|
|
57496
|
-
{
|
|
57497
|
-
id: "rec-5",
|
|
57498
|
-
type: "cycle_time",
|
|
57499
|
-
title: "Slow Setup Time",
|
|
57500
|
-
location: "Machine 5",
|
|
57501
|
-
line: "Line 1",
|
|
57502
|
-
description: "Setup time for product changeover took 45 mins, standard is 20 mins.",
|
|
57503
|
-
evidence: {
|
|
57504
|
-
type: "chart",
|
|
57505
|
-
label: "Changeover Duration",
|
|
57506
|
-
chartData: [
|
|
57507
|
-
{ label: "Prev 1", value: 25, standard: 20 },
|
|
57508
|
-
{ label: "Prev 2", value: 22, standard: 20 },
|
|
57509
|
-
{ label: "Current", value: 45, standard: 20 },
|
|
57510
|
-
{ label: "Avg", value: 30, standard: 20 },
|
|
57511
|
-
{ label: "Target", value: 20, standard: 20 }
|
|
57512
|
-
]
|
|
57513
|
-
},
|
|
57514
|
-
impact: "25 mins of lost production time.",
|
|
57515
|
-
date: "2 Days Ago",
|
|
57516
|
-
timestamp: new Date(Date.now() - 1728e5).toISOString()
|
|
57786
|
+
|
|
57787
|
+
// src/lib/api/s3-clips-parser.ts
|
|
57788
|
+
function parseS3Uri(s3Uri, sopCategories) {
|
|
57789
|
+
const path = new URL(s3Uri).pathname;
|
|
57790
|
+
const parts = path.split("/").filter((p) => p);
|
|
57791
|
+
if (s3Uri.includes("missed_qchecks")) {
|
|
57792
|
+
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
57793
|
+
return null;
|
|
57517
57794
|
}
|
|
57795
|
+
if (parts.length < 8) {
|
|
57796
|
+
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
57797
|
+
return null;
|
|
57798
|
+
}
|
|
57799
|
+
try {
|
|
57800
|
+
const datePart = parts[2];
|
|
57801
|
+
const shiftPart = parts[3];
|
|
57802
|
+
const violationType = parts[4];
|
|
57803
|
+
let folderName = "";
|
|
57804
|
+
let timestamp = "";
|
|
57805
|
+
for (let i = 5; i < parts.length; i++) {
|
|
57806
|
+
const part = parts[i];
|
|
57807
|
+
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
57808
|
+
folderName = part;
|
|
57809
|
+
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
57810
|
+
if (timeMatch) {
|
|
57811
|
+
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
57812
|
+
break;
|
|
57813
|
+
}
|
|
57814
|
+
}
|
|
57815
|
+
}
|
|
57816
|
+
if (!timestamp) {
|
|
57817
|
+
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
57818
|
+
timestamp = "00:00:00";
|
|
57819
|
+
}
|
|
57820
|
+
let severity = "low";
|
|
57821
|
+
let type = "bottleneck";
|
|
57822
|
+
let description = "Analysis Clip";
|
|
57823
|
+
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
57824
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
57825
|
+
const matchedCategory = sopCategories.find((category) => {
|
|
57826
|
+
const categoryId = category.id.toLowerCase();
|
|
57827
|
+
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
57828
|
+
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
57829
|
+
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
57830
|
+
});
|
|
57831
|
+
if (matchedCategory) {
|
|
57832
|
+
type = matchedCategory.id;
|
|
57833
|
+
description = matchedCategory.description || matchedCategory.label;
|
|
57834
|
+
if (matchedCategory.color.includes("red")) {
|
|
57835
|
+
severity = "high";
|
|
57836
|
+
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
57837
|
+
severity = "medium";
|
|
57838
|
+
} else {
|
|
57839
|
+
severity = "low";
|
|
57840
|
+
}
|
|
57841
|
+
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
57842
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
57843
|
+
}
|
|
57844
|
+
}
|
|
57845
|
+
switch (normalizedViolationType) {
|
|
57846
|
+
case "idle_time":
|
|
57847
|
+
case "idle":
|
|
57848
|
+
case "low_value":
|
|
57849
|
+
case "low value":
|
|
57850
|
+
case "low_value_moment":
|
|
57851
|
+
case "low_value_moments":
|
|
57852
|
+
case "low value moment":
|
|
57853
|
+
case "low value moments":
|
|
57854
|
+
type = "low_value";
|
|
57855
|
+
severity = "low";
|
|
57856
|
+
description = "Idle Time Detected";
|
|
57857
|
+
break;
|
|
57858
|
+
case "sop_deviation":
|
|
57859
|
+
type = "missing_quality_check";
|
|
57860
|
+
severity = "high";
|
|
57861
|
+
description = "SOP Deviations";
|
|
57862
|
+
break;
|
|
57863
|
+
case "long_cycle_time":
|
|
57864
|
+
severity = "high";
|
|
57865
|
+
type = "long_cycle_time";
|
|
57866
|
+
description = "Long Cycle Time Detected";
|
|
57867
|
+
break;
|
|
57868
|
+
case "best_cycle_time":
|
|
57869
|
+
type = "best_cycle_time";
|
|
57870
|
+
severity = "low";
|
|
57871
|
+
description = "Best Cycle Time Performance";
|
|
57872
|
+
break;
|
|
57873
|
+
case "worst_cycle_time":
|
|
57874
|
+
type = "worst_cycle_time";
|
|
57875
|
+
severity = "high";
|
|
57876
|
+
description = "Worst Cycle Time Performance";
|
|
57877
|
+
break;
|
|
57878
|
+
case "cycle_completion":
|
|
57879
|
+
case "completed_cycles":
|
|
57880
|
+
case "completed_cycle":
|
|
57881
|
+
type = "cycle_completion";
|
|
57882
|
+
severity = "low";
|
|
57883
|
+
description = "Cycle Completion";
|
|
57884
|
+
break;
|
|
57885
|
+
case "running_cycle":
|
|
57886
|
+
case "active_cycle":
|
|
57887
|
+
case "production_cycle":
|
|
57888
|
+
type = "running_cycle";
|
|
57889
|
+
severity = "low";
|
|
57890
|
+
description = "Active Production Cycle";
|
|
57891
|
+
break;
|
|
57892
|
+
case "setup_state":
|
|
57893
|
+
case "machine_setup":
|
|
57894
|
+
case "line_setup":
|
|
57895
|
+
type = "setup_state";
|
|
57896
|
+
severity = "medium";
|
|
57897
|
+
description = "Machine Setup Activity";
|
|
57898
|
+
break;
|
|
57899
|
+
case "medium_bottleneck":
|
|
57900
|
+
severity = "medium";
|
|
57901
|
+
description = "Medium Bottleneck Identified";
|
|
57902
|
+
break;
|
|
57903
|
+
case "minor_bottleneck":
|
|
57904
|
+
case "mild_bottleneck":
|
|
57905
|
+
severity = "low";
|
|
57906
|
+
description = "Minor Bottleneck Identified";
|
|
57907
|
+
break;
|
|
57908
|
+
default:
|
|
57909
|
+
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
57910
|
+
type = "missing_quality_check";
|
|
57911
|
+
severity = "high";
|
|
57912
|
+
description = "SOP Deviations";
|
|
57913
|
+
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
57914
|
+
type = "worst_cycle_time";
|
|
57915
|
+
severity = "high";
|
|
57916
|
+
description = "Worst Cycle Time Performance";
|
|
57917
|
+
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
57918
|
+
type = "best_cycle_time";
|
|
57919
|
+
severity = "low";
|
|
57920
|
+
description = "Best Cycle Time Performance";
|
|
57921
|
+
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
57922
|
+
type = "long_cycle_time";
|
|
57923
|
+
severity = "high";
|
|
57924
|
+
description = "Long Cycle Time Detected";
|
|
57925
|
+
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
57926
|
+
type = "cycle_completion";
|
|
57927
|
+
severity = "low";
|
|
57928
|
+
description = "Cycle Completion";
|
|
57929
|
+
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
57930
|
+
type = "running_cycle";
|
|
57931
|
+
severity = "low";
|
|
57932
|
+
description = "Active Production Cycle";
|
|
57933
|
+
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
57934
|
+
type = "setup_state";
|
|
57935
|
+
severity = "medium";
|
|
57936
|
+
description = "Machine Setup Activity";
|
|
57937
|
+
} else {
|
|
57938
|
+
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
57939
|
+
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
57940
|
+
}
|
|
57941
|
+
break;
|
|
57942
|
+
}
|
|
57943
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
57944
|
+
} catch (error) {
|
|
57945
|
+
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
57946
|
+
return null;
|
|
57947
|
+
}
|
|
57948
|
+
}
|
|
57949
|
+
function shuffleArray(array) {
|
|
57950
|
+
const shuffled = [...array];
|
|
57951
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
57952
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
57953
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
57954
|
+
}
|
|
57955
|
+
return shuffled;
|
|
57956
|
+
}
|
|
57957
|
+
var CATEGORY_OPTIONS = [
|
|
57958
|
+
{ id: "all", label: "All" },
|
|
57959
|
+
{ id: "cycle_time", label: "Cycle Time" },
|
|
57960
|
+
{ id: "efficiency", label: "Efficiency" },
|
|
57961
|
+
{ id: "downtime", label: "Downtime" }
|
|
57518
57962
|
];
|
|
57519
|
-
var
|
|
57963
|
+
var ClipVideoCarousel = ({ clips, clipsService }) => {
|
|
57520
57964
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
57965
|
+
const [videos, setVideos] = useState([]);
|
|
57966
|
+
const [loading, setLoading] = useState(false);
|
|
57967
|
+
const [error, setError] = useState(null);
|
|
57968
|
+
useEffect(() => {
|
|
57969
|
+
let cancelled = false;
|
|
57970
|
+
const load = async () => {
|
|
57971
|
+
if (!clipsService) {
|
|
57972
|
+
setError("Clips are not configured for this dashboard");
|
|
57973
|
+
return;
|
|
57974
|
+
}
|
|
57975
|
+
if (!clips || clips.length === 0) return;
|
|
57976
|
+
setLoading(true);
|
|
57977
|
+
setError(null);
|
|
57978
|
+
setVideos([]);
|
|
57979
|
+
try {
|
|
57980
|
+
const resolved = await Promise.all(
|
|
57981
|
+
clips.map(async (c) => {
|
|
57982
|
+
const video = await clipsService.getClipById(c.clip_id);
|
|
57983
|
+
if (!video) return null;
|
|
57984
|
+
const cycleTime = typeof c.cycle_time_seconds === "number" ? c.cycle_time_seconds : typeof c.cycle_sec === "number" ? c.cycle_sec : void 0;
|
|
57985
|
+
return {
|
|
57986
|
+
...video,
|
|
57987
|
+
cycle_time_seconds: cycleTime ?? video.cycle_time_seconds
|
|
57988
|
+
};
|
|
57989
|
+
})
|
|
57990
|
+
);
|
|
57991
|
+
if (cancelled) return;
|
|
57992
|
+
setVideos(resolved.filter(Boolean) || []);
|
|
57993
|
+
} catch (err) {
|
|
57994
|
+
if (cancelled) return;
|
|
57995
|
+
setError(err?.message || "Failed to load clips");
|
|
57996
|
+
setVideos([]);
|
|
57997
|
+
} finally {
|
|
57998
|
+
if (!cancelled) setLoading(false);
|
|
57999
|
+
}
|
|
58000
|
+
};
|
|
58001
|
+
load();
|
|
58002
|
+
return () => {
|
|
58003
|
+
cancelled = true;
|
|
58004
|
+
};
|
|
58005
|
+
}, [clipsService, clips]);
|
|
58006
|
+
useEffect(() => {
|
|
58007
|
+
if (videos.length > 0 && currentIndex >= videos.length) {
|
|
58008
|
+
setCurrentIndex(0);
|
|
58009
|
+
}
|
|
58010
|
+
}, [videos.length, currentIndex]);
|
|
57521
58011
|
const nextVideo = () => {
|
|
57522
|
-
setCurrentIndex((prev) => (prev + 1) %
|
|
58012
|
+
setCurrentIndex((prev) => (prev + 1) % Math.max(videos.length, 1));
|
|
57523
58013
|
};
|
|
57524
58014
|
const prevVideo = () => {
|
|
57525
|
-
setCurrentIndex((prev) => (prev - 1 +
|
|
58015
|
+
setCurrentIndex((prev) => (prev - 1 + Math.max(videos.length, 1)) % Math.max(videos.length, 1));
|
|
57526
58016
|
};
|
|
57527
|
-
if (!
|
|
58017
|
+
if (!clips || clips.length === 0) return null;
|
|
58018
|
+
const currentVideo = videos[currentIndex];
|
|
57528
58019
|
return /* @__PURE__ */ jsxs("div", { className: "relative group bg-gray-900 rounded-lg overflow-hidden aspect-video", children: [
|
|
57529
|
-
/* @__PURE__ */ jsx(
|
|
57530
|
-
|
|
58020
|
+
loading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-white/80", children: "Loading clips\u2026" }),
|
|
58021
|
+
!loading && error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-white/80", children: error }),
|
|
58022
|
+
!loading && !error && currentVideo?.src && /* @__PURE__ */ jsx(
|
|
58023
|
+
VideoPlayer,
|
|
57531
58024
|
{
|
|
57532
|
-
src:
|
|
57533
|
-
className: "w-full h-full object-cover opacity-
|
|
57534
|
-
controls: true
|
|
58025
|
+
src: currentVideo.src,
|
|
58026
|
+
className: "w-full h-full object-cover opacity-90 group-hover:opacity-100 transition-opacity",
|
|
58027
|
+
controls: true,
|
|
58028
|
+
playsInline: true
|
|
57535
58029
|
},
|
|
57536
|
-
|
|
58030
|
+
currentVideo.src
|
|
57537
58031
|
),
|
|
57538
|
-
/* @__PURE__ */
|
|
57539
|
-
|
|
58032
|
+
!loading && !error && typeof currentVideo?.cycle_time_seconds === "number" && /* @__PURE__ */ jsxs("div", { className: "absolute top-3 left-3 bg-black/60 text-white text-xs px-2 py-1 rounded-full font-medium pointer-events-none", children: [
|
|
58033
|
+
currentVideo.cycle_time_seconds.toFixed(1),
|
|
58034
|
+
"s"
|
|
58035
|
+
] }),
|
|
58036
|
+
!loading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none group-hover:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx(PlayCircleIcon, { className: "w-16 h-16 text-white opacity-80" }) }),
|
|
58037
|
+
videos.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57540
58038
|
/* @__PURE__ */ jsx(
|
|
57541
58039
|
"button",
|
|
57542
58040
|
{
|
|
@@ -57564,70 +58062,273 @@ var VideoCarousel = ({ urls }) => {
|
|
|
57564
58062
|
/* @__PURE__ */ jsxs("div", { className: "absolute top-3 right-3 bg-black/60 text-white text-xs px-2 py-1 rounded-full font-medium pointer-events-none", children: [
|
|
57565
58063
|
currentIndex + 1,
|
|
57566
58064
|
" / ",
|
|
57567
|
-
|
|
58065
|
+
videos.length
|
|
57568
58066
|
] })
|
|
57569
58067
|
] })
|
|
57570
58068
|
] });
|
|
57571
58069
|
};
|
|
57572
|
-
var
|
|
57573
|
-
const
|
|
57574
|
-
|
|
57575
|
-
|
|
57576
|
-
|
|
57577
|
-
|
|
58070
|
+
var BarChartEvidence = ({ x, y, unit, data, series, referenceLines }) => {
|
|
58071
|
+
const scale2 = unit === "fraction" ? 100 : 1;
|
|
58072
|
+
const yAxisUnit = unit === "fraction" ? "%" : "";
|
|
58073
|
+
const targetLineIndex = referenceLines?.findIndex(
|
|
58074
|
+
(line) => (line.label || "").toLowerCase().includes("target")
|
|
58075
|
+
) ?? -1;
|
|
58076
|
+
const resolvedTargetLineIndex = targetLineIndex >= 0 ? targetLineIndex : referenceLines?.length === 1 ? 0 : -1;
|
|
58077
|
+
const unitLabel = unit === "fraction" ? "%" : unit === "pieces" ? "PPH" : unit === "ratio" ? "" : unit || "";
|
|
58078
|
+
const formatTargetValue = (value) => {
|
|
58079
|
+
if (unit === "fraction") return value.toFixed(0);
|
|
58080
|
+
if (Number.isInteger(value)) return value.toString();
|
|
58081
|
+
return value.toFixed(1);
|
|
58082
|
+
};
|
|
58083
|
+
const formatTargetLabel = (label, value) => {
|
|
58084
|
+
const base = label || "Target";
|
|
58085
|
+
const suffix = unitLabel ? unitLabel === "%" ? "%" : ` ${unitLabel}` : "";
|
|
58086
|
+
return `${base}: ${formatTargetValue(value)}${suffix}`;
|
|
58087
|
+
};
|
|
58088
|
+
const isMultiSeries = Array.isArray(series) && series.length > 0 && Array.isArray(data);
|
|
58089
|
+
const chartData = isMultiSeries ? (data || []).map((row) => {
|
|
58090
|
+
const scaled = { ...row };
|
|
58091
|
+
series?.forEach((s) => {
|
|
58092
|
+
const raw = row[s.dataKey];
|
|
58093
|
+
if (typeof raw === "number") {
|
|
58094
|
+
scaled[s.dataKey] = raw * scale2;
|
|
58095
|
+
}
|
|
58096
|
+
});
|
|
58097
|
+
return scaled;
|
|
58098
|
+
}) : (x || []).map((xv, idx) => {
|
|
58099
|
+
const yv = y?.[idx];
|
|
58100
|
+
if (yv === null || yv === void 0) return null;
|
|
58101
|
+
const label = typeof xv === "number" ? `${xv.toString().padStart(2, "0")}:00` : String(xv);
|
|
58102
|
+
return { name: label, value: yv * scale2 };
|
|
58103
|
+
}).filter(Boolean);
|
|
58104
|
+
const referenceLineConfigs = (referenceLines || []).map((line, idx) => {
|
|
58105
|
+
const scaledValue = line.y * scale2;
|
|
58106
|
+
const isTarget = idx === resolvedTargetLineIndex;
|
|
58107
|
+
return {
|
|
58108
|
+
...line,
|
|
58109
|
+
y: scaledValue,
|
|
58110
|
+
label: isTarget ? formatTargetLabel(line.label, scaledValue) : line.label,
|
|
58111
|
+
stroke: isTarget ? "#E34329" : line.stroke,
|
|
58112
|
+
strokeDasharray: isTarget ? "5 5" : line.strokeDasharray,
|
|
58113
|
+
strokeWidth: isTarget ? 2 : line.strokeWidth
|
|
58114
|
+
};
|
|
58115
|
+
});
|
|
58116
|
+
const targetLine = resolvedTargetLineIndex >= 0 ? (referenceLines || [])[resolvedTargetLineIndex] : void 0;
|
|
58117
|
+
const targetValue = targetLine ? targetLine.y * scale2 : null;
|
|
58118
|
+
const targetLabel = targetLine && targetValue !== null ? formatTargetLabel(targetLine.label, targetValue) : null;
|
|
58119
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-48 w-full bg-white border border-gray-100 rounded-lg p-2 relative", children: [
|
|
58120
|
+
targetLabel && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 z-10 pointer-events-none", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 bg-white/90 px-2 py-1 text-[11px] text-gray-700 shadow-sm", children: [
|
|
58121
|
+
/* @__PURE__ */ jsx("span", { className: "block w-6 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
|
|
58122
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: targetLabel })
|
|
58123
|
+
] }) }),
|
|
58124
|
+
/* @__PURE__ */ jsx(
|
|
58125
|
+
BarChart,
|
|
58126
|
+
{
|
|
58127
|
+
data: chartData,
|
|
58128
|
+
bars: isMultiSeries ? (series || []).map((s) => ({
|
|
58129
|
+
dataKey: s.dataKey,
|
|
58130
|
+
name: s.name,
|
|
58131
|
+
fill: s.fill,
|
|
58132
|
+
stackId: s.stackId,
|
|
58133
|
+
labelList: false
|
|
58134
|
+
})) : [
|
|
58135
|
+
{
|
|
58136
|
+
dataKey: "value",
|
|
58137
|
+
name: "Value",
|
|
58138
|
+
fill: "#f87171",
|
|
58139
|
+
// red-400
|
|
58140
|
+
labelList: false
|
|
58141
|
+
}
|
|
58142
|
+
],
|
|
58143
|
+
xAxisDataKey: "name",
|
|
58144
|
+
showLegend: isMultiSeries && (series || []).length > 1,
|
|
58145
|
+
showGrid: true,
|
|
58146
|
+
aspect: 2.5,
|
|
58147
|
+
yAxisUnit,
|
|
58148
|
+
referenceLines: referenceLineConfigs,
|
|
58149
|
+
className: "h-full"
|
|
58150
|
+
}
|
|
58151
|
+
)
|
|
58152
|
+
] });
|
|
58153
|
+
};
|
|
58154
|
+
var PieChartEvidence = ({ data }) => {
|
|
58155
|
+
return /* @__PURE__ */ jsx("div", { className: "h-48 w-full bg-white border border-gray-100 rounded-lg p-2", children: /* @__PURE__ */ jsx(IdleTimeReasonChart_default, { data }) });
|
|
58156
|
+
};
|
|
58157
|
+
var LineChartEvidence = ({ x, y, unit }) => {
|
|
58158
|
+
const scale2 = unit === "fraction" ? 100 : 1;
|
|
58159
|
+
const yAxisUnit = unit === "fraction" ? "%" : "";
|
|
58160
|
+
const chartData = x.map((xv, idx) => {
|
|
58161
|
+
const yv = y[idx];
|
|
58162
|
+
if (yv === null || yv === void 0) return null;
|
|
58163
|
+
return { name: String(xv), value: yv * scale2 };
|
|
58164
|
+
}).filter(Boolean);
|
|
57578
58165
|
return /* @__PURE__ */ jsx("div", { className: "h-48 w-full bg-white border border-gray-100 rounded-lg p-2", children: /* @__PURE__ */ jsx(
|
|
57579
|
-
|
|
58166
|
+
LineChart,
|
|
57580
58167
|
{
|
|
57581
58168
|
data: chartData,
|
|
57582
|
-
|
|
58169
|
+
lines: [
|
|
57583
58170
|
{
|
|
57584
58171
|
dataKey: "value",
|
|
57585
|
-
name: "
|
|
57586
|
-
|
|
57587
|
-
|
|
57588
|
-
|
|
58172
|
+
name: "Value",
|
|
58173
|
+
stroke: "#f87171",
|
|
58174
|
+
strokeWidth: 2,
|
|
58175
|
+
dot: true
|
|
57589
58176
|
}
|
|
57590
58177
|
],
|
|
57591
58178
|
xAxisDataKey: "name",
|
|
57592
58179
|
showLegend: false,
|
|
57593
58180
|
showGrid: true,
|
|
57594
58181
|
aspect: 2.5,
|
|
58182
|
+
yAxisUnit,
|
|
57595
58183
|
className: "h-full"
|
|
57596
58184
|
}
|
|
57597
58185
|
) });
|
|
57598
58186
|
};
|
|
58187
|
+
var TableEvidence = ({ columns, rows }) => {
|
|
58188
|
+
const formatValue = (value) => {
|
|
58189
|
+
if (typeof value === "number") {
|
|
58190
|
+
if (Number.isInteger(value)) return value.toString();
|
|
58191
|
+
return value.toFixed(3);
|
|
58192
|
+
}
|
|
58193
|
+
if (value === null || value === void 0) return "-";
|
|
58194
|
+
return String(value);
|
|
58195
|
+
};
|
|
58196
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full bg-white border border-gray-100 rounded-lg overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full text-xs", children: [
|
|
58197
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-gray-50 border-b border-gray-100", children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
58198
|
+
"th",
|
|
58199
|
+
{
|
|
58200
|
+
className: "px-3 py-2 text-left font-semibold text-gray-600 uppercase tracking-wide",
|
|
58201
|
+
children: col.replace(/_/g, " ")
|
|
58202
|
+
},
|
|
58203
|
+
col
|
|
58204
|
+
)) }) }),
|
|
58205
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.map((row, idx) => /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-100 last:border-b-0", children: columns.map((col) => /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-gray-700 whitespace-nowrap", children: formatValue(row[col]) }, col)) }, idx)) })
|
|
58206
|
+
] }) }) });
|
|
58207
|
+
};
|
|
57599
58208
|
var ImprovementCenterView = () => {
|
|
57600
58209
|
const { navigate } = useNavigation();
|
|
57601
|
-
const
|
|
57602
|
-
const
|
|
57603
|
-
const
|
|
57604
|
-
const
|
|
57605
|
-
|
|
57606
|
-
|
|
57607
|
-
|
|
57608
|
-
const
|
|
57609
|
-
|
|
57610
|
-
|
|
57611
|
-
|
|
57612
|
-
|
|
57613
|
-
|
|
57614
|
-
|
|
57615
|
-
|
|
58210
|
+
const supabase = useSupabase();
|
|
58211
|
+
const { user } = useAuth();
|
|
58212
|
+
const dashboardConfig = useDashboardConfig();
|
|
58213
|
+
const entityConfig = useEntityConfig();
|
|
58214
|
+
const [currentDate, setCurrentDate] = useState(/* @__PURE__ */ new Date());
|
|
58215
|
+
const [selectedCategory, setSelectedCategory] = useState("all");
|
|
58216
|
+
const [selectedLineId, setSelectedLineId] = useState("all");
|
|
58217
|
+
const [selectedStatus, setSelectedStatus] = useState("all");
|
|
58218
|
+
const [recommendations, setRecommendations] = useState([]);
|
|
58219
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
58220
|
+
const [loadError, setLoadError] = useState(null);
|
|
58221
|
+
const computeWeeksOpen = (firstSeenAt) => {
|
|
58222
|
+
if (!firstSeenAt) return void 0;
|
|
58223
|
+
const firstSeen = new Date(firstSeenAt);
|
|
58224
|
+
if (Number.isNaN(firstSeen.getTime())) return void 0;
|
|
58225
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
58226
|
+
const diffDays = Math.max(0, Math.floor((now2.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
58227
|
+
return Math.max(1, Math.ceil(diffDays / 7));
|
|
58228
|
+
};
|
|
58229
|
+
const configuredLines = useMemo(() => {
|
|
58230
|
+
return entityConfig.lines || entityConfig.lineNames || {};
|
|
58231
|
+
}, [entityConfig.lines, entityConfig.lineNames]);
|
|
58232
|
+
const configuredLineIds = useMemo(() => Object.keys(configuredLines), [configuredLines]);
|
|
58233
|
+
const { accessibleLineIds } = useUserLineAccess(configuredLineIds);
|
|
58234
|
+
const scopeLineIds = useMemo(() => {
|
|
58235
|
+
return accessibleLineIds.length > 0 ? accessibleLineIds : configuredLineIds;
|
|
58236
|
+
}, [accessibleLineIds, configuredLineIds]);
|
|
58237
|
+
const scopeLineIdsKey = useMemo(() => scopeLineIds.join(","), [scopeLineIds]);
|
|
58238
|
+
const companyId = user?.company_id || entityConfig.companyId;
|
|
58239
|
+
const clipsService = useMemo(() => {
|
|
58240
|
+
try {
|
|
58241
|
+
return new S3ClipsSupabaseService(dashboardConfig);
|
|
58242
|
+
} catch (err) {
|
|
58243
|
+
return null;
|
|
58244
|
+
}
|
|
58245
|
+
}, [dashboardConfig]);
|
|
58246
|
+
const nextMonth = () => {
|
|
58247
|
+
setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
|
|
58248
|
+
};
|
|
58249
|
+
const prevMonth = () => {
|
|
58250
|
+
setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
|
|
58251
|
+
};
|
|
58252
|
+
const formatMonth = (date) => {
|
|
58253
|
+
return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
58254
|
+
};
|
|
58255
|
+
useEffect(() => {
|
|
58256
|
+
let cancelled = false;
|
|
58257
|
+
const fetchRecommendations = async () => {
|
|
58258
|
+
if (!supabase || !companyId) return;
|
|
58259
|
+
setIsLoading(true);
|
|
58260
|
+
setLoadError(null);
|
|
58261
|
+
try {
|
|
58262
|
+
if (scopeLineIds.length === 0) {
|
|
58263
|
+
setRecommendations([]);
|
|
58264
|
+
return;
|
|
58265
|
+
}
|
|
58266
|
+
const params = new URLSearchParams({
|
|
58267
|
+
company_id: companyId,
|
|
58268
|
+
status: "all",
|
|
58269
|
+
limit: "500"
|
|
58270
|
+
});
|
|
58271
|
+
params.set("month", `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, "0")}`);
|
|
58272
|
+
if (scopeLineIds.length > 0) {
|
|
58273
|
+
params.set("line_ids", scopeLineIds.join(","));
|
|
58274
|
+
}
|
|
58275
|
+
const res = await fetchBackendJson(supabase, `/api/improvement-center/recommendations?${params.toString()}`);
|
|
58276
|
+
if (cancelled) return;
|
|
58277
|
+
let recs = res.recommendations || [];
|
|
58278
|
+
recs = recs.map((r2) => ({
|
|
58279
|
+
...r2,
|
|
58280
|
+
ticket_status: r2.issue_status === "resolved" ? "solved" : "active",
|
|
58281
|
+
weeks_open: typeof r2.weeks_open === "number" ? r2.weeks_open : computeWeeksOpen(r2.first_seen_at) ?? 1
|
|
58282
|
+
}));
|
|
58283
|
+
const allowed = new Set(scopeLineIds);
|
|
58284
|
+
setRecommendations(recs.filter((r2) => !r2.line_id || allowed.has(r2.line_id)));
|
|
58285
|
+
} catch (err) {
|
|
58286
|
+
if (cancelled) return;
|
|
58287
|
+
setLoadError(err?.message || "Failed to load recommendations");
|
|
58288
|
+
setRecommendations([]);
|
|
58289
|
+
} finally {
|
|
58290
|
+
if (!cancelled) setIsLoading(false);
|
|
57616
58291
|
}
|
|
58292
|
+
};
|
|
58293
|
+
fetchRecommendations();
|
|
58294
|
+
return () => {
|
|
58295
|
+
cancelled = true;
|
|
58296
|
+
};
|
|
58297
|
+
}, [supabase, companyId, scopeLineIdsKey, currentDate]);
|
|
58298
|
+
const filteredRecommendations = useMemo(() => {
|
|
58299
|
+
return recommendations.filter((rec) => {
|
|
58300
|
+
if (selectedCategory !== "all" && rec.type !== selectedCategory) return false;
|
|
58301
|
+
if (selectedLineId !== "all" && rec.line_id !== selectedLineId) return false;
|
|
58302
|
+
if (selectedStatus === "resolved" && rec.ticket_status !== "solved") return false;
|
|
58303
|
+
if (selectedStatus === "unresolved" && rec.ticket_status === "solved") return false;
|
|
57617
58304
|
return true;
|
|
57618
|
-
});
|
|
57619
|
-
}, [
|
|
57620
|
-
const
|
|
57621
|
-
|
|
57622
|
-
|
|
58305
|
+
}).sort((a, b) => (b.weeks_open || 0) - (a.weeks_open || 0));
|
|
58306
|
+
}, [recommendations, selectedCategory, selectedLineId, selectedStatus]);
|
|
58307
|
+
const stats = useMemo(() => {
|
|
58308
|
+
const total = recommendations.length;
|
|
58309
|
+
const resolved = recommendations.filter((r2) => r2.ticket_status === "solved").length;
|
|
58310
|
+
return {
|
|
58311
|
+
all: total,
|
|
58312
|
+
resolved,
|
|
58313
|
+
unresolved: total - resolved
|
|
58314
|
+
};
|
|
58315
|
+
}, [recommendations]);
|
|
57623
58316
|
const clearFilters = () => {
|
|
57624
|
-
|
|
57625
|
-
|
|
57626
|
-
|
|
57627
|
-
|
|
58317
|
+
setSelectedCategory("all");
|
|
58318
|
+
setSelectedLineId("all");
|
|
58319
|
+
setSelectedStatus("all");
|
|
58320
|
+
};
|
|
58321
|
+
const lineOptions = useMemo(() => {
|
|
58322
|
+
const options = [{ id: "all", label: "All Lines" }];
|
|
58323
|
+
scopeLineIds.forEach((lineId) => {
|
|
58324
|
+
const lineName = configuredLines[lineId] || lineId;
|
|
58325
|
+
options.push({ id: lineId, label: lineName });
|
|
58326
|
+
});
|
|
58327
|
+
return options;
|
|
58328
|
+
}, [scopeLineIds, configuredLines]);
|
|
57628
58329
|
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
|
|
57629
58330
|
/* @__PURE__ */ jsx("header", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
57630
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx(
|
|
58331
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 min-w-[120px]", children: /* @__PURE__ */ jsx(
|
|
57631
58332
|
BackButtonMinimal,
|
|
57632
58333
|
{
|
|
57633
58334
|
onClick: () => navigate("/"),
|
|
@@ -57635,121 +58336,190 @@ var ImprovementCenterView = () => {
|
|
|
57635
58336
|
}
|
|
57636
58337
|
) }),
|
|
57637
58338
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
57638
|
-
/* @__PURE__ */
|
|
57639
|
-
|
|
57640
|
-
"Improvement Center"
|
|
57641
|
-
] }),
|
|
57642
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4", children: "Daily AI-driven recommendations to optimize your line performance" })
|
|
58339
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Improvement Center" }),
|
|
58340
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4 hidden sm:block", children: "Track and resolve persistent issues across your production lines" })
|
|
57643
58341
|
] }),
|
|
57644
|
-
/* @__PURE__ */ jsx("div", { className: "w-
|
|
58342
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-[120px]" })
|
|
57645
58343
|
] }) }) }),
|
|
57646
|
-
/* @__PURE__ */
|
|
57647
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
58344
|
+
/* @__PURE__ */ jsxs("main", { className: "flex-1 p-4 sm:p-6 max-w-7xl mx-auto w-full", children: [
|
|
58345
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-white rounded-full px-4 py-2 border border-gray-200 shadow-sm", children: [
|
|
57648
58346
|
/* @__PURE__ */ jsx(
|
|
57649
|
-
"input",
|
|
57650
|
-
{
|
|
57651
|
-
ref: dateInputRef,
|
|
57652
|
-
type: "date",
|
|
57653
|
-
value: selectedDate,
|
|
57654
|
-
onChange: (e) => setSelectedDate(e.target.value),
|
|
57655
|
-
className: "sr-only"
|
|
57656
|
-
}
|
|
57657
|
-
),
|
|
57658
|
-
/* @__PURE__ */ jsxs(
|
|
57659
58347
|
"button",
|
|
57660
58348
|
{
|
|
57661
|
-
onClick:
|
|
57662
|
-
className:
|
|
57663
|
-
children:
|
|
57664
|
-
/* @__PURE__ */ jsx(CalendarIcon, { className: "w-4 h-4" }),
|
|
57665
|
-
/* @__PURE__ */ jsx("span", { children: selectedDate || "All Dates" })
|
|
57666
|
-
]
|
|
58349
|
+
onClick: prevMonth,
|
|
58350
|
+
className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
|
|
58351
|
+
children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-5 h-5" })
|
|
57667
58352
|
}
|
|
57668
|
-
)
|
|
57669
|
-
|
|
57670
|
-
/* @__PURE__ */ jsxs(
|
|
57671
|
-
"select",
|
|
57672
|
-
{
|
|
57673
|
-
value: selectedLine,
|
|
57674
|
-
onChange: (e) => setSelectedLine(e.target.value),
|
|
57675
|
-
className: `px-3 py-1.5 text-sm border rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer ${selectedLine ? "bg-blue-50 border-blue-200 text-blue-700" : "border-gray-300 text-gray-700"}`,
|
|
57676
|
-
children: [
|
|
57677
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "All Lines" }),
|
|
57678
|
-
uniqueLines.map((line) => /* @__PURE__ */ jsx("option", { value: line, children: line }, line))
|
|
57679
|
-
]
|
|
57680
|
-
}
|
|
57681
|
-
),
|
|
57682
|
-
hasActiveFilters && /* @__PURE__ */ jsx(
|
|
57683
|
-
"button",
|
|
57684
|
-
{
|
|
57685
|
-
onClick: clearFilters,
|
|
57686
|
-
className: "p-1.5 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100 transition-colors",
|
|
57687
|
-
title: "Clear filters",
|
|
57688
|
-
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "w-5 h-5" })
|
|
57689
|
-
}
|
|
57690
|
-
)
|
|
57691
|
-
] }) }) }),
|
|
57692
|
-
/* @__PURE__ */ jsx("main", { className: "flex-1 p-4 sm:p-6 max-w-7xl mx-auto w-full", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
|
|
57693
|
-
filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => /* @__PURE__ */ jsx(
|
|
57694
|
-
motion.div,
|
|
57695
|
-
{
|
|
57696
|
-
initial: { opacity: 0, y: 20 },
|
|
57697
|
-
animate: { opacity: 1, y: 0 },
|
|
57698
|
-
transition: { delay: index * 0.1 },
|
|
57699
|
-
className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden",
|
|
57700
|
-
children: /* @__PURE__ */ jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
|
|
57701
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
|
|
57702
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
57703
|
-
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${rec.type === "cycle_time" ? "bg-blue-100 text-blue-800" : rec.type === "efficiency" ? "bg-orange-100 text-orange-800" : "bg-gray-100 text-gray-800"}`, children: rec.type.replace("_", " ").toUpperCase() }),
|
|
57704
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 mt-2", children: rec.title }),
|
|
57705
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-500", children: [
|
|
57706
|
-
rec.location,
|
|
57707
|
-
" \u2022 ",
|
|
57708
|
-
rec.line,
|
|
57709
|
-
" \u2022 ",
|
|
57710
|
-
rec.date
|
|
57711
|
-
] })
|
|
57712
|
-
] }) }),
|
|
57713
|
-
/* @__PURE__ */ jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { children: rec.description }) }),
|
|
57714
|
-
/* @__PURE__ */ jsx("div", { className: "bg-amber-50 border border-amber-100 rounded-lg p-3", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-amber-800 font-medium flex items-start gap-2", children: [
|
|
57715
|
-
/* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 flex-shrink-0" }),
|
|
57716
|
-
"Impact: ",
|
|
57717
|
-
rec.impact
|
|
57718
|
-
] }) })
|
|
57719
|
-
] }),
|
|
57720
|
-
/* @__PURE__ */ jsxs("div", { className: "w-full md:w-1/2 lg:w-5/12 bg-gray-50 rounded-lg p-4 border border-gray-100", children: [
|
|
57721
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
57722
|
-
/* @__PURE__ */ jsxs("h4", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider flex items-center gap-2", children: [
|
|
57723
|
-
rec.evidence.type === "video" ? /* @__PURE__ */ jsx(PlayCircleIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-4 h-4" }),
|
|
57724
|
-
"Evidence: ",
|
|
57725
|
-
rec.evidence.label
|
|
57726
|
-
] }),
|
|
57727
|
-
rec.evidence.type === "video" && rec.evidence.videoUrls && rec.evidence.videoUrls.length > 1 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded", children: [
|
|
57728
|
-
rec.evidence.videoUrls.length,
|
|
57729
|
-
" clips"
|
|
57730
|
-
] })
|
|
57731
|
-
] }),
|
|
57732
|
-
rec.evidence.type === "video" && rec.evidence.videoUrls && /* @__PURE__ */ jsx(VideoCarousel, { urls: rec.evidence.videoUrls }),
|
|
57733
|
-
rec.evidence.type === "chart" && rec.evidence.chartData && /* @__PURE__ */ jsx(EvidenceChart, { data: rec.evidence.chartData })
|
|
57734
|
-
] })
|
|
57735
|
-
] }) })
|
|
57736
|
-
},
|
|
57737
|
-
rec.id
|
|
57738
|
-
)) : /* @__PURE__ */ jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-dashed border-gray-300", children: [
|
|
57739
|
-
/* @__PURE__ */ jsx("div", { className: "mx-auto flex items-center justify-center w-12 h-12 rounded-full bg-gray-100 mb-3", children: /* @__PURE__ */ jsx(FunnelIcon, { className: "w-6 h-6 text-gray-400" }) }),
|
|
57740
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-900", children: "No recommendations found" }),
|
|
57741
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Try adjusting your filters to see more results." }),
|
|
58353
|
+
),
|
|
58354
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-900 min-w-[160px] text-center", children: formatMonth(currentDate) }),
|
|
57742
58355
|
/* @__PURE__ */ jsx(
|
|
57743
58356
|
"button",
|
|
57744
58357
|
{
|
|
57745
|
-
onClick:
|
|
57746
|
-
className: "
|
|
57747
|
-
children: "
|
|
58358
|
+
onClick: nextMonth,
|
|
58359
|
+
className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
|
|
58360
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-5 h-5" })
|
|
57748
58361
|
}
|
|
57749
58362
|
)
|
|
58363
|
+
] }) }),
|
|
58364
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row items-center justify-between gap-4 mb-6", children: [
|
|
58365
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
58366
|
+
/* @__PURE__ */ jsxs(
|
|
58367
|
+
"button",
|
|
58368
|
+
{
|
|
58369
|
+
onClick: () => setSelectedStatus("all"),
|
|
58370
|
+
className: `px-4 py-2 rounded-lg text-sm font-medium transition-all cursor-pointer ${selectedStatus === "all" ? "bg-white text-gray-900 shadow-sm ring-1 ring-black/5" : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`,
|
|
58371
|
+
children: [
|
|
58372
|
+
"All ",
|
|
58373
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1.5 bg-gray-100 px-2 py-0.5 rounded-full text-xs text-gray-600", children: stats.all })
|
|
58374
|
+
]
|
|
58375
|
+
}
|
|
58376
|
+
),
|
|
58377
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
|
|
58378
|
+
/* @__PURE__ */ jsxs(
|
|
58379
|
+
"button",
|
|
58380
|
+
{
|
|
58381
|
+
onClick: () => setSelectedStatus("resolved"),
|
|
58382
|
+
className: `flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-all cursor-pointer ${selectedStatus === "resolved" ? "bg-green-50 text-green-700 ring-1 ring-green-200" : "text-gray-600 hover:bg-gray-100"}`,
|
|
58383
|
+
children: [
|
|
58384
|
+
/* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-4 h-4 text-green-500" }),
|
|
58385
|
+
"Resolved ",
|
|
58386
|
+
stats.resolved
|
|
58387
|
+
]
|
|
58388
|
+
}
|
|
58389
|
+
),
|
|
58390
|
+
/* @__PURE__ */ jsxs(
|
|
58391
|
+
"button",
|
|
58392
|
+
{
|
|
58393
|
+
onClick: () => setSelectedStatus("unresolved"),
|
|
58394
|
+
className: `flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-all cursor-pointer ${selectedStatus === "unresolved" ? "bg-red-50 text-red-700 ring-1 ring-red-200" : "text-gray-600 hover:bg-gray-100"}`,
|
|
58395
|
+
children: [
|
|
58396
|
+
/* @__PURE__ */ jsx(XCircleIcon, { className: "w-4 h-4 text-red-500" }),
|
|
58397
|
+
"Unresolved ",
|
|
58398
|
+
stats.unresolved
|
|
58399
|
+
]
|
|
58400
|
+
}
|
|
58401
|
+
)
|
|
58402
|
+
] }),
|
|
58403
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 w-full md:w-auto", children: [
|
|
58404
|
+
/* @__PURE__ */ jsx(
|
|
58405
|
+
"select",
|
|
58406
|
+
{
|
|
58407
|
+
value: selectedCategory,
|
|
58408
|
+
onChange: (e) => setSelectedCategory(e.target.value),
|
|
58409
|
+
className: "w-full md:w-auto appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
|
|
58410
|
+
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
|
|
58411
|
+
children: CATEGORY_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.id, children: opt.label }, opt.id))
|
|
58412
|
+
}
|
|
58413
|
+
),
|
|
58414
|
+
/* @__PURE__ */ jsx(
|
|
58415
|
+
"select",
|
|
58416
|
+
{
|
|
58417
|
+
value: selectedLineId,
|
|
58418
|
+
onChange: (e) => setSelectedLineId(e.target.value),
|
|
58419
|
+
className: "w-full md:w-auto appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
|
|
58420
|
+
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
|
|
58421
|
+
children: lineOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.id, children: opt.label }, opt.id))
|
|
58422
|
+
}
|
|
58423
|
+
)
|
|
58424
|
+
] })
|
|
57750
58425
|
] }),
|
|
57751
|
-
|
|
57752
|
-
|
|
58426
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
|
|
58427
|
+
filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => /* @__PURE__ */ jsx(
|
|
58428
|
+
motion.div,
|
|
58429
|
+
{
|
|
58430
|
+
initial: { opacity: 0, y: 20 },
|
|
58431
|
+
animate: { opacity: 1, y: 0 },
|
|
58432
|
+
transition: { delay: index * 0.1 },
|
|
58433
|
+
className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
|
|
58434
|
+
children: /* @__PURE__ */ jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
|
|
58435
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
|
|
58436
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 w-full", children: [
|
|
58437
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
58438
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${rec.type === "cycle_time" ? "bg-blue-100 text-blue-800" : rec.type === "efficiency" ? "bg-orange-100 text-orange-800" : "bg-gray-100 text-gray-800"}`, children: rec.type.replace("_", " ").toUpperCase() }),
|
|
58439
|
+
(rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-700", children: rec.shift_label || `Shift ${rec.shift_id}` }),
|
|
58440
|
+
rec.ticket_status === "solved" ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800", children: [
|
|
58441
|
+
/* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-3.5 h-3.5" }),
|
|
58442
|
+
"Resolved"
|
|
58443
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium border ${(rec.weeks_open || 0) > 2 ? "bg-red-50 text-red-700 border-red-200" : "bg-amber-50 text-amber-700 border-amber-200"}`, children: [
|
|
58444
|
+
/* @__PURE__ */ jsx(ClockIcon, { className: "w-3.5 h-3.5" }),
|
|
58445
|
+
"Open for ",
|
|
58446
|
+
rec.weeks_open || 1,
|
|
58447
|
+
" week",
|
|
58448
|
+
(rec.weeks_open || 1) !== 1 ? "s" : ""
|
|
58449
|
+
] })
|
|
58450
|
+
] }),
|
|
58451
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
58452
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: rec.title }),
|
|
58453
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1", children: [
|
|
58454
|
+
rec.location,
|
|
58455
|
+
" \u2022 ",
|
|
58456
|
+
rec.line
|
|
58457
|
+
] })
|
|
58458
|
+
] })
|
|
58459
|
+
] }) }),
|
|
58460
|
+
/* @__PURE__ */ jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { children: rec.description }) }),
|
|
58461
|
+
/* @__PURE__ */ jsx("div", { className: "bg-amber-50 border border-amber-100 rounded-lg p-3", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-amber-800 font-medium flex items-start gap-2", children: [
|
|
58462
|
+
/* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 flex-shrink-0" }),
|
|
58463
|
+
"Impact: ",
|
|
58464
|
+
rec.impact
|
|
58465
|
+
] }) })
|
|
58466
|
+
] }),
|
|
58467
|
+
/* @__PURE__ */ jsx("div", { className: "w-full md:w-1/2 lg:w-5/12 bg-gray-50 rounded-lg p-4 border border-gray-100", children: Array.isArray(rec.evidence) && rec.evidence.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-4", children: rec.evidence.map((ev, idx) => {
|
|
58468
|
+
const isVideo = ev.type === "video_gallery";
|
|
58469
|
+
const isChart = ev.type === "bar_chart" || ev.type === "timeseries" || ev.type === "pie_chart";
|
|
58470
|
+
const title = ev.title || "Evidence";
|
|
58471
|
+
const clips = Array.isArray(ev.clips) ? ev.clips : [];
|
|
58472
|
+
const clipCount = clips.length;
|
|
58473
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
58474
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
58475
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider flex items-center gap-2", children: [
|
|
58476
|
+
isVideo ? /* @__PURE__ */ jsx(PlayCircleIcon, { className: "w-4 h-4" }) : isChart ? /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-4 h-4" }),
|
|
58477
|
+
title
|
|
58478
|
+
] }),
|
|
58479
|
+
isVideo && clipCount > 1 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded", children: [
|
|
58480
|
+
clipCount,
|
|
58481
|
+
" clips"
|
|
58482
|
+
] })
|
|
58483
|
+
] }),
|
|
58484
|
+
ev.type === "video_gallery" && /* @__PURE__ */ jsx(ClipVideoCarousel, { clips, clipsService }),
|
|
58485
|
+
ev.type === "bar_chart" && (Array.isArray(ev.x) && Array.isArray(ev.y) || Array.isArray(ev.data) && Array.isArray(ev.series)) && /* @__PURE__ */ jsx(
|
|
58486
|
+
BarChartEvidence,
|
|
58487
|
+
{
|
|
58488
|
+
x: ev.x,
|
|
58489
|
+
y: ev.y,
|
|
58490
|
+
data: ev.data,
|
|
58491
|
+
series: ev.series,
|
|
58492
|
+
unit: ev.unit,
|
|
58493
|
+
referenceLines: Array.isArray(ev.reference_lines) ? ev.reference_lines : void 0
|
|
58494
|
+
}
|
|
58495
|
+
),
|
|
58496
|
+
ev.type === "pie_chart" && Array.isArray(ev.data) && /* @__PURE__ */ jsx(PieChartEvidence, { data: ev.data }),
|
|
58497
|
+
ev.type === "timeseries" && Array.isArray(ev.x) && Array.isArray(ev.y) && /* @__PURE__ */ jsx(LineChartEvidence, { x: ev.x, y: ev.y, unit: ev.unit }),
|
|
58498
|
+
ev.type === "table" && Array.isArray(ev.columns) && Array.isArray(ev.rows) && /* @__PURE__ */ jsx(TableEvidence, { columns: ev.columns, rows: ev.rows })
|
|
58499
|
+
] }, `${rec.id}-ev-${idx}`);
|
|
58500
|
+
}) }) : /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
|
|
58501
|
+
] }) })
|
|
58502
|
+
},
|
|
58503
|
+
rec.id
|
|
58504
|
+
)) : (
|
|
58505
|
+
// Success State (Zero Tickets)
|
|
58506
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-gray-200 shadow-sm", children: [
|
|
58507
|
+
/* @__PURE__ */ jsx("div", { className: "mx-auto flex items-center justify-center w-16 h-16 rounded-full bg-green-50 mb-4", children: /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-8 h-8 text-green-500" }) }),
|
|
58508
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "No tickets found" }),
|
|
58509
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 max-w-md mx-auto", children: selectedCategory !== "all" || selectedLineId !== "all" || selectedStatus !== "all" ? "Try adjusting your filters to see more results." : "All monitored long-term patterns were evaluated and remained within limits. This is a success state!" }),
|
|
58510
|
+
(selectedCategory !== "all" || selectedLineId !== "all" || selectedStatus !== "all") && /* @__PURE__ */ jsx(
|
|
58511
|
+
"button",
|
|
58512
|
+
{
|
|
58513
|
+
onClick: clearFilters,
|
|
58514
|
+
className: "mt-6 text-sm text-blue-600 hover:text-blue-800 font-medium bg-blue-50 px-4 py-2 rounded-lg hover:bg-blue-100 transition-colors",
|
|
58515
|
+
children: "Clear filters"
|
|
58516
|
+
}
|
|
58517
|
+
)
|
|
58518
|
+
] })
|
|
58519
|
+
),
|
|
58520
|
+
filteredRecommendations.length > 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-10", children: /* @__PURE__ */ jsx("p", { className: "text-gray-400 text-sm", children: "More recommendations will appear here as we analyze more data." }) })
|
|
58521
|
+
] })
|
|
58522
|
+
] })
|
|
57753
58523
|
] });
|
|
57754
58524
|
};
|
|
57755
58525
|
var ImprovementCenterView_default = ImprovementCenterView;
|
|
@@ -58208,175 +58978,4 @@ var streamProxyConfig = {
|
|
|
58208
58978
|
}
|
|
58209
58979
|
};
|
|
58210
58980
|
|
|
58211
|
-
// src/lib/api/s3-clips-parser.ts
|
|
58212
|
-
function parseS3Uri(s3Uri, sopCategories) {
|
|
58213
|
-
const path = new URL(s3Uri).pathname;
|
|
58214
|
-
const parts = path.split("/").filter((p) => p);
|
|
58215
|
-
if (s3Uri.includes("missed_qchecks")) {
|
|
58216
|
-
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
58217
|
-
return null;
|
|
58218
|
-
}
|
|
58219
|
-
if (parts.length < 8) {
|
|
58220
|
-
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
58221
|
-
return null;
|
|
58222
|
-
}
|
|
58223
|
-
try {
|
|
58224
|
-
const datePart = parts[2];
|
|
58225
|
-
const shiftPart = parts[3];
|
|
58226
|
-
const violationType = parts[4];
|
|
58227
|
-
let folderName = "";
|
|
58228
|
-
let timestamp = "";
|
|
58229
|
-
for (let i = 5; i < parts.length; i++) {
|
|
58230
|
-
const part = parts[i];
|
|
58231
|
-
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
58232
|
-
folderName = part;
|
|
58233
|
-
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
58234
|
-
if (timeMatch) {
|
|
58235
|
-
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
58236
|
-
break;
|
|
58237
|
-
}
|
|
58238
|
-
}
|
|
58239
|
-
}
|
|
58240
|
-
if (!timestamp) {
|
|
58241
|
-
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
58242
|
-
timestamp = "00:00:00";
|
|
58243
|
-
}
|
|
58244
|
-
let severity = "low";
|
|
58245
|
-
let type = "bottleneck";
|
|
58246
|
-
let description = "Analysis Clip";
|
|
58247
|
-
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
58248
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
58249
|
-
const matchedCategory = sopCategories.find((category) => {
|
|
58250
|
-
const categoryId = category.id.toLowerCase();
|
|
58251
|
-
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
58252
|
-
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
58253
|
-
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
58254
|
-
});
|
|
58255
|
-
if (matchedCategory) {
|
|
58256
|
-
type = matchedCategory.id;
|
|
58257
|
-
description = matchedCategory.description || matchedCategory.label;
|
|
58258
|
-
if (matchedCategory.color.includes("red")) {
|
|
58259
|
-
severity = "high";
|
|
58260
|
-
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
58261
|
-
severity = "medium";
|
|
58262
|
-
} else {
|
|
58263
|
-
severity = "low";
|
|
58264
|
-
}
|
|
58265
|
-
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
58266
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
58267
|
-
}
|
|
58268
|
-
}
|
|
58269
|
-
switch (normalizedViolationType) {
|
|
58270
|
-
case "idle_time":
|
|
58271
|
-
case "idle":
|
|
58272
|
-
case "low_value":
|
|
58273
|
-
case "low value":
|
|
58274
|
-
case "low_value_moment":
|
|
58275
|
-
case "low_value_moments":
|
|
58276
|
-
case "low value moment":
|
|
58277
|
-
case "low value moments":
|
|
58278
|
-
type = "low_value";
|
|
58279
|
-
severity = "low";
|
|
58280
|
-
description = "Idle Time Detected";
|
|
58281
|
-
break;
|
|
58282
|
-
case "sop_deviation":
|
|
58283
|
-
type = "missing_quality_check";
|
|
58284
|
-
severity = "high";
|
|
58285
|
-
description = "SOP Deviations";
|
|
58286
|
-
break;
|
|
58287
|
-
case "long_cycle_time":
|
|
58288
|
-
severity = "high";
|
|
58289
|
-
type = "long_cycle_time";
|
|
58290
|
-
description = "Long Cycle Time Detected";
|
|
58291
|
-
break;
|
|
58292
|
-
case "best_cycle_time":
|
|
58293
|
-
type = "best_cycle_time";
|
|
58294
|
-
severity = "low";
|
|
58295
|
-
description = "Best Cycle Time Performance";
|
|
58296
|
-
break;
|
|
58297
|
-
case "worst_cycle_time":
|
|
58298
|
-
type = "worst_cycle_time";
|
|
58299
|
-
severity = "high";
|
|
58300
|
-
description = "Worst Cycle Time Performance";
|
|
58301
|
-
break;
|
|
58302
|
-
case "cycle_completion":
|
|
58303
|
-
case "completed_cycles":
|
|
58304
|
-
case "completed_cycle":
|
|
58305
|
-
type = "cycle_completion";
|
|
58306
|
-
severity = "low";
|
|
58307
|
-
description = "Cycle Completion";
|
|
58308
|
-
break;
|
|
58309
|
-
case "running_cycle":
|
|
58310
|
-
case "active_cycle":
|
|
58311
|
-
case "production_cycle":
|
|
58312
|
-
type = "running_cycle";
|
|
58313
|
-
severity = "low";
|
|
58314
|
-
description = "Active Production Cycle";
|
|
58315
|
-
break;
|
|
58316
|
-
case "setup_state":
|
|
58317
|
-
case "machine_setup":
|
|
58318
|
-
case "line_setup":
|
|
58319
|
-
type = "setup_state";
|
|
58320
|
-
severity = "medium";
|
|
58321
|
-
description = "Machine Setup Activity";
|
|
58322
|
-
break;
|
|
58323
|
-
case "medium_bottleneck":
|
|
58324
|
-
severity = "medium";
|
|
58325
|
-
description = "Medium Bottleneck Identified";
|
|
58326
|
-
break;
|
|
58327
|
-
case "minor_bottleneck":
|
|
58328
|
-
case "mild_bottleneck":
|
|
58329
|
-
severity = "low";
|
|
58330
|
-
description = "Minor Bottleneck Identified";
|
|
58331
|
-
break;
|
|
58332
|
-
default:
|
|
58333
|
-
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
58334
|
-
type = "missing_quality_check";
|
|
58335
|
-
severity = "high";
|
|
58336
|
-
description = "SOP Deviations";
|
|
58337
|
-
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
58338
|
-
type = "worst_cycle_time";
|
|
58339
|
-
severity = "high";
|
|
58340
|
-
description = "Worst Cycle Time Performance";
|
|
58341
|
-
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
58342
|
-
type = "best_cycle_time";
|
|
58343
|
-
severity = "low";
|
|
58344
|
-
description = "Best Cycle Time Performance";
|
|
58345
|
-
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
58346
|
-
type = "long_cycle_time";
|
|
58347
|
-
severity = "high";
|
|
58348
|
-
description = "Long Cycle Time Detected";
|
|
58349
|
-
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
58350
|
-
type = "cycle_completion";
|
|
58351
|
-
severity = "low";
|
|
58352
|
-
description = "Cycle Completion";
|
|
58353
|
-
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
58354
|
-
type = "running_cycle";
|
|
58355
|
-
severity = "low";
|
|
58356
|
-
description = "Active Production Cycle";
|
|
58357
|
-
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
58358
|
-
type = "setup_state";
|
|
58359
|
-
severity = "medium";
|
|
58360
|
-
description = "Machine Setup Activity";
|
|
58361
|
-
} else {
|
|
58362
|
-
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
58363
|
-
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
58364
|
-
}
|
|
58365
|
-
break;
|
|
58366
|
-
}
|
|
58367
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
58368
|
-
} catch (error) {
|
|
58369
|
-
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
58370
|
-
return null;
|
|
58371
|
-
}
|
|
58372
|
-
}
|
|
58373
|
-
function shuffleArray(array) {
|
|
58374
|
-
const shuffled = [...array];
|
|
58375
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
58376
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
58377
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
58378
|
-
}
|
|
58379
|
-
return shuffled;
|
|
58380
|
-
}
|
|
58381
|
-
|
|
58382
58981
|
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, buildKPIsFromLineMetricsRow, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
|