@optifye/dashboard-core 6.10.50 → 6.10.52
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 +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +435 -218
- package/dist/index.mjs +435 -218
- 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,
|
|
@@ -17359,6 +17409,7 @@ function useCompanyClipsCost() {
|
|
|
17359
17409
|
const [data, setData] = React26.useState(null);
|
|
17360
17410
|
const [isLoading, setIsLoading] = React26.useState(true);
|
|
17361
17411
|
const [error, setError] = React26.useState(null);
|
|
17412
|
+
const hasFetchedOnceRef = React26.useRef(false);
|
|
17362
17413
|
const canViewClipsCost = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
17363
17414
|
const companyId = user?.properties?.company_id || user?.company_id || entityConfig.companyId;
|
|
17364
17415
|
const apiBaseUrl = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
@@ -17366,9 +17417,12 @@ function useCompanyClipsCost() {
|
|
|
17366
17417
|
if (!canViewClipsCost || !companyId || !supabase || !apiBaseUrl || !session?.access_token) {
|
|
17367
17418
|
setIsLoading(false);
|
|
17368
17419
|
setData(null);
|
|
17420
|
+
hasFetchedOnceRef.current = false;
|
|
17369
17421
|
return;
|
|
17370
17422
|
}
|
|
17371
|
-
|
|
17423
|
+
if (!hasFetchedOnceRef.current) {
|
|
17424
|
+
setIsLoading(true);
|
|
17425
|
+
}
|
|
17372
17426
|
setError(null);
|
|
17373
17427
|
try {
|
|
17374
17428
|
const [statsResponse, linesResult] = await Promise.all([
|
|
@@ -17385,9 +17439,18 @@ function useCompanyClipsCost() {
|
|
|
17385
17439
|
}
|
|
17386
17440
|
const statsResult = await statsResponse.json();
|
|
17387
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) : [];
|
|
17388
17448
|
const hasVlmEnabledLine = (linesResult.data?.length || 0) > 0;
|
|
17389
17449
|
setData({
|
|
17390
17450
|
totalClassifications,
|
|
17451
|
+
monthlyClassifications,
|
|
17452
|
+
monthStart,
|
|
17453
|
+
historicalMonthlyClassifications,
|
|
17391
17454
|
hasVlmEnabledLine
|
|
17392
17455
|
});
|
|
17393
17456
|
} catch (err) {
|
|
@@ -17395,6 +17458,7 @@ function useCompanyClipsCost() {
|
|
|
17395
17458
|
setError(err instanceof Error ? err.message : "Failed to load clips data");
|
|
17396
17459
|
setData(null);
|
|
17397
17460
|
} finally {
|
|
17461
|
+
hasFetchedOnceRef.current = true;
|
|
17398
17462
|
setIsLoading(false);
|
|
17399
17463
|
}
|
|
17400
17464
|
}, [canViewClipsCost, companyId, supabase, apiBaseUrl, session?.access_token]);
|
|
@@ -36765,6 +36829,7 @@ var FileManagerFilters = ({
|
|
|
36765
36829
|
const endInputRef = React26.useRef(null);
|
|
36766
36830
|
const [idleLabelFilter, setIdleLabelFilter] = React26.useState(null);
|
|
36767
36831
|
const [showIdleLabelFilterModal, setShowIdleLabelFilterModal] = React26.useState(false);
|
|
36832
|
+
const [isLoadingIdleReasonOptions, setIsLoadingIdleReasonOptions] = React26.useState(false);
|
|
36768
36833
|
const timezone = useAppTimezone();
|
|
36769
36834
|
const supabase = useSupabase();
|
|
36770
36835
|
const [clipMetadata, setClipMetadata] = React26.useState({});
|
|
@@ -36807,18 +36872,27 @@ var FileManagerFilters = ({
|
|
|
36807
36872
|
iconColor: colorConfig.text
|
|
36808
36873
|
};
|
|
36809
36874
|
};
|
|
36810
|
-
const
|
|
36811
|
-
"
|
|
36812
|
-
|
|
36813
|
-
"Machine Downtime",
|
|
36814
|
-
"No Material"
|
|
36815
|
-
];
|
|
36875
|
+
const normalizeIdleReasonLabel = React26.useCallback((label) => {
|
|
36876
|
+
return label.replace(/_/g, " ").trim();
|
|
36877
|
+
}, []);
|
|
36816
36878
|
const getIdleTimeRootCause = React26.useCallback((clipId) => {
|
|
36817
36879
|
const classification = mergedClipClassifications[clipId];
|
|
36818
36880
|
if (!classification) return "processing";
|
|
36819
36881
|
if (classification.status === "processing") return "processing";
|
|
36820
36882
|
return classification.label || "processing";
|
|
36821
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]);
|
|
36822
36896
|
const getClipBadge = React26.useCallback((node) => {
|
|
36823
36897
|
if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
|
|
36824
36898
|
return { text: "Idle", className: "bg-red-100 text-red-700" };
|
|
@@ -36846,6 +36920,79 @@ var FileManagerFilters = ({
|
|
|
36846
36920
|
return null;
|
|
36847
36921
|
}
|
|
36848
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]);
|
|
36849
36996
|
const fetchClipMetadata = React26.useCallback(async (categoryId, page = 1) => {
|
|
36850
36997
|
if (!workspaceId || !date || shift === void 0) {
|
|
36851
36998
|
console.warn("[FileManager] Missing required params for clip metadata fetch");
|
|
@@ -36854,69 +37001,13 @@ var FileManagerFilters = ({
|
|
|
36854
37001
|
const loadingKey = `${categoryId}-${page}`;
|
|
36855
37002
|
setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
|
|
36856
37003
|
try {
|
|
36857
|
-
const
|
|
36858
|
-
method: "POST",
|
|
36859
|
-
headers: {
|
|
36860
|
-
"Content-Type": "application/json"
|
|
36861
|
-
},
|
|
36862
|
-
body: JSON.stringify({
|
|
36863
|
-
action: "clip-metadata",
|
|
36864
|
-
workspaceId,
|
|
36865
|
-
date,
|
|
36866
|
-
shift: shift.toString(),
|
|
36867
|
-
category: categoryId,
|
|
36868
|
-
page,
|
|
36869
|
-
limit: 50,
|
|
36870
|
-
snapshotDateTime,
|
|
36871
|
-
snapshotClipId
|
|
36872
|
-
}),
|
|
36873
|
-
redirectReason: "session_expired"
|
|
36874
|
-
});
|
|
36875
|
-
if (!response.ok) {
|
|
36876
|
-
throw new Error(`API error: ${response.status}`);
|
|
36877
|
-
}
|
|
36878
|
-
const data = await response.json();
|
|
37004
|
+
const data = await fetchClipMetadataPage(categoryId, page);
|
|
36879
37005
|
setClipMetadata((prev) => ({
|
|
36880
37006
|
...prev,
|
|
36881
37007
|
[categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
|
|
36882
37008
|
}));
|
|
36883
|
-
|
|
36884
|
-
|
|
36885
|
-
const seededClassifications = {};
|
|
36886
|
-
(data.clips || []).forEach((clip) => {
|
|
36887
|
-
if (!clip.clipId) {
|
|
36888
|
-
return;
|
|
36889
|
-
}
|
|
36890
|
-
if (clip.classification_status) {
|
|
36891
|
-
seededClassifications[clip.clipId] = {
|
|
36892
|
-
status: clip.classification_status,
|
|
36893
|
-
label: clip.classification_label || void 0,
|
|
36894
|
-
confidence: clip.classification_confidence ?? void 0
|
|
36895
|
-
};
|
|
36896
|
-
}
|
|
36897
|
-
});
|
|
36898
|
-
if (Object.keys(seededClassifications).length > 0) {
|
|
36899
|
-
setLocalClipClassifications((prev) => ({
|
|
36900
|
-
...prev,
|
|
36901
|
-
...seededClassifications
|
|
36902
|
-
}));
|
|
36903
|
-
}
|
|
36904
|
-
const clipIds = (data.clips || []).map((clip) => clip.clipId || clip.id).filter(Boolean);
|
|
36905
|
-
const newClipIds = clipIds.filter((id3) => {
|
|
36906
|
-
if (mergedClipClassifications[id3]?.status === "classified") return false;
|
|
36907
|
-
if (seededClassifications[id3]?.status === "classified") return false;
|
|
36908
|
-
return true;
|
|
36909
|
-
});
|
|
36910
|
-
if (newClipIds.length > 0) {
|
|
36911
|
-
fetchClassifications(newClipIds, authToken).then((classifications) => {
|
|
36912
|
-
setLocalClipClassifications((prev) => ({
|
|
36913
|
-
...prev,
|
|
36914
|
-
...classifications
|
|
36915
|
-
}));
|
|
36916
|
-
}).catch((error) => {
|
|
36917
|
-
console.error("[FileManager] Error fetching idle classifications:", error);
|
|
36918
|
-
});
|
|
36919
|
-
}
|
|
37009
|
+
if (categoryId === "idle_time" && idleTimeVlmEnabled) {
|
|
37010
|
+
await seedIdleClassifications(data.clips || []);
|
|
36920
37011
|
}
|
|
36921
37012
|
setCategoryPages((prev) => ({ ...prev, [categoryId]: page }));
|
|
36922
37013
|
setCategoryHasMore((prev) => ({ ...prev, [categoryId]: data.hasMore }));
|
|
@@ -36930,7 +37021,64 @@ var FileManagerFilters = ({
|
|
|
36930
37021
|
return newSet;
|
|
36931
37022
|
});
|
|
36932
37023
|
}
|
|
36933
|
-
}, [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]);
|
|
36934
37082
|
const fetchPercentileClips = React26.useCallback(async (type) => {
|
|
36935
37083
|
if (!workspaceId || !date || shift === void 0) {
|
|
36936
37084
|
console.warn("[FileManager] Missing required params for percentile clips fetch");
|
|
@@ -37601,7 +37749,7 @@ var FileManagerFilters = ({
|
|
|
37601
37749
|
activeFilter === "idle_time" && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
37602
37750
|
"button",
|
|
37603
37751
|
{
|
|
37604
|
-
onClick:
|
|
37752
|
+
onClick: handleOpenIdleLabelFilterModal,
|
|
37605
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"}`,
|
|
37606
37754
|
title: "Filter by idle reason",
|
|
37607
37755
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Tag, { className: "h-5 w-5" })
|
|
@@ -37689,7 +37837,10 @@ var FileManagerFilters = ({
|
|
|
37689
37837
|
}
|
|
37690
37838
|
)
|
|
37691
37839
|
] }),
|
|
37692
|
-
/* @__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) => {
|
|
37693
37844
|
const config = getRootCauseConfig(reason);
|
|
37694
37845
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
37695
37846
|
"button",
|
|
@@ -49254,7 +49405,7 @@ var SideNavBar = React26.memo(({
|
|
|
49254
49405
|
if (hasClipsCostData) {
|
|
49255
49406
|
items.push({
|
|
49256
49407
|
key: "clips-analysis",
|
|
49257
|
-
label: "
|
|
49408
|
+
label: "Billing",
|
|
49258
49409
|
icon: outline.CurrencyDollarIcon,
|
|
49259
49410
|
onClick: () => {
|
|
49260
49411
|
handleClipsCostClick();
|
|
@@ -61537,25 +61688,39 @@ var ProfileView = () => {
|
|
|
61537
61688
|
var ProfileView_default = ProfileView;
|
|
61538
61689
|
var REFRESH_INTERVAL_MS = 30 * 1e3;
|
|
61539
61690
|
var ClipsCostView = () => {
|
|
61540
|
-
const { data, isLoading,
|
|
61691
|
+
const { data, isLoading, error, refetch } = useCompanyClipsCost();
|
|
61541
61692
|
const navigation = useNavigation();
|
|
61693
|
+
const refetchRef = React26.useRef(refetch);
|
|
61694
|
+
React26.useEffect(() => {
|
|
61695
|
+
refetchRef.current = refetch;
|
|
61696
|
+
}, [refetch]);
|
|
61542
61697
|
React26.useEffect(() => {
|
|
61543
61698
|
const intervalId = setInterval(() => {
|
|
61544
|
-
|
|
61699
|
+
void refetchRef.current();
|
|
61545
61700
|
}, REFRESH_INTERVAL_MS);
|
|
61546
61701
|
return () => clearInterval(intervalId);
|
|
61547
|
-
}, [
|
|
61702
|
+
}, []);
|
|
61548
61703
|
const mobileMenuContext = useMobileMenu();
|
|
61549
61704
|
useHideMobileHeader(!!mobileMenuContext);
|
|
61550
61705
|
const formatNumber = (num) => {
|
|
61551
61706
|
return new Intl.NumberFormat("en-US").format(num);
|
|
61552
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
|
+
};
|
|
61553
61718
|
if (isLoading) {
|
|
61554
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg" }) });
|
|
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..." }) });
|
|
61555
61720
|
}
|
|
61556
61721
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
|
|
61557
|
-
/* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-10 bg-white border-b flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-
|
|
61558
|
-
/* @__PURE__ */ jsxRuntime.
|
|
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: [
|
|
61559
61724
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: mobileMenuContext && /* @__PURE__ */ jsxRuntime.jsx(
|
|
61560
61725
|
HamburgerButton,
|
|
61561
61726
|
{
|
|
@@ -61563,46 +61728,57 @@ var ClipsCostView = () => {
|
|
|
61563
61728
|
className: "flex-shrink-0 -ml-1"
|
|
61564
61729
|
}
|
|
61565
61730
|
) }),
|
|
61566
|
-
/* @__PURE__ */ jsxRuntime.
|
|
61567
|
-
|
|
61568
|
-
|
|
61569
|
-
|
|
61570
|
-
|
|
61571
|
-
|
|
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(
|
|
61572
61739
|
BackButtonMinimal,
|
|
61573
61740
|
{
|
|
61574
61741
|
onClick: () => navigation.goToDashboard(),
|
|
61575
61742
|
text: "Back",
|
|
61576
61743
|
size: "default",
|
|
61577
|
-
|
|
61744
|
+
"aria-label": "Navigate back to dashboard"
|
|
61578
61745
|
}
|
|
61579
61746
|
) }),
|
|
61580
|
-
/* @__PURE__ */ jsxRuntime.
|
|
61581
|
-
|
|
61582
|
-
|
|
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]" })
|
|
61583
61752
|
] }) })
|
|
61584
61753
|
] }) }),
|
|
61585
|
-
/* @__PURE__ */ jsxRuntime.
|
|
61586
|
-
|
|
61587
|
-
|
|
61588
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
61589
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
61590
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Clip analysis data will appear here once clips have been analyzed." })
|
|
61591
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden", children: [
|
|
61592
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
61593
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 bg-blue-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Film, { className: "h-5 w-5 text-blue-600" }) }),
|
|
61594
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
61595
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Clips Analysis" }),
|
|
61596
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "AI-powered clip classification usage" })
|
|
61597
|
-
] })
|
|
61598
|
-
] }) }),
|
|
61599
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-4", children: [
|
|
61600
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-500 mb-2", children: "Total Clips Analyzed" }),
|
|
61601
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-4xl font-bold text-gray-900", children: formatNumber(data?.totalClassifications || 0) }),
|
|
61602
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "clips processed through AI classification" })
|
|
61603
|
-
] }) })
|
|
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 })
|
|
61604
61759
|
] })
|
|
61605
|
-
] })
|
|
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
|
+
] }) })
|
|
61606
61782
|
] });
|
|
61607
61783
|
};
|
|
61608
61784
|
var ClipsCostView_default = ClipsCostView;
|
|
@@ -62276,6 +62452,21 @@ var ShiftsView = ({
|
|
|
62276
62452
|
}
|
|
62277
62453
|
const factoryId = existingLineThreshold?.factory_id || lineFactoryId;
|
|
62278
62454
|
if (factoryId) {
|
|
62455
|
+
let resolvedLineThresholdSkuId = existingLineThreshold?.sku_id || null;
|
|
62456
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62457
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
62458
|
+
if (dummySkuError) {
|
|
62459
|
+
throw new Error(
|
|
62460
|
+
`Failed to resolve dummy SKU for line ${lineId}, shift ${shift.shiftId}: ${dummySkuError.message}`
|
|
62461
|
+
);
|
|
62462
|
+
}
|
|
62463
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
62464
|
+
}
|
|
62465
|
+
if (!resolvedLineThresholdSkuId) {
|
|
62466
|
+
throw new Error(
|
|
62467
|
+
`No active SKU found for line ${lineId}, shift ${shift.shiftId} while updating line thresholds`
|
|
62468
|
+
);
|
|
62469
|
+
}
|
|
62279
62470
|
const lineThresholdPayload = {
|
|
62280
62471
|
factory_id: factoryId,
|
|
62281
62472
|
line_id: lineId,
|
|
@@ -62283,12 +62474,10 @@ var ShiftsView = ({
|
|
|
62283
62474
|
shift_id: shift.shiftId,
|
|
62284
62475
|
product_code: existingLineThreshold?.product_code || "",
|
|
62285
62476
|
threshold_day_output: thresholdDayOutput,
|
|
62286
|
-
threshold_pph: thresholdPPH
|
|
62477
|
+
threshold_pph: thresholdPPH,
|
|
62478
|
+
sku_id: resolvedLineThresholdSkuId
|
|
62287
62479
|
};
|
|
62288
|
-
|
|
62289
|
-
lineThresholdPayload.sku_id = existingLineThreshold.sku_id;
|
|
62290
|
-
}
|
|
62291
|
-
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
62480
|
+
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
62292
62481
|
if (lineThresholdUpsertError) {
|
|
62293
62482
|
throw new Error(
|
|
62294
62483
|
`Failed to update line thresholds for line ${lineId}, shift ${shift.shiftId}: ${lineThresholdUpsertError.message}`
|
|
@@ -64076,6 +64265,17 @@ var TargetsView = ({
|
|
|
64076
64265
|
await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
|
|
64077
64266
|
console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
|
|
64078
64267
|
const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
|
|
64268
|
+
let resolvedLineThresholdSkuId = lineDataToSave.selectedSKU?.id || null;
|
|
64269
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64270
|
+
const { data: dummySkuRows, error: dummySkuError } = await supabase.from("skus").select("id").eq("line_id", lineId).eq("sku_definition", "dummy_definition").eq("is_active", true).limit(1);
|
|
64271
|
+
if (dummySkuError) {
|
|
64272
|
+
throw dummySkuError;
|
|
64273
|
+
}
|
|
64274
|
+
resolvedLineThresholdSkuId = dummySkuRows?.[0]?.id || null;
|
|
64275
|
+
}
|
|
64276
|
+
if (!resolvedLineThresholdSkuId) {
|
|
64277
|
+
throw new Error(`No active SKU found for line ${lineId} while saving line thresholds`);
|
|
64278
|
+
}
|
|
64079
64279
|
const lineThresholdData = {
|
|
64080
64280
|
factory_id: currentFactoryId,
|
|
64081
64281
|
line_id: lineId,
|
|
@@ -64085,10 +64285,10 @@ var TargetsView = ({
|
|
|
64085
64285
|
threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
|
|
64086
64286
|
threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (ws.targetPPH ? Math.round(Number(ws.targetPPH)) : 0), 0),
|
|
64087
64287
|
// Round each PPH value
|
|
64088
|
-
|
|
64288
|
+
sku_id: resolvedLineThresholdSkuId
|
|
64089
64289
|
};
|
|
64090
64290
|
console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
|
|
64091
|
-
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
64291
|
+
const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id,sku_id" });
|
|
64092
64292
|
if (lineUpsertError) {
|
|
64093
64293
|
console.error(`[handleSaveLine] Error upserting line_thresholds for ${lineId}:`, lineUpsertError);
|
|
64094
64294
|
sonner.toast.error(`Failed to save line thresholds for ${mergedLineNames[lineId] || lineId}`);
|
|
@@ -64884,6 +65084,7 @@ var WorkspaceDetailView = ({
|
|
|
64884
65084
|
}, [monthlyData, range]);
|
|
64885
65085
|
const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
|
|
64886
65086
|
const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY"));
|
|
65087
|
+
const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
|
|
64887
65088
|
const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
|
|
64888
65089
|
const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
|
|
64889
65090
|
const shiftDurationMinutes = React26.useMemo(
|
|
@@ -65487,107 +65688,123 @@ var WorkspaceDetailView = ({
|
|
|
65487
65688
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
|
|
65488
65689
|
] }),
|
|
65489
65690
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden lg:flex lg:flex-col lg:h-full lg:min-h-0 gap-3", children: [
|
|
65490
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65491
|
-
|
|
65492
|
-
|
|
65493
|
-
|
|
65494
|
-
|
|
65495
|
-
|
|
65496
|
-
|
|
65497
|
-
|
|
65498
|
-
|
|
65499
|
-
|
|
65500
|
-
|
|
65501
|
-
|
|
65502
|
-
|
|
65503
|
-
|
|
65504
|
-
|
|
65505
|
-
|
|
65506
|
-
|
|
65507
|
-
|
|
65508
|
-
|
|
65509
|
-
|
|
65510
|
-
|
|
65511
|
-
|
|
65512
|
-
|
|
65513
|
-
|
|
65514
|
-
|
|
65515
|
-
|
|
65516
|
-
|
|
65517
|
-
|
|
65518
|
-
|
|
65519
|
-
|
|
65520
|
-
|
|
65521
|
-
|
|
65522
|
-
|
|
65523
|
-
|
|
65524
|
-
|
|
65525
|
-
|
|
65526
|
-
|
|
65527
|
-
|
|
65528
|
-
|
|
65529
|
-
|
|
65530
|
-
|
|
65531
|
-
|
|
65532
|
-
|
|
65533
|
-
|
|
65534
|
-
|
|
65535
|
-
|
|
65536
|
-
|
|
65537
|
-
|
|
65538
|
-
|
|
65539
|
-
|
|
65540
|
-
|
|
65541
|
-
|
|
65542
|
-
|
|
65543
|
-
|
|
65544
|
-
|
|
65545
|
-
|
|
65546
|
-
|
|
65547
|
-
|
|
65548
|
-
|
|
65549
|
-
|
|
65550
|
-
|
|
65551
|
-
|
|
65552
|
-
|
|
65553
|
-
|
|
65554
|
-
|
|
65555
|
-
|
|
65556
|
-
|
|
65557
|
-
|
|
65558
|
-
|
|
65559
|
-
|
|
65560
|
-
|
|
65561
|
-
|
|
65562
|
-
|
|
65563
|
-
|
|
65564
|
-
|
|
65565
|
-
|
|
65566
|
-
|
|
65567
|
-
|
|
65568
|
-
|
|
65569
|
-
|
|
65570
|
-
|
|
65571
|
-
|
|
65572
|
-
|
|
65573
|
-
|
|
65574
|
-
|
|
65575
|
-
|
|
65576
|
-
|
|
65577
|
-
|
|
65578
|
-
|
|
65579
|
-
|
|
65580
|
-
|
|
65581
|
-
|
|
65582
|
-
|
|
65583
|
-
|
|
65584
|
-
|
|
65585
|
-
|
|
65586
|
-
|
|
65587
|
-
|
|
65588
|
-
|
|
65589
|
-
|
|
65590
|
-
|
|
65691
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65692
|
+
"div",
|
|
65693
|
+
{
|
|
65694
|
+
className: clsx(
|
|
65695
|
+
"grid grid-cols-1 gap-3 min-h-0",
|
|
65696
|
+
isUptimeMode && showIdleBreakdownChart ? "lg:grid-cols-3" : "lg:grid-cols-10",
|
|
65697
|
+
desktopTopSectionClass
|
|
65698
|
+
),
|
|
65699
|
+
children: [
|
|
65700
|
+
!shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
65701
|
+
motion.div,
|
|
65702
|
+
{
|
|
65703
|
+
className: "bg-white rounded-lg shadow-sm p-4 lg:col-span-2 flex flex-col min-h-0",
|
|
65704
|
+
variants: chartCardVariants,
|
|
65705
|
+
initial: "initial",
|
|
65706
|
+
animate: "animate",
|
|
65707
|
+
children: [
|
|
65708
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700 mb-4 text-center", children: "Today's Output" }),
|
|
65709
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
65710
|
+
OutputProgressChart,
|
|
65711
|
+
{
|
|
65712
|
+
currentOutput: workspace.total_actions || 0,
|
|
65713
|
+
targetOutput: workspace.target_output || 0
|
|
65714
|
+
}
|
|
65715
|
+
) })
|
|
65716
|
+
]
|
|
65717
|
+
}
|
|
65718
|
+
),
|
|
65719
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
65720
|
+
motion.div,
|
|
65721
|
+
{
|
|
65722
|
+
className: clsx(
|
|
65723
|
+
"bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
|
|
65724
|
+
isUptimeMode && showIdleBreakdownChart ? "lg:col-span-2" : shouldShowCycleTimeChart || isUptimeMode ? "lg:col-span-10" : idleTimeVlmEnabled ? "lg:col-span-6" : "lg:col-span-8"
|
|
65725
|
+
),
|
|
65726
|
+
variants: chartCardVariants,
|
|
65727
|
+
initial: "initial",
|
|
65728
|
+
animate: "animate",
|
|
65729
|
+
children: [
|
|
65730
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
|
|
65731
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle Time (last 60 minutes)" : "Hourly Output" }),
|
|
65732
|
+
!isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
65733
|
+
"button",
|
|
65734
|
+
{
|
|
65735
|
+
onClick: () => setShowChartIdleTime(!showChartIdleTime),
|
|
65736
|
+
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"}`,
|
|
65737
|
+
children: showChartIdleTime ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
65738
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { className: "w-4 h-4 mr-1.5" }),
|
|
65739
|
+
"Hide Idle Time"
|
|
65740
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
65741
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "w-4 h-4 mr-1.5" }),
|
|
65742
|
+
"Show Idle Time"
|
|
65743
|
+
] })
|
|
65744
|
+
}
|
|
65745
|
+
)
|
|
65746
|
+
] }),
|
|
65747
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
65748
|
+
HourlyUptimeChart,
|
|
65749
|
+
{
|
|
65750
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
65751
|
+
shiftStart: workspace.shift_start,
|
|
65752
|
+
shiftEnd: workspace.shift_end,
|
|
65753
|
+
shiftDate: idleClipDate,
|
|
65754
|
+
timezone,
|
|
65755
|
+
elapsedMinutes: elapsedShiftMinutes
|
|
65756
|
+
}
|
|
65757
|
+
) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
65758
|
+
CycleTimeOverTimeChart,
|
|
65759
|
+
{
|
|
65760
|
+
data: workspace.hourly_action_counts || [],
|
|
65761
|
+
idealCycleTime: workspace.ideal_cycle_time || 0,
|
|
65762
|
+
shiftStart: workspace.shift_start || ""
|
|
65763
|
+
}
|
|
65764
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
65765
|
+
HourlyOutputChart2,
|
|
65766
|
+
{
|
|
65767
|
+
data: workspace.hourly_action_counts || [],
|
|
65768
|
+
pphThreshold: workspace.pph_threshold || 0,
|
|
65769
|
+
shiftStart: workspace.shift_start || "06:00",
|
|
65770
|
+
shiftEnd: workspace.shift_end,
|
|
65771
|
+
showIdleTime: showChartIdleTime,
|
|
65772
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
65773
|
+
idleTimeClips,
|
|
65774
|
+
idleTimeClipClassifications,
|
|
65775
|
+
shiftDate: idleClipDate,
|
|
65776
|
+
timezone
|
|
65777
|
+
}
|
|
65778
|
+
) })
|
|
65779
|
+
]
|
|
65780
|
+
}
|
|
65781
|
+
),
|
|
65782
|
+
!shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
65783
|
+
motion.div,
|
|
65784
|
+
{
|
|
65785
|
+
className: clsx(
|
|
65786
|
+
"bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
|
|
65787
|
+
isUptimeMode ? "lg:col-span-1" : "lg:col-span-2"
|
|
65788
|
+
),
|
|
65789
|
+
variants: chartCardVariants,
|
|
65790
|
+
initial: "initial",
|
|
65791
|
+
animate: "animate",
|
|
65792
|
+
children: [
|
|
65793
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-700 mb-4 text-center", children: "Idle Time Breakdown" }),
|
|
65794
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[220px] min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
65795
|
+
IdleTimeReasonChart,
|
|
65796
|
+
{
|
|
65797
|
+
data: idleTimeData.chartData,
|
|
65798
|
+
isLoading: idleTimeData.isLoading,
|
|
65799
|
+
error: idleTimeData.error
|
|
65800
|
+
}
|
|
65801
|
+
) })
|
|
65802
|
+
]
|
|
65803
|
+
}
|
|
65804
|
+
)
|
|
65805
|
+
]
|
|
65806
|
+
}
|
|
65807
|
+
),
|
|
65591
65808
|
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: [
|
|
65592
65809
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
65593
65810
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Efficiency" }) }),
|