@optifye/dashboard-core 6.10.13 → 6.10.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +141 -33
- package/dist/index.d.mts +67 -2
- package/dist/index.d.ts +67 -2
- package/dist/index.js +1722 -1082
- package/dist/index.mjs +1726 -1087
- package/global.css +12 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1581,7 +1581,7 @@ var dashboardService = {
|
|
|
1581
1581
|
};
|
|
1582
1582
|
const formattedStartDate = formatDate2(startDate);
|
|
1583
1583
|
const formattedEndDate = formatDate2(endDate);
|
|
1584
|
-
let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces").gte("date", formattedStartDate).lte("date", formattedEndDate);
|
|
1584
|
+
let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces, current_output, ideal_output, line_threshold").gte("date", formattedStartDate).lte("date", formattedEndDate);
|
|
1585
1585
|
if (lineIdInput === factoryViewId) {
|
|
1586
1586
|
if (!isValidFactoryViewConfiguration(entityConfig)) {
|
|
1587
1587
|
throw new Error("Factory View requires at least one configured line for monthly data.");
|
|
@@ -1600,7 +1600,10 @@ var dashboardService = {
|
|
|
1600
1600
|
shift_id: item.shift_id,
|
|
1601
1601
|
avg_efficiency: item.avg_efficiency || 0,
|
|
1602
1602
|
underperforming_workspaces: item.underperforming_workspaces || 0,
|
|
1603
|
-
total_workspaces: item.total_workspaces || 0
|
|
1603
|
+
total_workspaces: item.total_workspaces || 0,
|
|
1604
|
+
current_output: item.current_output || 0,
|
|
1605
|
+
ideal_output: item.ideal_output || 0,
|
|
1606
|
+
line_threshold: item.line_threshold || 0
|
|
1604
1607
|
}));
|
|
1605
1608
|
return transformedData;
|
|
1606
1609
|
} catch (err) {
|
|
@@ -9415,7 +9418,56 @@ var getUniformShiftGroup = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new
|
|
|
9415
9418
|
return groups.length === 1 ? groups[0] : null;
|
|
9416
9419
|
};
|
|
9417
9420
|
|
|
9421
|
+
// src/lib/types/efficiencyLegend.ts
|
|
9422
|
+
var DEFAULT_EFFICIENCY_LEGEND = {
|
|
9423
|
+
green_min: 80,
|
|
9424
|
+
green_max: 100,
|
|
9425
|
+
yellow_min: 70,
|
|
9426
|
+
yellow_max: 79,
|
|
9427
|
+
red_min: 0,
|
|
9428
|
+
red_max: 69,
|
|
9429
|
+
critical_threshold: 50
|
|
9430
|
+
};
|
|
9431
|
+
function getEfficiencyColor(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
|
|
9432
|
+
if (efficiency >= legend.green_min) {
|
|
9433
|
+
return "green";
|
|
9434
|
+
} else if (efficiency >= legend.yellow_min) {
|
|
9435
|
+
return "yellow";
|
|
9436
|
+
} else {
|
|
9437
|
+
return "red";
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
function getEfficiencyColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
|
|
9441
|
+
const color2 = getEfficiencyColor(efficiency, legend);
|
|
9442
|
+
switch (color2) {
|
|
9443
|
+
case "green":
|
|
9444
|
+
return "bg-[#00AB45]/90 hover:bg-[#00AB45]/95";
|
|
9445
|
+
case "yellow":
|
|
9446
|
+
return "bg-[#FFB020]/90 hover:bg-[#FFB020]/95";
|
|
9447
|
+
case "red":
|
|
9448
|
+
return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
|
|
9449
|
+
default:
|
|
9450
|
+
return "bg-gray-300/90";
|
|
9451
|
+
}
|
|
9452
|
+
}
|
|
9453
|
+
|
|
9418
9454
|
// src/lib/hooks/useDashboardMetrics.ts
|
|
9455
|
+
var parseEfficiencyLegend = (legend) => {
|
|
9456
|
+
if (!legend) return null;
|
|
9457
|
+
const coerce = (value, fallback) => {
|
|
9458
|
+
const num = Number(value);
|
|
9459
|
+
return Number.isFinite(num) ? num : fallback;
|
|
9460
|
+
};
|
|
9461
|
+
return {
|
|
9462
|
+
green_min: coerce(legend.green_min, DEFAULT_EFFICIENCY_LEGEND.green_min),
|
|
9463
|
+
green_max: coerce(legend.green_max, DEFAULT_EFFICIENCY_LEGEND.green_max),
|
|
9464
|
+
yellow_min: coerce(legend.yellow_min, DEFAULT_EFFICIENCY_LEGEND.yellow_min),
|
|
9465
|
+
yellow_max: coerce(legend.yellow_max, DEFAULT_EFFICIENCY_LEGEND.yellow_max),
|
|
9466
|
+
red_min: coerce(legend.red_min, DEFAULT_EFFICIENCY_LEGEND.red_min),
|
|
9467
|
+
red_max: coerce(legend.red_max, DEFAULT_EFFICIENCY_LEGEND.red_max),
|
|
9468
|
+
critical_threshold: coerce(legend.critical_threshold, DEFAULT_EFFICIENCY_LEGEND.critical_threshold)
|
|
9469
|
+
};
|
|
9470
|
+
};
|
|
9419
9471
|
var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
|
|
9420
9472
|
const { supabaseUrl, supabaseKey } = useDashboardConfig();
|
|
9421
9473
|
const entityConfig = useEntityConfig();
|
|
@@ -9473,7 +9525,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9473
9525
|
);
|
|
9474
9526
|
React24.useEffect(() => {
|
|
9475
9527
|
lineIdRef.current = lineId;
|
|
9476
|
-
setMetrics({ workspaceMetrics: [], lineMetrics: [] });
|
|
9528
|
+
setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0 });
|
|
9477
9529
|
setIsLoading(true);
|
|
9478
9530
|
}, [lineId]);
|
|
9479
9531
|
const fetchAllMetrics = React24.useCallback(async () => {
|
|
@@ -9511,6 +9563,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9511
9563
|
}
|
|
9512
9564
|
let allWorkspaceMetrics = [];
|
|
9513
9565
|
let allLineMetrics = [];
|
|
9566
|
+
let hasFlowBuffers = false;
|
|
9567
|
+
let efficiencyLegend;
|
|
9514
9568
|
if (isFactory && shiftGroups.length > 0) {
|
|
9515
9569
|
console.log(`[useDashboardMetrics] \u{1F3ED} Factory view: Fetching for ${shiftGroups.length} shift group(s)`);
|
|
9516
9570
|
const metricsPromises = shiftGroups.map(async (group) => {
|
|
@@ -9541,6 +9595,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9541
9595
|
}
|
|
9542
9596
|
});
|
|
9543
9597
|
const results = await Promise.all(metricsPromises);
|
|
9598
|
+
hasFlowBuffers = results.some((result) => result?.metadata?.has_flow_buffers);
|
|
9599
|
+
const legendCandidate = results.find((result) => result?.efficiency_legend)?.efficiency_legend;
|
|
9600
|
+
efficiencyLegend = parseEfficiencyLegend(legendCandidate) ?? DEFAULT_EFFICIENCY_LEGEND;
|
|
9544
9601
|
results.forEach((result) => {
|
|
9545
9602
|
if (result.workspace_metrics) {
|
|
9546
9603
|
allWorkspaceMetrics.push(...result.workspace_metrics);
|
|
@@ -9593,6 +9650,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9593
9650
|
});
|
|
9594
9651
|
allWorkspaceMetrics = backendData.workspace_metrics || [];
|
|
9595
9652
|
allLineMetrics = backendData.line_metrics || [];
|
|
9653
|
+
hasFlowBuffers = Boolean(backendData?.metadata?.has_flow_buffers);
|
|
9654
|
+
efficiencyLegend = parseEfficiencyLegend(backendData?.efficiency_legend) ?? DEFAULT_EFFICIENCY_LEGEND;
|
|
9596
9655
|
}
|
|
9597
9656
|
const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
|
|
9598
9657
|
company_id: item.company_id || entityConfig.companyId,
|
|
@@ -9618,7 +9677,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9618
9677
|
});
|
|
9619
9678
|
const newMetricsState = {
|
|
9620
9679
|
workspaceMetrics: transformedWorkspaceData,
|
|
9621
|
-
lineMetrics: allLineMetrics || []
|
|
9680
|
+
lineMetrics: allLineMetrics || [],
|
|
9681
|
+
metadata: { hasFlowBuffers },
|
|
9682
|
+
efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
|
|
9622
9683
|
};
|
|
9623
9684
|
console.log("[useDashboardMetrics] Setting metrics state:", {
|
|
9624
9685
|
workspaceMetrics: newMetricsState.workspaceMetrics.length,
|
|
@@ -9770,6 +9831,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
|
|
|
9770
9831
|
return {
|
|
9771
9832
|
workspaceMetrics: metrics2?.workspaceMetrics || [],
|
|
9772
9833
|
lineMetrics: metrics2?.lineMetrics || [],
|
|
9834
|
+
efficiencyLegend: metrics2?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
|
|
9835
|
+
metadata: metrics2?.metadata,
|
|
9773
9836
|
isLoading,
|
|
9774
9837
|
error,
|
|
9775
9838
|
refetch: fetchAllMetrics
|
|
@@ -13786,6 +13849,256 @@ function useIdleTimeReasons({
|
|
|
13786
13849
|
refetch: fetchData
|
|
13787
13850
|
};
|
|
13788
13851
|
}
|
|
13852
|
+
|
|
13853
|
+
// src/lib/services/clipClassificationService.ts
|
|
13854
|
+
function parseCleanLabel(rawLabel) {
|
|
13855
|
+
if (!rawLabel) return null;
|
|
13856
|
+
const patterns = [
|
|
13857
|
+
/"Clip Label"\s*:\s*"([^"]+)"/,
|
|
13858
|
+
// Old format
|
|
13859
|
+
/"clip_label"\s*:\s*"([^"]+)"/,
|
|
13860
|
+
// New format
|
|
13861
|
+
/'Clip Label'\s*:\s*'([^']+)'/,
|
|
13862
|
+
// Single quotes
|
|
13863
|
+
/'clip_label'\s*:\s*'([^']+)'/
|
|
13864
|
+
// Single quotes
|
|
13865
|
+
];
|
|
13866
|
+
for (const pattern of patterns) {
|
|
13867
|
+
const match = rawLabel.match(pattern);
|
|
13868
|
+
if (match) {
|
|
13869
|
+
const label = match[1].trim();
|
|
13870
|
+
return label.replace(/ /g, "_");
|
|
13871
|
+
}
|
|
13872
|
+
}
|
|
13873
|
+
return null;
|
|
13874
|
+
}
|
|
13875
|
+
async function fetchClassifications(clipIds, token) {
|
|
13876
|
+
if (!clipIds || clipIds.length === 0) {
|
|
13877
|
+
return {};
|
|
13878
|
+
}
|
|
13879
|
+
try {
|
|
13880
|
+
const CHUNK_SIZE = 50;
|
|
13881
|
+
const chunks = [];
|
|
13882
|
+
for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
|
|
13883
|
+
chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
|
|
13884
|
+
}
|
|
13885
|
+
console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
|
|
13886
|
+
const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
|
|
13887
|
+
const chunkResults = await Promise.all(
|
|
13888
|
+
chunks.map(async (chunkIds) => {
|
|
13889
|
+
try {
|
|
13890
|
+
const response = await fetch(
|
|
13891
|
+
`${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
|
|
13892
|
+
{
|
|
13893
|
+
headers: {
|
|
13894
|
+
"Authorization": `Bearer ${token}`,
|
|
13895
|
+
"Content-Type": "application/json"
|
|
13896
|
+
}
|
|
13897
|
+
}
|
|
13898
|
+
);
|
|
13899
|
+
if (!response.ok) {
|
|
13900
|
+
console.error(`Classification API error for chunk: ${response.status}`);
|
|
13901
|
+
return Object.fromEntries(
|
|
13902
|
+
chunkIds.map((id3) => [id3, { status: "processing" }])
|
|
13903
|
+
);
|
|
13904
|
+
}
|
|
13905
|
+
const data = await response.json();
|
|
13906
|
+
return data.classifications || {};
|
|
13907
|
+
} catch (error) {
|
|
13908
|
+
console.error("Error fetching chunk:", error);
|
|
13909
|
+
return Object.fromEntries(
|
|
13910
|
+
chunkIds.map((id3) => [id3, { status: "processing" }])
|
|
13911
|
+
);
|
|
13912
|
+
}
|
|
13913
|
+
})
|
|
13914
|
+
);
|
|
13915
|
+
const allClassifications = Object.assign({}, ...chunkResults);
|
|
13916
|
+
console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
|
|
13917
|
+
return allClassifications;
|
|
13918
|
+
} catch (error) {
|
|
13919
|
+
console.error("Error fetching classifications:", error);
|
|
13920
|
+
return Object.fromEntries(
|
|
13921
|
+
clipIds.map((id3) => [id3, { status: "processing" }])
|
|
13922
|
+
);
|
|
13923
|
+
}
|
|
13924
|
+
}
|
|
13925
|
+
function useClassificationRealtimeUpdates({
|
|
13926
|
+
clipIds,
|
|
13927
|
+
enabled = true,
|
|
13928
|
+
onClassificationUpdate
|
|
13929
|
+
}) {
|
|
13930
|
+
const supabase = useSupabase();
|
|
13931
|
+
React24.useEffect(() => {
|
|
13932
|
+
if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
|
|
13933
|
+
return;
|
|
13934
|
+
}
|
|
13935
|
+
const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
|
|
13936
|
+
console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
|
|
13937
|
+
const channel = supabase.channel(channelName).on(
|
|
13938
|
+
"postgres_changes",
|
|
13939
|
+
{
|
|
13940
|
+
event: "INSERT",
|
|
13941
|
+
schema: "public",
|
|
13942
|
+
table: "clip_classification",
|
|
13943
|
+
filter: `clip_id=in.(${clipIds.join(",")})`
|
|
13944
|
+
},
|
|
13945
|
+
(payload) => {
|
|
13946
|
+
console.log("[useClassificationRealtimeUpdates] New classification:", payload);
|
|
13947
|
+
try {
|
|
13948
|
+
const { clip_id, clip_label, confidence_score } = payload.new;
|
|
13949
|
+
const parsedLabel = parseCleanLabel(clip_label);
|
|
13950
|
+
if (parsedLabel) {
|
|
13951
|
+
onClassificationUpdate(clip_id, {
|
|
13952
|
+
status: "classified",
|
|
13953
|
+
label: parsedLabel,
|
|
13954
|
+
confidence: confidence_score || 0
|
|
13955
|
+
});
|
|
13956
|
+
} else {
|
|
13957
|
+
console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
|
|
13958
|
+
}
|
|
13959
|
+
} catch (error) {
|
|
13960
|
+
console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
|
|
13961
|
+
}
|
|
13962
|
+
}
|
|
13963
|
+
).subscribe((status) => {
|
|
13964
|
+
console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
|
|
13965
|
+
});
|
|
13966
|
+
return () => {
|
|
13967
|
+
console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
|
|
13968
|
+
supabase.removeChannel(channel);
|
|
13969
|
+
};
|
|
13970
|
+
}, [clipIds, enabled, supabase, onClassificationUpdate]);
|
|
13971
|
+
}
|
|
13972
|
+
|
|
13973
|
+
// src/lib/hooks/useIdleTimeClipClassifications.ts
|
|
13974
|
+
var DEFAULT_LIMIT = 200;
|
|
13975
|
+
var MAX_PAGES = 5;
|
|
13976
|
+
function useIdleTimeClipClassifications({
|
|
13977
|
+
workspaceId,
|
|
13978
|
+
date,
|
|
13979
|
+
shiftId,
|
|
13980
|
+
enabled = true,
|
|
13981
|
+
limit = DEFAULT_LIMIT
|
|
13982
|
+
}) {
|
|
13983
|
+
const { session } = useAuth();
|
|
13984
|
+
const [idleClips, setIdleClips] = React24.useState([]);
|
|
13985
|
+
const [clipClassifications, setClipClassifications] = React24.useState({});
|
|
13986
|
+
const [isLoading, setIsLoading] = React24.useState(false);
|
|
13987
|
+
const [error, setError] = React24.useState(null);
|
|
13988
|
+
const lastRequestKeyRef = React24.useRef("");
|
|
13989
|
+
const requestKey = React24.useMemo(
|
|
13990
|
+
() => `${workspaceId || ""}-${date || ""}-${shiftId ?? ""}`,
|
|
13991
|
+
[workspaceId, date, shiftId]
|
|
13992
|
+
);
|
|
13993
|
+
const fetchIdleClips = React24.useCallback(async () => {
|
|
13994
|
+
if (!enabled) {
|
|
13995
|
+
setIsLoading(false);
|
|
13996
|
+
return;
|
|
13997
|
+
}
|
|
13998
|
+
if (!workspaceId || !date || shiftId === void 0) {
|
|
13999
|
+
setIdleClips([]);
|
|
14000
|
+
setIsLoading(false);
|
|
14001
|
+
return;
|
|
14002
|
+
}
|
|
14003
|
+
if (!session?.access_token) {
|
|
14004
|
+
setError("Not authenticated");
|
|
14005
|
+
setIsLoading(false);
|
|
14006
|
+
return;
|
|
14007
|
+
}
|
|
14008
|
+
setIsLoading(true);
|
|
14009
|
+
setError(null);
|
|
14010
|
+
lastRequestKeyRef.current = requestKey;
|
|
14011
|
+
try {
|
|
14012
|
+
const collected = [];
|
|
14013
|
+
let page = 1;
|
|
14014
|
+
let hasMore = true;
|
|
14015
|
+
while (hasMore && page <= MAX_PAGES && collected.length < limit) {
|
|
14016
|
+
const response = await fetch("/api/clips/supabase", {
|
|
14017
|
+
method: "POST",
|
|
14018
|
+
headers: {
|
|
14019
|
+
"Content-Type": "application/json",
|
|
14020
|
+
"Authorization": `Bearer ${session.access_token}`
|
|
14021
|
+
},
|
|
14022
|
+
body: JSON.stringify({
|
|
14023
|
+
action: "clip-metadata",
|
|
14024
|
+
workspaceId,
|
|
14025
|
+
date,
|
|
14026
|
+
shift: shiftId.toString(),
|
|
14027
|
+
category: "idle_time",
|
|
14028
|
+
page,
|
|
14029
|
+
limit
|
|
14030
|
+
})
|
|
14031
|
+
});
|
|
14032
|
+
if (!response.ok) {
|
|
14033
|
+
throw new Error(`Idle clip metadata request failed: ${response.status}`);
|
|
14034
|
+
}
|
|
14035
|
+
const data = await response.json();
|
|
14036
|
+
const clips = Array.isArray(data?.clips) ? data.clips : [];
|
|
14037
|
+
const mapped = clips.map((clip) => ({
|
|
14038
|
+
id: clip.id,
|
|
14039
|
+
idle_start_time: clip.idle_start_time,
|
|
14040
|
+
idle_end_time: clip.idle_end_time
|
|
14041
|
+
}));
|
|
14042
|
+
collected.push(...mapped);
|
|
14043
|
+
hasMore = Boolean(data?.hasMore);
|
|
14044
|
+
page += 1;
|
|
14045
|
+
}
|
|
14046
|
+
if (lastRequestKeyRef.current === requestKey) {
|
|
14047
|
+
setIdleClips(collected);
|
|
14048
|
+
}
|
|
14049
|
+
} catch (err) {
|
|
14050
|
+
console.error("[useIdleTimeClipClassifications] Error fetching idle clips:", err);
|
|
14051
|
+
if (lastRequestKeyRef.current === requestKey) {
|
|
14052
|
+
setError(err instanceof Error ? err.message : "Failed to fetch idle clips");
|
|
14053
|
+
setIdleClips([]);
|
|
14054
|
+
}
|
|
14055
|
+
} finally {
|
|
14056
|
+
if (lastRequestKeyRef.current === requestKey) {
|
|
14057
|
+
setIsLoading(false);
|
|
14058
|
+
}
|
|
14059
|
+
}
|
|
14060
|
+
}, [enabled, workspaceId, date, shiftId, session?.access_token, requestKey, limit]);
|
|
14061
|
+
React24.useEffect(() => {
|
|
14062
|
+
fetchIdleClips();
|
|
14063
|
+
}, [fetchIdleClips]);
|
|
14064
|
+
React24.useEffect(() => {
|
|
14065
|
+
setClipClassifications({});
|
|
14066
|
+
}, [requestKey]);
|
|
14067
|
+
const idleClipIds = React24.useMemo(
|
|
14068
|
+
() => idleClips.map((clip) => clip.id).filter(Boolean),
|
|
14069
|
+
[idleClips]
|
|
14070
|
+
);
|
|
14071
|
+
React24.useEffect(() => {
|
|
14072
|
+
if (!enabled || idleClipIds.length === 0 || !session?.access_token) {
|
|
14073
|
+
return;
|
|
14074
|
+
}
|
|
14075
|
+
fetchClassifications(idleClipIds, session.access_token).then((classifications) => {
|
|
14076
|
+
setClipClassifications((prev) => ({
|
|
14077
|
+
...prev,
|
|
14078
|
+
...classifications
|
|
14079
|
+
}));
|
|
14080
|
+
}).catch((err) => {
|
|
14081
|
+
console.error("[useIdleTimeClipClassifications] Error fetching classifications:", err);
|
|
14082
|
+
});
|
|
14083
|
+
}, [enabled, idleClipIds, session?.access_token]);
|
|
14084
|
+
useClassificationRealtimeUpdates({
|
|
14085
|
+
clipIds: idleClipIds,
|
|
14086
|
+
enabled: enabled && idleClipIds.length > 0,
|
|
14087
|
+
onClassificationUpdate: React24.useCallback((clipId, classification) => {
|
|
14088
|
+
setClipClassifications((prev) => ({
|
|
14089
|
+
...prev,
|
|
14090
|
+
[clipId]: classification
|
|
14091
|
+
}));
|
|
14092
|
+
}, [])
|
|
14093
|
+
});
|
|
14094
|
+
return {
|
|
14095
|
+
idleClips,
|
|
14096
|
+
clipClassifications,
|
|
14097
|
+
isLoading,
|
|
14098
|
+
error,
|
|
14099
|
+
refetch: fetchIdleClips
|
|
14100
|
+
};
|
|
14101
|
+
}
|
|
13789
14102
|
function useUserUsage(userId, options = {}) {
|
|
13790
14103
|
const { startDate, endDate, enabled = true } = options;
|
|
13791
14104
|
const { session } = useAuth();
|
|
@@ -26493,6 +26806,10 @@ var HourlyOutputChartComponent = ({
|
|
|
26493
26806
|
shiftEnd,
|
|
26494
26807
|
showIdleTime = false,
|
|
26495
26808
|
idleTimeHourly,
|
|
26809
|
+
idleTimeClips,
|
|
26810
|
+
idleTimeClipClassifications,
|
|
26811
|
+
shiftDate,
|
|
26812
|
+
timezone,
|
|
26496
26813
|
className = ""
|
|
26497
26814
|
}) => {
|
|
26498
26815
|
const containerRef = React24__namespace.default.useRef(null);
|
|
@@ -26505,6 +26822,37 @@ var HourlyOutputChartComponent = ({
|
|
|
26505
26822
|
return { hour, minute, decimalHour };
|
|
26506
26823
|
};
|
|
26507
26824
|
const shiftStartTime = getTimeFromTimeString(shiftStart);
|
|
26825
|
+
const shiftStartDateTime = React24__namespace.default.useMemo(() => {
|
|
26826
|
+
if (!shiftDate || !timezone) return null;
|
|
26827
|
+
const hour = shiftStartTime.hour.toString().padStart(2, "0");
|
|
26828
|
+
const minute = shiftStartTime.minute.toString().padStart(2, "0");
|
|
26829
|
+
return dateFnsTz.fromZonedTime(`${shiftDate}T${hour}:${minute}:00`, timezone);
|
|
26830
|
+
}, [shiftDate, timezone, shiftStartTime.hour, shiftStartTime.minute]);
|
|
26831
|
+
const idleClipRanges = React24__namespace.default.useMemo(() => {
|
|
26832
|
+
if (!idleTimeClips || idleTimeClips.length === 0) return [];
|
|
26833
|
+
return idleTimeClips.map((clip) => ({
|
|
26834
|
+
id: clip.id,
|
|
26835
|
+
start: clip.idle_start_time ? new Date(clip.idle_start_time) : null,
|
|
26836
|
+
end: clip.idle_end_time ? new Date(clip.idle_end_time) : null
|
|
26837
|
+
})).filter((clip) => clip.start && clip.end);
|
|
26838
|
+
}, [idleTimeClips]);
|
|
26839
|
+
const getIdleReasonForRange = React24__namespace.default.useCallback((rangeStart, rangeEnd) => {
|
|
26840
|
+
if (!rangeStart || !rangeEnd || idleClipRanges.length === 0) {
|
|
26841
|
+
return "Reason unavailable";
|
|
26842
|
+
}
|
|
26843
|
+
const matchingClip = idleClipRanges.find((clip) => rangeStart >= clip.start && rangeEnd <= clip.end) || idleClipRanges.find((clip) => rangeStart < clip.end && rangeEnd > clip.start);
|
|
26844
|
+
if (!matchingClip) {
|
|
26845
|
+
return "Reason unavailable";
|
|
26846
|
+
}
|
|
26847
|
+
const classification = idleTimeClipClassifications?.[matchingClip.id];
|
|
26848
|
+
if (!classification || classification.status === "processing") {
|
|
26849
|
+
return "Analyzing...";
|
|
26850
|
+
}
|
|
26851
|
+
if (!classification.label) {
|
|
26852
|
+
return "Reason unavailable";
|
|
26853
|
+
}
|
|
26854
|
+
return classification.label.replace(/_/g, " ");
|
|
26855
|
+
}, [idleClipRanges, idleTimeClipClassifications]);
|
|
26508
26856
|
const { shiftDuration, shiftEndTime, hasPartialLastHour } = React24__namespace.default.useMemo(() => {
|
|
26509
26857
|
console.log("[HourlyOutputChart] Calculating shift duration with:", {
|
|
26510
26858
|
shiftStart,
|
|
@@ -26752,6 +27100,7 @@ var HourlyOutputChartComponent = ({
|
|
|
26752
27100
|
}
|
|
26753
27101
|
idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
|
|
26754
27102
|
return {
|
|
27103
|
+
hourIndex: i,
|
|
26755
27104
|
hour: formatHour(i),
|
|
26756
27105
|
timeRange: formatTimeRange2(i),
|
|
26757
27106
|
output: animatedData[i] || 0,
|
|
@@ -26945,9 +27294,10 @@ var HourlyOutputChartComponent = ({
|
|
|
26945
27294
|
}
|
|
26946
27295
|
}
|
|
26947
27296
|
const formatIdleTimestamp = (minuteIdx) => {
|
|
26948
|
-
const
|
|
26949
|
-
const hourOffset = hourIndex
|
|
26950
|
-
const
|
|
27297
|
+
const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
|
|
27298
|
+
const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
|
|
27299
|
+
const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
|
|
27300
|
+
const totalMinutes = (shiftStartTime.hour + safeHourOffset) * 60 + shiftStartTime.minute + minuteIdx;
|
|
26951
27301
|
const hour = Math.floor(totalMinutes / 60) % 24;
|
|
26952
27302
|
const minute = totalMinutes % 60;
|
|
26953
27303
|
const period = hour >= 12 ? "PM" : "AM";
|
|
@@ -26978,27 +27328,23 @@ var HourlyOutputChartComponent = ({
|
|
|
26978
27328
|
const duration = range.end - range.start + 1;
|
|
26979
27329
|
const startTime = formatIdleTimestamp(range.start);
|
|
26980
27330
|
const endTime = formatIdleTimestamp(range.end + 1);
|
|
27331
|
+
const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
|
|
27332
|
+
const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
|
|
27333
|
+
const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
|
|
27334
|
+
const rangeStartDate = shiftStartDateTime ? dateFns.addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.start) : null;
|
|
27335
|
+
const rangeEndDate = shiftStartDateTime ? dateFns.addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.end + 1) : null;
|
|
27336
|
+
const reasonLabel = getIdleReasonForRange(rangeStartDate, rangeEndDate);
|
|
26981
27337
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
|
|
26982
27338
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
|
|
26983
|
-
/* @__PURE__ */ jsxRuntime.
|
|
26984
|
-
startTime,
|
|
26985
|
-
|
|
26986
|
-
|
|
26987
|
-
|
|
26988
|
-
|
|
26989
|
-
|
|
26990
|
-
|
|
26991
|
-
] })
|
|
26992
|
-
startTime,
|
|
26993
|
-
" - ",
|
|
26994
|
-
endTime,
|
|
26995
|
-
" ",
|
|
26996
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
|
|
26997
|
-
"(",
|
|
26998
|
-
duration,
|
|
26999
|
-
" min)"
|
|
27000
|
-
] })
|
|
27001
|
-
] }) })
|
|
27339
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
27340
|
+
duration === 1 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: startTime }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
27341
|
+
startTime,
|
|
27342
|
+
" - ",
|
|
27343
|
+
endTime
|
|
27344
|
+
] }),
|
|
27345
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: " | " }),
|
|
27346
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600", children: reasonLabel })
|
|
27347
|
+
] })
|
|
27002
27348
|
] }, index);
|
|
27003
27349
|
}) })
|
|
27004
27350
|
] })
|
|
@@ -27096,7 +27442,7 @@ var HourlyOutputChartComponent = ({
|
|
|
27096
27442
|
);
|
|
27097
27443
|
};
|
|
27098
27444
|
var HourlyOutputChart = React24__namespace.default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
|
|
27099
|
-
if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
|
|
27445
|
+
if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.shiftEnd !== nextProps.shiftEnd || prevProps.shiftDate !== nextProps.shiftDate || prevProps.timezone !== nextProps.timezone || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
|
|
27100
27446
|
return false;
|
|
27101
27447
|
}
|
|
27102
27448
|
if (prevProps.data.length !== nextProps.data.length) {
|
|
@@ -27119,6 +27465,33 @@ var HourlyOutputChart = React24__namespace.default.memo(HourlyOutputChartCompone
|
|
|
27119
27465
|
if (prevArray.length !== nextArray.length) return false;
|
|
27120
27466
|
if (!prevArray.every((val, idx) => val === nextArray[idx])) return false;
|
|
27121
27467
|
}
|
|
27468
|
+
const prevIdleClips = prevProps.idleTimeClips || [];
|
|
27469
|
+
const nextIdleClips = nextProps.idleTimeClips || [];
|
|
27470
|
+
if (prevIdleClips.length !== nextIdleClips.length) {
|
|
27471
|
+
return false;
|
|
27472
|
+
}
|
|
27473
|
+
for (let i = 0; i < prevIdleClips.length; i += 1) {
|
|
27474
|
+
const prevClip = prevIdleClips[i];
|
|
27475
|
+
const nextClip = nextIdleClips[i];
|
|
27476
|
+
if (prevClip.id !== nextClip.id || prevClip.idle_start_time !== nextClip.idle_start_time || prevClip.idle_end_time !== nextClip.idle_end_time) {
|
|
27477
|
+
return false;
|
|
27478
|
+
}
|
|
27479
|
+
}
|
|
27480
|
+
const prevClassifications = prevProps.idleTimeClipClassifications || {};
|
|
27481
|
+
const nextClassifications = nextProps.idleTimeClipClassifications || {};
|
|
27482
|
+
const prevClassKeys = Object.keys(prevClassifications);
|
|
27483
|
+
const nextClassKeys = Object.keys(nextClassifications);
|
|
27484
|
+
if (prevClassKeys.length !== nextClassKeys.length) {
|
|
27485
|
+
return false;
|
|
27486
|
+
}
|
|
27487
|
+
for (const key of prevClassKeys) {
|
|
27488
|
+
const prevClass = prevClassifications[key];
|
|
27489
|
+
const nextClass = nextClassifications[key];
|
|
27490
|
+
if (!nextClass) return false;
|
|
27491
|
+
if (prevClass.status !== nextClass.status || prevClass.label !== nextClass.label || prevClass.confidence !== nextClass.confidence) {
|
|
27492
|
+
return false;
|
|
27493
|
+
}
|
|
27494
|
+
}
|
|
27122
27495
|
return true;
|
|
27123
27496
|
});
|
|
27124
27497
|
HourlyOutputChart.displayName = "HourlyOutputChart";
|
|
@@ -27138,6 +27511,7 @@ var VideoCard = React24__namespace.default.memo(({
|
|
|
27138
27511
|
onClick,
|
|
27139
27512
|
onFatalError,
|
|
27140
27513
|
isVeryLowEfficiency = false,
|
|
27514
|
+
legend,
|
|
27141
27515
|
cropping,
|
|
27142
27516
|
canvasFps = 30,
|
|
27143
27517
|
useRAF = true,
|
|
@@ -27149,6 +27523,7 @@ var VideoCard = React24__namespace.default.memo(({
|
|
|
27149
27523
|
}) => {
|
|
27150
27524
|
const videoRef = React24.useRef(null);
|
|
27151
27525
|
const canvasRef = React24.useRef(null);
|
|
27526
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
27152
27527
|
if (cropping) {
|
|
27153
27528
|
useHlsStreamWithCropping(videoRef, canvasRef, {
|
|
27154
27529
|
src: hlsUrl,
|
|
@@ -27166,39 +27541,23 @@ var VideoCard = React24__namespace.default.memo(({
|
|
|
27166
27541
|
});
|
|
27167
27542
|
}
|
|
27168
27543
|
const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
27169
|
-
const
|
|
27170
|
-
|
|
27171
|
-
|
|
27172
|
-
|
|
27173
|
-
return "bg-[#FFD700]/30";
|
|
27174
|
-
} else {
|
|
27175
|
-
return "bg-[#FF2D0A]/30";
|
|
27176
|
-
}
|
|
27177
|
-
};
|
|
27178
|
-
const getEfficiencyBarColor = (efficiency) => {
|
|
27179
|
-
if (efficiency >= 80) {
|
|
27180
|
-
return "bg-[#00AB45]";
|
|
27181
|
-
} else if (efficiency >= 70) {
|
|
27182
|
-
return "bg-[#FFB020]";
|
|
27183
|
-
} else {
|
|
27184
|
-
return "bg-[#E34329]";
|
|
27185
|
-
}
|
|
27186
|
-
};
|
|
27187
|
-
const efficiencyOverlayClass = getEfficiencyOverlayColor(workspace.efficiency);
|
|
27188
|
-
const efficiencyBarClass = getEfficiencyBarColor(workspace.efficiency);
|
|
27544
|
+
const efficiencyColor = getEfficiencyColor(workspace.efficiency, effectiveLegend);
|
|
27545
|
+
const efficiencyOverlayClass = efficiencyColor === "green" ? "bg-[#00D654]/25" : efficiencyColor === "yellow" ? "bg-[#FFD700]/30" : "bg-[#FF2D0A]/30";
|
|
27546
|
+
const efficiencyBarClass = efficiencyColor === "green" ? "bg-[#00AB45]" : efficiencyColor === "yellow" ? "bg-[#FFB020]" : "bg-[#E34329]";
|
|
27547
|
+
const efficiencyStatus = efficiencyColor === "green" ? "High" : efficiencyColor === "yellow" ? "Medium" : "Low";
|
|
27189
27548
|
const trendInfo = workspace.trend !== void 0 ? getTrendArrowAndColor(workspace.trend) : null;
|
|
27190
27549
|
const handleClick = React24.useCallback(() => {
|
|
27191
27550
|
trackCoreEvent("Workspace Card Clicked", {
|
|
27192
27551
|
workspace_id: workspace.workspace_uuid,
|
|
27193
27552
|
workspace_name: workspace.workspace_name,
|
|
27194
27553
|
efficiency: workspace.efficiency,
|
|
27195
|
-
status:
|
|
27554
|
+
status: efficiencyStatus,
|
|
27196
27555
|
trend: workspace.trend
|
|
27197
27556
|
});
|
|
27198
27557
|
if (onClick) {
|
|
27199
27558
|
onClick();
|
|
27200
27559
|
}
|
|
27201
|
-
}, [onClick, workspace]);
|
|
27560
|
+
}, [onClick, workspace, efficiencyStatus]);
|
|
27202
27561
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27203
27562
|
"div",
|
|
27204
27563
|
{
|
|
@@ -27301,6 +27660,7 @@ var VideoGridView = React24__namespace.default.memo(({
|
|
|
27301
27660
|
workspaces,
|
|
27302
27661
|
selectedLine,
|
|
27303
27662
|
className = "",
|
|
27663
|
+
legend,
|
|
27304
27664
|
videoSources = {},
|
|
27305
27665
|
displayNames = {},
|
|
27306
27666
|
onWorkspaceHover,
|
|
@@ -27316,6 +27676,7 @@ var VideoGridView = React24__namespace.default.memo(({
|
|
|
27316
27676
|
const videoConfig = useVideoConfig();
|
|
27317
27677
|
const dashboardConfig = useDashboardConfig();
|
|
27318
27678
|
const { cropping, canvasConfig, hlsUrls } = videoConfig;
|
|
27679
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
27319
27680
|
const mergedVideoSources = {
|
|
27320
27681
|
defaultHlsUrl: videoSources.defaultHlsUrl || hlsUrls?.defaultHlsUrl || DEFAULT_HLS_URL,
|
|
27321
27682
|
workspaceHlsUrls: { ...videoSources.workspaceHlsUrls, ...hlsUrls?.workspaceHlsUrls },
|
|
@@ -27510,6 +27871,7 @@ var VideoGridView = React24__namespace.default.memo(({
|
|
|
27510
27871
|
onClick: () => handleWorkspaceClick(workspace),
|
|
27511
27872
|
onFatalError: () => handleStreamError(workspaceId),
|
|
27512
27873
|
isVeryLowEfficiency,
|
|
27874
|
+
legend: effectiveLegend,
|
|
27513
27875
|
cropping: workspaceCropping,
|
|
27514
27876
|
canvasFps: canvasConfig?.fps,
|
|
27515
27877
|
displayName: (
|
|
@@ -27537,10 +27899,12 @@ var MapGridView = React24__namespace.default.memo(({
|
|
|
27537
27899
|
className = "",
|
|
27538
27900
|
displayNames = {},
|
|
27539
27901
|
onWorkspaceHover,
|
|
27540
|
-
onWorkspaceHoverEnd
|
|
27902
|
+
onWorkspaceHoverEnd,
|
|
27903
|
+
legend
|
|
27541
27904
|
}) => {
|
|
27542
27905
|
const router$1 = router.useRouter();
|
|
27543
27906
|
const dashboardConfig = useDashboardConfig();
|
|
27907
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
27544
27908
|
const mapViewConfig = dashboardConfig?.mapViewConfig;
|
|
27545
27909
|
const workspacePositions = mapViewConfig?.workspacePositions || [];
|
|
27546
27910
|
const aspectRatio2 = mapViewConfig?.aspectRatio || 16 / 9;
|
|
@@ -27552,10 +27916,11 @@ var MapGridView = React24__namespace.default.memo(({
|
|
|
27552
27916
|
return map;
|
|
27553
27917
|
}, [workspaces]);
|
|
27554
27918
|
const getPerformanceColor = React24.useCallback((efficiency) => {
|
|
27555
|
-
|
|
27556
|
-
if (
|
|
27919
|
+
const color2 = getEfficiencyColor(efficiency, effectiveLegend);
|
|
27920
|
+
if (color2 === "green") return "border-green-500 bg-green-50";
|
|
27921
|
+
if (color2 === "yellow") return "border-yellow-500 bg-yellow-50";
|
|
27557
27922
|
return "border-red-500 bg-red-50";
|
|
27558
|
-
}, []);
|
|
27923
|
+
}, [effectiveLegend]);
|
|
27559
27924
|
const handleWorkspaceClick = React24.useCallback((workspace) => {
|
|
27560
27925
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
27561
27926
|
trackCoreEvent("Workspace Detail Clicked", {
|
|
@@ -27595,7 +27960,7 @@ var MapGridView = React24__namespace.default.memo(({
|
|
|
27595
27960
|
const workspace = workspaceMetricsMap.get(wsKey);
|
|
27596
27961
|
if (!workspace) return null;
|
|
27597
27962
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
27598
|
-
getPerformanceColor(workspace.efficiency);
|
|
27963
|
+
const performanceColor = getPerformanceColor(workspace.efficiency);
|
|
27599
27964
|
const showExclamation = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
|
|
27600
27965
|
const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
|
|
27601
27966
|
getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
@@ -27620,7 +27985,7 @@ var MapGridView = React24__namespace.default.memo(({
|
|
|
27620
27985
|
className: `
|
|
27621
27986
|
relative rounded-lg border-2 shadow-lg hover:shadow-xl
|
|
27622
27987
|
transition-all duration-200 hover:scale-105 bg-white
|
|
27623
|
-
${
|
|
27988
|
+
${performanceColor}
|
|
27624
27989
|
${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : "w-36 h-28"}
|
|
27625
27990
|
`,
|
|
27626
27991
|
children: [
|
|
@@ -28201,7 +28566,7 @@ var PieChart4 = ({
|
|
|
28201
28566
|
}
|
|
28202
28567
|
);
|
|
28203
28568
|
};
|
|
28204
|
-
const
|
|
28569
|
+
const CustomTooltip4 = ({ active, payload }) => {
|
|
28205
28570
|
if (active && payload && payload.length) {
|
|
28206
28571
|
const data2 = payload[0];
|
|
28207
28572
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white px-3 py-2 shadow-lg rounded-lg border border-gray-200", children: [
|
|
@@ -28232,7 +28597,7 @@ var PieChart4 = ({
|
|
|
28232
28597
|
children: dataWithPercentage.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: colors[index % colors.length] }, `cell-${index}`))
|
|
28233
28598
|
}
|
|
28234
28599
|
),
|
|
28235
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28600
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}) }),
|
|
28236
28601
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28237
28602
|
recharts.Legend,
|
|
28238
28603
|
{
|
|
@@ -28444,31 +28809,6 @@ var WhatsAppShareButton = ({
|
|
|
28444
28809
|
}
|
|
28445
28810
|
);
|
|
28446
28811
|
};
|
|
28447
|
-
var AxelOrb = ({
|
|
28448
|
-
className = "",
|
|
28449
|
-
size = "md",
|
|
28450
|
-
animate = false
|
|
28451
|
-
}) => {
|
|
28452
|
-
const sizeClasses = {
|
|
28453
|
-
sm: "w-8 h-8",
|
|
28454
|
-
md: "w-10 h-10",
|
|
28455
|
-
lg: "w-12 h-12",
|
|
28456
|
-
xl: "w-16 h-16",
|
|
28457
|
-
"2xl": "w-20 h-20"
|
|
28458
|
-
};
|
|
28459
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28460
|
-
"div",
|
|
28461
|
-
{
|
|
28462
|
-
className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
|
|
28463
|
-
style: {
|
|
28464
|
-
background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
|
|
28465
|
-
boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
|
|
28466
|
-
},
|
|
28467
|
-
"aria-label": "Axel AI",
|
|
28468
|
-
role: "img"
|
|
28469
|
-
}
|
|
28470
|
-
);
|
|
28471
|
-
};
|
|
28472
28812
|
var BreakNotificationPopup = ({
|
|
28473
28813
|
activeBreaks,
|
|
28474
28814
|
onDismiss,
|
|
@@ -28556,7 +28896,7 @@ var BreakNotificationPopup = ({
|
|
|
28556
28896
|
className: "relative z-10",
|
|
28557
28897
|
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-xl overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
28558
28898
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
|
|
28559
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28899
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-amber-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-5 h-5 text-amber-600" }) }) }),
|
|
28560
28900
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
28561
28901
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
|
|
28562
28902
|
/* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
|
|
@@ -28829,7 +29169,11 @@ var AxelNotificationPopup = ({
|
|
|
28829
29169
|
className: "p-3",
|
|
28830
29170
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
28831
29171
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
|
|
28832
|
-
/* @__PURE__ */ jsxRuntime.
|
|
29172
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0", children: [
|
|
29173
|
+
suggestion.type === "improvement" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "w-5 h-5 text-blue-600" }) }),
|
|
29174
|
+
(suggestion.type === "alert" || suggestion.type === "bottleneck_triage") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-5 h-5 text-red-600" }) }),
|
|
29175
|
+
suggestion.type === "insight" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-5 h-5 text-purple-600" }) })
|
|
29176
|
+
] }),
|
|
28833
29177
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
28834
29178
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
|
|
28835
29179
|
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm text-gray-900", children: suggestion.title }),
|
|
@@ -29564,10 +29908,9 @@ var HlsVideoPlayer = React24.forwardRef(({
|
|
|
29564
29908
|
fetchSetup: function(context, initParams) {
|
|
29565
29909
|
const url = context.url;
|
|
29566
29910
|
if (isR2WorkerUrl(url, r2WorkerDomain) && authToken) {
|
|
29567
|
-
initParams.headers
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
};
|
|
29911
|
+
const headers = initParams.headers instanceof Headers ? initParams.headers : new Headers(initParams.headers || {});
|
|
29912
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
29913
|
+
initParams.headers = headers;
|
|
29571
29914
|
}
|
|
29572
29915
|
return new Request(url, initParams);
|
|
29573
29916
|
}
|
|
@@ -32890,126 +33233,6 @@ function useClipsRealtimeUpdates({
|
|
|
32890
33233
|
hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
|
|
32891
33234
|
};
|
|
32892
33235
|
}
|
|
32893
|
-
|
|
32894
|
-
// src/lib/services/clipClassificationService.ts
|
|
32895
|
-
function parseCleanLabel(rawLabel) {
|
|
32896
|
-
if (!rawLabel) return null;
|
|
32897
|
-
const patterns = [
|
|
32898
|
-
/"Clip Label"\s*:\s*"([^"]+)"/,
|
|
32899
|
-
// Old format
|
|
32900
|
-
/"clip_label"\s*:\s*"([^"]+)"/,
|
|
32901
|
-
// New format
|
|
32902
|
-
/'Clip Label'\s*:\s*'([^']+)'/,
|
|
32903
|
-
// Single quotes
|
|
32904
|
-
/'clip_label'\s*:\s*'([^']+)'/
|
|
32905
|
-
// Single quotes
|
|
32906
|
-
];
|
|
32907
|
-
for (const pattern of patterns) {
|
|
32908
|
-
const match = rawLabel.match(pattern);
|
|
32909
|
-
if (match) {
|
|
32910
|
-
const label = match[1].trim();
|
|
32911
|
-
return label.replace(/ /g, "_");
|
|
32912
|
-
}
|
|
32913
|
-
}
|
|
32914
|
-
return null;
|
|
32915
|
-
}
|
|
32916
|
-
async function fetchClassifications(clipIds, token) {
|
|
32917
|
-
if (!clipIds || clipIds.length === 0) {
|
|
32918
|
-
return {};
|
|
32919
|
-
}
|
|
32920
|
-
try {
|
|
32921
|
-
const CHUNK_SIZE = 50;
|
|
32922
|
-
const chunks = [];
|
|
32923
|
-
for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
|
|
32924
|
-
chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
|
|
32925
|
-
}
|
|
32926
|
-
console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
|
|
32927
|
-
const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
|
|
32928
|
-
const chunkResults = await Promise.all(
|
|
32929
|
-
chunks.map(async (chunkIds) => {
|
|
32930
|
-
try {
|
|
32931
|
-
const response = await fetch(
|
|
32932
|
-
`${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
|
|
32933
|
-
{
|
|
32934
|
-
headers: {
|
|
32935
|
-
"Authorization": `Bearer ${token}`,
|
|
32936
|
-
"Content-Type": "application/json"
|
|
32937
|
-
}
|
|
32938
|
-
}
|
|
32939
|
-
);
|
|
32940
|
-
if (!response.ok) {
|
|
32941
|
-
console.error(`Classification API error for chunk: ${response.status}`);
|
|
32942
|
-
return Object.fromEntries(
|
|
32943
|
-
chunkIds.map((id3) => [id3, { status: "processing" }])
|
|
32944
|
-
);
|
|
32945
|
-
}
|
|
32946
|
-
const data = await response.json();
|
|
32947
|
-
return data.classifications || {};
|
|
32948
|
-
} catch (error) {
|
|
32949
|
-
console.error("Error fetching chunk:", error);
|
|
32950
|
-
return Object.fromEntries(
|
|
32951
|
-
chunkIds.map((id3) => [id3, { status: "processing" }])
|
|
32952
|
-
);
|
|
32953
|
-
}
|
|
32954
|
-
})
|
|
32955
|
-
);
|
|
32956
|
-
const allClassifications = Object.assign({}, ...chunkResults);
|
|
32957
|
-
console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
|
|
32958
|
-
return allClassifications;
|
|
32959
|
-
} catch (error) {
|
|
32960
|
-
console.error("Error fetching classifications:", error);
|
|
32961
|
-
return Object.fromEntries(
|
|
32962
|
-
clipIds.map((id3) => [id3, { status: "processing" }])
|
|
32963
|
-
);
|
|
32964
|
-
}
|
|
32965
|
-
}
|
|
32966
|
-
function useClassificationRealtimeUpdates({
|
|
32967
|
-
clipIds,
|
|
32968
|
-
enabled = true,
|
|
32969
|
-
onClassificationUpdate
|
|
32970
|
-
}) {
|
|
32971
|
-
const supabase = useSupabase();
|
|
32972
|
-
React24.useEffect(() => {
|
|
32973
|
-
if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
|
|
32974
|
-
return;
|
|
32975
|
-
}
|
|
32976
|
-
const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
|
|
32977
|
-
console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
|
|
32978
|
-
const channel = supabase.channel(channelName).on(
|
|
32979
|
-
"postgres_changes",
|
|
32980
|
-
{
|
|
32981
|
-
event: "INSERT",
|
|
32982
|
-
schema: "public",
|
|
32983
|
-
table: "clip_classification",
|
|
32984
|
-
filter: `clip_id=in.(${clipIds.join(",")})`
|
|
32985
|
-
},
|
|
32986
|
-
(payload) => {
|
|
32987
|
-
console.log("[useClassificationRealtimeUpdates] New classification:", payload);
|
|
32988
|
-
try {
|
|
32989
|
-
const { clip_id, clip_label, confidence_score } = payload.new;
|
|
32990
|
-
const parsedLabel = parseCleanLabel(clip_label);
|
|
32991
|
-
if (parsedLabel) {
|
|
32992
|
-
onClassificationUpdate(clip_id, {
|
|
32993
|
-
status: "classified",
|
|
32994
|
-
label: parsedLabel,
|
|
32995
|
-
confidence: confidence_score || 0
|
|
32996
|
-
});
|
|
32997
|
-
} else {
|
|
32998
|
-
console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
|
|
32999
|
-
}
|
|
33000
|
-
} catch (error) {
|
|
33001
|
-
console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
|
|
33002
|
-
}
|
|
33003
|
-
}
|
|
33004
|
-
).subscribe((status) => {
|
|
33005
|
-
console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
|
|
33006
|
-
});
|
|
33007
|
-
return () => {
|
|
33008
|
-
console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
|
|
33009
|
-
supabase.removeChannel(channel);
|
|
33010
|
-
};
|
|
33011
|
-
}, [clipIds, enabled, supabase, onClassificationUpdate]);
|
|
33012
|
-
}
|
|
33013
33236
|
var BottlenecksContent = ({
|
|
33014
33237
|
workspaceId,
|
|
33015
33238
|
workspaceName,
|
|
@@ -36457,6 +36680,31 @@ var DetailedHealthStatus = ({
|
|
|
36457
36680
|
}
|
|
36458
36681
|
);
|
|
36459
36682
|
};
|
|
36683
|
+
var AxelOrb = ({
|
|
36684
|
+
className = "",
|
|
36685
|
+
size = "md",
|
|
36686
|
+
animate = false
|
|
36687
|
+
}) => {
|
|
36688
|
+
const sizeClasses = {
|
|
36689
|
+
sm: "w-8 h-8",
|
|
36690
|
+
md: "w-10 h-10",
|
|
36691
|
+
lg: "w-12 h-12",
|
|
36692
|
+
xl: "w-16 h-16",
|
|
36693
|
+
"2xl": "w-20 h-20"
|
|
36694
|
+
};
|
|
36695
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36696
|
+
"div",
|
|
36697
|
+
{
|
|
36698
|
+
className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
|
|
36699
|
+
style: {
|
|
36700
|
+
background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
|
|
36701
|
+
boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
|
|
36702
|
+
},
|
|
36703
|
+
"aria-label": "Axel AI",
|
|
36704
|
+
role: "img"
|
|
36705
|
+
}
|
|
36706
|
+
);
|
|
36707
|
+
};
|
|
36460
36708
|
var LinePdfExportButton = ({
|
|
36461
36709
|
targetElement,
|
|
36462
36710
|
fileName = "line-export",
|
|
@@ -36574,10 +36822,10 @@ var LineHistoryCalendar = ({
|
|
|
36574
36822
|
}, [configuredTimezone]);
|
|
36575
36823
|
const monthBounds = React24.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
|
|
36576
36824
|
const calendarData = React24.useMemo(() => {
|
|
36577
|
-
const
|
|
36578
|
-
const
|
|
36579
|
-
const totalDays =
|
|
36580
|
-
let startOffset =
|
|
36825
|
+
const startOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
|
|
36826
|
+
const endOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
|
|
36827
|
+
const totalDays = endOfMonth2.getDate();
|
|
36828
|
+
let startOffset = startOfMonth2.getDay() - 1;
|
|
36581
36829
|
if (startOffset === -1) startOffset = 6;
|
|
36582
36830
|
const calendar = Array(startOffset).fill(null);
|
|
36583
36831
|
for (let day = 1; day <= totalDays; day++) {
|
|
@@ -36608,12 +36856,13 @@ var LineHistoryCalendar = ({
|
|
|
36608
36856
|
if (shift.hasData !== void 0) return shift.hasData;
|
|
36609
36857
|
return shift.total_workspaces > 0 || shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0;
|
|
36610
36858
|
};
|
|
36611
|
-
const getPerformanceColor = (efficiency, date, hasData) => {
|
|
36859
|
+
const getPerformanceColor = (efficiency, date, hasData, isFilteredOut = false) => {
|
|
36612
36860
|
const istNow = todayInZone;
|
|
36613
36861
|
const nowString = `${istNow.getFullYear()}-${String(istNow.getMonth() + 1).padStart(2, "0")}-${String(istNow.getDate()).padStart(2, "0")}`;
|
|
36614
36862
|
const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
36615
36863
|
if (dateString > nowString) return "bg-gray-200 dark:bg-gray-700";
|
|
36616
36864
|
if (!hasData) return "bg-gray-300 dark:bg-gray-600";
|
|
36865
|
+
if (isFilteredOut) return "bg-gray-200 dark:bg-gray-700";
|
|
36617
36866
|
return efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
|
|
36618
36867
|
};
|
|
36619
36868
|
const isCurrentDate = (date) => {
|
|
@@ -36658,12 +36907,13 @@ var LineHistoryCalendar = ({
|
|
|
36658
36907
|
const showRange = rangeStart && rangeEnd ? !(rangeStart === monthBounds.startKey && rangeEnd === monthBounds.endKey) : false;
|
|
36659
36908
|
const inRange = showRange ? dateKey >= rangeStart && dateKey <= rangeEnd : false;
|
|
36660
36909
|
const isRangeEdge = inRange && (dateKey === rangeStart || dateKey === rangeEnd);
|
|
36910
|
+
const isFilteredOut = showRange && !inRange;
|
|
36661
36911
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
36662
36912
|
"div",
|
|
36663
36913
|
{
|
|
36664
|
-
className: `group h-full ${isFuture || !hasData ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
|
|
36914
|
+
className: `group h-full ${isFuture || !hasData || isFilteredOut ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
|
|
36665
36915
|
onClick: () => {
|
|
36666
|
-
if (!isFuture && hasData) {
|
|
36916
|
+
if (!isFuture && hasData && !isFilteredOut) {
|
|
36667
36917
|
const dateObj2 = day.date instanceof Date ? day.date : new Date(day.date);
|
|
36668
36918
|
const year2 = dateObj2.getFullYear();
|
|
36669
36919
|
const month2 = String(dateObj2.getMonth() + 1).padStart(2, "0");
|
|
@@ -36695,7 +36945,7 @@ var LineHistoryCalendar = ({
|
|
|
36695
36945
|
}
|
|
36696
36946
|
},
|
|
36697
36947
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `
|
|
36698
|
-
${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData)}
|
|
36948
|
+
${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData, isFilteredOut)}
|
|
36699
36949
|
rounded-lg h-full p-2 relative
|
|
36700
36950
|
${isToday2 ? "ring-2 ring-blue-500 dark:ring-blue-400 ring-offset-2 dark:ring-offset-gray-800 shadow-md" : ""}
|
|
36701
36951
|
`, children: [
|
|
@@ -36706,10 +36956,11 @@ var LineHistoryCalendar = ({
|
|
|
36706
36956
|
}
|
|
36707
36957
|
),
|
|
36708
36958
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `
|
|
36709
|
-
text-base font-medium
|
|
36959
|
+
text-base font-medium flex items-center relative z-10
|
|
36960
|
+
${hasData && !isFuture && !isFilteredOut ? "text-white" : "text-gray-400"}
|
|
36710
36961
|
${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
|
|
36711
36962
|
`, children: dateObj.getDate() }),
|
|
36712
|
-
!isFuture && hasData && renderStats(shiftData, dateObj)
|
|
36963
|
+
!isFuture && hasData && !isFilteredOut && renderStats(shiftData, dateObj)
|
|
36713
36964
|
] })
|
|
36714
36965
|
}
|
|
36715
36966
|
);
|
|
@@ -36884,54 +37135,19 @@ var IdleTimeReasonChart = ({
|
|
|
36884
37135
|
] });
|
|
36885
37136
|
};
|
|
36886
37137
|
var IdleTimeReasonChart_default = IdleTimeReasonChart;
|
|
37138
|
+
var WEEKDAYS2 = ["S", "M", "T", "W", "T", "F", "S"];
|
|
36887
37139
|
var MonthlyRangeFilter = ({
|
|
36888
37140
|
month,
|
|
36889
37141
|
year,
|
|
36890
|
-
timezone,
|
|
36891
37142
|
value,
|
|
36892
37143
|
onChange,
|
|
37144
|
+
onMonthNavigate,
|
|
36893
37145
|
className
|
|
36894
37146
|
}) => {
|
|
36895
37147
|
const monthBounds = React24.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
|
|
36896
|
-
const weekRanges = React24.useMemo(() => getMonthWeekRanges(year, month, timezone), [year, month, timezone]);
|
|
36897
37148
|
const normalizedRange = React24.useMemo(() => {
|
|
36898
37149
|
return normalizeDateKeyRange(value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey);
|
|
36899
37150
|
}, [value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey]);
|
|
36900
|
-
const monthLabel = React24.useMemo(() => {
|
|
36901
|
-
return new Date(year, month).toLocaleString("default", { month: "long", year: "numeric" });
|
|
36902
|
-
}, [year, month]);
|
|
36903
|
-
const presetOptions = React24.useMemo(() => {
|
|
36904
|
-
const fullMonthRangeLabel = `${formatDateKeyForDisplay(monthBounds.startKey, "MMM d")} - ${formatDateKeyForDisplay(monthBounds.endKey, "MMM d")}`;
|
|
36905
|
-
const presets = [
|
|
36906
|
-
{
|
|
36907
|
-
id: "full-month",
|
|
36908
|
-
label: "Full month",
|
|
36909
|
-
rangeLabel: fullMonthRangeLabel,
|
|
36910
|
-
startKey: monthBounds.startKey,
|
|
36911
|
-
endKey: monthBounds.endKey
|
|
36912
|
-
}
|
|
36913
|
-
];
|
|
36914
|
-
weekRanges.forEach((range, index) => {
|
|
36915
|
-
const rangeLabel = `${formatDateKeyForDisplay(range.startKey, "MMM d")} - ${formatDateKeyForDisplay(range.endKey, "MMM d")}`;
|
|
36916
|
-
presets.push({
|
|
36917
|
-
id: `week-${index + 1}`,
|
|
36918
|
-
label: `Week ${index + 1}`,
|
|
36919
|
-
rangeLabel,
|
|
36920
|
-
startKey: range.startKey,
|
|
36921
|
-
endKey: range.endKey
|
|
36922
|
-
});
|
|
36923
|
-
});
|
|
36924
|
-
return presets;
|
|
36925
|
-
}, [monthBounds.startKey, monthBounds.endKey, weekRanges]);
|
|
36926
|
-
const activePreset = React24.useMemo(() => {
|
|
36927
|
-
return presetOptions.find(
|
|
36928
|
-
(preset) => preset.startKey === normalizedRange.startKey && preset.endKey === normalizedRange.endKey
|
|
36929
|
-
);
|
|
36930
|
-
}, [presetOptions, normalizedRange.startKey, normalizedRange.endKey]);
|
|
36931
|
-
const displayLabel = React24.useMemo(() => {
|
|
36932
|
-
if (activePreset) return activePreset.label;
|
|
36933
|
-
return "Custom range";
|
|
36934
|
-
}, [activePreset]);
|
|
36935
37151
|
const fullMonthLabel = React24.useMemo(() => {
|
|
36936
37152
|
const startLabel = formatDateKeyForDisplay(monthBounds.startKey, "MMM d");
|
|
36937
37153
|
const endLabel = formatDateKeyForDisplay(monthBounds.endKey, "MMM d, yyyy");
|
|
@@ -36940,14 +37156,26 @@ var MonthlyRangeFilter = ({
|
|
|
36940
37156
|
const displayRange = React24.useMemo(() => {
|
|
36941
37157
|
return formatRangeLabel(normalizedRange, fullMonthLabel);
|
|
36942
37158
|
}, [normalizedRange, fullMonthLabel]);
|
|
36943
|
-
const [customStart, setCustomStart] = React24.useState(normalizedRange.startKey);
|
|
36944
|
-
const [customEnd, setCustomEnd] = React24.useState(normalizedRange.endKey);
|
|
36945
37159
|
const [isOpen, setIsOpen] = React24.useState(false);
|
|
36946
37160
|
const dropdownRef = React24.useRef(null);
|
|
37161
|
+
const [calendarMonth, setCalendarMonth] = React24.useState(month);
|
|
37162
|
+
const [calendarYear, setCalendarYear] = React24.useState(year);
|
|
37163
|
+
const [rangeStart, setRangeStart] = React24.useState(parseDateKeyToDate(normalizedRange.startKey));
|
|
37164
|
+
const [rangeEnd, setRangeEnd] = React24.useState(parseDateKeyToDate(normalizedRange.endKey));
|
|
37165
|
+
const [selecting, setSelecting] = React24.useState(false);
|
|
36947
37166
|
React24.useEffect(() => {
|
|
36948
|
-
|
|
36949
|
-
|
|
36950
|
-
}, [
|
|
37167
|
+
setCalendarMonth(month);
|
|
37168
|
+
setCalendarYear(year);
|
|
37169
|
+
}, [month, year]);
|
|
37170
|
+
React24.useEffect(() => {
|
|
37171
|
+
if (isOpen) {
|
|
37172
|
+
setRangeStart(parseDateKeyToDate(normalizedRange.startKey));
|
|
37173
|
+
setRangeEnd(parseDateKeyToDate(normalizedRange.endKey));
|
|
37174
|
+
setSelecting(false);
|
|
37175
|
+
setCalendarMonth(month);
|
|
37176
|
+
setCalendarYear(year);
|
|
37177
|
+
}
|
|
37178
|
+
}, [isOpen, normalizedRange.startKey, normalizedRange.endKey, month, year]);
|
|
36951
37179
|
React24.useEffect(() => {
|
|
36952
37180
|
const handleClickOutside = (event) => {
|
|
36953
37181
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
@@ -36957,107 +37185,211 @@ var MonthlyRangeFilter = ({
|
|
|
36957
37185
|
document.addEventListener("mousedown", handleClickOutside);
|
|
36958
37186
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
36959
37187
|
}, []);
|
|
36960
|
-
const
|
|
36961
|
-
|
|
36962
|
-
|
|
36963
|
-
|
|
37188
|
+
const handleDayClick = (day) => {
|
|
37189
|
+
if (!selecting || !rangeStart) {
|
|
37190
|
+
setRangeStart(day);
|
|
37191
|
+
setRangeEnd(null);
|
|
37192
|
+
setSelecting(true);
|
|
37193
|
+
} else {
|
|
37194
|
+
if (day < rangeStart) {
|
|
37195
|
+
setRangeEnd(rangeStart);
|
|
37196
|
+
setRangeStart(day);
|
|
37197
|
+
} else {
|
|
37198
|
+
setRangeEnd(day);
|
|
37199
|
+
}
|
|
37200
|
+
setSelecting(false);
|
|
37201
|
+
}
|
|
36964
37202
|
};
|
|
36965
|
-
const
|
|
36966
|
-
if (
|
|
36967
|
-
|
|
36968
|
-
|
|
36969
|
-
|
|
37203
|
+
const handleApply = () => {
|
|
37204
|
+
if (rangeStart) {
|
|
37205
|
+
const endDate = rangeEnd || rangeStart;
|
|
37206
|
+
const startKey = dateFns.format(rangeStart, "yyyy-MM-dd");
|
|
37207
|
+
const endKey = dateFns.format(endDate, "yyyy-MM-dd");
|
|
37208
|
+
const normalized = normalizeDateKeyRange(startKey, endKey, monthBounds.startKey, monthBounds.endKey);
|
|
37209
|
+
onChange(normalized);
|
|
37210
|
+
setIsOpen(false);
|
|
37211
|
+
}
|
|
37212
|
+
};
|
|
37213
|
+
const handlePreviousMonth = () => {
|
|
37214
|
+
const prevMonthDate = dateFns.subMonths(new Date(calendarYear, calendarMonth), 1);
|
|
37215
|
+
const newMonth = prevMonthDate.getMonth();
|
|
37216
|
+
const newYear = prevMonthDate.getFullYear();
|
|
37217
|
+
if (newYear < 2023) return;
|
|
37218
|
+
setCalendarMonth(newMonth);
|
|
37219
|
+
setCalendarYear(newYear);
|
|
37220
|
+
onMonthNavigate?.(newMonth, newYear);
|
|
36970
37221
|
};
|
|
37222
|
+
const handleNextMonth = () => {
|
|
37223
|
+
const nextMonthDate = dateFns.addMonths(new Date(calendarYear, calendarMonth), 1);
|
|
37224
|
+
const newMonth = nextMonthDate.getMonth();
|
|
37225
|
+
const newYear = nextMonthDate.getFullYear();
|
|
37226
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
37227
|
+
if (newYear > currentDate.getFullYear() || newYear === currentDate.getFullYear() && newMonth > currentDate.getMonth()) {
|
|
37228
|
+
return;
|
|
37229
|
+
}
|
|
37230
|
+
setCalendarMonth(newMonth);
|
|
37231
|
+
setCalendarYear(newYear);
|
|
37232
|
+
onMonthNavigate?.(newMonth, newYear);
|
|
37233
|
+
};
|
|
37234
|
+
const canGoPrevious = !(calendarYear === 2023 && calendarMonth === 0);
|
|
37235
|
+
const canGoNext = !(calendarYear === (/* @__PURE__ */ new Date()).getFullYear() && calendarMonth === (/* @__PURE__ */ new Date()).getMonth());
|
|
37236
|
+
const monthDate = React24.useMemo(() => new Date(calendarYear, calendarMonth), [calendarYear, calendarMonth]);
|
|
37237
|
+
const calendarDays = React24.useMemo(() => {
|
|
37238
|
+
const start = dateFns.startOfMonth(monthDate);
|
|
37239
|
+
const end = dateFns.endOfMonth(monthDate);
|
|
37240
|
+
const days = dateFns.eachDayOfInterval({ start, end });
|
|
37241
|
+
const startDayOfWeek = dateFns.getDay(start);
|
|
37242
|
+
const emptySlots = Array(startDayOfWeek).fill(null);
|
|
37243
|
+
return [...emptySlots, ...days];
|
|
37244
|
+
}, [monthDate]);
|
|
37245
|
+
const isInRange = React24.useCallback((day) => {
|
|
37246
|
+
if (!rangeStart) return false;
|
|
37247
|
+
if (!rangeEnd) return dateFns.isSameDay(day, rangeStart);
|
|
37248
|
+
return dateFns.isWithinInterval(day, {
|
|
37249
|
+
start: dateFns.startOfDay(rangeStart),
|
|
37250
|
+
end: dateFns.startOfDay(rangeEnd)
|
|
37251
|
+
});
|
|
37252
|
+
}, [rangeStart, rangeEnd]);
|
|
37253
|
+
const isRangeStart = React24.useCallback((day) => {
|
|
37254
|
+
return rangeStart && dateFns.isSameDay(day, rangeStart);
|
|
37255
|
+
}, [rangeStart]);
|
|
37256
|
+
const isRangeEnd = React24.useCallback((day) => {
|
|
37257
|
+
return rangeEnd && dateFns.isSameDay(day, rangeEnd);
|
|
37258
|
+
}, [rangeEnd]);
|
|
37259
|
+
const monthName = dateFns.format(monthDate, "MMMM yyyy");
|
|
37260
|
+
const startDisplay = rangeStart ? dateFns.format(rangeStart, "MMM d, yyyy") : "Start Date";
|
|
37261
|
+
const endDisplay = rangeEnd ? dateFns.format(rangeEnd, "MMM d, yyyy") : "End Date";
|
|
36971
37262
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className ?? ""}`, ref: dropdownRef, children: [
|
|
36972
37263
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
36973
37264
|
"button",
|
|
36974
37265
|
{
|
|
36975
37266
|
type: "button",
|
|
36976
37267
|
onClick: () => setIsOpen((prev) => !prev),
|
|
36977
|
-
className:
|
|
37268
|
+
className: clsx(
|
|
37269
|
+
"flex items-center gap-2 rounded-lg border bg-white px-3 py-2 text-left shadow-sm transition-all duration-200 focus:outline-none",
|
|
37270
|
+
isOpen ? "border-blue-500 ring-2 ring-blue-500/20" : "border-gray-200 hover:border-gray-300 hover:shadow"
|
|
37271
|
+
),
|
|
36978
37272
|
children: [
|
|
36979
|
-
/* @__PURE__ */ jsxRuntime.jsx(outline.CalendarIcon, { className: "h-4 w-4 text-
|
|
36980
|
-
/* @__PURE__ */ jsxRuntime.
|
|
36981
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] uppercase tracking-[0.2em] text-slate-500", children: displayLabel }),
|
|
36982
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-slate-900", children: displayRange })
|
|
36983
|
-
] }),
|
|
37273
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.CalendarIcon, { className: "h-4 w-4 text-gray-400" }),
|
|
37274
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: displayRange }),
|
|
36984
37275
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36985
37276
|
outline.ChevronDownIcon,
|
|
36986
37277
|
{
|
|
36987
|
-
className:
|
|
37278
|
+
className: clsx(
|
|
37279
|
+
"ml-2 h-4 w-4 text-gray-400 transition-transform duration-200",
|
|
37280
|
+
isOpen && "rotate-180"
|
|
37281
|
+
)
|
|
36988
37282
|
}
|
|
36989
37283
|
)
|
|
36990
37284
|
]
|
|
36991
37285
|
}
|
|
36992
37286
|
),
|
|
36993
|
-
isOpen && /* @__PURE__ */ jsxRuntime.
|
|
36994
|
-
/* @__PURE__ */ jsxRuntime.
|
|
36995
|
-
|
|
36996
|
-
|
|
36997
|
-
|
|
37287
|
+
isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 z-50 mt-2 w-[320px] overflow-hidden rounded-lg border border-gray-200 bg-white shadow-xl p-4 animate-in fade-in zoom-in-95 duration-200", children: [
|
|
37288
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-4 pb-4 border-b border-gray-100", children: [
|
|
37289
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
37290
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "Start" }),
|
|
37291
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx(
|
|
37292
|
+
"px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
|
|
37293
|
+
rangeStart ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
|
|
37294
|
+
), children: startDisplay })
|
|
37295
|
+
] }),
|
|
37296
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4 text-gray-300", children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "h-4 w-4" }) }),
|
|
37297
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
37298
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "End" }),
|
|
37299
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx(
|
|
37300
|
+
"px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
|
|
37301
|
+
rangeEnd ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
|
|
37302
|
+
), children: endDisplay })
|
|
37303
|
+
] })
|
|
37304
|
+
] }),
|
|
37305
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
37306
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
36998
37307
|
"button",
|
|
36999
37308
|
{
|
|
37000
37309
|
type: "button",
|
|
37001
|
-
onClick:
|
|
37002
|
-
|
|
37003
|
-
|
|
37004
|
-
|
|
37005
|
-
|
|
37006
|
-
|
|
37310
|
+
onClick: handlePreviousMonth,
|
|
37311
|
+
disabled: !canGoPrevious,
|
|
37312
|
+
className: clsx(
|
|
37313
|
+
"p-1 rounded-md transition-colors",
|
|
37314
|
+
canGoPrevious ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
|
|
37315
|
+
),
|
|
37316
|
+
"aria-label": "Previous month",
|
|
37317
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "h-5 w-5" })
|
|
37318
|
+
}
|
|
37319
|
+
),
|
|
37320
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900", children: monthName }),
|
|
37321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37322
|
+
"button",
|
|
37323
|
+
{
|
|
37324
|
+
type: "button",
|
|
37325
|
+
onClick: handleNextMonth,
|
|
37326
|
+
disabled: !canGoNext,
|
|
37327
|
+
className: clsx(
|
|
37328
|
+
"p-1 rounded-md transition-colors",
|
|
37329
|
+
canGoNext ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
|
|
37330
|
+
),
|
|
37331
|
+
"aria-label": "Next month",
|
|
37332
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "h-5 w-5" })
|
|
37333
|
+
}
|
|
37334
|
+
)
|
|
37335
|
+
] }),
|
|
37336
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEKDAYS2.map((day, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 flex items-center justify-center text-xs font-medium text-gray-400", children: day }, i)) }),
|
|
37337
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-0", children: calendarDays.map((day, i) => {
|
|
37338
|
+
if (!day) {
|
|
37339
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10" }, `empty-${i}`);
|
|
37340
|
+
}
|
|
37341
|
+
const inRange = isInRange(day);
|
|
37342
|
+
const isStart = isRangeStart(day);
|
|
37343
|
+
const isEnd = isRangeEnd(day);
|
|
37344
|
+
const isSingleDaySelection = isStart && (!rangeEnd || rangeEnd && dateFns.isSameDay(rangeStart, rangeEnd));
|
|
37345
|
+
const hasSelection = rangeStart !== null;
|
|
37346
|
+
const dayNum = day.getDate();
|
|
37347
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
37348
|
+
"button",
|
|
37349
|
+
{
|
|
37350
|
+
type: "button",
|
|
37351
|
+
onClick: () => handleDayClick(day),
|
|
37352
|
+
className: clsx(
|
|
37353
|
+
"h-10 w-full flex items-center justify-center text-sm font-medium transition-all duration-150 relative",
|
|
37354
|
+
// Not in range - fade when there's a selection
|
|
37355
|
+
!inRange && hasSelection && "text-gray-300 hover:text-gray-500 hover:bg-gray-50 rounded-md",
|
|
37356
|
+
!inRange && !hasSelection && "text-gray-700 hover:bg-gray-100 rounded-md",
|
|
37357
|
+
// Middle of range - subtle highlight
|
|
37358
|
+
inRange && !isStart && !isEnd && "bg-blue-50 text-blue-600",
|
|
37359
|
+
// Start of range
|
|
37360
|
+
isStart && !isSingleDaySelection && "bg-blue-600 text-white rounded-l-md shadow-sm z-10",
|
|
37361
|
+
// End of range
|
|
37362
|
+
isEnd && !isSingleDaySelection && "bg-blue-600 text-white rounded-r-md shadow-sm z-10",
|
|
37363
|
+
// Single day selection
|
|
37364
|
+
isSingleDaySelection && "bg-blue-600 text-white rounded-md shadow-sm z-10"
|
|
37365
|
+
),
|
|
37366
|
+
children: dayNum
|
|
37007
37367
|
},
|
|
37008
|
-
|
|
37368
|
+
day.toISOString()
|
|
37009
37369
|
);
|
|
37010
37370
|
}) }),
|
|
37011
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-
|
|
37012
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37013
|
-
|
|
37014
|
-
|
|
37015
|
-
|
|
37016
|
-
|
|
37017
|
-
|
|
37018
|
-
|
|
37019
|
-
|
|
37020
|
-
|
|
37021
|
-
{
|
|
37022
|
-
type: "date",
|
|
37023
|
-
value: customStart,
|
|
37024
|
-
onChange: (event) => setCustomStart(event.target.value),
|
|
37025
|
-
min: monthBounds.startKey,
|
|
37026
|
-
max: monthBounds.endKey,
|
|
37027
|
-
className: "mt-1 w-full rounded-md border border-slate-200 bg-white px-2 py-2 text-sm text-slate-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
|
|
37028
|
-
}
|
|
37029
|
-
)
|
|
37030
|
-
] }),
|
|
37031
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
37032
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[11px] font-medium text-slate-600", children: "End" }),
|
|
37033
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37034
|
-
"input",
|
|
37035
|
-
{
|
|
37036
|
-
type: "date",
|
|
37037
|
-
value: customEnd,
|
|
37038
|
-
onChange: (event) => setCustomEnd(event.target.value),
|
|
37039
|
-
min: monthBounds.startKey,
|
|
37040
|
-
max: monthBounds.endKey,
|
|
37041
|
-
className: "mt-1 w-full rounded-md border border-slate-200 bg-white px-2 py-2 text-sm text-slate-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
|
|
37042
|
-
}
|
|
37043
|
-
)
|
|
37044
|
-
] })
|
|
37045
|
-
] }),
|
|
37371
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-2 mt-4 pt-4 border-t border-gray-100", children: [
|
|
37372
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37373
|
+
"button",
|
|
37374
|
+
{
|
|
37375
|
+
type: "button",
|
|
37376
|
+
onClick: () => setIsOpen(false),
|
|
37377
|
+
className: "rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors",
|
|
37378
|
+
children: "Cancel"
|
|
37379
|
+
}
|
|
37380
|
+
),
|
|
37046
37381
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37047
37382
|
"button",
|
|
37048
37383
|
{
|
|
37049
37384
|
type: "button",
|
|
37050
|
-
onClick:
|
|
37051
|
-
|
|
37052
|
-
|
|
37385
|
+
onClick: handleApply,
|
|
37386
|
+
disabled: !rangeStart,
|
|
37387
|
+
className: "rounded-lg bg-blue-600 px-4 py-1.5 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all",
|
|
37388
|
+
children: "Apply"
|
|
37053
37389
|
}
|
|
37054
37390
|
)
|
|
37055
|
-
] }),
|
|
37056
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 text-xs text-slate-400", children: [
|
|
37057
|
-
"Month: ",
|
|
37058
|
-
monthLabel
|
|
37059
37391
|
] })
|
|
37060
|
-
] })
|
|
37392
|
+
] })
|
|
37061
37393
|
] });
|
|
37062
37394
|
};
|
|
37063
37395
|
var MonthlyRangeFilter_default = MonthlyRangeFilter;
|
|
@@ -37065,7 +37397,36 @@ var DEFAULT_PERFORMANCE_DATA = {
|
|
|
37065
37397
|
avg_efficiency: 0,
|
|
37066
37398
|
underperforming_workspaces: 0,
|
|
37067
37399
|
total_workspaces: 0,
|
|
37068
|
-
hasData: false
|
|
37400
|
+
hasData: false,
|
|
37401
|
+
output: 0,
|
|
37402
|
+
idealOutput: 0
|
|
37403
|
+
};
|
|
37404
|
+
var getOrdinal = (n) => {
|
|
37405
|
+
const suffix = ["th", "st", "nd", "rd"];
|
|
37406
|
+
const v = n % 100;
|
|
37407
|
+
return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
37408
|
+
};
|
|
37409
|
+
var CustomTooltip2 = ({ active, payload, label }) => {
|
|
37410
|
+
if (active && payload && payload.length) {
|
|
37411
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
|
|
37412
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
|
|
37413
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
37414
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
37415
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Actual:" }),
|
|
37416
|
+
" ",
|
|
37417
|
+
Math.round(payload[0].value),
|
|
37418
|
+
" units"
|
|
37419
|
+
] }),
|
|
37420
|
+
payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
37421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Target:" }),
|
|
37422
|
+
" ",
|
|
37423
|
+
Math.round(payload[0].payload.idealOutput),
|
|
37424
|
+
" units"
|
|
37425
|
+
] })
|
|
37426
|
+
] })
|
|
37427
|
+
] });
|
|
37428
|
+
}
|
|
37429
|
+
return null;
|
|
37069
37430
|
};
|
|
37070
37431
|
var getShiftData2 = (day, shiftId) => {
|
|
37071
37432
|
const shift = day.shifts[shiftId];
|
|
@@ -37104,7 +37465,7 @@ var LineMonthlyHistory = ({
|
|
|
37104
37465
|
}, [rangeStart, rangeEnd, monthBounds.startKey, monthBounds.endKey]);
|
|
37105
37466
|
const isFullRange = React24.useMemo(() => isFullMonthRange(normalizedRange, year, month), [normalizedRange, year, month]);
|
|
37106
37467
|
const monthLabel = React24.useMemo(() => new Date(year, month).toLocaleString("default", { month: "long" }), [year, month]);
|
|
37107
|
-
|
|
37468
|
+
React24.useMemo(() => {
|
|
37108
37469
|
return isFullRange ? monthLabel : formatRangeLabel(normalizedRange, monthLabel);
|
|
37109
37470
|
}, [isFullRange, normalizedRange, monthLabel]);
|
|
37110
37471
|
const analysisMonthlyData = React24.useMemo(() => {
|
|
@@ -37126,15 +37487,6 @@ var LineMonthlyHistory = ({
|
|
|
37126
37487
|
shiftId: selectedShiftId,
|
|
37127
37488
|
enabled: !!lineId
|
|
37128
37489
|
});
|
|
37129
|
-
if (isLoading) {
|
|
37130
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
37131
|
-
OptifyeLogoLoader_default,
|
|
37132
|
-
{
|
|
37133
|
-
size: "lg",
|
|
37134
|
-
message: "Loading monthly performance data..."
|
|
37135
|
-
}
|
|
37136
|
-
) });
|
|
37137
|
-
}
|
|
37138
37490
|
const averages = (analysisMonthlyData || []).reduce(
|
|
37139
37491
|
(acc, day) => {
|
|
37140
37492
|
const shiftData = getShiftData2(day, selectedShiftId);
|
|
@@ -37153,6 +37505,88 @@ var LineMonthlyHistory = ({
|
|
|
37153
37505
|
const avgEfficiency = averages.count > 0 ? averages.efficiency / averages.count : 0;
|
|
37154
37506
|
const avgUnderperforming = averages.count > 0 ? averages.underperforming / averages.count : 0;
|
|
37155
37507
|
const avgTotalWorkspaces = averages.count > 0 ? averages.totalWorkspaces / averages.count : 0;
|
|
37508
|
+
const hasRealData = (shift) => {
|
|
37509
|
+
if (shift.hasData !== void 0) return shift.hasData;
|
|
37510
|
+
return shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || shift.total_workspaces > 0 || (shift.output || 0) > 0 || (shift.idealOutput || 0) > 0;
|
|
37511
|
+
};
|
|
37512
|
+
const chartData = React24.useMemo(() => {
|
|
37513
|
+
const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
|
|
37514
|
+
const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
|
|
37515
|
+
const dayNumbers = [];
|
|
37516
|
+
for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
|
|
37517
|
+
dayNumbers.push(d.getDate());
|
|
37518
|
+
}
|
|
37519
|
+
const dailyData = [];
|
|
37520
|
+
let maxOutput = 0;
|
|
37521
|
+
let lastSetTarget = 0;
|
|
37522
|
+
for (let i = dayNumbers.length - 1; i >= 0; i--) {
|
|
37523
|
+
const day = dayNumbers[i];
|
|
37524
|
+
const dayData = analysisMonthlyData.find((d) => {
|
|
37525
|
+
const date = new Date(d.date);
|
|
37526
|
+
return date.getDate() === day;
|
|
37527
|
+
});
|
|
37528
|
+
const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
|
|
37529
|
+
const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
|
|
37530
|
+
if (idealOutput > 0) {
|
|
37531
|
+
lastSetTarget = idealOutput;
|
|
37532
|
+
break;
|
|
37533
|
+
}
|
|
37534
|
+
}
|
|
37535
|
+
for (const day of dayNumbers) {
|
|
37536
|
+
const dayData = analysisMonthlyData.find((d) => {
|
|
37537
|
+
const date = new Date(d.date);
|
|
37538
|
+
return date.getDate() === day;
|
|
37539
|
+
});
|
|
37540
|
+
const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
|
|
37541
|
+
const output = shiftData && hasRealData(shiftData) ? shiftData.output || 0 : 0;
|
|
37542
|
+
const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
|
|
37543
|
+
if (output > maxOutput) maxOutput = output;
|
|
37544
|
+
const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
|
|
37545
|
+
dailyData.push({
|
|
37546
|
+
hour: getOrdinal(day),
|
|
37547
|
+
// Using ordinal format (1st, 2nd, 3rd, etc.)
|
|
37548
|
+
timeRange: `Day ${day}`,
|
|
37549
|
+
output,
|
|
37550
|
+
originalOutput: output,
|
|
37551
|
+
// For label display
|
|
37552
|
+
idealOutput,
|
|
37553
|
+
color: color2
|
|
37554
|
+
});
|
|
37555
|
+
}
|
|
37556
|
+
const calculatedMax = Math.max(maxOutput, lastSetTarget);
|
|
37557
|
+
const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
|
|
37558
|
+
return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
|
|
37559
|
+
}, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId]);
|
|
37560
|
+
const yAxisTicks = React24.useMemo(() => {
|
|
37561
|
+
const max = chartData.yAxisMax;
|
|
37562
|
+
const target = chartData.lastSetTarget;
|
|
37563
|
+
if (!max || max <= 0) return void 0;
|
|
37564
|
+
const desiredIntervals = 4;
|
|
37565
|
+
const roughStep = max / desiredIntervals;
|
|
37566
|
+
const power = Math.pow(10, Math.floor(Math.log10(roughStep)));
|
|
37567
|
+
const normalized = roughStep / power;
|
|
37568
|
+
let step = 1 * power;
|
|
37569
|
+
if (normalized >= 1.5 && normalized < 3) step = 2 * power;
|
|
37570
|
+
else if (normalized >= 3 && normalized < 7) step = 5 * power;
|
|
37571
|
+
else if (normalized >= 7) step = 10 * power;
|
|
37572
|
+
const ticks = [];
|
|
37573
|
+
for (let v = 0; v <= max; v += step) {
|
|
37574
|
+
ticks.push(Math.round(v));
|
|
37575
|
+
}
|
|
37576
|
+
if (target > 0) {
|
|
37577
|
+
ticks.push(Math.round(target));
|
|
37578
|
+
}
|
|
37579
|
+
return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
|
|
37580
|
+
}, [chartData.yAxisMax, chartData.lastSetTarget]);
|
|
37581
|
+
if (isLoading) {
|
|
37582
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
37583
|
+
OptifyeLogoLoader_default,
|
|
37584
|
+
{
|
|
37585
|
+
size: "lg",
|
|
37586
|
+
message: "Loading monthly performance data..."
|
|
37587
|
+
}
|
|
37588
|
+
) });
|
|
37589
|
+
}
|
|
37156
37590
|
const renderPerformanceSquares = (performances) => {
|
|
37157
37591
|
if (!performances || !Array.isArray(performances)) {
|
|
37158
37592
|
return null;
|
|
@@ -37244,64 +37678,18 @@ var LineMonthlyHistory = ({
|
|
|
37244
37678
|
year,
|
|
37245
37679
|
timezone,
|
|
37246
37680
|
value: normalizedRange,
|
|
37247
|
-
onChange: (nextRange) => onRangeChange?.(nextRange)
|
|
37681
|
+
onChange: (nextRange) => onRangeChange?.(nextRange),
|
|
37682
|
+
onMonthNavigate: (newMonth, newYear) => onCalendarMonthChange?.(new Date(newYear, newMonth))
|
|
37248
37683
|
}
|
|
37249
37684
|
) })
|
|
37250
37685
|
] }),
|
|
37251
37686
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
|
|
37252
37687
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
|
|
37253
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37254
|
-
|
|
37255
|
-
|
|
37256
|
-
|
|
37257
|
-
|
|
37258
|
-
let newMonth = month - 1;
|
|
37259
|
-
let newYear = year;
|
|
37260
|
-
if (newMonth < 0) {
|
|
37261
|
-
newMonth = 11;
|
|
37262
|
-
newYear -= 1;
|
|
37263
|
-
}
|
|
37264
|
-
if (onCalendarMonthChange) {
|
|
37265
|
-
onCalendarMonthChange(new Date(newYear, newMonth));
|
|
37266
|
-
}
|
|
37267
|
-
},
|
|
37268
|
-
className: "p-2 rounded-full hover:bg-gray-100",
|
|
37269
|
-
"aria-label": "Previous month",
|
|
37270
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
|
|
37271
|
-
}
|
|
37272
|
-
),
|
|
37273
|
-
/* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
|
|
37274
|
-
new Date(year, month).toLocaleString("default", { month: "long" }),
|
|
37275
|
-
" ",
|
|
37276
|
-
year
|
|
37277
|
-
] }),
|
|
37278
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37279
|
-
"button",
|
|
37280
|
-
{
|
|
37281
|
-
onClick: () => {
|
|
37282
|
-
let newMonth = month + 1;
|
|
37283
|
-
let newYear = year;
|
|
37284
|
-
if (newMonth > 11) {
|
|
37285
|
-
newMonth = 0;
|
|
37286
|
-
newYear += 1;
|
|
37287
|
-
}
|
|
37288
|
-
const currentDate = /* @__PURE__ */ new Date();
|
|
37289
|
-
const currentYear = currentDate.getFullYear();
|
|
37290
|
-
const currentMonth = currentDate.getMonth();
|
|
37291
|
-
if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
|
|
37292
|
-
return;
|
|
37293
|
-
}
|
|
37294
|
-
if (onCalendarMonthChange) {
|
|
37295
|
-
onCalendarMonthChange(new Date(newYear, newMonth));
|
|
37296
|
-
}
|
|
37297
|
-
},
|
|
37298
|
-
className: `p-2 rounded-full ${(/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month ? "text-gray-300 cursor-not-allowed" : "hover:bg-gray-100 text-gray-600"}`,
|
|
37299
|
-
disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
|
|
37300
|
-
"aria-label": "Next month",
|
|
37301
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "w-5 h-5" })
|
|
37302
|
-
}
|
|
37303
|
-
)
|
|
37304
|
-
] }),
|
|
37688
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
|
|
37689
|
+
new Date(year, month).toLocaleString("default", { month: "long" }),
|
|
37690
|
+
" ",
|
|
37691
|
+
year
|
|
37692
|
+
] }) }),
|
|
37305
37693
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37306
37694
|
LineHistoryCalendar_default,
|
|
37307
37695
|
{
|
|
@@ -37316,26 +37704,20 @@ var LineMonthlyHistory = ({
|
|
|
37316
37704
|
}
|
|
37317
37705
|
)
|
|
37318
37706
|
] }),
|
|
37319
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-
|
|
37320
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-
|
|
37321
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-
|
|
37322
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-
|
|
37323
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37324
|
-
|
|
37325
|
-
rangeLabel
|
|
37326
|
-
] }),
|
|
37327
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
|
|
37707
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
37708
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
37709
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
|
|
37710
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Efficiency" }),
|
|
37711
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
|
|
37712
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
|
|
37328
37713
|
avgEfficiency.toFixed(1),
|
|
37329
37714
|
"%"
|
|
37330
37715
|
] })
|
|
37331
37716
|
] }),
|
|
37332
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-
|
|
37333
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-
|
|
37334
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37335
|
-
|
|
37336
|
-
rangeLabel
|
|
37337
|
-
] }),
|
|
37338
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
|
|
37717
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
|
|
37718
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Avg. Underperforming" }),
|
|
37719
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
|
|
37720
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
|
|
37339
37721
|
avgUnderperforming.toFixed(1),
|
|
37340
37722
|
"/",
|
|
37341
37723
|
avgTotalWorkspaces.toFixed(1)
|
|
@@ -37343,9 +37725,9 @@ var LineMonthlyHistory = ({
|
|
|
37343
37725
|
] })
|
|
37344
37726
|
] }),
|
|
37345
37727
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4", children: [
|
|
37346
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col", children: [
|
|
37347
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-
|
|
37348
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-
|
|
37728
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col h-[220px]", children: [
|
|
37729
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1", children: "Idle time Breakdown" }),
|
|
37730
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 relative -ml-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
37349
37731
|
IdleTimeReasonChart,
|
|
37350
37732
|
{
|
|
37351
37733
|
data: idleReasonsChartData,
|
|
@@ -37355,34 +37737,117 @@ var LineMonthlyHistory = ({
|
|
|
37355
37737
|
chartKey
|
|
37356
37738
|
) })
|
|
37357
37739
|
] }),
|
|
37358
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-
|
|
37359
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-
|
|
37360
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-
|
|
37740
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 h-[220px] flex flex-col", children: [
|
|
37741
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Top 3 Poorest Performers" }),
|
|
37742
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto pr-1 space-y-1 custom-scrollbar", children: [
|
|
37361
37743
|
(underperformingWorkspaces[selectedShiftId] || []).slice(0, 3).map((workspace) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
37362
37744
|
"button",
|
|
37363
37745
|
{
|
|
37364
37746
|
onClick: () => handleWorkspaceClick(workspace),
|
|
37365
|
-
className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left",
|
|
37366
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-
|
|
37367
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-900 text-
|
|
37747
|
+
className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left group",
|
|
37748
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-50 group-last:border-b-0", children: [
|
|
37749
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-900 text-xs", children: [
|
|
37368
37750
|
getWorkspaceDisplayName(workspace.workspace_name, lineId),
|
|
37369
|
-
workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 text-
|
|
37751
|
+
workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 text-[10px] text-gray-500", children: [
|
|
37370
37752
|
"(",
|
|
37371
37753
|
(workspace.avg_efficiency || 0).toFixed(1),
|
|
37372
37754
|
"%)"
|
|
37373
37755
|
] })
|
|
37374
37756
|
] }),
|
|
37375
|
-
/* @__PURE__ */ jsxRuntime.
|
|
37376
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 hidden xl:block", children: "Last 5 days:" }),
|
|
37377
|
-
renderPerformanceSquares(workspace.last_5_days)
|
|
37378
|
-
] })
|
|
37757
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: renderPerformanceSquares(workspace.last_5_days) })
|
|
37379
37758
|
] })
|
|
37380
37759
|
},
|
|
37381
37760
|
workspace.workspace_uuid
|
|
37382
37761
|
)),
|
|
37383
|
-
(!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-gray-
|
|
37762
|
+
(!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-gray-400 py-8 text-xs italic", children: "No underperforming workspaces" })
|
|
37384
37763
|
] })
|
|
37385
37764
|
] })
|
|
37765
|
+
] }),
|
|
37766
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4", children: [
|
|
37767
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Daily Output" }) }),
|
|
37768
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
37769
|
+
recharts.BarChart,
|
|
37770
|
+
{
|
|
37771
|
+
data: chartData.data,
|
|
37772
|
+
margin: { top: 20, right: 10, bottom: 40, left: 10 },
|
|
37773
|
+
children: [
|
|
37774
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
|
|
37775
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37776
|
+
recharts.XAxis,
|
|
37777
|
+
{
|
|
37778
|
+
dataKey: "hour",
|
|
37779
|
+
tick: { fontSize: 10, fill: "#6b7280" },
|
|
37780
|
+
interval: 0,
|
|
37781
|
+
angle: -45,
|
|
37782
|
+
textAnchor: "end",
|
|
37783
|
+
height: 60
|
|
37784
|
+
}
|
|
37785
|
+
),
|
|
37786
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37787
|
+
recharts.YAxis,
|
|
37788
|
+
{
|
|
37789
|
+
domain: [0, chartData.yAxisMax],
|
|
37790
|
+
width: 40,
|
|
37791
|
+
ticks: yAxisTicks,
|
|
37792
|
+
tick: (props) => {
|
|
37793
|
+
const { x, y, payload } = props;
|
|
37794
|
+
const value = Math.round(payload.value);
|
|
37795
|
+
const targetValue = Math.round(chartData.lastSetTarget);
|
|
37796
|
+
const isTarget = value === targetValue && targetValue > 0;
|
|
37797
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
37798
|
+
"text",
|
|
37799
|
+
{
|
|
37800
|
+
x: x - 5,
|
|
37801
|
+
y: y + 4,
|
|
37802
|
+
textAnchor: "end",
|
|
37803
|
+
fontSize: "10",
|
|
37804
|
+
fill: isTarget ? "#E34329" : "#6b7280",
|
|
37805
|
+
fontWeight: isTarget ? "600" : "normal",
|
|
37806
|
+
children: value < 1e-3 ? "" : value.toLocaleString()
|
|
37807
|
+
}
|
|
37808
|
+
);
|
|
37809
|
+
}
|
|
37810
|
+
}
|
|
37811
|
+
),
|
|
37812
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37813
|
+
recharts.Tooltip,
|
|
37814
|
+
{
|
|
37815
|
+
cursor: false,
|
|
37816
|
+
content: CustomTooltip2
|
|
37817
|
+
}
|
|
37818
|
+
),
|
|
37819
|
+
chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
37820
|
+
recharts.ReferenceLine,
|
|
37821
|
+
{
|
|
37822
|
+
y: chartData.lastSetTarget,
|
|
37823
|
+
stroke: "#E34329",
|
|
37824
|
+
strokeDasharray: "5 5",
|
|
37825
|
+
strokeWidth: 2
|
|
37826
|
+
}
|
|
37827
|
+
),
|
|
37828
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37829
|
+
recharts.Bar,
|
|
37830
|
+
{
|
|
37831
|
+
dataKey: "output",
|
|
37832
|
+
radius: [4, 4, 0, 0],
|
|
37833
|
+
isAnimationActive: true,
|
|
37834
|
+
animationBegin: 0,
|
|
37835
|
+
animationDuration: 1e3,
|
|
37836
|
+
animationEasing: "ease-out",
|
|
37837
|
+
children: chartData.data.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
37838
|
+
recharts.Cell,
|
|
37839
|
+
{
|
|
37840
|
+
fill: entry.output === 0 ? "#f3f4f6" : entry.color,
|
|
37841
|
+
className: entry.output > 0 ? "hover:opacity-80 transition-opacity cursor-pointer" : ""
|
|
37842
|
+
},
|
|
37843
|
+
`cell-${index}`
|
|
37844
|
+
))
|
|
37845
|
+
}
|
|
37846
|
+
)
|
|
37847
|
+
]
|
|
37848
|
+
},
|
|
37849
|
+
chartKey
|
|
37850
|
+
) }) })
|
|
37386
37851
|
] })
|
|
37387
37852
|
] })
|
|
37388
37853
|
] })
|
|
@@ -38523,7 +38988,7 @@ var styles = `
|
|
|
38523
38988
|
transform: scale(1) !important;
|
|
38524
38989
|
}
|
|
38525
38990
|
`;
|
|
38526
|
-
var
|
|
38991
|
+
var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
38527
38992
|
var getTimeInZoneAsDate = (timezone) => {
|
|
38528
38993
|
const time2 = getCurrentTimeInZone(timezone);
|
|
38529
38994
|
return typeof time2 === "string" ? new Date(time2) : time2;
|
|
@@ -38555,10 +39020,10 @@ var WorkspaceHistoryCalendar = ({
|
|
|
38555
39020
|
return () => clearTimeout(timer);
|
|
38556
39021
|
}, [month, year]);
|
|
38557
39022
|
const calendarData = React24.useMemo(() => {
|
|
38558
|
-
const
|
|
38559
|
-
const
|
|
38560
|
-
const totalDays =
|
|
38561
|
-
let startOffset =
|
|
39023
|
+
const startOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
|
|
39024
|
+
const endOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
|
|
39025
|
+
const totalDays = endOfMonth2.getDate();
|
|
39026
|
+
let startOffset = startOfMonth2.getDay() - 1;
|
|
38562
39027
|
if (startOffset === -1) startOffset = 6;
|
|
38563
39028
|
const calendar = Array(startOffset).fill(null);
|
|
38564
39029
|
for (let day = 1; day <= totalDays; day++) {
|
|
@@ -38767,7 +39232,7 @@ var WorkspaceHistoryCalendar = ({
|
|
|
38767
39232
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs sm:text-sm text-gray-500 mt-1", children: "Calendar view of daily performance" })
|
|
38768
39233
|
] }),
|
|
38769
39234
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:gap-4 lg:gap-6", children: [
|
|
38770
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children:
|
|
39235
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: WEEKDAYS3.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs sm:text-sm font-medium text-gray-600 text-center", children: day.slice(0, 3) }, day)) }),
|
|
38771
39236
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: calendarData.calendar.map((day, index) => {
|
|
38772
39237
|
const startOffset = calendarData.startOffset;
|
|
38773
39238
|
const dayNumber = index >= startOffset ? index - startOffset + 1 : null;
|
|
@@ -38820,13 +39285,13 @@ var WorkspaceHistoryCalendar = ({
|
|
|
38820
39285
|
] })
|
|
38821
39286
|
] });
|
|
38822
39287
|
};
|
|
38823
|
-
var
|
|
38824
|
-
var
|
|
39288
|
+
var WEEKDAYS4 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
39289
|
+
var getOrdinal2 = (n) => {
|
|
38825
39290
|
const suffix = ["th", "st", "nd", "rd"];
|
|
38826
39291
|
const v = n % 100;
|
|
38827
39292
|
return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
38828
39293
|
};
|
|
38829
|
-
var
|
|
39294
|
+
var CustomTooltip3 = ({ active, payload, label }) => {
|
|
38830
39295
|
if (active && payload && payload.length) {
|
|
38831
39296
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
|
|
38832
39297
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
|
|
@@ -38929,7 +39394,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
38929
39394
|
if (output > maxOutput) maxOutput = output;
|
|
38930
39395
|
const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
|
|
38931
39396
|
dailyData.push({
|
|
38932
|
-
hour:
|
|
39397
|
+
hour: getOrdinal2(day),
|
|
38933
39398
|
// Using ordinal format (1st, 2nd, 3rd, etc.)
|
|
38934
39399
|
timeRange: `Day ${day}`,
|
|
38935
39400
|
output,
|
|
@@ -38963,9 +39428,26 @@ var WorkspaceMonthlyHistory = ({
|
|
|
38963
39428
|
ticks.push(Math.round(v));
|
|
38964
39429
|
}
|
|
38965
39430
|
if (target > 0) {
|
|
38966
|
-
|
|
39431
|
+
const roundedTarget = Math.round(target);
|
|
39432
|
+
if (!ticks.includes(roundedTarget)) {
|
|
39433
|
+
let nearestIndex = -1;
|
|
39434
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
39435
|
+
ticks.forEach((tick, index) => {
|
|
39436
|
+
if (tick === 0) return;
|
|
39437
|
+
const distance2 = Math.abs(tick - roundedTarget);
|
|
39438
|
+
if (distance2 < nearestDistance) {
|
|
39439
|
+
nearestDistance = distance2;
|
|
39440
|
+
nearestIndex = index;
|
|
39441
|
+
}
|
|
39442
|
+
});
|
|
39443
|
+
if (nearestIndex >= 0) {
|
|
39444
|
+
ticks[nearestIndex] = roundedTarget;
|
|
39445
|
+
} else {
|
|
39446
|
+
ticks.push(roundedTarget);
|
|
39447
|
+
}
|
|
39448
|
+
}
|
|
38967
39449
|
}
|
|
38968
|
-
return
|
|
39450
|
+
return ticks.filter((v) => v >= 0 && v <= max * 1.05).sort((a, b) => a - b);
|
|
38969
39451
|
}, [chartData.yAxisMax, chartData.lastSetTarget]);
|
|
38970
39452
|
const pieChartData = React24.useMemo(() => {
|
|
38971
39453
|
const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(hasRealData);
|
|
@@ -39001,10 +39483,10 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39001
39483
|
};
|
|
39002
39484
|
}, [analysisMonthlyData, selectedShiftId]);
|
|
39003
39485
|
const calendarData = React24.useMemo(() => {
|
|
39004
|
-
const
|
|
39005
|
-
const
|
|
39006
|
-
const totalDays =
|
|
39007
|
-
let startOffset =
|
|
39486
|
+
const startOfMonth2 = new Date(year, month, 1);
|
|
39487
|
+
const endOfMonth2 = new Date(year, month + 1, 0);
|
|
39488
|
+
const totalDays = endOfMonth2.getDate();
|
|
39489
|
+
let startOffset = startOfMonth2.getDay() - 1;
|
|
39008
39490
|
if (startOffset === -1) startOffset = 6;
|
|
39009
39491
|
const calendar = Array(startOffset).fill(null);
|
|
39010
39492
|
for (let day = 1; day <= totalDays; day++) {
|
|
@@ -39071,68 +39553,19 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39071
39553
|
year,
|
|
39072
39554
|
timezone,
|
|
39073
39555
|
value: normalizedRange,
|
|
39074
|
-
onChange: (nextRange) => onRangeChange?.(nextRange)
|
|
39556
|
+
onChange: (nextRange) => onRangeChange?.(nextRange),
|
|
39557
|
+
onMonthNavigate
|
|
39075
39558
|
}
|
|
39076
39559
|
) })
|
|
39077
39560
|
] }),
|
|
39078
39561
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
|
|
39079
39562
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
|
|
39080
|
-
/* @__PURE__ */ jsxRuntime.
|
|
39081
|
-
|
|
39082
|
-
|
|
39083
|
-
|
|
39084
|
-
|
|
39085
|
-
|
|
39086
|
-
let newYear = year;
|
|
39087
|
-
if (newMonth < 0) {
|
|
39088
|
-
newMonth = 11;
|
|
39089
|
-
newYear -= 1;
|
|
39090
|
-
}
|
|
39091
|
-
if (newYear < 2023) {
|
|
39092
|
-
return;
|
|
39093
|
-
}
|
|
39094
|
-
if (onMonthNavigate) {
|
|
39095
|
-
onMonthNavigate(newMonth, newYear);
|
|
39096
|
-
}
|
|
39097
|
-
},
|
|
39098
|
-
className: "p-2 rounded-full hover:bg-gray-100",
|
|
39099
|
-
"aria-label": "Previous month",
|
|
39100
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
|
|
39101
|
-
}
|
|
39102
|
-
),
|
|
39103
|
-
/* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
|
|
39104
|
-
new Date(year, month).toLocaleString("default", { month: "long" }),
|
|
39105
|
-
" ",
|
|
39106
|
-
year
|
|
39107
|
-
] }),
|
|
39108
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
39109
|
-
"button",
|
|
39110
|
-
{
|
|
39111
|
-
onClick: () => {
|
|
39112
|
-
let newMonth = month + 1;
|
|
39113
|
-
let newYear = year;
|
|
39114
|
-
if (newMonth > 11) {
|
|
39115
|
-
newMonth = 0;
|
|
39116
|
-
newYear += 1;
|
|
39117
|
-
}
|
|
39118
|
-
const currentDate = /* @__PURE__ */ new Date();
|
|
39119
|
-
const currentYear = currentDate.getFullYear();
|
|
39120
|
-
const currentMonth = currentDate.getMonth();
|
|
39121
|
-
if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
|
|
39122
|
-
return;
|
|
39123
|
-
}
|
|
39124
|
-
if (onMonthNavigate) {
|
|
39125
|
-
onMonthNavigate(newMonth, newYear);
|
|
39126
|
-
}
|
|
39127
|
-
},
|
|
39128
|
-
className: `p-2 rounded-full ${(/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month ? "text-gray-300 cursor-not-allowed" : "hover:bg-gray-100 text-gray-600"}`,
|
|
39129
|
-
disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
|
|
39130
|
-
"aria-label": "Next month",
|
|
39131
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "w-5 h-5" })
|
|
39132
|
-
}
|
|
39133
|
-
)
|
|
39134
|
-
] }),
|
|
39135
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS3.map((day, idx) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
|
|
39563
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
|
|
39564
|
+
new Date(year, month).toLocaleString("default", { month: "long" }),
|
|
39565
|
+
" ",
|
|
39566
|
+
year
|
|
39567
|
+
] }) }),
|
|
39568
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS4.map((day, idx) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
|
|
39136
39569
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2 mt-6", children: calendarData.calendar.map((day, index) => {
|
|
39137
39570
|
const dayNumber = index >= calendarData.startOffset ? index - calendarData.startOffset + 1 : null;
|
|
39138
39571
|
if (!dayNumber || dayNumber > new Date(year, month + 1, 0).getDate()) {
|
|
@@ -39149,6 +39582,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39149
39582
|
const getPerformanceColor = () => {
|
|
39150
39583
|
if (isFuture) return "bg-gray-200 dark:bg-gray-700";
|
|
39151
39584
|
if (!hasData || !shiftData) return "bg-gray-300 dark:bg-gray-600";
|
|
39585
|
+
if (showRange && !inRange) return "bg-gray-200 dark:bg-gray-700";
|
|
39152
39586
|
return shiftData.efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
|
|
39153
39587
|
};
|
|
39154
39588
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -39168,7 +39602,8 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39168
39602
|
}
|
|
39169
39603
|
),
|
|
39170
39604
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `
|
|
39171
|
-
text-base font-medium
|
|
39605
|
+
text-base font-medium flex items-center relative z-10
|
|
39606
|
+
${hasData && !isFuture && (!showRange || inRange) ? "text-white" : "text-gray-400"}
|
|
39172
39607
|
${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
|
|
39173
39608
|
`, children: dayNumber }),
|
|
39174
39609
|
!isFuture && hasData && shiftData && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/80 rounded-lg p-2 text-white opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs space-y-1", children: [
|
|
@@ -39324,13 +39759,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39324
39759
|
recharts.YAxis,
|
|
39325
39760
|
{
|
|
39326
39761
|
domain: [0, chartData.yAxisMax],
|
|
39327
|
-
width:
|
|
39762
|
+
width: 40,
|
|
39328
39763
|
ticks: yAxisTicks,
|
|
39329
39764
|
tick: (props) => {
|
|
39330
39765
|
const { x, y, payload } = props;
|
|
39331
39766
|
const value = Math.round(payload.value);
|
|
39332
39767
|
const targetValue = Math.round(chartData.lastSetTarget);
|
|
39333
|
-
const isTarget =
|
|
39768
|
+
const isTarget = value === targetValue && targetValue > 0;
|
|
39334
39769
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39335
39770
|
"text",
|
|
39336
39771
|
{
|
|
@@ -39340,7 +39775,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39340
39775
|
fontSize: "10",
|
|
39341
39776
|
fill: isTarget ? "#E34329" : "#6b7280",
|
|
39342
39777
|
fontWeight: isTarget ? "600" : "normal",
|
|
39343
|
-
children: value < 1e-3 ? "" : value.
|
|
39778
|
+
children: value < 1e-3 ? "" : value.toLocaleString()
|
|
39344
39779
|
}
|
|
39345
39780
|
);
|
|
39346
39781
|
}
|
|
@@ -39350,7 +39785,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
39350
39785
|
recharts.Tooltip,
|
|
39351
39786
|
{
|
|
39352
39787
|
cursor: false,
|
|
39353
|
-
content:
|
|
39788
|
+
content: CustomTooltip3
|
|
39354
39789
|
}
|
|
39355
39790
|
),
|
|
39356
39791
|
chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -39979,13 +40414,15 @@ var LiveTimer = () => {
|
|
|
39979
40414
|
});
|
|
39980
40415
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatter.format(time2) });
|
|
39981
40416
|
};
|
|
39982
|
-
var
|
|
39983
|
-
|
|
39984
|
-
|
|
39985
|
-
|
|
39986
|
-
|
|
39987
|
-
|
|
39988
|
-
|
|
40417
|
+
var getGradientEfficiencyClasses = (color2) => {
|
|
40418
|
+
switch (color2) {
|
|
40419
|
+
case "green":
|
|
40420
|
+
return "bg-gradient-to-r from-[#00AB45]/90 to-[#00AB45]/95";
|
|
40421
|
+
case "yellow":
|
|
40422
|
+
return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
|
|
40423
|
+
case "red":
|
|
40424
|
+
default:
|
|
40425
|
+
return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
|
|
39989
40426
|
}
|
|
39990
40427
|
};
|
|
39991
40428
|
var TREND_STYLES = {
|
|
@@ -40053,30 +40490,40 @@ var getWorkspaceStyles = (position, isPlaceholder = false) => {
|
|
|
40053
40490
|
${additionalStyles}
|
|
40054
40491
|
${isPlaceholder ? "cursor-default" : ""}`;
|
|
40055
40492
|
};
|
|
40056
|
-
var
|
|
40057
|
-
|
|
40058
|
-
|
|
40059
|
-
|
|
40060
|
-
|
|
40061
|
-
|
|
40062
|
-
|
|
40063
|
-
|
|
40064
|
-
|
|
40065
|
-
|
|
40493
|
+
var formatPercentRange = (min, max) => {
|
|
40494
|
+
const format7 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
|
|
40495
|
+
return `${format7(min)}-${format7(max)}%`;
|
|
40496
|
+
};
|
|
40497
|
+
var Legend6 = ({ useBottleneckLabel = false, legend }) => {
|
|
40498
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
40499
|
+
const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
|
|
40500
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-3 bg-white/95 rounded-lg shadow-sm px-2 sm:px-4 py-1 sm:py-1.5 border border-gray-200/60 backdrop-blur-sm text-[10px] sm:text-sm", children: [
|
|
40501
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-gray-700 hidden sm:block", children: "Efficiency:" }),
|
|
40502
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
|
|
40503
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
40504
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#00AB45]/90 ring-1 ring-[#00AB45]/20" }),
|
|
40505
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.green_min, effectiveLegend.green_max) })
|
|
40506
|
+
] }),
|
|
40507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
40508
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#FFB020]/90 ring-1 ring-[#FFB020]/20" }),
|
|
40509
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.yellow_min, effectiveLegend.yellow_max) })
|
|
40510
|
+
] }),
|
|
40511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
40512
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#E34329]/90 ring-1 ring-[#E34329]/20" }),
|
|
40513
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
|
|
40514
|
+
] })
|
|
40066
40515
|
] }),
|
|
40516
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
|
|
40067
40517
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
40068
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-
|
|
40069
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children:
|
|
40518
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-5 sm:h-5 bg-[#E34329]/90 rounded-full ring-1 ring-[#E34329]/20 text-white font-bold text-[8px] sm:text-xs", children: "!" }),
|
|
40519
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: exclamationLabel })
|
|
40070
40520
|
] })
|
|
40071
|
-
] })
|
|
40072
|
-
|
|
40073
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
40074
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-5 sm:h-5 bg-[#E34329]/90 rounded-full ring-1 ring-[#E34329]/20 text-white font-bold text-[8px] sm:text-xs", children: "!" }),
|
|
40075
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: "Bottleneck" })
|
|
40076
|
-
] })
|
|
40077
|
-
] });
|
|
40521
|
+
] });
|
|
40522
|
+
};
|
|
40078
40523
|
var arePropsEqual = (prevProps, nextProps) => {
|
|
40079
|
-
|
|
40524
|
+
const prevLegend = prevProps.legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
40525
|
+
const nextLegend = nextProps.legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
40526
|
+
return prevProps.data.efficiency === nextProps.data.efficiency && prevProps.data.trend_score === nextProps.data.trend_score && prevProps.data.workspace_id === nextProps.data.workspace_id && prevProps.data.workspace_name === nextProps.data.workspace_name && prevProps.isBottleneck === nextProps.isBottleneck && prevProps.isLowEfficiency === nextProps.isLowEfficiency && prevProps.isVeryLowEfficiency === nextProps.isVeryLowEfficiency && prevLegend.green_min === nextLegend.green_min && prevLegend.green_max === nextLegend.green_max && prevLegend.yellow_min === nextLegend.yellow_min && prevLegend.yellow_max === nextLegend.yellow_max && prevLegend.red_min === nextLegend.red_min && prevLegend.red_max === nextLegend.red_max && prevLegend.critical_threshold === nextLegend.critical_threshold && // Position doesn't need deep equality check as it's generally static
|
|
40080
40527
|
prevProps.position.id === nextProps.position.id;
|
|
40081
40528
|
};
|
|
40082
40529
|
var WorkspaceGridItem = React24__namespace.default.memo(({
|
|
@@ -40085,20 +40532,21 @@ var WorkspaceGridItem = React24__namespace.default.memo(({
|
|
|
40085
40532
|
isBottleneck = false,
|
|
40086
40533
|
isLowEfficiency = false,
|
|
40087
40534
|
isVeryLowEfficiency = false,
|
|
40088
|
-
onHoverChange
|
|
40535
|
+
onHoverChange,
|
|
40536
|
+
legend
|
|
40089
40537
|
}) => {
|
|
40090
40538
|
const { navigate } = useNavigation();
|
|
40539
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
40091
40540
|
const isInactive = React24.useMemo(() => !data.workspace_id || data.efficiency < 10, [data.workspace_id, data.efficiency]);
|
|
40092
40541
|
const colorClass = React24.useMemo(() => {
|
|
40093
40542
|
const isConveyorEnd = position.size === "conveyor" && position.orientation;
|
|
40543
|
+
const efficiencyColor = getEfficiencyColor(data.efficiency, effectiveLegend);
|
|
40094
40544
|
if (isConveyorEnd) {
|
|
40095
40545
|
if (isInactive) return "bg-gray-300/90";
|
|
40096
|
-
|
|
40097
|
-
if (data.efficiency >= 70) return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
|
|
40098
|
-
return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
|
|
40546
|
+
return getGradientEfficiencyClasses(efficiencyColor);
|
|
40099
40547
|
}
|
|
40100
|
-
return isInactive ? "bg-gray-300/90" :
|
|
40101
|
-
}, [data.efficiency, isInactive, position.size, position.orientation]);
|
|
40548
|
+
return isInactive ? "bg-gray-300/90" : getEfficiencyColorClasses(data.efficiency, effectiveLegend);
|
|
40549
|
+
}, [data.efficiency, effectiveLegend, isInactive, position.size, position.orientation]);
|
|
40102
40550
|
const { arrow, color: arrowColor } = React24.useMemo(() => getTrendArrowAndColor2(data.trend_score), [data.trend_score]);
|
|
40103
40551
|
const workspaceNumber = React24.useMemo(() => getWorkspaceNumber(data.workspace_name), [data.workspace_name]);
|
|
40104
40552
|
const styles2 = React24.useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
|
|
@@ -40180,6 +40628,8 @@ var WorkspaceGrid = React24__namespace.default.memo(({
|
|
|
40180
40628
|
factoryView = "factory",
|
|
40181
40629
|
line2Uuid = "line-2",
|
|
40182
40630
|
className = "",
|
|
40631
|
+
hasFlowBuffers = false,
|
|
40632
|
+
legend = DEFAULT_EFFICIENCY_LEGEND,
|
|
40183
40633
|
videoSources = {},
|
|
40184
40634
|
displayNames = {},
|
|
40185
40635
|
onWorkspaceHover,
|
|
@@ -40216,7 +40666,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
|
|
|
40216
40666
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
|
|
40217
40667
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
|
|
40218
40668
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
|
|
40219
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) }),
|
|
40669
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) }),
|
|
40220
40670
|
mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
40221
40671
|
"button",
|
|
40222
40672
|
{
|
|
@@ -40233,7 +40683,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
|
|
|
40233
40683
|
}
|
|
40234
40684
|
)
|
|
40235
40685
|
] }),
|
|
40236
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) })
|
|
40686
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) })
|
|
40237
40687
|
] }),
|
|
40238
40688
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-14 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
40239
40689
|
motion.div,
|
|
@@ -40249,6 +40699,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
|
|
|
40249
40699
|
workspaces,
|
|
40250
40700
|
videoSources,
|
|
40251
40701
|
displayNames,
|
|
40702
|
+
legend,
|
|
40252
40703
|
onWorkspaceHover,
|
|
40253
40704
|
onWorkspaceHoverEnd
|
|
40254
40705
|
}
|
|
@@ -40268,6 +40719,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
|
|
|
40268
40719
|
{
|
|
40269
40720
|
workspaces,
|
|
40270
40721
|
displayNames,
|
|
40722
|
+
legend,
|
|
40271
40723
|
onWorkspaceHover,
|
|
40272
40724
|
onWorkspaceHoverEnd
|
|
40273
40725
|
}
|
|
@@ -47748,7 +48200,7 @@ var AIAgentView = () => {
|
|
|
47748
48200
|
barRadius: [4, 4, 0, 0]
|
|
47749
48201
|
// Top corners rounded
|
|
47750
48202
|
};
|
|
47751
|
-
const
|
|
48203
|
+
const CustomTooltip4 = ({ active, payload, label }) => {
|
|
47752
48204
|
if (active && payload && payload.length) {
|
|
47753
48205
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white px-4 py-3 shadow-lg rounded-lg border border-gray-200", children: [
|
|
47754
48206
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900 mb-1", children: label }),
|
|
@@ -47848,7 +48300,7 @@ var AIAgentView = () => {
|
|
|
47848
48300
|
tickFormatter: (value) => formatNumber(value)
|
|
47849
48301
|
}
|
|
47850
48302
|
),
|
|
47851
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48303
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
47852
48304
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47853
48305
|
recharts.Bar,
|
|
47854
48306
|
{
|
|
@@ -47892,7 +48344,7 @@ var AIAgentView = () => {
|
|
|
47892
48344
|
tickFormatter: (value) => formatNumber(value)
|
|
47893
48345
|
}
|
|
47894
48346
|
),
|
|
47895
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48347
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
47896
48348
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47897
48349
|
recharts.Line,
|
|
47898
48350
|
{
|
|
@@ -48012,7 +48464,7 @@ var AIAgentView = () => {
|
|
|
48012
48464
|
tickFormatter: (value) => formatNumber(value)
|
|
48013
48465
|
}
|
|
48014
48466
|
),
|
|
48015
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48467
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
48016
48468
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48017
48469
|
recharts.Legend,
|
|
48018
48470
|
{
|
|
@@ -48072,7 +48524,7 @@ var AIAgentView = () => {
|
|
|
48072
48524
|
tickFormatter: (value) => formatNumber(value)
|
|
48073
48525
|
}
|
|
48074
48526
|
),
|
|
48075
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48527
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
48076
48528
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48077
48529
|
recharts.Legend,
|
|
48078
48530
|
{
|
|
@@ -48236,7 +48688,7 @@ var AIAgentView = () => {
|
|
|
48236
48688
|
recharts.Tooltip,
|
|
48237
48689
|
{
|
|
48238
48690
|
cursor: { strokeDasharray: "3 3" },
|
|
48239
|
-
content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48691
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {})
|
|
48240
48692
|
}
|
|
48241
48693
|
),
|
|
48242
48694
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -48306,7 +48758,7 @@ var AIAgentView = () => {
|
|
|
48306
48758
|
tickFormatter: (value) => formatNumber(value)
|
|
48307
48759
|
}
|
|
48308
48760
|
),
|
|
48309
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48761
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
48310
48762
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48311
48763
|
recharts.Legend,
|
|
48312
48764
|
{
|
|
@@ -48380,7 +48832,7 @@ var AIAgentView = () => {
|
|
|
48380
48832
|
tickFormatter: (value) => formatNumber(value)
|
|
48381
48833
|
}
|
|
48382
48834
|
),
|
|
48383
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48835
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
48384
48836
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48385
48837
|
recharts.Legend,
|
|
48386
48838
|
{
|
|
@@ -49639,6 +50091,8 @@ function HomeView({
|
|
|
49639
50091
|
const {
|
|
49640
50092
|
workspaceMetrics,
|
|
49641
50093
|
lineMetrics,
|
|
50094
|
+
efficiencyLegend,
|
|
50095
|
+
metadata: metricsMetadata,
|
|
49642
50096
|
isLoading: metricsLoading,
|
|
49643
50097
|
error: metricsError,
|
|
49644
50098
|
refetch: refetchMetrics
|
|
@@ -49647,6 +50101,7 @@ function HomeView({
|
|
|
49647
50101
|
userAccessibleLineIds: allLineIds
|
|
49648
50102
|
// Pass user's accessible lines for supervisor filtering
|
|
49649
50103
|
});
|
|
50104
|
+
const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
|
|
49650
50105
|
const kpis = React24.useMemo(() => {
|
|
49651
50106
|
const lineMetricsRows = lineMetrics || [];
|
|
49652
50107
|
if (selectedLineId === factoryViewId) {
|
|
@@ -50105,8 +50560,10 @@ function HomeView({
|
|
|
50105
50560
|
workspaces: memoizedWorkspaceMetrics,
|
|
50106
50561
|
lineNames,
|
|
50107
50562
|
factoryView: factoryViewId,
|
|
50563
|
+
legend: efficiencyLegend,
|
|
50108
50564
|
videoSources,
|
|
50109
50565
|
displayNames: workspaceDisplayNames,
|
|
50566
|
+
hasFlowBuffers,
|
|
50110
50567
|
className: "h-full",
|
|
50111
50568
|
onWorkspaceHover: handleWorkspaceHover,
|
|
50112
50569
|
onWorkspaceHoverEnd: handleWorkspaceHoverEnd
|
|
@@ -50133,8 +50590,10 @@ function HomeView({
|
|
|
50133
50590
|
// Show empty grid while loading
|
|
50134
50591
|
lineNames,
|
|
50135
50592
|
factoryView: factoryViewId,
|
|
50593
|
+
legend: efficiencyLegend,
|
|
50136
50594
|
videoSources,
|
|
50137
50595
|
displayNames: workspaceDisplayNames,
|
|
50596
|
+
hasFlowBuffers,
|
|
50138
50597
|
className: "h-full",
|
|
50139
50598
|
onWorkspaceHover: handleWorkspaceHover,
|
|
50140
50599
|
onWorkspaceHoverEnd: handleWorkspaceHoverEnd
|
|
@@ -50755,6 +51214,8 @@ var KPIDetailView = ({
|
|
|
50755
51214
|
avg_efficiency: metric.avg_efficiency || 0,
|
|
50756
51215
|
underperforming_workspaces: metric.underperforming_workspaces || 0,
|
|
50757
51216
|
total_workspaces: metric.total_workspaces || 0,
|
|
51217
|
+
output: metric.current_output || 0,
|
|
51218
|
+
idealOutput: metric.ideal_output || metric.line_threshold || 0,
|
|
50758
51219
|
compliance_percentage: 95 + Math.random() * 5,
|
|
50759
51220
|
// Mock data: random value between 95-100%
|
|
50760
51221
|
hasData: true
|
|
@@ -53210,6 +53671,201 @@ var ACTION_NAMES = {
|
|
|
53210
53671
|
QUALITY_CONTROL: "Quality Control"
|
|
53211
53672
|
};
|
|
53212
53673
|
|
|
53674
|
+
// src/lib/services/efficiencyLegendService.ts
|
|
53675
|
+
var EfficiencyLegendService = class {
|
|
53676
|
+
// 5 minutes
|
|
53677
|
+
constructor(supabase) {
|
|
53678
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
53679
|
+
this.cacheExpiry = /* @__PURE__ */ new Map();
|
|
53680
|
+
this.CACHE_DURATION = 5 * 60 * 1e3;
|
|
53681
|
+
this.supabase = supabase;
|
|
53682
|
+
}
|
|
53683
|
+
/**
|
|
53684
|
+
* Get efficiency legend for a company
|
|
53685
|
+
*/
|
|
53686
|
+
async getEfficiencyLegend(companyId) {
|
|
53687
|
+
try {
|
|
53688
|
+
const cached = this.cache.get(companyId);
|
|
53689
|
+
const expiry = this.cacheExpiry.get(companyId);
|
|
53690
|
+
if (cached && expiry && Date.now() < expiry) {
|
|
53691
|
+
return cached;
|
|
53692
|
+
}
|
|
53693
|
+
const params = new URLSearchParams({ company_id: companyId });
|
|
53694
|
+
const response = await fetchBackendJson(
|
|
53695
|
+
this.supabase,
|
|
53696
|
+
`/api/efficiency-legend?${params.toString()}`
|
|
53697
|
+
);
|
|
53698
|
+
const normalized = this.normalizeLegend(response?.legend);
|
|
53699
|
+
this.cache.set(companyId, normalized);
|
|
53700
|
+
this.cacheExpiry.set(companyId, Date.now() + this.CACHE_DURATION);
|
|
53701
|
+
return normalized;
|
|
53702
|
+
} catch (error) {
|
|
53703
|
+
console.error("Error fetching efficiency legend:", error);
|
|
53704
|
+
return DEFAULT_EFFICIENCY_LEGEND;
|
|
53705
|
+
}
|
|
53706
|
+
}
|
|
53707
|
+
/**
|
|
53708
|
+
* Update or create efficiency legend for a company
|
|
53709
|
+
*/
|
|
53710
|
+
async updateEfficiencyLegend(companyId, legend) {
|
|
53711
|
+
try {
|
|
53712
|
+
const normalizedLegend = this.normalizeLegend(legend);
|
|
53713
|
+
const validation = this.validateLegend(normalizedLegend);
|
|
53714
|
+
if (!validation.valid) {
|
|
53715
|
+
return { success: false, error: validation.error };
|
|
53716
|
+
}
|
|
53717
|
+
const params = new URLSearchParams({ company_id: companyId });
|
|
53718
|
+
await fetchBackendJson(
|
|
53719
|
+
this.supabase,
|
|
53720
|
+
`/api/efficiency-legend?${params.toString()}`,
|
|
53721
|
+
{
|
|
53722
|
+
method: "PUT",
|
|
53723
|
+
body: JSON.stringify(normalizedLegend)
|
|
53724
|
+
}
|
|
53725
|
+
);
|
|
53726
|
+
this.cache.delete(companyId);
|
|
53727
|
+
this.cacheExpiry.delete(companyId);
|
|
53728
|
+
return { success: true };
|
|
53729
|
+
} catch (error) {
|
|
53730
|
+
console.error("Error updating efficiency legend:", error);
|
|
53731
|
+
return { success: false, error: error.message || "Failed to update efficiency legend" };
|
|
53732
|
+
}
|
|
53733
|
+
}
|
|
53734
|
+
/**
|
|
53735
|
+
* Clear cache for a specific company
|
|
53736
|
+
*/
|
|
53737
|
+
clearCache(companyId) {
|
|
53738
|
+
this.cache.delete(companyId);
|
|
53739
|
+
this.cacheExpiry.delete(companyId);
|
|
53740
|
+
}
|
|
53741
|
+
/**
|
|
53742
|
+
* Clear all cache
|
|
53743
|
+
*/
|
|
53744
|
+
clearAllCache() {
|
|
53745
|
+
this.cache.clear();
|
|
53746
|
+
this.cacheExpiry.clear();
|
|
53747
|
+
}
|
|
53748
|
+
/**
|
|
53749
|
+
* Validate legend configuration
|
|
53750
|
+
*/
|
|
53751
|
+
validateLegend(legend) {
|
|
53752
|
+
if (legend.green_min < 0 || legend.green_min > 100) {
|
|
53753
|
+
return { valid: false, error: "Green minimum must be between 0 and 100" };
|
|
53754
|
+
}
|
|
53755
|
+
if (legend.green_max < 0 || legend.green_max > 100) {
|
|
53756
|
+
return { valid: false, error: "Green maximum must be between 0 and 100" };
|
|
53757
|
+
}
|
|
53758
|
+
if (legend.yellow_min < 0 || legend.yellow_min > 100) {
|
|
53759
|
+
return { valid: false, error: "Yellow minimum must be between 0 and 100" };
|
|
53760
|
+
}
|
|
53761
|
+
if (legend.yellow_max < 0 || legend.yellow_max > 100) {
|
|
53762
|
+
return { valid: false, error: "Yellow maximum must be between 0 and 100" };
|
|
53763
|
+
}
|
|
53764
|
+
if (legend.red_min < 0 || legend.red_min > 100) {
|
|
53765
|
+
return { valid: false, error: "Red minimum must be between 0 and 100" };
|
|
53766
|
+
}
|
|
53767
|
+
if (legend.red_max < 0 || legend.red_max > 100) {
|
|
53768
|
+
return { valid: false, error: "Red maximum must be between 0 and 100" };
|
|
53769
|
+
}
|
|
53770
|
+
if (legend.critical_threshold < 0 || legend.critical_threshold > 100) {
|
|
53771
|
+
return { valid: false, error: "Critical threshold must be between 0 and 100" };
|
|
53772
|
+
}
|
|
53773
|
+
if (legend.yellow_min >= legend.green_min) {
|
|
53774
|
+
return { valid: false, error: "Yellow minimum must be less than green minimum" };
|
|
53775
|
+
}
|
|
53776
|
+
if (legend.red_max >= legend.yellow_min) {
|
|
53777
|
+
return { valid: false, error: "Red maximum must be less than yellow minimum" };
|
|
53778
|
+
}
|
|
53779
|
+
if (legend.critical_threshold > legend.red_max) {
|
|
53780
|
+
return { valid: false, error: "Critical threshold must be less than or equal to red maximum" };
|
|
53781
|
+
}
|
|
53782
|
+
return { valid: true };
|
|
53783
|
+
}
|
|
53784
|
+
normalizeLegend(legend) {
|
|
53785
|
+
const fallback = DEFAULT_EFFICIENCY_LEGEND;
|
|
53786
|
+
if (!legend) return fallback;
|
|
53787
|
+
const coerce = (value, fallbackValue) => {
|
|
53788
|
+
const num = Number(value);
|
|
53789
|
+
return Number.isFinite(num) ? num : fallbackValue;
|
|
53790
|
+
};
|
|
53791
|
+
return {
|
|
53792
|
+
green_min: coerce(legend.green_min, fallback.green_min),
|
|
53793
|
+
green_max: coerce(legend.green_max, fallback.green_max),
|
|
53794
|
+
yellow_min: coerce(legend.yellow_min, fallback.yellow_min),
|
|
53795
|
+
yellow_max: coerce(legend.yellow_max, fallback.yellow_max),
|
|
53796
|
+
red_min: coerce(legend.red_min, fallback.red_min),
|
|
53797
|
+
red_max: coerce(legend.red_max, fallback.red_max),
|
|
53798
|
+
critical_threshold: coerce(legend.critical_threshold, fallback.critical_threshold)
|
|
53799
|
+
};
|
|
53800
|
+
}
|
|
53801
|
+
};
|
|
53802
|
+
var efficiencyLegendServiceInstance = null;
|
|
53803
|
+
function getEfficiencyLegendService(supabase) {
|
|
53804
|
+
if (!efficiencyLegendServiceInstance) {
|
|
53805
|
+
efficiencyLegendServiceInstance = new EfficiencyLegendService(supabase);
|
|
53806
|
+
}
|
|
53807
|
+
return efficiencyLegendServiceInstance;
|
|
53808
|
+
}
|
|
53809
|
+
|
|
53810
|
+
// src/lib/hooks/useEfficiencyLegend.ts
|
|
53811
|
+
function useEfficiencyLegend(companyId) {
|
|
53812
|
+
const supabase = useSupabase();
|
|
53813
|
+
const config = useDashboardConfig();
|
|
53814
|
+
const [legend, setLegend] = React24.useState(DEFAULT_EFFICIENCY_LEGEND);
|
|
53815
|
+
const [isLoading, setIsLoading] = React24.useState(true);
|
|
53816
|
+
const [error, setError] = React24.useState(null);
|
|
53817
|
+
const effectiveCompanyId = companyId || config.entityConfig?.companyId;
|
|
53818
|
+
const fetchLegend = React24.useCallback(async () => {
|
|
53819
|
+
if (!supabase || !effectiveCompanyId) {
|
|
53820
|
+
setIsLoading(false);
|
|
53821
|
+
return;
|
|
53822
|
+
}
|
|
53823
|
+
try {
|
|
53824
|
+
setIsLoading(true);
|
|
53825
|
+
setError(null);
|
|
53826
|
+
const service = getEfficiencyLegendService(supabase);
|
|
53827
|
+
const fetchedLegend = await service.getEfficiencyLegend(effectiveCompanyId);
|
|
53828
|
+
setLegend(fetchedLegend);
|
|
53829
|
+
} catch (err) {
|
|
53830
|
+
console.error("Error fetching efficiency legend:", err);
|
|
53831
|
+
setError(err.message || "Failed to fetch efficiency legend");
|
|
53832
|
+
setLegend(DEFAULT_EFFICIENCY_LEGEND);
|
|
53833
|
+
} finally {
|
|
53834
|
+
setIsLoading(false);
|
|
53835
|
+
}
|
|
53836
|
+
}, [supabase, effectiveCompanyId]);
|
|
53837
|
+
React24.useEffect(() => {
|
|
53838
|
+
fetchLegend();
|
|
53839
|
+
}, [fetchLegend]);
|
|
53840
|
+
const updateLegend = React24.useCallback(async (newLegend) => {
|
|
53841
|
+
if (!supabase || !effectiveCompanyId) {
|
|
53842
|
+
return { success: false, error: "Supabase client or company ID not available" };
|
|
53843
|
+
}
|
|
53844
|
+
try {
|
|
53845
|
+
const service = getEfficiencyLegendService(supabase);
|
|
53846
|
+
const result = await service.updateEfficiencyLegend(effectiveCompanyId, newLegend);
|
|
53847
|
+
if (result.success) {
|
|
53848
|
+
setLegend(newLegend);
|
|
53849
|
+
setError(null);
|
|
53850
|
+
} else {
|
|
53851
|
+
setError(result.error || "Failed to update efficiency legend");
|
|
53852
|
+
}
|
|
53853
|
+
return result;
|
|
53854
|
+
} catch (err) {
|
|
53855
|
+
const errorMsg = err.message || "Failed to update efficiency legend";
|
|
53856
|
+
setError(errorMsg);
|
|
53857
|
+
return { success: false, error: errorMsg };
|
|
53858
|
+
}
|
|
53859
|
+
}, [supabase, effectiveCompanyId]);
|
|
53860
|
+
return {
|
|
53861
|
+
legend,
|
|
53862
|
+
isLoading,
|
|
53863
|
+
error,
|
|
53864
|
+
updateLegend,
|
|
53865
|
+
refetch: fetchLegend
|
|
53866
|
+
};
|
|
53867
|
+
}
|
|
53868
|
+
|
|
53213
53869
|
// src/views/TargetsView.utils.ts
|
|
53214
53870
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
53215
53871
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
@@ -53236,369 +53892,245 @@ var getStoredLineState2 = (lineId) => {
|
|
|
53236
53892
|
return false;
|
|
53237
53893
|
}
|
|
53238
53894
|
};
|
|
53239
|
-
var
|
|
53895
|
+
var ConfigureLegendModal = ({
|
|
53240
53896
|
isOpen,
|
|
53241
53897
|
onClose,
|
|
53242
|
-
lineWorkspaces,
|
|
53243
|
-
lineNames,
|
|
53244
53898
|
onSave,
|
|
53245
|
-
|
|
53899
|
+
legend,
|
|
53900
|
+
canSave = true
|
|
53246
53901
|
}) => {
|
|
53247
|
-
const
|
|
53248
|
-
const [
|
|
53249
|
-
const [
|
|
53250
|
-
const [
|
|
53251
|
-
const [
|
|
53252
|
-
const [targetDayOutput, setTargetDayOutput] = React24.useState("");
|
|
53253
|
-
const [productId, setProductId] = React24.useState("");
|
|
53254
|
-
const [selectedCount, setSelectedCount] = React24.useState(0);
|
|
53255
|
-
const shiftHours = React24.useMemo(() => {
|
|
53256
|
-
if (selectedLine && lineWorkspaces[selectedLine]) {
|
|
53257
|
-
return lineWorkspaces[selectedLine].shiftHours;
|
|
53258
|
-
}
|
|
53259
|
-
return 8;
|
|
53260
|
-
}, [selectedLine, lineWorkspaces]);
|
|
53261
|
-
const selectedLineBreaks = React24.useMemo(() => {
|
|
53262
|
-
if (selectedLine && lineWorkspaces[selectedLine]) {
|
|
53263
|
-
return lineWorkspaces[selectedLine].breaks;
|
|
53264
|
-
}
|
|
53265
|
-
return [];
|
|
53266
|
-
}, [selectedLine, lineWorkspaces]);
|
|
53267
|
-
React24.useEffect(() => {
|
|
53268
|
-
if (!isOpen) {
|
|
53269
|
-
setTimeout(() => {
|
|
53270
|
-
setSelectedLine("");
|
|
53271
|
-
setSelectedWorkspaces([]);
|
|
53272
|
-
setActionType("assembly");
|
|
53273
|
-
setTargetPPH("");
|
|
53274
|
-
setTargetCycleTime("");
|
|
53275
|
-
setTargetDayOutput("");
|
|
53276
|
-
setProductId("");
|
|
53277
|
-
}, 200);
|
|
53278
|
-
}
|
|
53279
|
-
}, [isOpen]);
|
|
53902
|
+
const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
|
|
53903
|
+
const [redThreshold, setRedThreshold] = React24.useState(effectiveLegend.yellow_min);
|
|
53904
|
+
const [greenThreshold, setGreenThreshold] = React24.useState(effectiveLegend.green_min);
|
|
53905
|
+
const [isSaving, setIsSaving] = React24.useState(false);
|
|
53906
|
+
const [saveSuccess, setSaveSuccess] = React24.useState(false);
|
|
53280
53907
|
React24.useEffect(() => {
|
|
53281
|
-
if (
|
|
53282
|
-
|
|
53908
|
+
if (isOpen) {
|
|
53909
|
+
setRedThreshold(effectiveLegend.yellow_min);
|
|
53910
|
+
setGreenThreshold(effectiveLegend.green_min);
|
|
53911
|
+
setSaveSuccess(false);
|
|
53283
53912
|
}
|
|
53284
|
-
}, [
|
|
53285
|
-
React24.useEffect(() => {
|
|
53286
|
-
setSelectedCount(selectedWorkspaces.length);
|
|
53287
|
-
}, [selectedWorkspaces]);
|
|
53913
|
+
}, [isOpen, effectiveLegend]);
|
|
53288
53914
|
const handleSave = async () => {
|
|
53289
|
-
if (
|
|
53290
|
-
sonner.toast.error("
|
|
53915
|
+
if (!canSave) {
|
|
53916
|
+
sonner.toast.error("You do not have permission to update the legend.");
|
|
53291
53917
|
return;
|
|
53292
53918
|
}
|
|
53293
|
-
if (
|
|
53294
|
-
sonner.toast.error("Please enter
|
|
53919
|
+
if (redThreshold === "" || greenThreshold === "") {
|
|
53920
|
+
sonner.toast.error("Please enter valid threshold values");
|
|
53295
53921
|
return;
|
|
53296
53922
|
}
|
|
53297
|
-
if (
|
|
53298
|
-
sonner.toast.error("
|
|
53923
|
+
if (Number(redThreshold) >= Number(greenThreshold)) {
|
|
53924
|
+
sonner.toast.error("Red threshold must be lower than Green threshold");
|
|
53299
53925
|
return;
|
|
53300
53926
|
}
|
|
53301
|
-
|
|
53302
|
-
|
|
53303
|
-
|
|
53304
|
-
...targetCycleTime !== "" && { targetCycleTime },
|
|
53305
|
-
...targetDayOutput !== "" && { targetDayOutput }
|
|
53306
|
-
};
|
|
53307
|
-
if (targetCycleTime !== "" && targetPPH === "") {
|
|
53308
|
-
const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
|
|
53309
|
-
const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
|
|
53310
|
-
const calculatedPPH = calculatePPH(targetCycleTime, lineBreaks, lineShiftHours);
|
|
53311
|
-
if (calculatedPPH !== "") {
|
|
53312
|
-
updates.targetPPH = calculatedPPH;
|
|
53313
|
-
}
|
|
53314
|
-
}
|
|
53315
|
-
if (updates.targetPPH !== void 0 && targetDayOutput === "") {
|
|
53316
|
-
const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
|
|
53317
|
-
const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
|
|
53318
|
-
const calculatedOutput = calculateDayOutput(updates.targetPPH, lineShiftHours, lineBreaks);
|
|
53319
|
-
if (calculatedOutput !== "") {
|
|
53320
|
-
updates.targetDayOutput = calculatedOutput;
|
|
53321
|
-
}
|
|
53322
|
-
}
|
|
53323
|
-
onSave({
|
|
53324
|
-
lineId: selectedLine,
|
|
53325
|
-
workspaceIds: selectedWorkspaces,
|
|
53326
|
-
productId,
|
|
53327
|
-
updates
|
|
53328
|
-
});
|
|
53329
|
-
onClose();
|
|
53330
|
-
};
|
|
53331
|
-
const toggleAllWorkspaces = (lineId) => {
|
|
53332
|
-
if (!lineWorkspaces[lineId]) return;
|
|
53333
|
-
const allWorkspaceIds = lineWorkspaces[lineId].workspaces.map((w) => w.id);
|
|
53334
|
-
if (selectedWorkspaces.length === allWorkspaceIds.length) {
|
|
53335
|
-
setSelectedWorkspaces([]);
|
|
53336
|
-
} else {
|
|
53337
|
-
setSelectedWorkspaces(allWorkspaceIds);
|
|
53927
|
+
if (Number(redThreshold) < 1 || Number(redThreshold) > 100 || Number(greenThreshold) > 100) {
|
|
53928
|
+
sonner.toast.error("Thresholds must be between 1 and 100");
|
|
53929
|
+
return;
|
|
53338
53930
|
}
|
|
53339
|
-
|
|
53340
|
-
|
|
53341
|
-
|
|
53342
|
-
|
|
53343
|
-
|
|
53931
|
+
const redMax = Number(redThreshold) - 1;
|
|
53932
|
+
const yellowMax = Number(greenThreshold) - 1;
|
|
53933
|
+
if (redMax < DEFAULT_EFFICIENCY_LEGEND.critical_threshold) {
|
|
53934
|
+
sonner.toast.error("Red threshold must be above 50% while critical alerts are fixed at <50%.");
|
|
53935
|
+
return;
|
|
53936
|
+
}
|
|
53937
|
+
setIsSaving(true);
|
|
53938
|
+
setSaveSuccess(false);
|
|
53939
|
+
try {
|
|
53940
|
+
const result = await onSave({
|
|
53941
|
+
green_min: Number(greenThreshold),
|
|
53942
|
+
green_max: 100,
|
|
53943
|
+
yellow_min: Number(redThreshold),
|
|
53944
|
+
yellow_max: yellowMax,
|
|
53945
|
+
red_min: 0,
|
|
53946
|
+
red_max: redMax,
|
|
53947
|
+
critical_threshold: DEFAULT_EFFICIENCY_LEGEND.critical_threshold
|
|
53948
|
+
});
|
|
53949
|
+
if (result.success) {
|
|
53950
|
+
setSaveSuccess(true);
|
|
53951
|
+
setTimeout(() => setSaveSuccess(false), 3e3);
|
|
53952
|
+
} else {
|
|
53953
|
+
sonner.toast.error(result.error || "Failed to update legend");
|
|
53344
53954
|
}
|
|
53345
|
-
}
|
|
53346
|
-
|
|
53347
|
-
|
|
53955
|
+
} catch (error) {
|
|
53956
|
+
console.error("Error saving legend:", error);
|
|
53957
|
+
sonner.toast.error("Failed to update legend");
|
|
53958
|
+
} finally {
|
|
53959
|
+
setIsSaving(false);
|
|
53348
53960
|
}
|
|
53349
|
-
|
|
53350
|
-
document.removeEventListener("keydown", handleEscape);
|
|
53351
|
-
};
|
|
53352
|
-
}, [isOpen, onClose]);
|
|
53961
|
+
};
|
|
53353
53962
|
if (!isOpen) return null;
|
|
53354
53963
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
53355
53964
|
"div",
|
|
53356
53965
|
{
|
|
53357
|
-
className: "fixed inset-0 bg-gray-900/
|
|
53966
|
+
className: "fixed inset-0 bg-gray-900/40 backdrop-blur-sm z-50 flex items-center justify-center transition-opacity duration-300",
|
|
53358
53967
|
onClick: (e) => {
|
|
53359
53968
|
if (e.target === e.currentTarget) {
|
|
53360
53969
|
onClose();
|
|
53361
53970
|
}
|
|
53362
53971
|
},
|
|
53363
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-
|
|
53364
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-
|
|
53972
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-xl w-full max-w-xl flex flex-col animate-modal-in transform transition-all", children: [
|
|
53973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-5 border-b border-gray-100 flex items-center justify-between bg-white rounded-t-lg", children: [
|
|
53365
53974
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
53366
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-
|
|
53367
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "
|
|
53975
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-gray-900", children: "Performance Thresholds" }),
|
|
53976
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Define acceptance criteria for production metrics." })
|
|
53368
53977
|
] }),
|
|
53369
53978
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53370
53979
|
"button",
|
|
53371
53980
|
{
|
|
53372
53981
|
onClick: onClose,
|
|
53373
|
-
className: "text-gray-400 hover:text-gray-
|
|
53374
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-
|
|
53982
|
+
className: "text-gray-400 hover:text-gray-600 transition-colors p-1.5 hover:bg-gray-50 rounded-md",
|
|
53983
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
53375
53984
|
}
|
|
53376
53985
|
)
|
|
53377
53986
|
] }),
|
|
53378
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-
|
|
53379
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
|
|
53380
|
-
|
|
53381
|
-
|
|
53382
|
-
|
|
53383
|
-
|
|
53384
|
-
|
|
53385
|
-
|
|
53386
|
-
|
|
53387
|
-
|
|
53388
|
-
|
|
53389
|
-
|
|
53390
|
-
|
|
53391
|
-
|
|
53392
|
-
|
|
53393
|
-
|
|
53394
|
-
|
|
53395
|
-
|
|
53396
|
-
|
|
53397
|
-
|
|
53398
|
-
|
|
53399
|
-
|
|
53400
|
-
|
|
53401
|
-
|
|
53402
|
-
|
|
53403
|
-
{
|
|
53404
|
-
|
|
53405
|
-
|
|
53406
|
-
|
|
53407
|
-
|
|
53408
|
-
|
|
53409
|
-
|
|
53410
|
-
|
|
53411
|
-
|
|
53412
|
-
|
|
53413
|
-
|
|
53414
|
-
|
|
53415
|
-
|
|
53416
|
-
|
|
53417
|
-
|
|
53418
|
-
|
|
53419
|
-
|
|
53420
|
-
|
|
53421
|
-
|
|
53422
|
-
|
|
53423
|
-
|
|
53424
|
-
|
|
53425
|
-
|
|
53426
|
-
|
|
53427
|
-
|
|
53428
|
-
|
|
53429
|
-
}
|
|
53430
|
-
|
|
53431
|
-
|
|
53432
|
-
|
|
53433
|
-
|
|
53434
|
-
|
|
53435
|
-
|
|
53436
|
-
children: [
|
|
53437
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53438
|
-
"input",
|
|
53439
|
-
{
|
|
53440
|
-
type: "checkbox",
|
|
53441
|
-
checked: selectedWorkspaces.includes(workspace.id),
|
|
53442
|
-
onChange: (e) => {
|
|
53443
|
-
if (e.target.checked) {
|
|
53444
|
-
setSelectedWorkspaces((prev) => [...prev, workspace.id]);
|
|
53445
|
-
} else {
|
|
53446
|
-
setSelectedWorkspaces((prev) => prev.filter((id3) => id3 !== workspace.id));
|
|
53447
|
-
}
|
|
53448
|
-
},
|
|
53449
|
-
className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
53450
|
-
}
|
|
53451
|
-
),
|
|
53452
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${selectedWorkspaces.includes(workspace.id) ? "text-blue-900 font-medium" : "text-gray-900"}`, children: formatWorkspaceName(workspace.name, selectedLine) })
|
|
53453
|
-
]
|
|
53454
|
-
},
|
|
53455
|
-
workspace.id
|
|
53456
|
-
)) })
|
|
53457
|
-
] }),
|
|
53458
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
53459
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Action Type" }),
|
|
53460
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
53461
|
-
"select",
|
|
53462
|
-
{
|
|
53463
|
-
value: actionType,
|
|
53464
|
-
onChange: (e) => setActionType(e.target.value),
|
|
53465
|
-
className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-blue-400\n appearance-none bg-no-repeat bg-right pr-10\n hover:bg-blue-50/50",
|
|
53466
|
-
children: [
|
|
53467
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
|
|
53468
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
|
|
53469
|
-
]
|
|
53470
|
-
}
|
|
53471
|
-
)
|
|
53472
|
-
] })
|
|
53987
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 space-y-8", children: [
|
|
53988
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 pt-6", children: [
|
|
53989
|
+
" ",
|
|
53990
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-6 w-full rounded-md bg-gray-100 relative overflow-visible ring-1 ring-black/5 flex mt-2", children: [
|
|
53991
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53992
|
+
"div",
|
|
53993
|
+
{
|
|
53994
|
+
className: "h-full bg-red-500 transition-all duration-500 ease-in-out relative group first:rounded-l-md",
|
|
53995
|
+
style: { width: `${Math.min(Number(redThreshold) || 0, 100)}%` },
|
|
53996
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
|
|
53997
|
+
}
|
|
53998
|
+
),
|
|
53999
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54000
|
+
"div",
|
|
54001
|
+
{
|
|
54002
|
+
className: "h-full bg-yellow-400 transition-all duration-500 ease-in-out relative group",
|
|
54003
|
+
style: { width: `${Math.max(0, Math.min((Number(greenThreshold) || 0) - (Number(redThreshold) || 0), 100 - (Number(redThreshold) || 0)))}%` },
|
|
54004
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
|
|
54005
|
+
}
|
|
54006
|
+
),
|
|
54007
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54008
|
+
"div",
|
|
54009
|
+
{
|
|
54010
|
+
className: "h-full bg-green-500 transition-all duration-500 ease-in-out relative group last:rounded-r-md",
|
|
54011
|
+
style: { width: `${Math.max(0, 100 - (Number(greenThreshold) || 0))}%` },
|
|
54012
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
|
|
54013
|
+
}
|
|
54014
|
+
),
|
|
54015
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54016
|
+
"div",
|
|
54017
|
+
{
|
|
54018
|
+
className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
|
|
54019
|
+
style: { left: `${Math.min(Number(redThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
|
|
54020
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
54021
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-gray-900 bg-white px-1.5 py-0.5 rounded shadow-sm border border-gray-200 mb-1 whitespace-nowrap", children: [
|
|
54022
|
+
redThreshold,
|
|
54023
|
+
"%"
|
|
54024
|
+
] }),
|
|
54025
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-gray-400/50" })
|
|
54026
|
+
] })
|
|
54027
|
+
}
|
|
54028
|
+
),
|
|
54029
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54030
|
+
"div",
|
|
54031
|
+
{
|
|
54032
|
+
className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
|
|
54033
|
+
style: { left: `${Math.min(Number(greenThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
|
|
54034
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
54035
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-gray-900 bg-white px-1.5 py-0.5 rounded shadow-sm border border-gray-200 mb-1 whitespace-nowrap", children: [
|
|
54036
|
+
greenThreshold,
|
|
54037
|
+
"%"
|
|
54038
|
+
] }),
|
|
54039
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-gray-400/50" })
|
|
54040
|
+
] })
|
|
54041
|
+
}
|
|
54042
|
+
),
|
|
54043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 bottom-0 w-px bg-white/50 z-10 pointer-events-none", style: { left: `${Math.min(Number(redThreshold) || 0, 100)}%` } }),
|
|
54044
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 bottom-0 w-px bg-white/50 z-10 pointer-events-none", style: { left: `${Math.min(Number(greenThreshold) || 0, 100)}%` } })
|
|
53473
54045
|
] }),
|
|
53474
|
-
/* @__PURE__ */ jsxRuntime.
|
|
53475
|
-
/* @__PURE__ */ jsxRuntime.
|
|
53476
|
-
|
|
53477
|
-
|
|
53478
|
-
|
|
53479
|
-
|
|
53480
|
-
|
|
54046
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-xs font-medium text-gray-400 pt-1", children: [
|
|
54047
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "0%" }),
|
|
54048
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "100%+" })
|
|
54049
|
+
] })
|
|
54050
|
+
] }),
|
|
54051
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-8 relative", children: [
|
|
54052
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-gray-300", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-5 h-5" }) }),
|
|
54053
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
54054
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Critical Limit" }),
|
|
54055
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
54056
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-red-500 mr-2.5" }),
|
|
54057
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
|
|
53481
54058
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53482
54059
|
"input",
|
|
53483
54060
|
{
|
|
53484
54061
|
type: "number",
|
|
53485
|
-
value:
|
|
54062
|
+
value: redThreshold,
|
|
53486
54063
|
onChange: (e) => {
|
|
53487
|
-
|
|
53488
|
-
|
|
53489
|
-
if (newValue !== "" && newValue > 0) {
|
|
53490
|
-
const pph = calculatePPH(newValue, selectedLineBreaks, shiftHours);
|
|
53491
|
-
setTargetPPH(pph);
|
|
53492
|
-
const dayOutput = calculateDayOutput(pph, shiftHours, selectedLineBreaks);
|
|
53493
|
-
setTargetDayOutput(dayOutput);
|
|
53494
|
-
} else if (newValue === "") {
|
|
53495
|
-
setTargetPPH("");
|
|
53496
|
-
setTargetDayOutput("");
|
|
53497
|
-
}
|
|
54064
|
+
setRedThreshold(e.target.value === "" ? "" : Number(e.target.value));
|
|
54065
|
+
setSaveSuccess(false);
|
|
53498
54066
|
},
|
|
53499
|
-
|
|
53500
|
-
|
|
53501
|
-
|
|
53502
|
-
placeholder: "
|
|
54067
|
+
min: 1,
|
|
54068
|
+
max: 100,
|
|
54069
|
+
className: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2 px-3 bg-white no-spinner",
|
|
54070
|
+
placeholder: "70"
|
|
53503
54071
|
}
|
|
53504
54072
|
),
|
|
53505
|
-
|
|
53506
|
-
"button",
|
|
53507
|
-
{
|
|
53508
|
-
onClick: () => {
|
|
53509
|
-
setTargetCycleTime("");
|
|
53510
|
-
setTargetPPH("");
|
|
53511
|
-
setTargetDayOutput("");
|
|
53512
|
-
},
|
|
53513
|
-
className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
|
|
53514
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
53515
|
-
}
|
|
53516
|
-
)
|
|
54073
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
|
|
53517
54074
|
] })
|
|
53518
54075
|
] }),
|
|
53519
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
53520
|
-
|
|
53521
|
-
|
|
53522
|
-
|
|
54076
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
|
|
54077
|
+
"Efficiency below ",
|
|
54078
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-gray-900", children: [
|
|
54079
|
+
redThreshold,
|
|
54080
|
+
"%"
|
|
53523
54081
|
] }),
|
|
53524
|
-
|
|
54082
|
+
" will show in ",
|
|
54083
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-red-600", children: "red" }),
|
|
54084
|
+
"."
|
|
54085
|
+
] })
|
|
54086
|
+
] }),
|
|
54087
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
54088
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Target Limit" }),
|
|
54089
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
54090
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 mr-2.5" }),
|
|
54091
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
|
|
53525
54092
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53526
54093
|
"input",
|
|
53527
54094
|
{
|
|
53528
54095
|
type: "number",
|
|
53529
|
-
value:
|
|
54096
|
+
value: greenThreshold,
|
|
53530
54097
|
onChange: (e) => {
|
|
53531
|
-
|
|
53532
|
-
|
|
53533
|
-
if (newValue !== "") {
|
|
53534
|
-
const dayOutput = calculateDayOutput(newValue, shiftHours, selectedLineBreaks);
|
|
53535
|
-
setTargetDayOutput(dayOutput);
|
|
53536
|
-
}
|
|
54098
|
+
setGreenThreshold(e.target.value === "" ? "" : Number(e.target.value));
|
|
54099
|
+
setSaveSuccess(false);
|
|
53537
54100
|
},
|
|
53538
|
-
|
|
53539
|
-
|
|
53540
|
-
|
|
53541
|
-
placeholder: "
|
|
54101
|
+
min: 1,
|
|
54102
|
+
max: 100,
|
|
54103
|
+
className: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2 px-3 bg-white no-spinner",
|
|
54104
|
+
placeholder: "80"
|
|
53542
54105
|
}
|
|
53543
54106
|
),
|
|
53544
|
-
|
|
53545
|
-
"button",
|
|
53546
|
-
{
|
|
53547
|
-
onClick: () => {
|
|
53548
|
-
setTargetPPH("");
|
|
53549
|
-
setTargetDayOutput("");
|
|
53550
|
-
},
|
|
53551
|
-
className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
|
|
53552
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
53553
|
-
}
|
|
53554
|
-
)
|
|
54107
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
|
|
53555
54108
|
] })
|
|
53556
54109
|
] }),
|
|
53557
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
53558
|
-
|
|
53559
|
-
|
|
53560
|
-
|
|
54110
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
|
|
54111
|
+
"Efficiency above ",
|
|
54112
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-gray-900", children: [
|
|
54113
|
+
greenThreshold,
|
|
54114
|
+
"%"
|
|
53561
54115
|
] }),
|
|
53562
|
-
|
|
53563
|
-
|
|
53564
|
-
|
|
53565
|
-
{
|
|
53566
|
-
type: "number",
|
|
53567
|
-
value: targetDayOutput,
|
|
53568
|
-
onChange: (e) => setTargetDayOutput(e.target.value ? Number(e.target.value) : ""),
|
|
53569
|
-
className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-blue-400",
|
|
53570
|
-
min: "0",
|
|
53571
|
-
step: "1",
|
|
53572
|
-
placeholder: "Enter day output"
|
|
53573
|
-
}
|
|
53574
|
-
),
|
|
53575
|
-
targetDayOutput !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
53576
|
-
"button",
|
|
53577
|
-
{
|
|
53578
|
-
onClick: () => setTargetDayOutput(""),
|
|
53579
|
-
className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
|
|
53580
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
53581
|
-
}
|
|
53582
|
-
)
|
|
53583
|
-
] })
|
|
54116
|
+
" will show in ",
|
|
54117
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-green-600", children: "green" }),
|
|
54118
|
+
"."
|
|
53584
54119
|
] })
|
|
53585
|
-
] })
|
|
53586
|
-
] })
|
|
53587
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-white to-transparent pointer-events-none" })
|
|
54120
|
+
] })
|
|
54121
|
+
] })
|
|
53588
54122
|
] }),
|
|
53589
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-
|
|
53590
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
53591
|
-
"
|
|
53592
|
-
|
|
53593
|
-
" workspace",
|
|
53594
|
-
selectedCount !== 1 ? "s" : ""
|
|
54123
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-100 flex items-center justify-between gap-3 rounded-b-lg", children: [
|
|
54124
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[20px]", children: saveSuccess && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-green-600 font-medium flex items-center gap-1.5", children: [
|
|
54125
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "w-4 h-4" }),
|
|
54126
|
+
"Saved successfully"
|
|
53595
54127
|
] }) }),
|
|
53596
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
54128
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
53597
54129
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
53598
54130
|
"button",
|
|
53599
54131
|
{
|
|
53600
54132
|
onClick: onClose,
|
|
53601
|
-
className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300
|
|
54133
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 shadow-sm transition-all",
|
|
53602
54134
|
children: "Cancel"
|
|
53603
54135
|
}
|
|
53604
54136
|
),
|
|
@@ -53606,10 +54138,9 @@ var BulkConfigureModal = ({
|
|
|
53606
54138
|
"button",
|
|
53607
54139
|
{
|
|
53608
54140
|
onClick: handleSave,
|
|
53609
|
-
disabled: !
|
|
53610
|
-
className:
|
|
53611
|
-
|
|
53612
|
-
children: "Apply Changes"
|
|
54141
|
+
disabled: !canSave || isSaving,
|
|
54142
|
+
className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 shadow-sm transition-all disabled:bg-gray-300 disabled:cursor-not-allowed",
|
|
54143
|
+
children: isSaving ? "Saving..." : "Save Changes"
|
|
53613
54144
|
}
|
|
53614
54145
|
)
|
|
53615
54146
|
] })
|
|
@@ -53618,7 +54149,7 @@ var BulkConfigureModal = ({
|
|
|
53618
54149
|
}
|
|
53619
54150
|
);
|
|
53620
54151
|
};
|
|
53621
|
-
var
|
|
54152
|
+
var ConfigureLegendModal_default = ConfigureLegendModal;
|
|
53622
54153
|
var SKUModal = ({
|
|
53623
54154
|
isOpen,
|
|
53624
54155
|
onClose,
|
|
@@ -54019,7 +54550,8 @@ var TargetsViewUI = ({
|
|
|
54019
54550
|
{ id: 0, name: "Day Shift" },
|
|
54020
54551
|
{ id: 1, name: "Night Shift" }
|
|
54021
54552
|
],
|
|
54022
|
-
|
|
54553
|
+
isConfigureLegendOpen,
|
|
54554
|
+
legend,
|
|
54023
54555
|
navItems = [],
|
|
54024
54556
|
currentPathname = "/targets",
|
|
54025
54557
|
canSaveTargets = true,
|
|
@@ -54030,8 +54562,8 @@ var TargetsViewUI = ({
|
|
|
54030
54562
|
onActionTypeChange,
|
|
54031
54563
|
onShiftChange,
|
|
54032
54564
|
onSaveLine,
|
|
54033
|
-
|
|
54034
|
-
|
|
54565
|
+
onToggleConfigureLegend,
|
|
54566
|
+
onSaveLegend,
|
|
54035
54567
|
onUpdateWorkspaceDisplayName,
|
|
54036
54568
|
// SKU props
|
|
54037
54569
|
skuEnabled = false,
|
|
@@ -54057,11 +54589,13 @@ var TargetsViewUI = ({
|
|
|
54057
54589
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:absolute sm:right-0 w-full sm:w-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
54058
54590
|
"button",
|
|
54059
54591
|
{
|
|
54060
|
-
className:
|
|
54061
|
-
onClick:
|
|
54592
|
+
className: `w-full sm:w-auto px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm font-medium rounded-lg transition-all duration-200 ${canSaveTargets ? "text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" : "text-gray-400 bg-gray-100 border border-gray-200 cursor-not-allowed"}`,
|
|
54593
|
+
onClick: onToggleConfigureLegend,
|
|
54594
|
+
disabled: !canSaveTargets,
|
|
54595
|
+
title: !canSaveTargets ? "You do not have permission to update the legend" : "",
|
|
54062
54596
|
children: [
|
|
54063
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
54064
|
-
"
|
|
54597
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Palette, { className: "w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 inline-block" }),
|
|
54598
|
+
"Configure Legend"
|
|
54065
54599
|
]
|
|
54066
54600
|
}
|
|
54067
54601
|
) }),
|
|
@@ -54363,14 +54897,13 @@ var TargetsViewUI = ({
|
|
|
54363
54897
|
)) })
|
|
54364
54898
|
] }) }),
|
|
54365
54899
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
54366
|
-
|
|
54900
|
+
ConfigureLegendModal_default,
|
|
54367
54901
|
{
|
|
54368
|
-
isOpen:
|
|
54369
|
-
onClose:
|
|
54370
|
-
|
|
54371
|
-
|
|
54372
|
-
|
|
54373
|
-
selectedShift
|
|
54902
|
+
isOpen: isConfigureLegendOpen,
|
|
54903
|
+
onClose: onToggleConfigureLegend,
|
|
54904
|
+
onSave: onSaveLegend,
|
|
54905
|
+
legend,
|
|
54906
|
+
canSave: canSaveTargets
|
|
54374
54907
|
}
|
|
54375
54908
|
)
|
|
54376
54909
|
] });
|
|
@@ -54416,7 +54949,7 @@ var TargetsView = ({
|
|
|
54416
54949
|
() => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: false }), {})
|
|
54417
54950
|
);
|
|
54418
54951
|
const [isLoading, setIsLoading] = React24.useState(true);
|
|
54419
|
-
const [
|
|
54952
|
+
const [isConfigureLegendOpen, setIsConfigureLegendOpen] = React24.useState(false);
|
|
54420
54953
|
const [selectedWorkspaces, setSelectedWorkspaces] = React24.useState([]);
|
|
54421
54954
|
const [selectedShift, setSelectedShift] = React24.useState(0);
|
|
54422
54955
|
const [shiftOptions, setShiftOptions] = React24.useState([]);
|
|
@@ -54433,6 +54966,7 @@ var TargetsView = ({
|
|
|
54433
54966
|
const supabase = useSupabase();
|
|
54434
54967
|
const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
54435
54968
|
const dashboardConfig = useDashboardConfig();
|
|
54969
|
+
const { legend, updateLegend } = useEfficiencyLegend(companyId);
|
|
54436
54970
|
const { skus, isLoading: skusLoading } = useSKUs(companyId);
|
|
54437
54971
|
const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
|
|
54438
54972
|
React24.useEffect(() => {
|
|
@@ -54868,41 +55402,23 @@ var TargetsView = ({
|
|
|
54868
55402
|
console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
|
|
54869
55403
|
}
|
|
54870
55404
|
}, [canSaveTargets, supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
|
|
54871
|
-
const
|
|
54872
|
-
if (!
|
|
54873
|
-
|
|
54874
|
-
|
|
54875
|
-
|
|
54876
|
-
|
|
54877
|
-
|
|
54878
|
-
|
|
54879
|
-
|
|
54880
|
-
|
|
55405
|
+
const handleSaveLegend = React24.useCallback(async (nextLegend) => {
|
|
55406
|
+
if (!canSaveTargets) {
|
|
55407
|
+
sonner.toast.error("You do not have permission to update the legend.");
|
|
55408
|
+
return { success: false, error: "Permission denied" };
|
|
55409
|
+
}
|
|
55410
|
+
const result = await updateLegend(nextLegend);
|
|
55411
|
+
if (result.success) {
|
|
55412
|
+
sonner.toast.success("Efficiency legend updated successfully");
|
|
55413
|
+
} else if (result.error) {
|
|
55414
|
+
sonner.toast.error(result.error);
|
|
55415
|
+
} else {
|
|
55416
|
+
sonner.toast.error("Failed to update efficiency legend");
|
|
54881
55417
|
}
|
|
54882
|
-
|
|
54883
|
-
|
|
54884
|
-
|
|
54885
|
-
|
|
54886
|
-
workspaces: prev[updates.lineId].workspaces.map((ws) => {
|
|
54887
|
-
if (!updates.workspaceIds.includes(ws.id)) return ws;
|
|
54888
|
-
return {
|
|
54889
|
-
...ws,
|
|
54890
|
-
...updates.updates.actionType && {
|
|
54891
|
-
actionType: updates.updates.actionType,
|
|
54892
|
-
actionId: actionIds[updates.updates.actionType]
|
|
54893
|
-
},
|
|
54894
|
-
...updates.updates.targetPPH !== void 0 && { targetPPH: updates.updates.targetPPH },
|
|
54895
|
-
...updates.updates.targetCycleTime !== void 0 && { targetCycleTime: updates.updates.targetCycleTime },
|
|
54896
|
-
...updates.updates.targetDayOutput !== void 0 && { targetDayOutput: updates.updates.targetDayOutput }
|
|
54897
|
-
};
|
|
54898
|
-
})
|
|
54899
|
-
}
|
|
54900
|
-
}));
|
|
54901
|
-
sonner.toast.success(`Updated ${updates.workspaceIds.length} workspaces in ${lineNames[updates.lineId] || updates.lineId}`);
|
|
54902
|
-
setIsBulkConfigureOpen(false);
|
|
54903
|
-
};
|
|
54904
|
-
const handleToggleBulkConfigure = () => {
|
|
54905
|
-
setIsBulkConfigureOpen((prev) => !prev);
|
|
55418
|
+
return result;
|
|
55419
|
+
}, [canSaveTargets, updateLegend]);
|
|
55420
|
+
const handleToggleConfigureLegend = () => {
|
|
55421
|
+
setIsConfigureLegendOpen((prev) => !prev);
|
|
54906
55422
|
};
|
|
54907
55423
|
const handleNavigateBack = () => {
|
|
54908
55424
|
if (router && router.push) {
|
|
@@ -54940,7 +55456,7 @@ var TargetsView = ({
|
|
|
54940
55456
|
saveSuccess,
|
|
54941
55457
|
selectedShift,
|
|
54942
55458
|
shiftOptions,
|
|
54943
|
-
|
|
55459
|
+
isConfigureLegendOpen,
|
|
54944
55460
|
navItems: [],
|
|
54945
55461
|
currentPathname: "/targets",
|
|
54946
55462
|
canSaveTargets,
|
|
@@ -54951,8 +55467,9 @@ var TargetsView = ({
|
|
|
54951
55467
|
onActionTypeChange: handleActionTypeChange,
|
|
54952
55468
|
onShiftChange: handleShiftChange,
|
|
54953
55469
|
onSaveLine: handleSaveLine,
|
|
54954
|
-
|
|
54955
|
-
|
|
55470
|
+
onToggleConfigureLegend: handleToggleConfigureLegend,
|
|
55471
|
+
onSaveLegend: handleSaveLegend,
|
|
55472
|
+
legend,
|
|
54956
55473
|
onUpdateWorkspaceDisplayName: handleUpdateWorkspaceDisplayName,
|
|
54957
55474
|
skuEnabled,
|
|
54958
55475
|
skus,
|
|
@@ -55333,6 +55850,20 @@ var WorkspaceDetailView = ({
|
|
|
55333
55850
|
}, [monthlyData, range]);
|
|
55334
55851
|
const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
|
|
55335
55852
|
const shouldShowCycleTimeChart = showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY");
|
|
55853
|
+
const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
|
|
55854
|
+
const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
|
|
55855
|
+
const idleClipFetchEnabled = Boolean(
|
|
55856
|
+
workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && showIdleTime && !shouldShowCycleTimeChart
|
|
55857
|
+
);
|
|
55858
|
+
const {
|
|
55859
|
+
idleClips: idleTimeClips,
|
|
55860
|
+
clipClassifications: idleTimeClipClassifications
|
|
55861
|
+
} = useIdleTimeClipClassifications({
|
|
55862
|
+
workspaceId,
|
|
55863
|
+
date: idleClipDate,
|
|
55864
|
+
shiftId: idleClipShiftId,
|
|
55865
|
+
enabled: idleClipFetchEnabled
|
|
55866
|
+
});
|
|
55336
55867
|
const handleBackNavigation = () => {
|
|
55337
55868
|
if (returnUrl) {
|
|
55338
55869
|
if (onNavigate) {
|
|
@@ -55719,7 +56250,11 @@ var WorkspaceDetailView = ({
|
|
|
55719
56250
|
shiftStart: workspace.shift_start || "06:00",
|
|
55720
56251
|
shiftEnd: workspace.shift_end,
|
|
55721
56252
|
showIdleTime,
|
|
55722
|
-
idleTimeHourly: workspace.idle_time_hourly
|
|
56253
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
56254
|
+
idleTimeClips,
|
|
56255
|
+
idleTimeClipClassifications,
|
|
56256
|
+
shiftDate: idleClipDate,
|
|
56257
|
+
timezone
|
|
55723
56258
|
}
|
|
55724
56259
|
)
|
|
55725
56260
|
}
|
|
@@ -55845,7 +56380,11 @@ var WorkspaceDetailView = ({
|
|
|
55845
56380
|
shiftStart: workspace.shift_start || "06:00",
|
|
55846
56381
|
shiftEnd: workspace.shift_end,
|
|
55847
56382
|
showIdleTime,
|
|
55848
|
-
idleTimeHourly: workspace.idle_time_hourly
|
|
56383
|
+
idleTimeHourly: workspace.idle_time_hourly,
|
|
56384
|
+
idleTimeClips,
|
|
56385
|
+
idleTimeClipClassifications,
|
|
56386
|
+
shiftDate: idleClipDate,
|
|
56387
|
+
timezone
|
|
55849
56388
|
}
|
|
55850
56389
|
)
|
|
55851
56390
|
}
|
|
@@ -58979,7 +59518,12 @@ var ImprovementCenterView = () => {
|
|
|
58979
59518
|
const { user } = useAuth();
|
|
58980
59519
|
const dashboardConfig = useDashboardConfig();
|
|
58981
59520
|
const entityConfig = useEntityConfig();
|
|
58982
|
-
const
|
|
59521
|
+
const timezone = useAppTimezone();
|
|
59522
|
+
const today = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
|
|
59523
|
+
const [selectedMonth, setSelectedMonth] = React24.useState(today.getMonth());
|
|
59524
|
+
const [selectedYear, setSelectedYear] = React24.useState(today.getFullYear());
|
|
59525
|
+
const monthBounds = React24.useMemo(() => getMonthKeyBounds(selectedYear, selectedMonth), [selectedYear, selectedMonth]);
|
|
59526
|
+
const [dateRange, setDateRange] = React24.useState(monthBounds);
|
|
58983
59527
|
const [selectedLineId, setSelectedLineId] = React24.useState("all");
|
|
58984
59528
|
const [selectedStatus, setSelectedStatus] = React24.useState("all");
|
|
58985
59529
|
const [selectedShift, setSelectedShift] = React24.useState("all");
|
|
@@ -58990,14 +59534,36 @@ var ImprovementCenterView = () => {
|
|
|
58990
59534
|
const [loadError, setLoadError] = React24.useState(null);
|
|
58991
59535
|
const [teamMembers, setTeamMembers] = React24.useState([]);
|
|
58992
59536
|
const [companyLines, setCompanyLines] = React24.useState([]);
|
|
59537
|
+
const [isFilterOpen, setIsFilterOpen] = React24.useState(false);
|
|
59538
|
+
const filterRef = React24.useRef(null);
|
|
59539
|
+
React24.useEffect(() => {
|
|
59540
|
+
const handleClickOutside = (event) => {
|
|
59541
|
+
if (filterRef.current && !filterRef.current.contains(event.target)) {
|
|
59542
|
+
setIsFilterOpen(false);
|
|
59543
|
+
}
|
|
59544
|
+
};
|
|
59545
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
59546
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
59547
|
+
}, []);
|
|
58993
59548
|
const computeWeeksOpen = (firstSeenAt) => {
|
|
58994
59549
|
if (!firstSeenAt) return void 0;
|
|
58995
|
-
const firstSeen = new Date(firstSeenAt);
|
|
59550
|
+
const firstSeen = new Date(new Date(firstSeenAt).toLocaleString("en-US", { timeZone: timezone }));
|
|
58996
59551
|
if (Number.isNaN(firstSeen.getTime())) return void 0;
|
|
58997
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
59552
|
+
const now2 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
|
|
58998
59553
|
const diffDays = Math.max(0, Math.floor((now2.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
58999
59554
|
return Math.max(1, Math.ceil(diffDays / 7));
|
|
59000
59555
|
};
|
|
59556
|
+
const formatOpenedDate = (firstSeenAt) => {
|
|
59557
|
+
if (!firstSeenAt) return void 0;
|
|
59558
|
+
const raw = new Date(firstSeenAt);
|
|
59559
|
+
if (Number.isNaN(raw.getTime())) return void 0;
|
|
59560
|
+
const zoned = new Date(raw.toLocaleString("en-US", { timeZone: timezone }));
|
|
59561
|
+
const day = zoned.getDate();
|
|
59562
|
+
const month = zoned.toLocaleString("en-US", { month: "short" });
|
|
59563
|
+
const dayMod = day % 100;
|
|
59564
|
+
const suffix = dayMod >= 11 && dayMod <= 13 ? "th" : day % 10 === 1 ? "st" : day % 10 === 2 ? "nd" : day % 10 === 3 ? "rd" : "th";
|
|
59565
|
+
return `${day}${suffix} ${month}`;
|
|
59566
|
+
};
|
|
59001
59567
|
const configuredLines = React24.useMemo(() => {
|
|
59002
59568
|
return entityConfig.lines || entityConfig.lineNames || {};
|
|
59003
59569
|
}, [entityConfig.lines, entityConfig.lineNames]);
|
|
@@ -59106,14 +59672,38 @@ var ImprovementCenterView = () => {
|
|
|
59106
59672
|
cancelled = true;
|
|
59107
59673
|
};
|
|
59108
59674
|
}, [supabase, companyId]);
|
|
59109
|
-
|
|
59110
|
-
|
|
59111
|
-
};
|
|
59112
|
-
const
|
|
59113
|
-
|
|
59675
|
+
React24.useEffect(() => {
|
|
59676
|
+
setDateRange(monthBounds);
|
|
59677
|
+
}, [monthBounds]);
|
|
59678
|
+
const handleRangeChange = (newRange) => {
|
|
59679
|
+
setDateRange(newRange);
|
|
59680
|
+
trackCoreEvent("Improvement Center Date Range Changed", {
|
|
59681
|
+
start_date: newRange.startKey,
|
|
59682
|
+
end_date: newRange.endKey
|
|
59683
|
+
});
|
|
59114
59684
|
};
|
|
59115
|
-
const
|
|
59116
|
-
|
|
59685
|
+
const handleMonthNavigate = (newMonth, newYear) => {
|
|
59686
|
+
let validMonth = newMonth;
|
|
59687
|
+
let validYear = newYear;
|
|
59688
|
+
if (validMonth < 0) {
|
|
59689
|
+
validMonth = 11;
|
|
59690
|
+
validYear -= 1;
|
|
59691
|
+
} else if (validMonth > 11) {
|
|
59692
|
+
validMonth = 0;
|
|
59693
|
+
validYear += 1;
|
|
59694
|
+
}
|
|
59695
|
+
if (validYear < 2023) return;
|
|
59696
|
+
if (validYear > today.getFullYear() || validYear === today.getFullYear() && validMonth > today.getMonth()) {
|
|
59697
|
+
return;
|
|
59698
|
+
}
|
|
59699
|
+
const nextBounds = getMonthKeyBounds(validYear, validMonth);
|
|
59700
|
+
setSelectedMonth(validMonth);
|
|
59701
|
+
setSelectedYear(validYear);
|
|
59702
|
+
setDateRange(nextBounds);
|
|
59703
|
+
trackCoreEvent("Improvement Center Month Changed", {
|
|
59704
|
+
direction: validMonth > selectedMonth || validYear > selectedYear ? "next" : "previous",
|
|
59705
|
+
new_month: `${validYear}-${String(validMonth + 1).padStart(2, "0")}`
|
|
59706
|
+
});
|
|
59117
59707
|
};
|
|
59118
59708
|
React24.useEffect(() => {
|
|
59119
59709
|
let cancelled = false;
|
|
@@ -59131,7 +59721,9 @@ var ImprovementCenterView = () => {
|
|
|
59131
59721
|
status: "all",
|
|
59132
59722
|
limit: "500"
|
|
59133
59723
|
});
|
|
59134
|
-
params.set("month", `${
|
|
59724
|
+
params.set("month", `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`);
|
|
59725
|
+
params.set("start_date", dateRange.startKey);
|
|
59726
|
+
params.set("end_date", dateRange.endKey);
|
|
59135
59727
|
if (scopeLineIds.length > 0) {
|
|
59136
59728
|
params.set("line_ids", scopeLineIds.join(","));
|
|
59137
59729
|
}
|
|
@@ -59162,7 +59754,7 @@ var ImprovementCenterView = () => {
|
|
|
59162
59754
|
return () => {
|
|
59163
59755
|
cancelled = true;
|
|
59164
59756
|
};
|
|
59165
|
-
}, [supabase, companyId, scopeLineIdsKey,
|
|
59757
|
+
}, [supabase, companyId, scopeLineIdsKey, selectedMonth, selectedYear, dateRange.startKey, dateRange.endKey]);
|
|
59166
59758
|
const teamMembersById = React24.useMemo(() => {
|
|
59167
59759
|
return new Map(teamMembers.map((member) => [member.id, member]));
|
|
59168
59760
|
}, [teamMembers]);
|
|
@@ -59204,6 +59796,15 @@ var ImprovementCenterView = () => {
|
|
|
59204
59796
|
};
|
|
59205
59797
|
}, [recommendations, selectedLineId, selectedShift, selectedWeeksRange, selectedMemberId]);
|
|
59206
59798
|
const clearFilters = () => {
|
|
59799
|
+
trackCoreEvent("Improvement Center Filters Cleared", {
|
|
59800
|
+
previous_filters: {
|
|
59801
|
+
line_id: selectedLineId,
|
|
59802
|
+
status: selectedStatus,
|
|
59803
|
+
shift: selectedShift,
|
|
59804
|
+
weeks_range: selectedWeeksRange,
|
|
59805
|
+
member_id: selectedMemberId
|
|
59806
|
+
}
|
|
59807
|
+
});
|
|
59207
59808
|
setSelectedLineId("all");
|
|
59208
59809
|
setSelectedStatus("all");
|
|
59209
59810
|
setSelectedShift("all");
|
|
@@ -59237,8 +59838,60 @@ var ImprovementCenterView = () => {
|
|
|
59237
59838
|
setSelectedLineId("all");
|
|
59238
59839
|
}
|
|
59239
59840
|
}, [scopeLineIds, selectedLineId]);
|
|
59841
|
+
React24.useEffect(() => {
|
|
59842
|
+
trackCoreEvent("Improvement Center Viewed", {
|
|
59843
|
+
company_id: companyId,
|
|
59844
|
+
user_role: user?.role_level,
|
|
59845
|
+
initial_month: `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`,
|
|
59846
|
+
total_scope_lines: scopeLineIds.length
|
|
59847
|
+
});
|
|
59848
|
+
}, []);
|
|
59849
|
+
const handleStatusFilterChange = (status) => {
|
|
59850
|
+
trackCoreEvent("Improvement Center Filter Applied", {
|
|
59851
|
+
filter_type: "status",
|
|
59852
|
+
filter_value: status,
|
|
59853
|
+
previous_value: selectedStatus
|
|
59854
|
+
});
|
|
59855
|
+
setSelectedStatus(status);
|
|
59856
|
+
};
|
|
59857
|
+
const handleShiftFilterChange = (shift) => {
|
|
59858
|
+
trackCoreEvent("Improvement Center Filter Applied", {
|
|
59859
|
+
filter_type: "shift",
|
|
59860
|
+
filter_value: shift,
|
|
59861
|
+
previous_value: selectedShift
|
|
59862
|
+
});
|
|
59863
|
+
setSelectedShift(shift);
|
|
59864
|
+
};
|
|
59865
|
+
const handleWeeksFilterChange = (weeksRange) => {
|
|
59866
|
+
trackCoreEvent("Improvement Center Filter Applied", {
|
|
59867
|
+
filter_type: "weeks_open",
|
|
59868
|
+
filter_value: weeksRange,
|
|
59869
|
+
previous_value: selectedWeeksRange
|
|
59870
|
+
});
|
|
59871
|
+
setSelectedWeeksRange(weeksRange);
|
|
59872
|
+
};
|
|
59873
|
+
const handleMemberFilterChange = (memberId) => {
|
|
59874
|
+
const memberName = memberId === "all" ? "all" : teamMembers.find((m) => m.id === memberId)?.name || memberId;
|
|
59875
|
+
trackCoreEvent("Improvement Center Filter Applied", {
|
|
59876
|
+
filter_type: "member",
|
|
59877
|
+
filter_value: memberName,
|
|
59878
|
+
member_id: memberId,
|
|
59879
|
+
previous_value: selectedMemberId
|
|
59880
|
+
});
|
|
59881
|
+
setSelectedMemberId(memberId);
|
|
59882
|
+
};
|
|
59883
|
+
const handleLineFilterChange = (lineId) => {
|
|
59884
|
+
const lineName = lineId === "all" ? "all" : lineNameById.get(lineId) || lineId;
|
|
59885
|
+
trackCoreEvent("Improvement Center Filter Applied", {
|
|
59886
|
+
filter_type: "line",
|
|
59887
|
+
filter_value: lineName,
|
|
59888
|
+
line_id: lineId,
|
|
59889
|
+
previous_value: selectedLineId
|
|
59890
|
+
});
|
|
59891
|
+
setSelectedLineId(lineId);
|
|
59892
|
+
};
|
|
59240
59893
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
|
|
59241
|
-
/* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-
|
|
59894
|
+
/* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-30 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
59242
59895
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 min-w-[120px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
59243
59896
|
BackButtonMinimal,
|
|
59244
59897
|
{
|
|
@@ -59250,187 +59903,173 @@ var ImprovementCenterView = () => {
|
|
|
59250
59903
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Improvement Center" }),
|
|
59251
59904
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4 hidden sm:block", children: "Track and resolve persistent issues across your production lines" })
|
|
59252
59905
|
] }),
|
|
59253
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[
|
|
59906
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[180px] flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
59907
|
+
MonthlyRangeFilter_default,
|
|
59908
|
+
{
|
|
59909
|
+
month: selectedMonth,
|
|
59910
|
+
year: selectedYear,
|
|
59911
|
+
timezone,
|
|
59912
|
+
value: dateRange,
|
|
59913
|
+
onChange: handleRangeChange,
|
|
59914
|
+
onMonthNavigate: handleMonthNavigate
|
|
59915
|
+
}
|
|
59916
|
+
) })
|
|
59254
59917
|
] }) }) }),
|
|
59255
59918
|
/* @__PURE__ */ jsxRuntime.jsxs("main", { className: "flex-1 p-4 sm:p-6 max-w-7xl mx-auto w-full", children: [
|
|
59256
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59257
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
59258
|
-
"
|
|
59259
|
-
{
|
|
59260
|
-
|
|
59261
|
-
|
|
59262
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
|
|
59263
|
-
}
|
|
59264
|
-
),
|
|
59265
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900 min-w-[160px] text-center", children: formatMonth(currentDate) }),
|
|
59266
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
59919
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "-mx-4 sm:-mx-6 px-4 sm:px-6 py-3 border-b border-gray-200 mb-6 flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4 transition-all", children: [
|
|
59920
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex p-1 bg-gray-50 border border-gray-200 rounded-lg", children: [
|
|
59921
|
+
{ id: "all", label: "All", count: stats.all },
|
|
59922
|
+
{ id: "resolved", label: "Resolved", count: stats.resolved, icon: outline.CheckCircleIcon },
|
|
59923
|
+
{ id: "unresolved", label: "Unresolved", count: stats.unresolved, icon: outline.XCircleIcon }
|
|
59924
|
+
].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
59267
59925
|
"button",
|
|
59268
59926
|
{
|
|
59269
|
-
onClick:
|
|
59270
|
-
className:
|
|
59271
|
-
children:
|
|
59272
|
-
|
|
59273
|
-
|
|
59274
|
-
|
|
59275
|
-
|
|
59276
|
-
|
|
59277
|
-
|
|
59278
|
-
|
|
59279
|
-
|
|
59280
|
-
onClick: () => setSelectedStatus("all"),
|
|
59281
|
-
className: `px-4 py-2 rounded-lg text-sm font-medium transition-all cursor-pointer ${selectedStatus === "all" ? "bg-white text-gray-900 shadow-sm ring-1 ring-black/5" : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`,
|
|
59282
|
-
children: [
|
|
59283
|
-
"All ",
|
|
59284
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1.5 bg-gray-100 px-2 py-0.5 rounded-full text-xs text-gray-600", children: stats.all })
|
|
59285
|
-
]
|
|
59286
|
-
}
|
|
59287
|
-
),
|
|
59288
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
|
|
59289
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59290
|
-
"button",
|
|
59291
|
-
{
|
|
59292
|
-
onClick: () => setSelectedStatus("resolved"),
|
|
59293
|
-
className: `flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-all cursor-pointer ${selectedStatus === "resolved" ? "bg-green-50 text-green-700 ring-1 ring-green-200" : "text-gray-600 hover:bg-gray-100"}`,
|
|
59294
|
-
children: [
|
|
59295
|
-
/* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-4 h-4 text-green-500" }),
|
|
59296
|
-
"Resolved ",
|
|
59297
|
-
stats.resolved
|
|
59298
|
-
]
|
|
59299
|
-
}
|
|
59300
|
-
),
|
|
59927
|
+
onClick: () => handleStatusFilterChange(tab.id),
|
|
59928
|
+
className: `flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200 ${selectedStatus === tab.id ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`,
|
|
59929
|
+
children: [
|
|
59930
|
+
tab.icon && /* @__PURE__ */ jsxRuntime.jsx(tab.icon, { className: `w-4 h-4 ${selectedStatus === tab.id ? "text-blue-600" : "text-gray-400"}` }),
|
|
59931
|
+
tab.label,
|
|
59932
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs px-1.5 py-0.5 rounded-full ${selectedStatus === tab.id ? "bg-blue-50 text-blue-600" : "bg-gray-200/50 text-gray-500"}`, children: tab.count })
|
|
59933
|
+
]
|
|
59934
|
+
},
|
|
59935
|
+
tab.id
|
|
59936
|
+
)) }),
|
|
59937
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: filterRef, children: [
|
|
59301
59938
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59302
59939
|
"button",
|
|
59303
59940
|
{
|
|
59304
|
-
onClick: () =>
|
|
59305
|
-
className: `flex items-center gap-
|
|
59306
|
-
children: [
|
|
59307
|
-
/* @__PURE__ */ jsxRuntime.jsx(outline.XCircleIcon, { className: "w-4 h-4 text-red-500" }),
|
|
59308
|
-
"Unresolved ",
|
|
59309
|
-
stats.unresolved
|
|
59310
|
-
]
|
|
59311
|
-
}
|
|
59312
|
-
)
|
|
59313
|
-
] }),
|
|
59314
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 w-full md:w-auto", children: [
|
|
59315
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59316
|
-
"select",
|
|
59317
|
-
{
|
|
59318
|
-
value: selectedShift,
|
|
59319
|
-
onChange: (e) => setSelectedShift(e.target.value),
|
|
59320
|
-
className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
|
|
59321
|
-
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
|
|
59322
|
-
children: [
|
|
59323
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Shifts" }),
|
|
59324
|
-
shiftOptions.filter((s) => s !== "all").map((shift) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: shift, children: shift }, shift))
|
|
59325
|
-
]
|
|
59326
|
-
}
|
|
59327
|
-
),
|
|
59328
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
59329
|
-
"select",
|
|
59330
|
-
{
|
|
59331
|
-
value: selectedWeeksRange,
|
|
59332
|
-
onChange: (e) => setSelectedWeeksRange(e.target.value),
|
|
59333
|
-
className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
|
|
59334
|
-
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
|
|
59335
|
-
children: weekOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.id, children: opt.label }, opt.id))
|
|
59336
|
-
}
|
|
59337
|
-
),
|
|
59338
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59339
|
-
"select",
|
|
59340
|
-
{
|
|
59341
|
-
value: selectedMemberId,
|
|
59342
|
-
onChange: (e) => setSelectedMemberId(e.target.value),
|
|
59343
|
-
className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
|
|
59344
|
-
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
|
|
59941
|
+
onClick: () => setIsFilterOpen(!isFilterOpen),
|
|
59942
|
+
className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-white border-gray-200 text-gray-700 hover:bg-gray-50"}`,
|
|
59345
59943
|
children: [
|
|
59346
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
59347
|
-
|
|
59944
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.FunnelIcon, { className: "w-4 h-4" }),
|
|
59945
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Filters" }),
|
|
59946
|
+
(selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center w-5 h-5 bg-blue-100 text-blue-700 text-xs rounded-full font-bold ml-1", children: [selectedShift, selectedWeeksRange, selectedMemberId, selectedLineId].filter((v) => v !== "all").length }),
|
|
59947
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.ChevronDownIcon, { className: `w-3 h-3 ml-1 transition-transform ${isFilterOpen ? "rotate-180" : ""}` })
|
|
59348
59948
|
]
|
|
59349
59949
|
}
|
|
59350
59950
|
),
|
|
59351
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59352
|
-
"
|
|
59353
|
-
|
|
59354
|
-
|
|
59355
|
-
|
|
59356
|
-
|
|
59357
|
-
|
|
59358
|
-
|
|
59359
|
-
|
|
59360
|
-
|
|
59951
|
+
isFilterOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-full mt-2 w-72 bg-white rounded-xl shadow-xl border border-gray-100 p-4 z-50 animate-in fade-in zoom-in-95 duration-100", children: [
|
|
59952
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
59953
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Filter View" }),
|
|
59954
|
+
(selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
59955
|
+
"button",
|
|
59956
|
+
{
|
|
59957
|
+
onClick: clearFilters,
|
|
59958
|
+
className: "text-xs text-red-600 hover:text-red-700 font-medium",
|
|
59959
|
+
children: "Clear all"
|
|
59960
|
+
}
|
|
59961
|
+
)
|
|
59962
|
+
] }),
|
|
59963
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [
|
|
59964
|
+
{ value: selectedShift, onChange: handleShiftFilterChange, options: shiftOptions, label: "Shift" },
|
|
59965
|
+
{ value: selectedWeeksRange, onChange: handleWeeksFilterChange, options: weekOptions.map((o) => o.id), labels: weekOptions, label: "Duration" },
|
|
59966
|
+
{ value: selectedMemberId, onChange: handleMemberFilterChange, options: ["all", ...teamMembers.map((m) => m.id)], labels: teamMembers, label: "Member" },
|
|
59967
|
+
{ value: selectedLineId, onChange: handleLineFilterChange, options: lineOptions.map((o) => o.id), labels: lineOptions, label: "Line" }
|
|
59968
|
+
].map((filter2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
59969
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: filter2.label }),
|
|
59970
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
59971
|
+
"select",
|
|
59972
|
+
{
|
|
59973
|
+
value: filter2.value,
|
|
59974
|
+
onChange: (e) => filter2.onChange(e.target.value),
|
|
59975
|
+
className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
|
|
59976
|
+
style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
|
|
59977
|
+
children: filter2.options.map((opt) => {
|
|
59978
|
+
const val = typeof opt === "string" ? opt : opt.id;
|
|
59979
|
+
let label = val;
|
|
59980
|
+
if (filter2.labels) {
|
|
59981
|
+
const found = filter2.labels.find((l) => (l.id || l.user_id || l) === val);
|
|
59982
|
+
label = found ? found.label || found.name || found : val;
|
|
59983
|
+
}
|
|
59984
|
+
if (val === "all") label = `All ${filter2.label}s`;
|
|
59985
|
+
return /* @__PURE__ */ jsxRuntime.jsx("option", { value: val, children: label }, val);
|
|
59986
|
+
})
|
|
59987
|
+
}
|
|
59988
|
+
) })
|
|
59989
|
+
] }, idx)) })
|
|
59990
|
+
] })
|
|
59361
59991
|
] })
|
|
59362
59992
|
] }),
|
|
59363
59993
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6", children: [
|
|
59364
|
-
filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) =>
|
|
59365
|
-
|
|
59366
|
-
{
|
|
59367
|
-
|
|
59368
|
-
|
|
59369
|
-
|
|
59370
|
-
|
|
59371
|
-
|
|
59372
|
-
|
|
59373
|
-
|
|
59374
|
-
|
|
59375
|
-
|
|
59376
|
-
|
|
59377
|
-
|
|
59378
|
-
|
|
59379
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59380
|
-
|
|
59381
|
-
|
|
59382
|
-
|
|
59383
|
-
|
|
59384
|
-
|
|
59385
|
-
|
|
59386
|
-
/* @__PURE__ */ jsxRuntime.
|
|
59387
|
-
|
|
59388
|
-
|
|
59389
|
-
|
|
59994
|
+
filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => {
|
|
59995
|
+
const weeksOpen = rec.weeks_open || 1;
|
|
59996
|
+
const openLabel = weeksOpen <= 1 ? "Opened this week" : `Opened for ${weeksOpen} weeks`;
|
|
59997
|
+
const openedOnLabel = formatOpenedDate(rec.first_seen_at);
|
|
59998
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
59999
|
+
motion.div,
|
|
60000
|
+
{
|
|
60001
|
+
initial: { opacity: 0, y: 20 },
|
|
60002
|
+
animate: { opacity: 1, y: 0 },
|
|
60003
|
+
transition: { delay: index * 0.1 },
|
|
60004
|
+
className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
|
|
60005
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 relative", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
|
|
60006
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-4", children: [
|
|
60007
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 w-full", children: [
|
|
60008
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
60009
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-semibold text-gray-600 bg-gray-100 border border-gray-200", children: [
|
|
60010
|
+
"#",
|
|
60011
|
+
rec.issue_number ?? index + 1
|
|
60012
|
+
] }),
|
|
60013
|
+
rec.ticket_status === "solved" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-50 text-emerald-700 border border-emerald-100", children: [
|
|
60014
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-3.5 h-3.5" }),
|
|
60015
|
+
"Resolved"
|
|
60016
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium border ${weeksOpen > 2 ? "bg-red-50 text-red-700 border-red-100" : "bg-amber-50 text-amber-700 border-amber-100"}`, children: [
|
|
60017
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.ClockIcon, { className: "w-3.5 h-3.5" }),
|
|
60018
|
+
openLabel
|
|
60019
|
+
] }),
|
|
60020
|
+
openedOnLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium text-gray-600 bg-gray-100 border border-gray-200", children: [
|
|
60021
|
+
"Opened on ",
|
|
60022
|
+
openedOnLabel
|
|
60023
|
+
] }),
|
|
60024
|
+
rec.assigned_user_ids && rec.assigned_user_ids.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
60025
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-px bg-gray-200" }),
|
|
60026
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rec.assigned_user_ids.map((uid) => {
|
|
60027
|
+
const user2 = teamMembersById.get(uid);
|
|
60028
|
+
if (!user2) {
|
|
60029
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
60030
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-500 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: "??" }),
|
|
60031
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500", children: "Unknown" })
|
|
60032
|
+
] }, uid);
|
|
60033
|
+
}
|
|
59390
60034
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
59391
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-
|
|
59392
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-
|
|
59393
|
-
] },
|
|
59394
|
-
}
|
|
59395
|
-
|
|
59396
|
-
|
|
59397
|
-
|
|
59398
|
-
|
|
59399
|
-
|
|
59400
|
-
|
|
59401
|
-
|
|
59402
|
-
|
|
59403
|
-
|
|
59404
|
-
|
|
59405
|
-
|
|
59406
|
-
|
|
59407
|
-
rec.line,
|
|
59408
|
-
(rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
59409
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2022" }),
|
|
59410
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
|
|
60035
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-600 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: user2.initials }),
|
|
60036
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-600", children: user2.name })
|
|
60037
|
+
] }, user2.id);
|
|
60038
|
+
}) })
|
|
60039
|
+
] })
|
|
60040
|
+
] }),
|
|
60041
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
60042
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900 pr-12", children: rec.title }),
|
|
60043
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1 flex items-center gap-2", children: [
|
|
60044
|
+
rec.location,
|
|
60045
|
+
" \u2022 ",
|
|
60046
|
+
rec.line,
|
|
60047
|
+
(rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
60048
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2022" }),
|
|
60049
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
|
|
60050
|
+
] })
|
|
59411
60051
|
] })
|
|
59412
60052
|
] })
|
|
59413
|
-
] })
|
|
59414
|
-
|
|
59415
|
-
|
|
59416
|
-
|
|
59417
|
-
|
|
59418
|
-
|
|
59419
|
-
|
|
59420
|
-
|
|
59421
|
-
|
|
59422
|
-
|
|
59423
|
-
|
|
59424
|
-
|
|
59425
|
-
|
|
59426
|
-
|
|
59427
|
-
|
|
59428
|
-
|
|
59429
|
-
|
|
59430
|
-
|
|
59431
|
-
|
|
59432
|
-
|
|
59433
|
-
)) : (
|
|
60053
|
+
] }) }),
|
|
60054
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: rec.description }) }),
|
|
60055
|
+
rec.resolution_instructions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-50 border border-blue-100 rounded-lg p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-blue-800 font-medium flex items-start gap-2", children: [
|
|
60056
|
+
/* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-5 h-5 flex-shrink-0" }),
|
|
60057
|
+
rec.resolution_instructions
|
|
60058
|
+
] }) })
|
|
60059
|
+
] }),
|
|
60060
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full md:w-1/2 lg:w-5/12 bg-gray-50 rounded-lg p-4 border border-gray-100", children: Array.isArray(rec.evidence) && rec.evidence.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
60061
|
+
EvidenceCarousel,
|
|
60062
|
+
{
|
|
60063
|
+
evidence: rec.evidence,
|
|
60064
|
+
recId: rec.id,
|
|
60065
|
+
clipsService
|
|
60066
|
+
}
|
|
60067
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
|
|
60068
|
+
] }) })
|
|
60069
|
+
},
|
|
60070
|
+
rec.id
|
|
60071
|
+
);
|
|
60072
|
+
}) : (
|
|
59434
60073
|
// Success State (Zero Tickets)
|
|
59435
60074
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-gray-200 shadow-sm", children: [
|
|
59436
60075
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex items-center justify-center w-16 h-16 rounded-full bg-green-50 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-8 h-8 text-green-500" }) }),
|
|
@@ -60306,6 +60945,7 @@ exports.useHookOverride = useHookOverride;
|
|
|
60306
60945
|
exports.useHourEndTimer = useHourEndTimer;
|
|
60307
60946
|
exports.useHourlyTargetAchievements = useHourlyTargetAchievements;
|
|
60308
60947
|
exports.useHourlyTargetMisses = useHourlyTargetMisses;
|
|
60948
|
+
exports.useIdleTimeClipClassifications = useIdleTimeClipClassifications;
|
|
60309
60949
|
exports.useIdleTimeReasons = useIdleTimeReasons;
|
|
60310
60950
|
exports.useLeaderboardMetrics = useLeaderboardMetrics;
|
|
60311
60951
|
exports.useLineDetailedMetrics = useLineDetailedMetrics;
|