@optifye/dashboard-core 6.10.6 → 6.10.8
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 +21 -0
- package/dist/index.d.mts +26 -19
- package/dist/index.d.ts +26 -19
- package/dist/index.js +895 -538
- package/dist/index.mjs +897 -540
- 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) {
|
|
@@ -29022,6 +29040,38 @@ var ERROR_MAPPING = {
|
|
|
29022
29040
|
canRetry: false
|
|
29023
29041
|
}
|
|
29024
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
|
+
};
|
|
29025
29075
|
var hlsVideoPlayerStyles = `
|
|
29026
29076
|
.hls-video-player-container {
|
|
29027
29077
|
width: 100%;
|
|
@@ -29123,8 +29173,12 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29123
29173
|
const videoRef = useRef(null);
|
|
29124
29174
|
const hlsRef = useRef(null);
|
|
29125
29175
|
const blobUrlRef = useRef(null);
|
|
29176
|
+
const clipIdRef = useRef(null);
|
|
29177
|
+
const r2FallbackAttemptedRef = useRef(false);
|
|
29126
29178
|
const [isReady, setIsReady] = useState(false);
|
|
29127
29179
|
const [isLoading, setIsLoading] = useState(true);
|
|
29180
|
+
const [overrideSource, setOverrideSource] = useState(null);
|
|
29181
|
+
const effectiveSrc = overrideSource && overrideSource.baseSrc === src ? overrideSource.value : src;
|
|
29128
29182
|
const [showControls, setShowControls] = useState(true);
|
|
29129
29183
|
const [controlsPinned, setControlsPinned] = useState(false);
|
|
29130
29184
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
@@ -29184,10 +29238,16 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29184
29238
|
onSeeked,
|
|
29185
29239
|
onLoadingChange
|
|
29186
29240
|
]);
|
|
29241
|
+
useEffect(() => {
|
|
29242
|
+
clipIdRef.current = extractClipIdFromSource(src);
|
|
29243
|
+
r2FallbackAttemptedRef.current = false;
|
|
29244
|
+
setOverrideSource(null);
|
|
29245
|
+
}, [src]);
|
|
29187
29246
|
const stableHlsConfigRef = useRef(hlsConfig);
|
|
29188
29247
|
const stableOptionsRef = useRef(options);
|
|
29189
29248
|
const configSignatureRef = useRef("");
|
|
29190
29249
|
const [configVersion, setConfigVersion] = useState(0);
|
|
29250
|
+
const r2WorkerDomain = process.env.NEXT_PUBLIC_R2_WORKER_DOMAIN || "https://r2-stream-proxy.optifye-r2.workers.dev";
|
|
29191
29251
|
useEffect(() => {
|
|
29192
29252
|
const serialized = JSON.stringify({
|
|
29193
29253
|
hlsConfig: hlsConfig || null,
|
|
@@ -29206,6 +29266,50 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29206
29266
|
setConfigVersion((prev) => prev + 1);
|
|
29207
29267
|
}
|
|
29208
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]);
|
|
29209
29313
|
const cleanupBlobUrl = useCallback(() => {
|
|
29210
29314
|
if (blobUrlRef.current) {
|
|
29211
29315
|
URL.revokeObjectURL(blobUrlRef.current);
|
|
@@ -29247,70 +29351,55 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29247
29351
|
};
|
|
29248
29352
|
}, [dispose]);
|
|
29249
29353
|
const initializePlayer = useCallback(async () => {
|
|
29250
|
-
if (!videoRef.current || !
|
|
29354
|
+
if (!videoRef.current || !effectiveSrc) return;
|
|
29251
29355
|
const video = videoRef.current;
|
|
29252
29356
|
const player = playerLikeObject();
|
|
29253
29357
|
let authToken = null;
|
|
29254
29358
|
try {
|
|
29255
29359
|
authToken = await getAuthTokenForHls(supabase);
|
|
29256
|
-
if (authToken) {
|
|
29257
|
-
console.log("[HLS Auth] Retrieved token for R2 streaming");
|
|
29258
|
-
} else {
|
|
29360
|
+
if (!authToken) {
|
|
29259
29361
|
console.warn("[HLS Auth] No active session - R2 streaming may fail");
|
|
29260
29362
|
}
|
|
29261
29363
|
} catch (error) {
|
|
29262
|
-
console.error("[HLS Auth] Error
|
|
29364
|
+
console.error("[HLS Auth] Error getting auth token:", error);
|
|
29263
29365
|
}
|
|
29264
|
-
const r2WorkerDomain = "https://r2-stream-proxy.optifye-r2.workers.dev";
|
|
29265
29366
|
const mergedHlsConfig = {
|
|
29266
29367
|
...BASE_HLS_CONFIG,
|
|
29267
29368
|
...stableHlsConfigRef.current || {},
|
|
29268
29369
|
...stableOptionsRef.current || {},
|
|
29269
|
-
// Modern HLS.js uses Fetch API - override fetch to add Authorization header
|
|
29370
|
+
// Modern HLS.js uses Fetch API - override fetch to add Authorization header for R2 Worker requests
|
|
29270
29371
|
fetchSetup: function(context, initParams) {
|
|
29271
29372
|
const url = context.url;
|
|
29272
|
-
|
|
29273
|
-
|
|
29274
|
-
|
|
29275
|
-
|
|
29276
|
-
|
|
29277
|
-
if (isR2) {
|
|
29278
|
-
if (authToken) {
|
|
29279
|
-
initParams.headers = {
|
|
29280
|
-
...initParams.headers,
|
|
29281
|
-
"Authorization": `Bearer ${authToken}`
|
|
29282
|
-
};
|
|
29283
|
-
console.log("[HLS Auth] \u2705 Injected JWT for R2 request:", url);
|
|
29284
|
-
} else {
|
|
29285
|
-
console.warn("[HLS Auth] \u26A0\uFE0F No token available for R2 request:", url);
|
|
29286
|
-
}
|
|
29287
|
-
} else {
|
|
29288
|
-
console.log("[HLS Auth] CloudFront URL - no auth needed:", url);
|
|
29373
|
+
if (isR2WorkerUrl(url, r2WorkerDomain) && authToken) {
|
|
29374
|
+
initParams.headers = {
|
|
29375
|
+
...initParams.headers,
|
|
29376
|
+
"Authorization": `Bearer ${authToken}`
|
|
29377
|
+
};
|
|
29289
29378
|
}
|
|
29290
29379
|
return new Request(url, initParams);
|
|
29291
29380
|
}
|
|
29292
29381
|
};
|
|
29293
29382
|
cleanupBlobUrl();
|
|
29294
|
-
const isHLS =
|
|
29383
|
+
const isHLS = effectiveSrc.endsWith(".m3u8") || effectiveSrc.startsWith("#EXTM3U");
|
|
29295
29384
|
if (isHLS) {
|
|
29296
|
-
if (
|
|
29385
|
+
if (effectiveSrc.startsWith("#EXTM3U")) {
|
|
29297
29386
|
const safariMode = isSafari();
|
|
29298
29387
|
const browserName = getBrowserName();
|
|
29299
29388
|
if (safariMode) {
|
|
29300
|
-
const clipIdMatch =
|
|
29389
|
+
const clipIdMatch = effectiveSrc.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
29301
29390
|
if (clipIdMatch) {
|
|
29302
29391
|
const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
29303
29392
|
console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
|
|
29304
29393
|
video.src = proxyUrl;
|
|
29305
29394
|
} else {
|
|
29306
29395
|
console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
|
|
29307
|
-
const blob = new Blob([
|
|
29396
|
+
const blob = new Blob([effectiveSrc], { type: "application/vnd.apple.mpegurl" });
|
|
29308
29397
|
const blobUrl = URL.createObjectURL(blob);
|
|
29309
29398
|
blobUrlRef.current = blobUrl;
|
|
29310
29399
|
video.src = blobUrl;
|
|
29311
29400
|
}
|
|
29312
29401
|
} else if (Hls3.isSupported()) {
|
|
29313
|
-
const blob = new Blob([
|
|
29402
|
+
const blob = new Blob([effectiveSrc], { type: "application/vnd.apple.mpegurl" });
|
|
29314
29403
|
const blobUrl = URL.createObjectURL(blob);
|
|
29315
29404
|
blobUrlRef.current = blobUrl;
|
|
29316
29405
|
console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
|
|
@@ -29323,24 +29412,16 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29323
29412
|
});
|
|
29324
29413
|
hls.on(Events.ERROR, (event, data) => {
|
|
29325
29414
|
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
29415
|
+
if (maybeHandleR2Fallback(data)) {
|
|
29416
|
+
return;
|
|
29417
|
+
}
|
|
29326
29418
|
if (data.fatal) {
|
|
29327
29419
|
let errorInfo;
|
|
29328
29420
|
switch (data.type) {
|
|
29329
29421
|
case ErrorTypes.NETWORK_ERROR:
|
|
29330
|
-
|
|
29331
|
-
|
|
29332
|
-
|
|
29333
|
-
type: "recoverable",
|
|
29334
|
-
message: "Authentication expired. Please refresh the page.",
|
|
29335
|
-
canRetry: true,
|
|
29336
|
-
details: "JWT_EXPIRED"
|
|
29337
|
-
};
|
|
29338
|
-
console.error("[HLS Auth] 401 Unauthorized - token may be expired");
|
|
29339
|
-
} else {
|
|
29340
|
-
errorInfo = ERROR_MAPPING.networkError;
|
|
29341
|
-
console.log("[HlsVideoPlayer] Attempting to recover from network error");
|
|
29342
|
-
hls.startLoad();
|
|
29343
|
-
}
|
|
29422
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
29423
|
+
console.log("[HlsVideoPlayer] Attempting to recover from network error");
|
|
29424
|
+
hls.startLoad();
|
|
29344
29425
|
break;
|
|
29345
29426
|
case ErrorTypes.MEDIA_ERROR:
|
|
29346
29427
|
errorInfo = ERROR_MAPPING.mediaError;
|
|
@@ -29381,23 +29462,15 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29381
29462
|
});
|
|
29382
29463
|
hls.on(Events.ERROR, (event, data) => {
|
|
29383
29464
|
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
29465
|
+
if (maybeHandleR2Fallback(data)) {
|
|
29466
|
+
return;
|
|
29467
|
+
}
|
|
29384
29468
|
if (data.fatal) {
|
|
29385
29469
|
let errorInfo;
|
|
29386
29470
|
switch (data.type) {
|
|
29387
29471
|
case ErrorTypes.NETWORK_ERROR:
|
|
29388
|
-
|
|
29389
|
-
|
|
29390
|
-
code: 401,
|
|
29391
|
-
type: "recoverable",
|
|
29392
|
-
message: "Authentication expired. Please refresh the page.",
|
|
29393
|
-
canRetry: true,
|
|
29394
|
-
details: "JWT_EXPIRED"
|
|
29395
|
-
};
|
|
29396
|
-
console.error("[HLS Auth] 401 Unauthorized - token may be expired");
|
|
29397
|
-
} else {
|
|
29398
|
-
errorInfo = ERROR_MAPPING.networkError;
|
|
29399
|
-
hls.startLoad();
|
|
29400
|
-
}
|
|
29472
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
29473
|
+
hls.startLoad();
|
|
29401
29474
|
break;
|
|
29402
29475
|
case ErrorTypes.MEDIA_ERROR:
|
|
29403
29476
|
errorInfo = ERROR_MAPPING.mediaError;
|
|
@@ -29411,14 +29484,14 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29411
29484
|
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
29412
29485
|
}
|
|
29413
29486
|
});
|
|
29414
|
-
hls.loadSource(
|
|
29487
|
+
hls.loadSource(effectiveSrc);
|
|
29415
29488
|
hls.attachMedia(video);
|
|
29416
29489
|
} else {
|
|
29417
|
-
video.src =
|
|
29490
|
+
video.src = effectiveSrc;
|
|
29418
29491
|
}
|
|
29419
29492
|
}
|
|
29420
29493
|
} else {
|
|
29421
|
-
video.src =
|
|
29494
|
+
video.src = effectiveSrc;
|
|
29422
29495
|
}
|
|
29423
29496
|
const handleCanPlay = () => {
|
|
29424
29497
|
if (!hlsRef.current) {
|
|
@@ -29494,6 +29567,11 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29494
29567
|
eventCallbacksRef.current.onSeeked?.(player);
|
|
29495
29568
|
};
|
|
29496
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
|
+
}
|
|
29497
29575
|
const error = video.error;
|
|
29498
29576
|
if (error) {
|
|
29499
29577
|
const errorInfo = {
|
|
@@ -29542,10 +29620,14 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29542
29620
|
video.removeEventListener("ratechange", handlePlaybackRateChange2);
|
|
29543
29621
|
};
|
|
29544
29622
|
}, [
|
|
29545
|
-
|
|
29623
|
+
effectiveSrc,
|
|
29546
29624
|
cleanupBlobUrl,
|
|
29547
29625
|
playerLikeObject,
|
|
29548
|
-
configVersion
|
|
29626
|
+
configVersion,
|
|
29627
|
+
supabase,
|
|
29628
|
+
attemptS3Fallback,
|
|
29629
|
+
maybeHandleR2Fallback,
|
|
29630
|
+
r2WorkerDomain
|
|
29549
29631
|
]);
|
|
29550
29632
|
useEffect(() => {
|
|
29551
29633
|
let cleanup;
|
|
@@ -29561,7 +29643,7 @@ var HlsVideoPlayer = forwardRef(({
|
|
|
29561
29643
|
cleanupBlobUrl();
|
|
29562
29644
|
setIsReady(false);
|
|
29563
29645
|
};
|
|
29564
|
-
}, [
|
|
29646
|
+
}, [effectiveSrc, initializePlayer, cleanupBlobUrl]);
|
|
29565
29647
|
useEffect(() => {
|
|
29566
29648
|
if (videoRef.current) {
|
|
29567
29649
|
if (autoplay) {
|
|
@@ -36391,7 +36473,16 @@ var STATIC_COLORS = {
|
|
|
36391
36473
|
"Operator Idle": "#8b5cf6"
|
|
36392
36474
|
// violet-500 - Low Priority/Behavioral
|
|
36393
36475
|
};
|
|
36476
|
+
var PRODUCTIVE_COLOR = "#00AB45";
|
|
36477
|
+
var IDLE_COLOR = "#e5e7eb";
|
|
36394
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
|
+
}
|
|
36395
36486
|
if (STATIC_COLORS[name]) {
|
|
36396
36487
|
return STATIC_COLORS[name];
|
|
36397
36488
|
}
|
|
@@ -36535,6 +36626,7 @@ var IdleTimeReasonChart = ({
|
|
|
36535
36626
|
)
|
|
36536
36627
|
] });
|
|
36537
36628
|
};
|
|
36629
|
+
var IdleTimeReasonChart_default = IdleTimeReasonChart;
|
|
36538
36630
|
var DEFAULT_PERFORMANCE_DATA = {
|
|
36539
36631
|
avg_efficiency: 0,
|
|
36540
36632
|
underperforming_workspaces: 0,
|
|
@@ -37344,7 +37436,7 @@ var LinePdfGenerator = ({
|
|
|
37344
37436
|
};
|
|
37345
37437
|
const hourlyTimeRanges = lineInfo.metrics.shift_start ? getHourlyTimeRanges(lineInfo.metrics.shift_start, lineInfo.metrics.shift_end) : [];
|
|
37346
37438
|
const shiftDuration = hourlyTimeRanges.length || 11;
|
|
37347
|
-
const targetOutputPerHour = Math.round(lineInfo.metrics.
|
|
37439
|
+
const targetOutputPerHour = Math.round(lineInfo.metrics.threshold_pph ?? 0);
|
|
37348
37440
|
let hourlyActualOutput = [];
|
|
37349
37441
|
if (lineInfo.metrics.output_hourly && Object.keys(lineInfo.metrics.output_hourly).length > 0) {
|
|
37350
37442
|
const [startHourStr, startMinuteStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
|
|
@@ -41452,6 +41544,17 @@ var SideNavBar = memo(({
|
|
|
41452
41544
|
});
|
|
41453
41545
|
onMobileMenuClose?.();
|
|
41454
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]);
|
|
41455
41558
|
const handleTargetsClick = useCallback(() => {
|
|
41456
41559
|
navigate("/targets", {
|
|
41457
41560
|
trackingEvent: {
|
|
@@ -41569,6 +41672,7 @@ var SideNavBar = memo(({
|
|
|
41569
41672
|
const homeButtonClasses = useMemo(() => getButtonClasses("/"), [getButtonClasses, pathname]);
|
|
41570
41673
|
const leaderboardButtonClasses = useMemo(() => getButtonClasses("/leaderboard"), [getButtonClasses, pathname]);
|
|
41571
41674
|
const kpisButtonClasses = useMemo(() => getButtonClasses("/kpis"), [getButtonClasses, pathname]);
|
|
41675
|
+
const improvementButtonClasses = useMemo(() => getButtonClasses("/improvement-center"), [getButtonClasses, pathname]);
|
|
41572
41676
|
const targetsButtonClasses = useMemo(() => getButtonClasses("/targets"), [getButtonClasses, pathname]);
|
|
41573
41677
|
const shiftsButtonClasses = useMemo(() => getButtonClasses("/shifts"), [getButtonClasses, pathname]);
|
|
41574
41678
|
const teamManagementButtonClasses = useMemo(() => getButtonClasses("/team-management"), [getButtonClasses, pathname]);
|
|
@@ -41636,6 +41740,22 @@ var SideNavBar = memo(({
|
|
|
41636
41740
|
]
|
|
41637
41741
|
}
|
|
41638
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
|
+
),
|
|
41639
41759
|
/* @__PURE__ */ jsxs(
|
|
41640
41760
|
"button",
|
|
41641
41761
|
{
|
|
@@ -41836,6 +41956,19 @@ var SideNavBar = memo(({
|
|
|
41836
41956
|
]
|
|
41837
41957
|
}
|
|
41838
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
|
+
),
|
|
41839
41972
|
/* @__PURE__ */ jsxs(
|
|
41840
41973
|
"button",
|
|
41841
41974
|
{
|
|
@@ -57650,135 +57783,258 @@ Please ensure:
|
|
|
57650
57783
|
}
|
|
57651
57784
|
var AuthenticatedTicketsView = withAuth(React24__default.memo(TicketsView));
|
|
57652
57785
|
var TicketsView_default = TicketsView;
|
|
57653
|
-
|
|
57654
|
-
|
|
57655
|
-
|
|
57656
|
-
|
|
57657
|
-
|
|
57658
|
-
|
|
57659
|
-
|
|
57660
|
-
|
|
57661
|
-
evidence: {
|
|
57662
|
-
type: "video",
|
|
57663
|
-
label: "Operator Cycle Recording",
|
|
57664
|
-
videoUrls: [
|
|
57665
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4",
|
|
57666
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4",
|
|
57667
|
-
// Mock duplicate for demo
|
|
57668
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4"
|
|
57669
|
-
// Mock duplicate for demo
|
|
57670
|
-
]
|
|
57671
|
-
},
|
|
57672
|
-
impact: "Potential 50% efficiency gain or standard adjustment needed.",
|
|
57673
|
-
date: "Today",
|
|
57674
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
57675
|
-
},
|
|
57676
|
-
{
|
|
57677
|
-
id: "rec-2",
|
|
57678
|
-
type: "efficiency",
|
|
57679
|
-
title: "Consistent Low Efficiency Period",
|
|
57680
|
-
location: "Line 2",
|
|
57681
|
-
line: "Line 2",
|
|
57682
|
-
description: "Efficiency from 7pm - 8pm is consistently lower than standard. This pattern has been observed for the last 3 days.",
|
|
57683
|
-
evidence: {
|
|
57684
|
-
type: "chart",
|
|
57685
|
-
label: "7pm - 8pm Efficiency Trend",
|
|
57686
|
-
chartData: [
|
|
57687
|
-
{ label: "7:00", value: 45, standard: 85 },
|
|
57688
|
-
{ label: "7:15", value: 42, standard: 85 },
|
|
57689
|
-
{ label: "7:30", value: 40, standard: 85 },
|
|
57690
|
-
{ label: "7:45", value: 48, standard: 85 },
|
|
57691
|
-
{ label: "8:00", value: 50, standard: 85 }
|
|
57692
|
-
]
|
|
57693
|
-
},
|
|
57694
|
-
impact: "Loss of approx. 40 units per hour during shift changeover.",
|
|
57695
|
-
date: "Today",
|
|
57696
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
57697
|
-
},
|
|
57698
|
-
{
|
|
57699
|
-
id: "rec-3",
|
|
57700
|
-
type: "downtime",
|
|
57701
|
-
title: "Recurring Micro-stoppages",
|
|
57702
|
-
location: "Unit 3: Conveyor",
|
|
57703
|
-
line: "Line 1",
|
|
57704
|
-
description: "Frequent micro-stoppages detected every 15 minutes. This suggests a potential jam or sensor issue.",
|
|
57705
|
-
evidence: {
|
|
57706
|
-
type: "chart",
|
|
57707
|
-
label: "Downtime Events",
|
|
57708
|
-
chartData: [
|
|
57709
|
-
{ label: "9:00", value: 2, standard: 0 },
|
|
57710
|
-
{ label: "9:15", value: 3, standard: 0 },
|
|
57711
|
-
{ label: "9:30", value: 1, standard: 0 },
|
|
57712
|
-
{ label: "9:45", value: 4, standard: 0 },
|
|
57713
|
-
{ label: "10:00", value: 2, standard: 0 }
|
|
57714
|
-
]
|
|
57715
|
-
},
|
|
57716
|
-
impact: "Cumulative downtime of 45 mins per shift.",
|
|
57717
|
-
date: "Yesterday",
|
|
57718
|
-
timestamp: new Date(Date.now() - 864e5).toISOString()
|
|
57719
|
-
},
|
|
57720
|
-
{
|
|
57721
|
-
id: "rec-4",
|
|
57722
|
-
type: "efficiency",
|
|
57723
|
-
title: "High Idle Time Detected",
|
|
57724
|
-
location: "Packaging Station",
|
|
57725
|
-
line: "Line 2",
|
|
57726
|
-
description: "Operator idle time exceeds 20% of shift duration. Consider rebalancing workload.",
|
|
57727
|
-
evidence: {
|
|
57728
|
-
type: "video",
|
|
57729
|
-
label: "Idle Time Observation",
|
|
57730
|
-
videoUrls: [
|
|
57731
|
-
"/faa70d7a-d687-40c7-b01f-e04a3bb5a425_cam1_cycle_completion_20250708_091413_a54ee206.mp4"
|
|
57732
|
-
]
|
|
57733
|
-
},
|
|
57734
|
-
impact: "Potential to reassign 1 FTE to other tasks.",
|
|
57735
|
-
date: "Yesterday",
|
|
57736
|
-
timestamp: new Date(Date.now() - 864e5).toISOString()
|
|
57737
|
-
},
|
|
57738
|
-
{
|
|
57739
|
-
id: "rec-5",
|
|
57740
|
-
type: "cycle_time",
|
|
57741
|
-
title: "Slow Setup Time",
|
|
57742
|
-
location: "Machine 5",
|
|
57743
|
-
line: "Line 1",
|
|
57744
|
-
description: "Setup time for product changeover took 45 mins, standard is 20 mins.",
|
|
57745
|
-
evidence: {
|
|
57746
|
-
type: "chart",
|
|
57747
|
-
label: "Changeover Duration",
|
|
57748
|
-
chartData: [
|
|
57749
|
-
{ label: "Prev 1", value: 25, standard: 20 },
|
|
57750
|
-
{ label: "Prev 2", value: 22, standard: 20 },
|
|
57751
|
-
{ label: "Current", value: 45, standard: 20 },
|
|
57752
|
-
{ label: "Avg", value: 30, standard: 20 },
|
|
57753
|
-
{ label: "Target", value: 20, standard: 20 }
|
|
57754
|
-
]
|
|
57755
|
-
},
|
|
57756
|
-
impact: "25 mins of lost production time.",
|
|
57757
|
-
date: "2 Days Ago",
|
|
57758
|
-
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;
|
|
57759
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" }
|
|
57760
57962
|
];
|
|
57761
|
-
var
|
|
57963
|
+
var ClipVideoCarousel = ({ clips, clipsService }) => {
|
|
57762
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]);
|
|
57763
58011
|
const nextVideo = () => {
|
|
57764
|
-
setCurrentIndex((prev) => (prev + 1) %
|
|
58012
|
+
setCurrentIndex((prev) => (prev + 1) % Math.max(videos.length, 1));
|
|
57765
58013
|
};
|
|
57766
58014
|
const prevVideo = () => {
|
|
57767
|
-
setCurrentIndex((prev) => (prev - 1 +
|
|
58015
|
+
setCurrentIndex((prev) => (prev - 1 + Math.max(videos.length, 1)) % Math.max(videos.length, 1));
|
|
57768
58016
|
};
|
|
57769
|
-
if (!
|
|
58017
|
+
if (!clips || clips.length === 0) return null;
|
|
58018
|
+
const currentVideo = videos[currentIndex];
|
|
57770
58019
|
return /* @__PURE__ */ jsxs("div", { className: "relative group bg-gray-900 rounded-lg overflow-hidden aspect-video", children: [
|
|
57771
|
-
/* @__PURE__ */ jsx(
|
|
57772
|
-
|
|
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,
|
|
57773
58024
|
{
|
|
57774
|
-
src:
|
|
57775
|
-
className: "w-full h-full object-cover opacity-
|
|
57776
|
-
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
|
|
57777
58029
|
},
|
|
57778
|
-
|
|
58030
|
+
currentVideo.src
|
|
57779
58031
|
),
|
|
57780
|
-
/* @__PURE__ */
|
|
57781
|
-
|
|
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: [
|
|
57782
58038
|
/* @__PURE__ */ jsx(
|
|
57783
58039
|
"button",
|
|
57784
58040
|
{
|
|
@@ -57806,70 +58062,273 @@ var VideoCarousel = ({ urls }) => {
|
|
|
57806
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: [
|
|
57807
58063
|
currentIndex + 1,
|
|
57808
58064
|
" / ",
|
|
57809
|
-
|
|
58065
|
+
videos.length
|
|
57810
58066
|
] })
|
|
57811
58067
|
] })
|
|
57812
58068
|
] });
|
|
57813
58069
|
};
|
|
57814
|
-
var
|
|
57815
|
-
const
|
|
57816
|
-
|
|
57817
|
-
|
|
57818
|
-
|
|
57819
|
-
|
|
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);
|
|
57820
58165
|
return /* @__PURE__ */ jsx("div", { className: "h-48 w-full bg-white border border-gray-100 rounded-lg p-2", children: /* @__PURE__ */ jsx(
|
|
57821
|
-
|
|
58166
|
+
LineChart,
|
|
57822
58167
|
{
|
|
57823
58168
|
data: chartData,
|
|
57824
|
-
|
|
58169
|
+
lines: [
|
|
57825
58170
|
{
|
|
57826
58171
|
dataKey: "value",
|
|
57827
|
-
name: "
|
|
57828
|
-
|
|
57829
|
-
|
|
57830
|
-
|
|
58172
|
+
name: "Value",
|
|
58173
|
+
stroke: "#f87171",
|
|
58174
|
+
strokeWidth: 2,
|
|
58175
|
+
dot: true
|
|
57831
58176
|
}
|
|
57832
58177
|
],
|
|
57833
58178
|
xAxisDataKey: "name",
|
|
57834
58179
|
showLegend: false,
|
|
57835
58180
|
showGrid: true,
|
|
57836
58181
|
aspect: 2.5,
|
|
58182
|
+
yAxisUnit,
|
|
57837
58183
|
className: "h-full"
|
|
57838
58184
|
}
|
|
57839
58185
|
) });
|
|
57840
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
|
+
};
|
|
57841
58208
|
var ImprovementCenterView = () => {
|
|
57842
58209
|
const { navigate } = useNavigation();
|
|
57843
|
-
const
|
|
57844
|
-
const
|
|
57845
|
-
const
|
|
57846
|
-
const
|
|
57847
|
-
|
|
57848
|
-
|
|
57849
|
-
|
|
57850
|
-
const
|
|
57851
|
-
|
|
57852
|
-
|
|
57853
|
-
|
|
57854
|
-
|
|
57855
|
-
|
|
57856
|
-
|
|
57857
|
-
|
|
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);
|
|
57858
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;
|
|
57859
58304
|
return true;
|
|
57860
|
-
});
|
|
57861
|
-
}, [
|
|
57862
|
-
const
|
|
57863
|
-
|
|
57864
|
-
|
|
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]);
|
|
57865
58316
|
const clearFilters = () => {
|
|
57866
|
-
|
|
57867
|
-
|
|
57868
|
-
|
|
57869
|
-
|
|
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]);
|
|
57870
58329
|
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
|
|
57871
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: [
|
|
57872
|
-
/* @__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(
|
|
57873
58332
|
BackButtonMinimal,
|
|
57874
58333
|
{
|
|
57875
58334
|
onClick: () => navigate("/"),
|
|
@@ -57877,121 +58336,190 @@ var ImprovementCenterView = () => {
|
|
|
57877
58336
|
}
|
|
57878
58337
|
) }),
|
|
57879
58338
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
57880
|
-
/* @__PURE__ */
|
|
57881
|
-
|
|
57882
|
-
"Improvement Center"
|
|
57883
|
-
] }),
|
|
57884
|
-
/* @__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" })
|
|
57885
58341
|
] }),
|
|
57886
|
-
/* @__PURE__ */ jsx("div", { className: "w-
|
|
58342
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-[120px]" })
|
|
57887
58343
|
] }) }) }),
|
|
57888
|
-
/* @__PURE__ */
|
|
57889
|
-
/* @__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: [
|
|
57890
58346
|
/* @__PURE__ */ jsx(
|
|
57891
|
-
"input",
|
|
57892
|
-
{
|
|
57893
|
-
ref: dateInputRef,
|
|
57894
|
-
type: "date",
|
|
57895
|
-
value: selectedDate,
|
|
57896
|
-
onChange: (e) => setSelectedDate(e.target.value),
|
|
57897
|
-
className: "sr-only"
|
|
57898
|
-
}
|
|
57899
|
-
),
|
|
57900
|
-
/* @__PURE__ */ jsxs(
|
|
57901
58347
|
"button",
|
|
57902
58348
|
{
|
|
57903
|
-
onClick:
|
|
57904
|
-
className:
|
|
57905
|
-
children:
|
|
57906
|
-
/* @__PURE__ */ jsx(CalendarIcon, { className: "w-4 h-4" }),
|
|
57907
|
-
/* @__PURE__ */ jsx("span", { children: selectedDate || "All Dates" })
|
|
57908
|
-
]
|
|
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" })
|
|
57909
58352
|
}
|
|
57910
|
-
)
|
|
57911
|
-
|
|
57912
|
-
/* @__PURE__ */ jsxs(
|
|
57913
|
-
"select",
|
|
57914
|
-
{
|
|
57915
|
-
value: selectedLine,
|
|
57916
|
-
onChange: (e) => setSelectedLine(e.target.value),
|
|
57917
|
-
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"}`,
|
|
57918
|
-
children: [
|
|
57919
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "All Lines" }),
|
|
57920
|
-
uniqueLines.map((line) => /* @__PURE__ */ jsx("option", { value: line, children: line }, line))
|
|
57921
|
-
]
|
|
57922
|
-
}
|
|
57923
|
-
),
|
|
57924
|
-
hasActiveFilters && /* @__PURE__ */ jsx(
|
|
57925
|
-
"button",
|
|
57926
|
-
{
|
|
57927
|
-
onClick: clearFilters,
|
|
57928
|
-
className: "p-1.5 text-gray-400 hover:text-gray-600 rounded-full hover:bg-gray-100 transition-colors",
|
|
57929
|
-
title: "Clear filters",
|
|
57930
|
-
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "w-5 h-5" })
|
|
57931
|
-
}
|
|
57932
|
-
)
|
|
57933
|
-
] }) }) }),
|
|
57934
|
-
/* @__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: [
|
|
57935
|
-
filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => /* @__PURE__ */ jsx(
|
|
57936
|
-
motion.div,
|
|
57937
|
-
{
|
|
57938
|
-
initial: { opacity: 0, y: 20 },
|
|
57939
|
-
animate: { opacity: 1, y: 0 },
|
|
57940
|
-
transition: { delay: index * 0.1 },
|
|
57941
|
-
className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden",
|
|
57942
|
-
children: /* @__PURE__ */ jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
|
|
57943
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
|
|
57944
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
57945
|
-
/* @__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() }),
|
|
57946
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 mt-2", children: rec.title }),
|
|
57947
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-500", children: [
|
|
57948
|
-
rec.location,
|
|
57949
|
-
" \u2022 ",
|
|
57950
|
-
rec.line,
|
|
57951
|
-
" \u2022 ",
|
|
57952
|
-
rec.date
|
|
57953
|
-
] })
|
|
57954
|
-
] }) }),
|
|
57955
|
-
/* @__PURE__ */ jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { children: rec.description }) }),
|
|
57956
|
-
/* @__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: [
|
|
57957
|
-
/* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 flex-shrink-0" }),
|
|
57958
|
-
"Impact: ",
|
|
57959
|
-
rec.impact
|
|
57960
|
-
] }) })
|
|
57961
|
-
] }),
|
|
57962
|
-
/* @__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: [
|
|
57963
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
57964
|
-
/* @__PURE__ */ jsxs("h4", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider flex items-center gap-2", children: [
|
|
57965
|
-
rec.evidence.type === "video" ? /* @__PURE__ */ jsx(PlayCircleIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-4 h-4" }),
|
|
57966
|
-
"Evidence: ",
|
|
57967
|
-
rec.evidence.label
|
|
57968
|
-
] }),
|
|
57969
|
-
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: [
|
|
57970
|
-
rec.evidence.videoUrls.length,
|
|
57971
|
-
" clips"
|
|
57972
|
-
] })
|
|
57973
|
-
] }),
|
|
57974
|
-
rec.evidence.type === "video" && rec.evidence.videoUrls && /* @__PURE__ */ jsx(VideoCarousel, { urls: rec.evidence.videoUrls }),
|
|
57975
|
-
rec.evidence.type === "chart" && rec.evidence.chartData && /* @__PURE__ */ jsx(EvidenceChart, { data: rec.evidence.chartData })
|
|
57976
|
-
] })
|
|
57977
|
-
] }) })
|
|
57978
|
-
},
|
|
57979
|
-
rec.id
|
|
57980
|
-
)) : /* @__PURE__ */ jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-dashed border-gray-300", children: [
|
|
57981
|
-
/* @__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" }) }),
|
|
57982
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-900", children: "No recommendations found" }),
|
|
57983
|
-
/* @__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) }),
|
|
57984
58355
|
/* @__PURE__ */ jsx(
|
|
57985
58356
|
"button",
|
|
57986
58357
|
{
|
|
57987
|
-
onClick:
|
|
57988
|
-
className: "
|
|
57989
|
-
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" })
|
|
57990
58361
|
}
|
|
57991
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
|
+
] })
|
|
57992
58425
|
] }),
|
|
57993
|
-
|
|
57994
|
-
|
|
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
|
+
] })
|
|
57995
58523
|
] });
|
|
57996
58524
|
};
|
|
57997
58525
|
var ImprovementCenterView_default = ImprovementCenterView;
|
|
@@ -58450,175 +58978,4 @@ var streamProxyConfig = {
|
|
|
58450
58978
|
}
|
|
58451
58979
|
};
|
|
58452
58980
|
|
|
58453
|
-
// src/lib/api/s3-clips-parser.ts
|
|
58454
|
-
function parseS3Uri(s3Uri, sopCategories) {
|
|
58455
|
-
const path = new URL(s3Uri).pathname;
|
|
58456
|
-
const parts = path.split("/").filter((p) => p);
|
|
58457
|
-
if (s3Uri.includes("missed_qchecks")) {
|
|
58458
|
-
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
58459
|
-
return null;
|
|
58460
|
-
}
|
|
58461
|
-
if (parts.length < 8) {
|
|
58462
|
-
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
58463
|
-
return null;
|
|
58464
|
-
}
|
|
58465
|
-
try {
|
|
58466
|
-
const datePart = parts[2];
|
|
58467
|
-
const shiftPart = parts[3];
|
|
58468
|
-
const violationType = parts[4];
|
|
58469
|
-
let folderName = "";
|
|
58470
|
-
let timestamp = "";
|
|
58471
|
-
for (let i = 5; i < parts.length; i++) {
|
|
58472
|
-
const part = parts[i];
|
|
58473
|
-
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
58474
|
-
folderName = part;
|
|
58475
|
-
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
58476
|
-
if (timeMatch) {
|
|
58477
|
-
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
58478
|
-
break;
|
|
58479
|
-
}
|
|
58480
|
-
}
|
|
58481
|
-
}
|
|
58482
|
-
if (!timestamp) {
|
|
58483
|
-
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
58484
|
-
timestamp = "00:00:00";
|
|
58485
|
-
}
|
|
58486
|
-
let severity = "low";
|
|
58487
|
-
let type = "bottleneck";
|
|
58488
|
-
let description = "Analysis Clip";
|
|
58489
|
-
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
58490
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
58491
|
-
const matchedCategory = sopCategories.find((category) => {
|
|
58492
|
-
const categoryId = category.id.toLowerCase();
|
|
58493
|
-
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
58494
|
-
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
58495
|
-
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
58496
|
-
});
|
|
58497
|
-
if (matchedCategory) {
|
|
58498
|
-
type = matchedCategory.id;
|
|
58499
|
-
description = matchedCategory.description || matchedCategory.label;
|
|
58500
|
-
if (matchedCategory.color.includes("red")) {
|
|
58501
|
-
severity = "high";
|
|
58502
|
-
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
58503
|
-
severity = "medium";
|
|
58504
|
-
} else {
|
|
58505
|
-
severity = "low";
|
|
58506
|
-
}
|
|
58507
|
-
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
58508
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
58509
|
-
}
|
|
58510
|
-
}
|
|
58511
|
-
switch (normalizedViolationType) {
|
|
58512
|
-
case "idle_time":
|
|
58513
|
-
case "idle":
|
|
58514
|
-
case "low_value":
|
|
58515
|
-
case "low value":
|
|
58516
|
-
case "low_value_moment":
|
|
58517
|
-
case "low_value_moments":
|
|
58518
|
-
case "low value moment":
|
|
58519
|
-
case "low value moments":
|
|
58520
|
-
type = "low_value";
|
|
58521
|
-
severity = "low";
|
|
58522
|
-
description = "Idle Time Detected";
|
|
58523
|
-
break;
|
|
58524
|
-
case "sop_deviation":
|
|
58525
|
-
type = "missing_quality_check";
|
|
58526
|
-
severity = "high";
|
|
58527
|
-
description = "SOP Deviations";
|
|
58528
|
-
break;
|
|
58529
|
-
case "long_cycle_time":
|
|
58530
|
-
severity = "high";
|
|
58531
|
-
type = "long_cycle_time";
|
|
58532
|
-
description = "Long Cycle Time Detected";
|
|
58533
|
-
break;
|
|
58534
|
-
case "best_cycle_time":
|
|
58535
|
-
type = "best_cycle_time";
|
|
58536
|
-
severity = "low";
|
|
58537
|
-
description = "Best Cycle Time Performance";
|
|
58538
|
-
break;
|
|
58539
|
-
case "worst_cycle_time":
|
|
58540
|
-
type = "worst_cycle_time";
|
|
58541
|
-
severity = "high";
|
|
58542
|
-
description = "Worst Cycle Time Performance";
|
|
58543
|
-
break;
|
|
58544
|
-
case "cycle_completion":
|
|
58545
|
-
case "completed_cycles":
|
|
58546
|
-
case "completed_cycle":
|
|
58547
|
-
type = "cycle_completion";
|
|
58548
|
-
severity = "low";
|
|
58549
|
-
description = "Cycle Completion";
|
|
58550
|
-
break;
|
|
58551
|
-
case "running_cycle":
|
|
58552
|
-
case "active_cycle":
|
|
58553
|
-
case "production_cycle":
|
|
58554
|
-
type = "running_cycle";
|
|
58555
|
-
severity = "low";
|
|
58556
|
-
description = "Active Production Cycle";
|
|
58557
|
-
break;
|
|
58558
|
-
case "setup_state":
|
|
58559
|
-
case "machine_setup":
|
|
58560
|
-
case "line_setup":
|
|
58561
|
-
type = "setup_state";
|
|
58562
|
-
severity = "medium";
|
|
58563
|
-
description = "Machine Setup Activity";
|
|
58564
|
-
break;
|
|
58565
|
-
case "medium_bottleneck":
|
|
58566
|
-
severity = "medium";
|
|
58567
|
-
description = "Medium Bottleneck Identified";
|
|
58568
|
-
break;
|
|
58569
|
-
case "minor_bottleneck":
|
|
58570
|
-
case "mild_bottleneck":
|
|
58571
|
-
severity = "low";
|
|
58572
|
-
description = "Minor Bottleneck Identified";
|
|
58573
|
-
break;
|
|
58574
|
-
default:
|
|
58575
|
-
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
58576
|
-
type = "missing_quality_check";
|
|
58577
|
-
severity = "high";
|
|
58578
|
-
description = "SOP Deviations";
|
|
58579
|
-
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
58580
|
-
type = "worst_cycle_time";
|
|
58581
|
-
severity = "high";
|
|
58582
|
-
description = "Worst Cycle Time Performance";
|
|
58583
|
-
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
58584
|
-
type = "best_cycle_time";
|
|
58585
|
-
severity = "low";
|
|
58586
|
-
description = "Best Cycle Time Performance";
|
|
58587
|
-
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
58588
|
-
type = "long_cycle_time";
|
|
58589
|
-
severity = "high";
|
|
58590
|
-
description = "Long Cycle Time Detected";
|
|
58591
|
-
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
58592
|
-
type = "cycle_completion";
|
|
58593
|
-
severity = "low";
|
|
58594
|
-
description = "Cycle Completion";
|
|
58595
|
-
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
58596
|
-
type = "running_cycle";
|
|
58597
|
-
severity = "low";
|
|
58598
|
-
description = "Active Production Cycle";
|
|
58599
|
-
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
58600
|
-
type = "setup_state";
|
|
58601
|
-
severity = "medium";
|
|
58602
|
-
description = "Machine Setup Activity";
|
|
58603
|
-
} else {
|
|
58604
|
-
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
58605
|
-
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
58606
|
-
}
|
|
58607
|
-
break;
|
|
58608
|
-
}
|
|
58609
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
58610
|
-
} catch (error) {
|
|
58611
|
-
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
58612
|
-
return null;
|
|
58613
|
-
}
|
|
58614
|
-
}
|
|
58615
|
-
function shuffleArray(array) {
|
|
58616
|
-
const shuffled = [...array];
|
|
58617
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
58618
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
58619
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
58620
|
-
}
|
|
58621
|
-
return shuffled;
|
|
58622
|
-
}
|
|
58623
|
-
|
|
58624
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 };
|