@optifye/dashboard-core 6.10.49 → 6.10.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +574 -175
- package/dist/index.mjs +575 -178
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1058,6 +1058,44 @@ var fetchBackendJson = async (supabase, endpoint, options = {}) => {
|
|
|
1058
1058
|
}
|
|
1059
1059
|
};
|
|
1060
1060
|
|
|
1061
|
+
// src/lib/services/lineMetricsSelection.ts
|
|
1062
|
+
var toTimestamp = (value) => {
|
|
1063
|
+
if (typeof value !== "string") return 0;
|
|
1064
|
+
const parsed = Date.parse(value);
|
|
1065
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
1066
|
+
};
|
|
1067
|
+
var compareByLatestThenSku = (a, b) => {
|
|
1068
|
+
const timeDiff = toTimestamp(b.last_updated) - toTimestamp(a.last_updated);
|
|
1069
|
+
if (timeDiff !== 0) return timeDiff;
|
|
1070
|
+
const aSku = typeof a.sku_id === "string" ? a.sku_id : "";
|
|
1071
|
+
const bSku = typeof b.sku_id === "string" ? b.sku_id : "";
|
|
1072
|
+
return aSku.localeCompare(bSku);
|
|
1073
|
+
};
|
|
1074
|
+
var pickPreferredLineMetricsRow = async (supabase, lineId, rows, skuTable = "skus") => {
|
|
1075
|
+
if (!rows || rows.length === 0) {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
if (rows.length === 1) {
|
|
1079
|
+
return rows[0];
|
|
1080
|
+
}
|
|
1081
|
+
let dummySkuId = null;
|
|
1082
|
+
try {
|
|
1083
|
+
const { data } = await supabase.from(skuTable).select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
1084
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
1085
|
+
dummySkuId = typeof data[0]?.id === "string" ? data[0].id : null;
|
|
1086
|
+
}
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
console.warn("[lineMetricsSelection] Failed dummy SKU lookup:", error);
|
|
1089
|
+
}
|
|
1090
|
+
if (dummySkuId) {
|
|
1091
|
+
const dummyRow = rows.find((row) => row.sku_id === dummySkuId);
|
|
1092
|
+
if (dummyRow) {
|
|
1093
|
+
return dummyRow;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return rows.slice().sort(compareByLatestThenSku)[0] ?? null;
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1061
1099
|
// src/lib/services/dashboardService.ts
|
|
1062
1100
|
var getTable = (dbConfig, tableName) => {
|
|
1063
1101
|
const defaults2 = DEFAULT_DATABASE_CONFIG.tables;
|
|
@@ -1076,6 +1114,7 @@ var dashboardService = {
|
|
|
1076
1114
|
const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
|
|
1077
1115
|
const linesTable = getTable(dbConfig, "lines");
|
|
1078
1116
|
const lineMetricsTable = getTable(dbConfig, "lineMetrics");
|
|
1117
|
+
const skuTable = dbConfig?.tables?.skus ?? "skus";
|
|
1079
1118
|
const companyId = entityConfig.companyId;
|
|
1080
1119
|
const metricsTablePrefixStr = getMetricsTablePrefix();
|
|
1081
1120
|
`${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
|
|
@@ -1169,9 +1208,14 @@ var dashboardService = {
|
|
|
1169
1208
|
if (!lineData) throw new Error(`Line with ID ${lineId} not found`);
|
|
1170
1209
|
let metricsFromDb = null;
|
|
1171
1210
|
try {
|
|
1172
|
-
const { data:
|
|
1211
|
+
const { data: fetchedMetricsRows, error } = await supabase.from(lineMetricsTable).select("*").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date);
|
|
1173
1212
|
if (error) throw error;
|
|
1174
|
-
metricsFromDb =
|
|
1213
|
+
metricsFromDb = await pickPreferredLineMetricsRow(
|
|
1214
|
+
supabase,
|
|
1215
|
+
lineId,
|
|
1216
|
+
fetchedMetricsRows,
|
|
1217
|
+
skuTable
|
|
1218
|
+
);
|
|
1175
1219
|
} catch (err) {
|
|
1176
1220
|
console.error(`Error fetching line metrics for ${lineId}:`, err);
|
|
1177
1221
|
}
|
|
@@ -1582,6 +1626,7 @@ var dashboardService = {
|
|
|
1582
1626
|
const workspaceConfig = config.workspaceConfig ?? DEFAULT_WORKSPACE_CONFIG;
|
|
1583
1627
|
const linesTable = getTable(dbConfig, "lines");
|
|
1584
1628
|
const lineMetricsTable = getTable(dbConfig, "lineMetrics");
|
|
1629
|
+
const skuTable = dbConfig?.tables?.skus ?? "skus";
|
|
1585
1630
|
const companyId = entityConfig.companyId;
|
|
1586
1631
|
const metricsTablePrefixStr = getMetricsTablePrefix();
|
|
1587
1632
|
`${metricsTablePrefixStr}_${companyId ? companyId.replace(/-/g, "_") : "unknown_company"}`;
|
|
@@ -1680,7 +1725,7 @@ var dashboardService = {
|
|
|
1680
1725
|
}
|
|
1681
1726
|
const [lineResult, metricsResult] = await Promise.all([
|
|
1682
1727
|
supabase.from(linesTable).select("id, line_name, factory_id, monitoring_mode, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
|
|
1683
|
-
supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
|
|
1728
|
+
supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
|
|
1684
1729
|
]);
|
|
1685
1730
|
if (lineResult.error) throw lineResult.error;
|
|
1686
1731
|
if (!lineResult.data) {
|
|
@@ -1688,7 +1733,12 @@ var dashboardService = {
|
|
|
1688
1733
|
}
|
|
1689
1734
|
if (metricsResult.error) throw metricsResult.error;
|
|
1690
1735
|
const lineData = lineResult.data;
|
|
1691
|
-
const metrics2 =
|
|
1736
|
+
const metrics2 = await pickPreferredLineMetricsRow(
|
|
1737
|
+
supabase,
|
|
1738
|
+
lineIdToQuery,
|
|
1739
|
+
metricsResult.data,
|
|
1740
|
+
skuTable
|
|
1741
|
+
);
|
|
1692
1742
|
return {
|
|
1693
1743
|
line_id: lineData.id,
|
|
1694
1744
|
line_name: lineData.line_name,
|
|
@@ -17351,6 +17401,79 @@ function useCompanyUsersUsage(companyId, options = {}) {
|
|
|
17351
17401
|
refetchToday: fetchTodayUsage
|
|
17352
17402
|
};
|
|
17353
17403
|
}
|
|
17404
|
+
function useCompanyClipsCost() {
|
|
17405
|
+
const { user, session } = useAuth();
|
|
17406
|
+
const supabase = useSupabase();
|
|
17407
|
+
const config = useDashboardConfig();
|
|
17408
|
+
const entityConfig = useEntityConfig();
|
|
17409
|
+
const [data, setData] = React26.useState(null);
|
|
17410
|
+
const [isLoading, setIsLoading] = React26.useState(true);
|
|
17411
|
+
const [error, setError] = React26.useState(null);
|
|
17412
|
+
const hasFetchedOnceRef = React26.useRef(false);
|
|
17413
|
+
const canViewClipsCost = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
17414
|
+
const companyId = user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
17415
|
+
const apiBaseUrl = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
17416
|
+
const fetchData = React26.useCallback(async () => {
|
|
17417
|
+
if (!canViewClipsCost || !companyId || !supabase || !apiBaseUrl || !session?.access_token) {
|
|
17418
|
+
setIsLoading(false);
|
|
17419
|
+
setData(null);
|
|
17420
|
+
hasFetchedOnceRef.current = false;
|
|
17421
|
+
return;
|
|
17422
|
+
}
|
|
17423
|
+
if (!hasFetchedOnceRef.current) {
|
|
17424
|
+
setIsLoading(true);
|
|
17425
|
+
}
|
|
17426
|
+
setError(null);
|
|
17427
|
+
try {
|
|
17428
|
+
const [statsResponse, linesResult] = await Promise.all([
|
|
17429
|
+
fetch(`${apiBaseUrl}/api/classification/company-stats?company_id=${encodeURIComponent(companyId)}`, {
|
|
17430
|
+
headers: {
|
|
17431
|
+
"Authorization": `Bearer ${session.access_token}`
|
|
17432
|
+
}
|
|
17433
|
+
}),
|
|
17434
|
+
supabase.from("lines").select("id").eq("company_id", companyId).eq("idle_time_vlm_enabled", true).limit(1)
|
|
17435
|
+
]);
|
|
17436
|
+
if (!statsResponse.ok) {
|
|
17437
|
+
const errorData = await statsResponse.json().catch(() => ({}));
|
|
17438
|
+
throw new Error(errorData.detail || `Failed to fetch classification stats: ${statsResponse.status}`);
|
|
17439
|
+
}
|
|
17440
|
+
const statsResult = await statsResponse.json();
|
|
17441
|
+
const totalClassifications = statsResult.total_classifications || 0;
|
|
17442
|
+
const monthlyClassifications = statsResult.monthly_classifications ?? totalClassifications;
|
|
17443
|
+
const monthStart = statsResult.month_start || null;
|
|
17444
|
+
const historicalMonthlyClassifications = Array.isArray(statsResult.historical_monthly_classifications) ? statsResult.historical_monthly_classifications.map((item) => ({
|
|
17445
|
+
monthStart: item?.month_start || "",
|
|
17446
|
+
classifications: Number(item?.classifications || 0)
|
|
17447
|
+
})).filter((item) => item.monthStart && item.classifications > 0) : [];
|
|
17448
|
+
const hasVlmEnabledLine = (linesResult.data?.length || 0) > 0;
|
|
17449
|
+
setData({
|
|
17450
|
+
totalClassifications,
|
|
17451
|
+
monthlyClassifications,
|
|
17452
|
+
monthStart,
|
|
17453
|
+
historicalMonthlyClassifications,
|
|
17454
|
+
hasVlmEnabledLine
|
|
17455
|
+
});
|
|
17456
|
+
} catch (err) {
|
|
17457
|
+
console.error("[useCompanyClipsCost] Error:", err);
|
|
17458
|
+
setError(err instanceof Error ? err.message : "Failed to load clips data");
|
|
17459
|
+
setData(null);
|
|
17460
|
+
} finally {
|
|
17461
|
+
hasFetchedOnceRef.current = true;
|
|
17462
|
+
setIsLoading(false);
|
|
17463
|
+
}
|
|
17464
|
+
}, [canViewClipsCost, companyId, supabase, apiBaseUrl, session?.access_token]);
|
|
17465
|
+
React26.useEffect(() => {
|
|
17466
|
+
fetchData();
|
|
17467
|
+
}, [fetchData]);
|
|
17468
|
+
const hasData = !!(data && data.totalClassifications > 0 && data.hasVlmEnabledLine);
|
|
17469
|
+
return {
|
|
17470
|
+
data,
|
|
17471
|
+
isLoading,
|
|
17472
|
+
error,
|
|
17473
|
+
hasData,
|
|
17474
|
+
refetch: fetchData
|
|
17475
|
+
};
|
|
17476
|
+
}
|
|
17354
17477
|
|
|
17355
17478
|
// src/lib/utils/api.ts
|
|
17356
17479
|
var apiUtils = {
|
|
@@ -34408,6 +34531,8 @@ var HlsVideoPlayer = React26.forwardRef(({
|
|
|
34408
34531
|
if (effectiveSrc.startsWith("#EXTM3U")) {
|
|
34409
34532
|
const safariMode = isSafari();
|
|
34410
34533
|
const browserName = getBrowserName();
|
|
34534
|
+
const r2WorkerHost = r2WorkerDomain.replace(/^https?:\/\//, "");
|
|
34535
|
+
const isR2Playlist = effectiveSrc.includes(r2WorkerHost);
|
|
34411
34536
|
const remuxUrl = extractRemuxUrl(effectiveSrc);
|
|
34412
34537
|
if (remuxUrl) {
|
|
34413
34538
|
console.log(`[HlsVideoPlayer] Remuxing enabled - using remux playlist URL: ${remuxUrl}`);
|
|
@@ -34451,10 +34576,58 @@ var HlsVideoPlayer = React26.forwardRef(({
|
|
|
34451
34576
|
} else {
|
|
34452
34577
|
video.src = remuxUrl;
|
|
34453
34578
|
}
|
|
34579
|
+
} else if (safariMode && isR2Playlist && Hls__default.default.isSupported()) {
|
|
34580
|
+
const blob = new Blob([effectiveSrc], { type: "application/vnd.apple.mpegurl" });
|
|
34581
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
34582
|
+
blobUrlRef.current = blobUrl;
|
|
34583
|
+
console.log(`[HlsVideoPlayer] Safari R2 stream - using HLS.js for auth header support (${browserName})`);
|
|
34584
|
+
const hls = new Hls__default.default(mergedHlsConfig);
|
|
34585
|
+
hlsRef.current = hls;
|
|
34586
|
+
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
34587
|
+
console.log("[HlsVideoPlayer] Manifest parsed (Safari R2 HLS.js), ready to play");
|
|
34588
|
+
setIsReady(true);
|
|
34589
|
+
eventCallbacksRef.current.onReady?.(player);
|
|
34590
|
+
});
|
|
34591
|
+
hls.on(Hls.Events.ERROR, (event, data) => {
|
|
34592
|
+
console.error("[HlsVideoPlayer] HLS.js error (Safari R2):", data);
|
|
34593
|
+
if (maybeHandleR2Fallback(data)) {
|
|
34594
|
+
return;
|
|
34595
|
+
}
|
|
34596
|
+
if (data.fatal) {
|
|
34597
|
+
let errorInfo;
|
|
34598
|
+
switch (data.type) {
|
|
34599
|
+
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
34600
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
34601
|
+
console.log("[HlsVideoPlayer] Attempting to recover from network error (Safari R2)");
|
|
34602
|
+
hls.startLoad();
|
|
34603
|
+
break;
|
|
34604
|
+
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
34605
|
+
errorInfo = ERROR_MAPPING.mediaError;
|
|
34606
|
+
console.log("[HlsVideoPlayer] Attempting to recover from media error (Safari R2)");
|
|
34607
|
+
hls.recoverMediaError();
|
|
34608
|
+
break;
|
|
34609
|
+
case Hls.ErrorTypes.MUX_ERROR:
|
|
34610
|
+
errorInfo = ERROR_MAPPING.muxError;
|
|
34611
|
+
break;
|
|
34612
|
+
default:
|
|
34613
|
+
errorInfo = ERROR_MAPPING.otherError;
|
|
34614
|
+
break;
|
|
34615
|
+
}
|
|
34616
|
+
errorInfo.details = data.details;
|
|
34617
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
34618
|
+
}
|
|
34619
|
+
});
|
|
34620
|
+
hls.on(Hls.Events.FRAG_LOADED, () => {
|
|
34621
|
+
setIsLoading(false);
|
|
34622
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
34623
|
+
});
|
|
34624
|
+
hls.loadSource(blobUrl);
|
|
34625
|
+
hls.attachMedia(video);
|
|
34454
34626
|
} else if (safariMode) {
|
|
34455
34627
|
const clipIdMatch = effectiveSrc.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
34456
34628
|
if (clipIdMatch) {
|
|
34457
|
-
const
|
|
34629
|
+
const safariParam = isR2Playlist ? "?safari=true" : "";
|
|
34630
|
+
const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}${safariParam}`;
|
|
34458
34631
|
console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
|
|
34459
34632
|
video.src = proxyUrl;
|
|
34460
34633
|
} else {
|
|
@@ -36656,6 +36829,7 @@ var FileManagerFilters = ({
|
|
|
36656
36829
|
const endInputRef = React26.useRef(null);
|
|
36657
36830
|
const [idleLabelFilter, setIdleLabelFilter] = React26.useState(null);
|
|
36658
36831
|
const [showIdleLabelFilterModal, setShowIdleLabelFilterModal] = React26.useState(false);
|
|
36832
|
+
const [isLoadingIdleReasonOptions, setIsLoadingIdleReasonOptions] = React26.useState(false);
|
|
36659
36833
|
const timezone = useAppTimezone();
|
|
36660
36834
|
const supabase = useSupabase();
|
|
36661
36835
|
const [clipMetadata, setClipMetadata] = React26.useState({});
|
|
@@ -36698,18 +36872,27 @@ var FileManagerFilters = ({
|
|
|
36698
36872
|
iconColor: colorConfig.text
|
|
36699
36873
|
};
|
|
36700
36874
|
};
|
|
36701
|
-
const
|
|
36702
|
-
"
|
|
36703
|
-
|
|
36704
|
-
"Machine Downtime",
|
|
36705
|
-
"No Material"
|
|
36706
|
-
];
|
|
36875
|
+
const normalizeIdleReasonLabel = React26.useCallback((label) => {
|
|
36876
|
+
return label.replace(/_/g, " ").trim();
|
|
36877
|
+
}, []);
|
|
36707
36878
|
const getIdleTimeRootCause = React26.useCallback((clipId) => {
|
|
36708
36879
|
const classification = mergedClipClassifications[clipId];
|
|
36709
36880
|
if (!classification) return "processing";
|
|
36710
36881
|
if (classification.status === "processing") return "processing";
|
|
36711
36882
|
return classification.label || "processing";
|
|
36712
36883
|
}, [mergedClipClassifications]);
|
|
36884
|
+
const idleReasonOptions = React26.useMemo(() => {
|
|
36885
|
+
const idleClips = clipMetadata["idle_time"] || [];
|
|
36886
|
+
const uniqueReasons = /* @__PURE__ */ new Set();
|
|
36887
|
+
idleClips.forEach((clip) => {
|
|
36888
|
+
const clipId = clip.clipId || clip.id;
|
|
36889
|
+
if (!clipId) return;
|
|
36890
|
+
const reason = getIdleTimeRootCause(clipId);
|
|
36891
|
+
if (!reason || reason === "processing") return;
|
|
36892
|
+
uniqueReasons.add(normalizeIdleReasonLabel(reason));
|
|
36893
|
+
});
|
|
36894
|
+
return Array.from(uniqueReasons).sort((a, b) => a.localeCompare(b));
|
|
36895
|
+
}, [clipMetadata, getIdleTimeRootCause, normalizeIdleReasonLabel]);
|
|
36713
36896
|
const getClipBadge = React26.useCallback((node) => {
|
|
36714
36897
|
if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
|
|
36715
36898
|
return { text: "Idle", className: "bg-red-100 text-red-700" };
|
|
@@ -36737,6 +36920,79 @@ var FileManagerFilters = ({
|
|
|
36737
36920
|
return null;
|
|
36738
36921
|
}
|
|
36739
36922
|
}, [supabase]);
|
|
36923
|
+
const fetchClipMetadataPage = React26.useCallback(async (categoryId, page = 1) => {
|
|
36924
|
+
if (!workspaceId || !date || shift === void 0) {
|
|
36925
|
+
throw new Error("Missing required params for clip metadata fetch");
|
|
36926
|
+
}
|
|
36927
|
+
const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
36928
|
+
method: "POST",
|
|
36929
|
+
headers: {
|
|
36930
|
+
"Content-Type": "application/json"
|
|
36931
|
+
},
|
|
36932
|
+
body: JSON.stringify({
|
|
36933
|
+
action: "clip-metadata",
|
|
36934
|
+
workspaceId,
|
|
36935
|
+
date,
|
|
36936
|
+
shift: shift.toString(),
|
|
36937
|
+
category: categoryId,
|
|
36938
|
+
page,
|
|
36939
|
+
limit: 50,
|
|
36940
|
+
snapshotDateTime,
|
|
36941
|
+
snapshotClipId
|
|
36942
|
+
}),
|
|
36943
|
+
redirectReason: "session_expired"
|
|
36944
|
+
});
|
|
36945
|
+
if (!response.ok) {
|
|
36946
|
+
throw new Error(`API error: ${response.status}`);
|
|
36947
|
+
}
|
|
36948
|
+
return response.json();
|
|
36949
|
+
}, [workspaceId, date, shift, snapshotDateTime, snapshotClipId, supabase]);
|
|
36950
|
+
const seedIdleClassifications = React26.useCallback(async (clips) => {
|
|
36951
|
+
if (!idleTimeVlmEnabled || clips.length === 0) {
|
|
36952
|
+
return;
|
|
36953
|
+
}
|
|
36954
|
+
const authToken = await getAuthToken3();
|
|
36955
|
+
if (!authToken) {
|
|
36956
|
+
return;
|
|
36957
|
+
}
|
|
36958
|
+
const seededClassifications = {};
|
|
36959
|
+
clips.forEach((clip) => {
|
|
36960
|
+
if (!clip.clipId) {
|
|
36961
|
+
return;
|
|
36962
|
+
}
|
|
36963
|
+
if (clip.classification_status) {
|
|
36964
|
+
seededClassifications[clip.clipId] = {
|
|
36965
|
+
status: clip.classification_status,
|
|
36966
|
+
label: clip.classification_label || void 0,
|
|
36967
|
+
confidence: clip.classification_confidence ?? void 0
|
|
36968
|
+
};
|
|
36969
|
+
}
|
|
36970
|
+
});
|
|
36971
|
+
if (Object.keys(seededClassifications).length > 0) {
|
|
36972
|
+
setLocalClipClassifications((prev) => ({
|
|
36973
|
+
...prev,
|
|
36974
|
+
...seededClassifications
|
|
36975
|
+
}));
|
|
36976
|
+
}
|
|
36977
|
+
const clipIdsToFetch = clips.map((clip) => clip.clipId || clip.id).filter(Boolean).filter((id3) => {
|
|
36978
|
+
if (!id3) return false;
|
|
36979
|
+
if (mergedClipClassifications[id3]?.status === "classified") return false;
|
|
36980
|
+
if (seededClassifications[id3]?.status === "classified") return false;
|
|
36981
|
+
return true;
|
|
36982
|
+
});
|
|
36983
|
+
if (clipIdsToFetch.length === 0) {
|
|
36984
|
+
return;
|
|
36985
|
+
}
|
|
36986
|
+
try {
|
|
36987
|
+
const classifications = await fetchClassifications(clipIdsToFetch, authToken);
|
|
36988
|
+
setLocalClipClassifications((prev) => ({
|
|
36989
|
+
...prev,
|
|
36990
|
+
...classifications
|
|
36991
|
+
}));
|
|
36992
|
+
} catch (error) {
|
|
36993
|
+
console.error("[FileManager] Error fetching idle classifications:", error);
|
|
36994
|
+
}
|
|
36995
|
+
}, [idleTimeVlmEnabled, getAuthToken3, mergedClipClassifications]);
|
|
36740
36996
|
const fetchClipMetadata = React26.useCallback(async (categoryId, page = 1) => {
|
|
36741
36997
|
if (!workspaceId || !date || shift === void 0) {
|
|
36742
36998
|
console.warn("[FileManager] Missing required params for clip metadata fetch");
|
|
@@ -36745,69 +37001,13 @@ var FileManagerFilters = ({
|
|
|
36745
37001
|
const loadingKey = `${categoryId}-${page}`;
|
|
36746
37002
|
setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
|
|
36747
37003
|
try {
|
|
36748
|
-
const
|
|
36749
|
-
method: "POST",
|
|
36750
|
-
headers: {
|
|
36751
|
-
"Content-Type": "application/json"
|
|
36752
|
-
},
|
|
36753
|
-
body: JSON.stringify({
|
|
36754
|
-
action: "clip-metadata",
|
|
36755
|
-
workspaceId,
|
|
36756
|
-
date,
|
|
36757
|
-
shift: shift.toString(),
|
|
36758
|
-
category: categoryId,
|
|
36759
|
-
page,
|
|
36760
|
-
limit: 50,
|
|
36761
|
-
snapshotDateTime,
|
|
36762
|
-
snapshotClipId
|
|
36763
|
-
}),
|
|
36764
|
-
redirectReason: "session_expired"
|
|
36765
|
-
});
|
|
36766
|
-
if (!response.ok) {
|
|
36767
|
-
throw new Error(`API error: ${response.status}`);
|
|
36768
|
-
}
|
|
36769
|
-
const data = await response.json();
|
|
37004
|
+
const data = await fetchClipMetadataPage(categoryId, page);
|
|
36770
37005
|
setClipMetadata((prev) => ({
|
|
36771
37006
|
...prev,
|
|
36772
37007
|
[categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
|
|
36773
37008
|
}));
|
|
36774
|
-
|
|
36775
|
-
|
|
36776
|
-
const seededClassifications = {};
|
|
36777
|
-
(data.clips || []).forEach((clip) => {
|
|
36778
|
-
if (!clip.clipId) {
|
|
36779
|
-
return;
|
|
36780
|
-
}
|
|
36781
|
-
if (clip.classification_status) {
|
|
36782
|
-
seededClassifications[clip.clipId] = {
|
|
36783
|
-
status: clip.classification_status,
|
|
36784
|
-
label: clip.classification_label || void 0,
|
|
36785
|
-
confidence: clip.classification_confidence ?? void 0
|
|
36786
|
-
};
|
|
36787
|
-
}
|
|
36788
|
-
});
|
|
36789
|
-
if (Object.keys(seededClassifications).length > 0) {
|
|
36790
|
-
setLocalClipClassifications((prev) => ({
|
|
36791
|
-
...prev,
|
|
36792
|
-
...seededClassifications
|
|
36793
|
-
}));
|
|
36794
|
-
}
|
|
36795
|
-
const clipIds = (data.clips || []).map((clip) => clip.clipId || clip.id).filter(Boolean);
|
|
36796
|
-
const newClipIds = clipIds.filter((id3) => {
|
|
36797
|
-
if (mergedClipClassifications[id3]?.status === "classified") return false;
|
|
36798
|
-
if (seededClassifications[id3]?.status === "classified") return false;
|
|
36799
|
-
return true;
|
|
36800
|
-
});
|
|
36801
|
-
if (newClipIds.length > 0) {
|
|
36802
|
-
fetchClassifications(newClipIds, authToken).then((classifications) => {
|
|
36803
|
-
setLocalClipClassifications((prev) => ({
|
|
36804
|
-
...prev,
|
|
36805
|
-
...classifications
|
|
36806
|
-
}));
|
|
36807
|
-
}).catch((error) => {
|
|
36808
|
-
console.error("[FileManager] Error fetching idle classifications:", error);
|
|
36809
|
-
});
|
|
36810
|
-
}
|
|
37009
|
+
if (categoryId === "idle_time" && idleTimeVlmEnabled) {
|
|
37010
|
+
await seedIdleClassifications(data.clips || []);
|
|
36811
37011
|
}
|
|
36812
37012
|
setCategoryPages((prev) => ({ ...prev, [categoryId]: page }));
|
|
36813
37013
|
setCategoryHasMore((prev) => ({ ...prev, [categoryId]: data.hasMore }));
|
|
@@ -36821,7 +37021,64 @@ var FileManagerFilters = ({
|
|
|
36821
37021
|
return newSet;
|
|
36822
37022
|
});
|
|
36823
37023
|
}
|
|
36824
|
-
}, [workspaceId, date, shift,
|
|
37024
|
+
}, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications]);
|
|
37025
|
+
const ensureAllIdleTimeClipMetadataLoaded = React26.useCallback(async () => {
|
|
37026
|
+
if (!workspaceId || !date || shift === void 0) {
|
|
37027
|
+
return;
|
|
37028
|
+
}
|
|
37029
|
+
let accumulatedClips = [...clipMetadata["idle_time"] || []];
|
|
37030
|
+
let currentPage = categoryPages["idle_time"] || 0;
|
|
37031
|
+
let hasMore = categoryHasMore["idle_time"];
|
|
37032
|
+
if (currentPage === 0) {
|
|
37033
|
+
const firstPageData = await fetchClipMetadataPage("idle_time", 1);
|
|
37034
|
+
accumulatedClips = firstPageData.clips || [];
|
|
37035
|
+
currentPage = 1;
|
|
37036
|
+
hasMore = firstPageData.hasMore;
|
|
37037
|
+
} else if (hasMore === void 0) {
|
|
37038
|
+
hasMore = false;
|
|
37039
|
+
}
|
|
37040
|
+
while (hasMore) {
|
|
37041
|
+
const nextPage = currentPage + 1;
|
|
37042
|
+
const nextPageData = await fetchClipMetadataPage("idle_time", nextPage);
|
|
37043
|
+
accumulatedClips = [...accumulatedClips, ...nextPageData.clips || []];
|
|
37044
|
+
currentPage = nextPage;
|
|
37045
|
+
hasMore = nextPageData.hasMore;
|
|
37046
|
+
}
|
|
37047
|
+
const dedupedClips = accumulatedClips.filter((clip, index, arr) => {
|
|
37048
|
+
const clipKey = clip.clipId || clip.id;
|
|
37049
|
+
return arr.findIndex((item) => (item.clipId || item.id) === clipKey) === index;
|
|
37050
|
+
});
|
|
37051
|
+
setClipMetadata((prev) => ({
|
|
37052
|
+
...prev,
|
|
37053
|
+
idle_time: dedupedClips
|
|
37054
|
+
}));
|
|
37055
|
+
setCategoryPages((prev) => ({ ...prev, idle_time: currentPage }));
|
|
37056
|
+
setCategoryHasMore((prev) => ({ ...prev, idle_time: false }));
|
|
37057
|
+
if (idleTimeVlmEnabled) {
|
|
37058
|
+
await seedIdleClassifications(dedupedClips);
|
|
37059
|
+
}
|
|
37060
|
+
}, [
|
|
37061
|
+
workspaceId,
|
|
37062
|
+
date,
|
|
37063
|
+
shift,
|
|
37064
|
+
clipMetadata,
|
|
37065
|
+
categoryPages,
|
|
37066
|
+
categoryHasMore,
|
|
37067
|
+
fetchClipMetadataPage,
|
|
37068
|
+
idleTimeVlmEnabled,
|
|
37069
|
+
seedIdleClassifications
|
|
37070
|
+
]);
|
|
37071
|
+
const handleOpenIdleLabelFilterModal = React26.useCallback(async () => {
|
|
37072
|
+
setShowIdleLabelFilterModal(true);
|
|
37073
|
+
setIsLoadingIdleReasonOptions(true);
|
|
37074
|
+
try {
|
|
37075
|
+
await ensureAllIdleTimeClipMetadataLoaded();
|
|
37076
|
+
} catch (error) {
|
|
37077
|
+
console.error("[FileManager] Error loading idle reason options:", error);
|
|
37078
|
+
} finally {
|
|
37079
|
+
setIsLoadingIdleReasonOptions(false);
|
|
37080
|
+
}
|
|
37081
|
+
}, [ensureAllIdleTimeClipMetadataLoaded]);
|
|
36825
37082
|
const fetchPercentileClips = React26.useCallback(async (type) => {
|
|
36826
37083
|
if (!workspaceId || !date || shift === void 0) {
|
|
36827
37084
|
console.warn("[FileManager] Missing required params for percentile clips fetch");
|
|
@@ -37492,7 +37749,7 @@ var FileManagerFilters = ({
|
|
|
37492
37749
|
activeFilter === "idle_time" && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
37493
37750
|
"button",
|
|
37494
37751
|
{
|
|
37495
|
-
onClick:
|
|
37752
|
+
onClick: handleOpenIdleLabelFilterModal,
|
|
37496
37753
|
className: `p-2 rounded-xl transition-all duration-200 ${idleLabelFilter ? "bg-purple-100 text-purple-600 hover:bg-purple-200 shadow-sm" : "bg-slate-100 text-slate-600 hover:bg-slate-200"}`,
|
|
37497
37754
|
title: "Filter by idle reason",
|
|
37498
37755
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tag, { className: "h-5 w-5" })
|
|
@@ -37580,7 +37837,10 @@ var FileManagerFilters = ({
|
|
|
37580
37837
|
}
|
|
37581
37838
|
)
|
|
37582
37839
|
] }),
|
|
37583
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 max-h-60 overflow-y-auto", children:
|
|
37840
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 max-h-60 overflow-y-auto", children: isLoadingIdleReasonOptions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-4 text-sm text-slate-500 flex items-center gap-2", children: [
|
|
37841
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
37842
|
+
"Loading idle reasons..."
|
|
37843
|
+
] }) : idleReasonOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-4 text-sm text-slate-500", children: "No classified idle reasons found yet." }) : idleReasonOptions.map((reason) => {
|
|
37584
37844
|
const config = getRootCauseConfig(reason);
|
|
37585
37845
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
37586
37846
|
"button",
|
|
@@ -48917,6 +49177,7 @@ var SideNavBar = React26.memo(({
|
|
|
48917
49177
|
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
48918
49178
|
const showSupervisorManagement = false;
|
|
48919
49179
|
const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
|
|
49180
|
+
const { hasData: hasClipsCostData } = useCompanyClipsCost();
|
|
48920
49181
|
console.log("\u{1F50D} [SideNavBar] dashboardConfig:", dashboardConfig);
|
|
48921
49182
|
console.log("\u{1F50D} [SideNavBar] ticketsConfig:", dashboardConfig?.ticketsConfig);
|
|
48922
49183
|
console.log("\u{1F50D} [SideNavBar] ticketsEnabled:", ticketsEnabled);
|
|
@@ -49073,6 +49334,17 @@ var SideNavBar = React26.memo(({
|
|
|
49073
49334
|
});
|
|
49074
49335
|
onMobileMenuClose?.();
|
|
49075
49336
|
}, [navigate, onMobileMenuClose]);
|
|
49337
|
+
const handleClipsCostClick = React26.useCallback(() => {
|
|
49338
|
+
navigate("/clips-cost", {
|
|
49339
|
+
trackingEvent: {
|
|
49340
|
+
name: "Clips Cost Page Clicked",
|
|
49341
|
+
properties: {
|
|
49342
|
+
source: "side_nav"
|
|
49343
|
+
}
|
|
49344
|
+
}
|
|
49345
|
+
});
|
|
49346
|
+
onMobileMenuClose?.();
|
|
49347
|
+
}, [navigate, onMobileMenuClose]);
|
|
49076
49348
|
const [isSettingsOpen, setIsSettingsOpen] = React26.useState(false);
|
|
49077
49349
|
const settingsTriggerRef = React26.useRef(null);
|
|
49078
49350
|
const settingsItems = React26.useMemo(() => {
|
|
@@ -49130,6 +49402,18 @@ var SideNavBar = React26.memo(({
|
|
|
49130
49402
|
isActive: pathname === "/tickets" || pathname.startsWith("/tickets/")
|
|
49131
49403
|
});
|
|
49132
49404
|
}
|
|
49405
|
+
if (hasClipsCostData) {
|
|
49406
|
+
items.push({
|
|
49407
|
+
key: "clips-analysis",
|
|
49408
|
+
label: "Billing",
|
|
49409
|
+
icon: outline.CurrencyDollarIcon,
|
|
49410
|
+
onClick: () => {
|
|
49411
|
+
handleClipsCostClick();
|
|
49412
|
+
setIsSettingsOpen(false);
|
|
49413
|
+
},
|
|
49414
|
+
isActive: pathname === "/clips-cost" || pathname.startsWith("/clips-cost/")
|
|
49415
|
+
});
|
|
49416
|
+
}
|
|
49133
49417
|
if (canAccessPath("/help")) {
|
|
49134
49418
|
items.push({
|
|
49135
49419
|
key: "help",
|
|
@@ -49143,7 +49427,7 @@ var SideNavBar = React26.memo(({
|
|
|
49143
49427
|
});
|
|
49144
49428
|
}
|
|
49145
49429
|
return items;
|
|
49146
|
-
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleHelpClick, pathname, ticketsEnabled, canAccessPath]);
|
|
49430
|
+
}, [handleTargetsClick, handleShiftsClick, handleTeamManagementClick, handleProfileClick, handleTicketsClick, handleClipsCostClick, handleHelpClick, pathname, ticketsEnabled, hasClipsCostData, canAccessPath]);
|
|
49147
49431
|
const handleLogout = React26.useCallback(async () => {
|
|
49148
49432
|
setIsSettingsOpen(false);
|
|
49149
49433
|
try {
|
|
@@ -61402,6 +61686,102 @@ var ProfileView = () => {
|
|
|
61402
61686
|
] });
|
|
61403
61687
|
};
|
|
61404
61688
|
var ProfileView_default = ProfileView;
|
|
61689
|
+
var REFRESH_INTERVAL_MS = 30 * 1e3;
|
|
61690
|
+
var ClipsCostView = () => {
|
|
61691
|
+
const { data, isLoading, error, refetch } = useCompanyClipsCost();
|
|
61692
|
+
const navigation = useNavigation();
|
|
61693
|
+
const refetchRef = React26.useRef(refetch);
|
|
61694
|
+
React26.useEffect(() => {
|
|
61695
|
+
refetchRef.current = refetch;
|
|
61696
|
+
}, [refetch]);
|
|
61697
|
+
React26.useEffect(() => {
|
|
61698
|
+
const intervalId = setInterval(() => {
|
|
61699
|
+
void refetchRef.current();
|
|
61700
|
+
}, REFRESH_INTERVAL_MS);
|
|
61701
|
+
return () => clearInterval(intervalId);
|
|
61702
|
+
}, []);
|
|
61703
|
+
const mobileMenuContext = useMobileMenu();
|
|
61704
|
+
useHideMobileHeader(!!mobileMenuContext);
|
|
61705
|
+
const formatNumber = (num) => {
|
|
61706
|
+
return new Intl.NumberFormat("en-US").format(num);
|
|
61707
|
+
};
|
|
61708
|
+
const formatMonthLabel = (monthStart) => {
|
|
61709
|
+
if (!monthStart) {
|
|
61710
|
+
return "Current Month";
|
|
61711
|
+
}
|
|
61712
|
+
const parsed = /* @__PURE__ */ new Date(`${monthStart}T00:00:00`);
|
|
61713
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
61714
|
+
return "Current Month";
|
|
61715
|
+
}
|
|
61716
|
+
return new Intl.DateTimeFormat("en-US", { month: "long", year: "numeric" }).format(parsed);
|
|
61717
|
+
};
|
|
61718
|
+
if (isLoading) {
|
|
61719
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-screen bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading usage data..." }) });
|
|
61720
|
+
}
|
|
61721
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
|
|
61722
|
+
/* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 shadow-sm flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 lg:px-8 py-3 sm:py-4 w-full", children: [
|
|
61723
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
61724
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: mobileMenuContext && /* @__PURE__ */ jsxRuntime.jsx(
|
|
61725
|
+
HamburgerButton,
|
|
61726
|
+
{
|
|
61727
|
+
onClick: mobileMenuContext.onMobileMenuOpen,
|
|
61728
|
+
className: "flex-shrink-0 -ml-1"
|
|
61729
|
+
}
|
|
61730
|
+
) }),
|
|
61731
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
|
|
61732
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Billing" }),
|
|
61733
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: "Track your usage based features spend" })
|
|
61734
|
+
] }),
|
|
61735
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9" })
|
|
61736
|
+
] }) }),
|
|
61737
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
61738
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 min-w-[120px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
61739
|
+
BackButtonMinimal,
|
|
61740
|
+
{
|
|
61741
|
+
onClick: () => navigation.goToDashboard(),
|
|
61742
|
+
text: "Back",
|
|
61743
|
+
size: "default",
|
|
61744
|
+
"aria-label": "Navigate back to dashboard"
|
|
61745
|
+
}
|
|
61746
|
+
) }),
|
|
61747
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
61748
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Billing" }),
|
|
61749
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1 text-center", children: "Track your usage based features spend" })
|
|
61750
|
+
] }),
|
|
61751
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[120px]" })
|
|
61752
|
+
] }) })
|
|
61753
|
+
] }) }),
|
|
61754
|
+
/* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 p-4 sm:p-6 lg:p-8 max-w-7xl mx-auto w-full", children: error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg flex items-start gap-3 text-red-700 animate-in fade-in slide-in-from-top-2 max-w-2xl mx-auto", children: [
|
|
61755
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-5 w-5 flex-shrink-0 mt-0.5" }),
|
|
61756
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
61757
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-medium", children: "Error loading usage data" }),
|
|
61758
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1 text-red-600", children: error })
|
|
61759
|
+
] })
|
|
61760
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-xl mt-8 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
|
|
61761
|
+
/* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "overflow-hidden shadow-sm border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "p-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center text-center", children: [
|
|
61762
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-blue-50 rounded-full mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-8 w-8 text-blue-600" }) }),
|
|
61763
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-medium text-gray-500 uppercase tracking-wider mb-2", children: "Clips Analyzed" }),
|
|
61764
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mb-3", children: formatMonthLabel(data?.monthStart) }),
|
|
61765
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-5xl font-bold text-gray-900 mb-2 tabular-nums tracking-tight", children: formatNumber(data?.monthlyClassifications || 0) })
|
|
61766
|
+
] }) }) }),
|
|
61767
|
+
/* @__PURE__ */ jsxRuntime.jsx(Card2, { className: "mt-4 overflow-hidden shadow-sm border-gray-200 bg-white", children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent2, { className: "p-6", children: [
|
|
61768
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3 uppercase tracking-wider", children: "Previous Months" }),
|
|
61769
|
+
(data?.historicalMonthlyClassifications?.length || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No historical month usage yet." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: data?.historicalMonthlyClassifications.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
61770
|
+
"div",
|
|
61771
|
+
{
|
|
61772
|
+
className: "flex items-center justify-between rounded-md border border-gray-100 bg-gray-50 px-3 py-2",
|
|
61773
|
+
children: [
|
|
61774
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700", children: formatMonthLabel(item.monthStart) }),
|
|
61775
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 tabular-nums", children: formatNumber(item.classifications) })
|
|
61776
|
+
]
|
|
61777
|
+
},
|
|
61778
|
+
item.monthStart
|
|
61779
|
+
)) })
|
|
61780
|
+
] }) })
|
|
61781
|
+
] }) })
|
|
61782
|
+
] });
|
|
61783
|
+
};
|
|
61784
|
+
var ClipsCostView_default = ClipsCostView;
|
|
61405
61785
|
|
|
61406
61786
|
// src/lib/constants/actions.ts
|
|
61407
61787
|
var ACTION_NAMES = {
|
|
@@ -64680,6 +65060,7 @@ var WorkspaceDetailView = ({
|
|
|
64680
65060
|
}, [monthlyData, range]);
|
|
64681
65061
|
const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
|
|
64682
65062
|
const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY"));
|
|
65063
|
+
const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
|
|
64683
65064
|
const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
|
|
64684
65065
|
const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
|
|
64685
65066
|
const shiftDurationMinutes = React26.useMemo(
|
|
@@ -65283,107 +65664,123 @@ var WorkspaceDetailView = ({
|
|
|
65283
65664
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
|
|
65284
65665
|
] }),
|
|
65285
65666
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden lg:flex lg:flex-col lg:h-full lg:min-h-0 gap-3", children: [
|
|
65286
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65287
|
-
|
|
65288
|
-
|
|
65289
|
-
|
|
65290
|
-
|
|
65291
|
-
|
|
65292
|
-
|
|
65293
|
-
|
|
65294
|
-
|
|
65295
|
-
|
|
65296
|
-
|
|
65297
|
-
|
|
65298
|
-
|
|
65299
|
-
|
|
65300
|
-
|
|
65301
|
-
|
|
65302
|
-
|
|
65303
|
-
|
|
65304
|
-
|
|
65305
|
-
|
|
65306
|
-
|
|
65307
|
-
|
|
65308
|
-
|
|
65309
|
-
|
|
65310
|
-
|
|
65311
|
-
|
|
65312
|
-
|
|
65313
|
-
|
|
65314
|
-
|
|
65315
|
-
|
|
65316
|
-
|
|
65317
|
-
|
|
65318
|
-
|
|
65319
|
-
|
|
65320
|
-
|
|
65321
|
-
|
|
65322
|
-
|
|
65323
|
-
|
|
65324
|
-
|
|
65325
|
-
|
|
65326
|
-
|
|
65327
|
-
|
|
65328
|
-
|
|
65329
|
-
|
|
65330
|
-
|
|
65331
|
-
|
|
65332
|
-
|
|
65333
|
-
|
|
65334
|
-
|
|
65335
|
-
|
|
65336
|
-
|
|
65337
|
-
|
|
65338
|
-
|
|
65339
|
-
|
|
65340
|
-
|
|
65341
|
-
|
|
65342
|
-
|
|
65343
|
-
|
|
65344
|
-
|
|
65345
|
-
|
|
65346
|
-
|
|
65347
|
-
|
|
65348
|
-
|
|
65349
|
-
|
|
65350
|
-
|
|
65351
|
-
|
|
65352
|
-
|
|
65353
|
-
|
|
65354
|
-
|
|
65355
|
-
|
|
65356
|
-
|
|
65357
|
-
|
|
65358
|
-
|
|
65359
|
-
|
|
65360
|
-
|
|
65361
|
-
|
|
65362
|
-
|
|
65363
|
-
|
|
65364
|
-
|
|
65365
|
-
|
|
65366
|
-
|
|
65367
|
-
|
|
65368
|
-
|
|
65369
|
-
|
|
65370
|
-
|
|
65371
|
-
|
|
65372
|
-
|
|
65373
|
-
|
|
65374
|
-
|
|
65375
|
-
|
|
65376
|
-
|
|
65377
|
-
|
|
65378
|
-
|
|
65379
|
-
|
|
65380
|
-
|
|
65381
|
-
|
|
65382
|
-
|
|
65383
|
-
|
|
65384
|
-
|
|
65385
|
-
|
|
65386
|
-
|
|
65667
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65668
|
+
"div",
|
|
65669
|
+
{
|
|
65670
|
+
className: clsx(
|
|
65671
|
+
"grid grid-cols-1 gap-3 min-h-0",
|
|
65672
|
+
isUptimeMode && showIdleBreakdownChart ? "lg:grid-cols-3" : "lg:grid-cols-10",
|
|
65673
|
+
desktopTopSectionClass
|
|
65674
|
+
),
|
|
65675
|
+
children: [
|
|
65676
|
+
!shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
65677
|
+
motion.div,
|
|
65678
|
+
{
|
|
65679
|
+
className: "bg-white rounded-lg shadow-sm p-4 lg:col-span-2 flex flex-col min-h-0",
|
|
65680
|
+
variants: chartCardVariants,
|
|
65681
|
+
initial: "initial",
|
|
65682
|
+
animate: "animate",
|
|
65683
|
+
children: [
|
|
65684
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700 mb-4 text-center", children: "Today's Output" }),
|
|
65685
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
65686
|
+
OutputProgressChart,
|
|
65687
|
+
{
|
|
65688
|
+
currentOutput: workspace.total_actions || 0,
|
|
65689
|
+
targetOutput: workspace.target_output || 0
|
|
65690
|
+
}
|
|
65691
|
+
) })
|
|
65692
|
+
]
|
|
65693
|
+
}
|
|
65694
|
+
),
|
|
65695
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65696
|
+
motion.div,
|
|
65697
|
+
{
|
|
65698
|
+
className: clsx(
|
|
65699
|
+
"bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
|
|
65700
|
+
isUptimeMode && showIdleBreakdownChart ? "lg:col-span-2" : shouldShowCycleTimeChart || isUptimeMode ? "lg:col-span-10" : idleTimeVlmEnabled ? "lg:col-span-6" : "lg:col-span-8"
|
|
65701
|
+
),
|
|
65702
|
+
variants: chartCardVariants,
|
|
65703
|
+
initial: "initial",
|
|
65704
|
+
animate: "animate",
|
|
65705
|
+
children: [
|
|
65706
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
|
|
65707
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle Time (last 60 minutes)" : "Hourly Output" }),
|
|
65708
|
+
!isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
65709
|
+
"button",
|
|
65710
|
+
{
|
|
65711
|
+
onClick: () => setShowChartIdleTime(!showChartIdleTime),
|
|
65712
|
+
className: `inline-flex items-center px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${showChartIdleTime ? "bg-blue-50 text-blue-700 border border-blue-200" : "bg-white text-gray-700 border border-gray-300 hover:bg-gray-50"}`,
|
|
65713
|
+
children: showChartIdleTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
65714
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { className: "w-4 h-4 mr-1.5" }),
|
|
65715
|
+
"Hide Idle Time"
|
|
65716
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
65717
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "w-4 h-4 mr-1.5" }),
|
|
65718
|
+
"Show Idle Time"
|
|
65719
|
+
] })
|
|
65720
|
+
}
|
|
65721
|
+
)
|
|
65722
|
+
] }),
|
|
65723
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
65724
|
+
HourlyUptimeChart,
|
|
65725
|
+
{
|
|
65726
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
65727
|
+
shiftStart: workspace.shift_start,
|
|
65728
|
+
shiftEnd: workspace.shift_end,
|
|
65729
|
+
shiftDate: idleClipDate,
|
|
65730
|
+
timezone,
|
|
65731
|
+
elapsedMinutes: elapsedShiftMinutes
|
|
65732
|
+
}
|
|
65733
|
+
) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
65734
|
+
CycleTimeOverTimeChart,
|
|
65735
|
+
{
|
|
65736
|
+
data: workspace.hourly_action_counts || [],
|
|
65737
|
+
idealCycleTime: workspace.ideal_cycle_time || 0,
|
|
65738
|
+
shiftStart: workspace.shift_start || ""
|
|
65739
|
+
}
|
|
65740
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
65741
|
+
HourlyOutputChart2,
|
|
65742
|
+
{
|
|
65743
|
+
data: workspace.hourly_action_counts || [],
|
|
65744
|
+
pphThreshold: workspace.pph_threshold || 0,
|
|
65745
|
+
shiftStart: workspace.shift_start || "06:00",
|
|
65746
|
+
shiftEnd: workspace.shift_end,
|
|
65747
|
+
showIdleTime: showChartIdleTime,
|
|
65748
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
65749
|
+
idleTimeClips,
|
|
65750
|
+
idleTimeClipClassifications,
|
|
65751
|
+
shiftDate: idleClipDate,
|
|
65752
|
+
timezone
|
|
65753
|
+
}
|
|
65754
|
+
) })
|
|
65755
|
+
]
|
|
65756
|
+
}
|
|
65757
|
+
),
|
|
65758
|
+
!shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
65759
|
+
motion.div,
|
|
65760
|
+
{
|
|
65761
|
+
className: clsx(
|
|
65762
|
+
"bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
|
|
65763
|
+
isUptimeMode ? "lg:col-span-1" : "lg:col-span-2"
|
|
65764
|
+
),
|
|
65765
|
+
variants: chartCardVariants,
|
|
65766
|
+
initial: "initial",
|
|
65767
|
+
animate: "animate",
|
|
65768
|
+
children: [
|
|
65769
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700 mb-4 text-center", children: "Idle Time Breakdown" }),
|
|
65770
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
65771
|
+
IdleTimeReasonChart,
|
|
65772
|
+
{
|
|
65773
|
+
data: idleTimeData.chartData,
|
|
65774
|
+
isLoading: idleTimeData.isLoading,
|
|
65775
|
+
error: idleTimeData.error
|
|
65776
|
+
}
|
|
65777
|
+
) })
|
|
65778
|
+
]
|
|
65779
|
+
}
|
|
65780
|
+
)
|
|
65781
|
+
]
|
|
65782
|
+
}
|
|
65783
|
+
),
|
|
65387
65784
|
isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData, className: "flex-1" }) }) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: clsx("grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 min-h-0", desktopBottomSectionClass), children: [
|
|
65388
65785
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
65389
65786
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Efficiency" }) }),
|
|
@@ -71708,6 +72105,7 @@ exports.CardHeader = CardHeader2;
|
|
|
71708
72105
|
exports.CardTitle = CardTitle2;
|
|
71709
72106
|
exports.ChangeRoleDialog = ChangeRoleDialog;
|
|
71710
72107
|
exports.ClipFilterProvider = ClipFilterProvider;
|
|
72108
|
+
exports.ClipsCostView = ClipsCostView_default;
|
|
71711
72109
|
exports.CompactWorkspaceHealthCard = CompactWorkspaceHealthCard;
|
|
71712
72110
|
exports.ConfirmRemoveUserDialog = ConfirmRemoveUserDialog;
|
|
71713
72111
|
exports.CongratulationsOverlay = CongratulationsOverlay;
|
|
@@ -72073,6 +72471,7 @@ exports.useClipFilter = useClipFilter;
|
|
|
72073
72471
|
exports.useClipTypes = useClipTypes;
|
|
72074
72472
|
exports.useClipTypesWithCounts = useClipTypesWithCounts;
|
|
72075
72473
|
exports.useClipsInit = useClipsInit;
|
|
72474
|
+
exports.useCompanyClipsCost = useCompanyClipsCost;
|
|
72076
72475
|
exports.useCompanyUsersUsage = useCompanyUsersUsage;
|
|
72077
72476
|
exports.useComponentOverride = useComponentOverride;
|
|
72078
72477
|
exports.useCustomConfig = useCustomConfig;
|