@optifye/dashboard-core 6.10.34 → 6.10.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +204 -60
- package/dist/index.mjs +204 -60
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -4674,6 +4674,8 @@ interface UseSupervisorsByLineIdsResult {
|
|
|
4674
4674
|
}
|
|
4675
4675
|
declare const useSupervisorsByLineIds: (lineIds: string[] | undefined, options?: {
|
|
4676
4676
|
enabled?: boolean;
|
|
4677
|
+
companyId?: string;
|
|
4678
|
+
useBackend?: boolean;
|
|
4677
4679
|
}) => UseSupervisorsByLineIdsResult;
|
|
4678
4680
|
|
|
4679
4681
|
/**
|
|
@@ -6084,6 +6086,10 @@ type LineLeaderboardEntry = {
|
|
|
6084
6086
|
days_with_output: number;
|
|
6085
6087
|
total_output: number;
|
|
6086
6088
|
};
|
|
6089
|
+
type DailyLineLeaderboardEntry = {
|
|
6090
|
+
line_id: string;
|
|
6091
|
+
avg_efficiency: number | null;
|
|
6092
|
+
};
|
|
6087
6093
|
declare const lineLeaderboardService: {
|
|
6088
6094
|
getLineLeaderboard(supabase: SupabaseClient$1, params: {
|
|
6089
6095
|
companyId: string;
|
|
@@ -6092,6 +6098,13 @@ declare const lineLeaderboardService: {
|
|
|
6092
6098
|
lineIds?: string[];
|
|
6093
6099
|
limit?: number;
|
|
6094
6100
|
}): Promise<LineLeaderboardEntry[]>;
|
|
6101
|
+
getDailyLineLeaderboard(supabase: SupabaseClient$1, params: {
|
|
6102
|
+
companyId: string;
|
|
6103
|
+
date: string;
|
|
6104
|
+
shiftId: number;
|
|
6105
|
+
lineIds?: string[];
|
|
6106
|
+
limit?: number;
|
|
6107
|
+
}): Promise<DailyLineLeaderboardEntry[]>;
|
|
6095
6108
|
};
|
|
6096
6109
|
|
|
6097
6110
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -4674,6 +4674,8 @@ interface UseSupervisorsByLineIdsResult {
|
|
|
4674
4674
|
}
|
|
4675
4675
|
declare const useSupervisorsByLineIds: (lineIds: string[] | undefined, options?: {
|
|
4676
4676
|
enabled?: boolean;
|
|
4677
|
+
companyId?: string;
|
|
4678
|
+
useBackend?: boolean;
|
|
4677
4679
|
}) => UseSupervisorsByLineIdsResult;
|
|
4678
4680
|
|
|
4679
4681
|
/**
|
|
@@ -6084,6 +6086,10 @@ type LineLeaderboardEntry = {
|
|
|
6084
6086
|
days_with_output: number;
|
|
6085
6087
|
total_output: number;
|
|
6086
6088
|
};
|
|
6089
|
+
type DailyLineLeaderboardEntry = {
|
|
6090
|
+
line_id: string;
|
|
6091
|
+
avg_efficiency: number | null;
|
|
6092
|
+
};
|
|
6087
6093
|
declare const lineLeaderboardService: {
|
|
6088
6094
|
getLineLeaderboard(supabase: SupabaseClient$1, params: {
|
|
6089
6095
|
companyId: string;
|
|
@@ -6092,6 +6098,13 @@ declare const lineLeaderboardService: {
|
|
|
6092
6098
|
lineIds?: string[];
|
|
6093
6099
|
limit?: number;
|
|
6094
6100
|
}): Promise<LineLeaderboardEntry[]>;
|
|
6101
|
+
getDailyLineLeaderboard(supabase: SupabaseClient$1, params: {
|
|
6102
|
+
companyId: string;
|
|
6103
|
+
date: string;
|
|
6104
|
+
shiftId: number;
|
|
6105
|
+
lineIds?: string[];
|
|
6106
|
+
limit?: number;
|
|
6107
|
+
}): Promise<DailyLineLeaderboardEntry[]>;
|
|
6095
6108
|
};
|
|
6096
6109
|
|
|
6097
6110
|
/**
|
package/dist/index.js
CHANGED
|
@@ -7754,6 +7754,23 @@ var lineLeaderboardService = {
|
|
|
7754
7754
|
`/api/dashboard/line-leaderboard?${searchParams.toString()}`
|
|
7755
7755
|
);
|
|
7756
7756
|
return data?.entries ?? [];
|
|
7757
|
+
},
|
|
7758
|
+
async getDailyLineLeaderboard(supabase, params) {
|
|
7759
|
+
const searchParams = new URLSearchParams();
|
|
7760
|
+
searchParams.set("company_id", params.companyId);
|
|
7761
|
+
searchParams.set("date", params.date);
|
|
7762
|
+
searchParams.set("shift_id", params.shiftId.toString());
|
|
7763
|
+
if (params.lineIds && params.lineIds.length > 0) {
|
|
7764
|
+
searchParams.set("line_ids", params.lineIds.join(","));
|
|
7765
|
+
}
|
|
7766
|
+
if (typeof params.limit === "number") {
|
|
7767
|
+
searchParams.set("limit", params.limit.toString());
|
|
7768
|
+
}
|
|
7769
|
+
const data = await fetchBackendJson(
|
|
7770
|
+
supabase,
|
|
7771
|
+
`/api/dashboard/line-leaderboard-daily?${searchParams.toString()}`
|
|
7772
|
+
);
|
|
7773
|
+
return data?.entries ?? [];
|
|
7757
7774
|
}
|
|
7758
7775
|
};
|
|
7759
7776
|
var SupabaseContext = React26.createContext(void 0);
|
|
@@ -15979,7 +15996,10 @@ function useLineSupervisor(lineId) {
|
|
|
15979
15996
|
}
|
|
15980
15997
|
var useSupervisorsByLineIds = (lineIds, options) => {
|
|
15981
15998
|
const supabase = useSupabase();
|
|
15999
|
+
const entityConfig = useEntityConfig();
|
|
15982
16000
|
const enabled = options?.enabled ?? true;
|
|
16001
|
+
const useBackend = options?.useBackend ?? false;
|
|
16002
|
+
const resolvedCompanyId = options?.companyId || entityConfig.companyId;
|
|
15983
16003
|
const lineIdsKey = React26.useMemo(() => (lineIds || []).slice().sort().join(","), [lineIds]);
|
|
15984
16004
|
const targetLineIdSet = React26.useMemo(() => new Set(lineIds || []), [lineIdsKey]);
|
|
15985
16005
|
const [supervisorsByLineId, setSupervisorsByLineId] = React26.useState(/* @__PURE__ */ new Map());
|
|
@@ -15995,37 +16015,72 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15995
16015
|
try {
|
|
15996
16016
|
setIsLoading(true);
|
|
15997
16017
|
setError(null);
|
|
15998
|
-
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
|
|
15999
|
-
if (fetchError) {
|
|
16000
|
-
throw fetchError;
|
|
16001
|
-
}
|
|
16002
16018
|
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
16003
16019
|
const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
|
|
16004
|
-
(
|
|
16005
|
-
const
|
|
16006
|
-
|
|
16007
|
-
let displayName;
|
|
16008
|
-
if (row.first_name) {
|
|
16009
|
-
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
16010
|
-
} else {
|
|
16011
|
-
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
16012
|
-
}
|
|
16013
|
-
const supervisor = {
|
|
16014
|
-
userId: row.user_id,
|
|
16015
|
-
email,
|
|
16016
|
-
displayName,
|
|
16017
|
-
profilePhotoUrl: row.profile_photo_url
|
|
16018
|
-
};
|
|
16019
|
-
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
16020
|
-
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
16021
|
-
assignedLineIds.forEach((lineId) => {
|
|
16022
|
-
if (typeof lineId !== "string") return;
|
|
16023
|
-
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
16024
|
-
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
16025
|
-
existing.push(supervisor);
|
|
16026
|
-
nextSupervisorsByLineId.set(lineId, existing);
|
|
16020
|
+
if (useBackend && resolvedCompanyId) {
|
|
16021
|
+
const searchParams = new URLSearchParams({
|
|
16022
|
+
company_id: resolvedCompanyId
|
|
16027
16023
|
});
|
|
16028
|
-
|
|
16024
|
+
if (lineIdsKey) {
|
|
16025
|
+
searchParams.set("line_ids", lineIdsKey);
|
|
16026
|
+
}
|
|
16027
|
+
const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
|
|
16028
|
+
(data?.supervisors || []).forEach((row) => {
|
|
16029
|
+
const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
|
|
16030
|
+
if (!assignedLineIds.length) return;
|
|
16031
|
+
const email = row.email || "";
|
|
16032
|
+
let displayName;
|
|
16033
|
+
if (row.first_name) {
|
|
16034
|
+
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
16035
|
+
} else {
|
|
16036
|
+
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
16037
|
+
}
|
|
16038
|
+
const supervisor = {
|
|
16039
|
+
userId: row.user_id,
|
|
16040
|
+
email,
|
|
16041
|
+
displayName,
|
|
16042
|
+
profilePhotoUrl: row.profile_photo_url ?? null
|
|
16043
|
+
};
|
|
16044
|
+
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
16045
|
+
assignedLineIds.forEach((lineId) => {
|
|
16046
|
+
if (typeof lineId !== "string") return;
|
|
16047
|
+
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
16048
|
+
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
16049
|
+
existing.push(supervisor);
|
|
16050
|
+
nextSupervisorsByLineId.set(lineId, existing);
|
|
16051
|
+
});
|
|
16052
|
+
});
|
|
16053
|
+
} else {
|
|
16054
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
|
|
16055
|
+
if (fetchError) {
|
|
16056
|
+
throw fetchError;
|
|
16057
|
+
}
|
|
16058
|
+
(data || []).forEach((row) => {
|
|
16059
|
+
const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
|
|
16060
|
+
const email = row.email || "";
|
|
16061
|
+
let displayName;
|
|
16062
|
+
if (row.first_name) {
|
|
16063
|
+
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
16064
|
+
} else {
|
|
16065
|
+
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
16066
|
+
}
|
|
16067
|
+
const supervisor = {
|
|
16068
|
+
userId: row.user_id,
|
|
16069
|
+
email,
|
|
16070
|
+
displayName,
|
|
16071
|
+
profilePhotoUrl: row.profile_photo_url
|
|
16072
|
+
};
|
|
16073
|
+
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
16074
|
+
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
16075
|
+
assignedLineIds.forEach((lineId) => {
|
|
16076
|
+
if (typeof lineId !== "string") return;
|
|
16077
|
+
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
16078
|
+
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
16079
|
+
existing.push(supervisor);
|
|
16080
|
+
nextSupervisorsByLineId.set(lineId, existing);
|
|
16081
|
+
});
|
|
16082
|
+
});
|
|
16083
|
+
}
|
|
16029
16084
|
const nextSupervisorNamesByLineId = /* @__PURE__ */ new Map();
|
|
16030
16085
|
nextSupervisorsByLineId.forEach((supervisors, lineId) => {
|
|
16031
16086
|
nextSupervisorNamesByLineId.set(lineId, supervisors.map((s) => s.displayName).join(", "));
|
|
@@ -16041,7 +16096,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
16041
16096
|
} finally {
|
|
16042
16097
|
setIsLoading(false);
|
|
16043
16098
|
}
|
|
16044
|
-
}, [enabled, supabase, targetLineIdSet]);
|
|
16099
|
+
}, [enabled, supabase, targetLineIdSet, useBackend, resolvedCompanyId, lineIdsKey]);
|
|
16045
16100
|
React26.useEffect(() => {
|
|
16046
16101
|
fetchSupervisors();
|
|
16047
16102
|
}, [fetchSupervisors, lineIdsKey]);
|
|
@@ -55012,6 +55067,7 @@ var LeaderboardCountdown = ({ targetDate, format: format7, finishedLabel = "Fini
|
|
|
55012
55067
|
var LinesLeaderboard = ({
|
|
55013
55068
|
lines,
|
|
55014
55069
|
onLineClick,
|
|
55070
|
+
assignedLineIds,
|
|
55015
55071
|
timeRange,
|
|
55016
55072
|
setTimeRange,
|
|
55017
55073
|
todayEfficiencyByLineId,
|
|
@@ -55024,6 +55080,17 @@ var LinesLeaderboard = ({
|
|
|
55024
55080
|
monthEndDate
|
|
55025
55081
|
}) => {
|
|
55026
55082
|
const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
|
|
55083
|
+
const assignedLineIdSet = React26__namespace.default.useMemo(
|
|
55084
|
+
() => new Set(assignedLineIds || []),
|
|
55085
|
+
[assignedLineIds]
|
|
55086
|
+
);
|
|
55087
|
+
const canClickLine = React26__namespace.default.useCallback(
|
|
55088
|
+
(lineId) => {
|
|
55089
|
+
if (!assignedLineIds) return true;
|
|
55090
|
+
return assignedLineIdSet.has(lineId);
|
|
55091
|
+
},
|
|
55092
|
+
[assignedLineIds, assignedLineIdSet]
|
|
55093
|
+
);
|
|
55027
55094
|
const handleTimeRangeChange = React26__namespace.default.useCallback((newRange) => {
|
|
55028
55095
|
if (newRange === timeRange) return;
|
|
55029
55096
|
trackCoreEvent("Leaderboard Time Range Changed", {
|
|
@@ -55036,6 +55103,7 @@ var LinesLeaderboard = ({
|
|
|
55036
55103
|
setTimeRange(newRange);
|
|
55037
55104
|
}, [timeRange, lines.length, monthlyEfficiencyByLineId, setTimeRange]);
|
|
55038
55105
|
const handleLeaderboardLineClick = React26__namespace.default.useCallback((item, clickSource) => {
|
|
55106
|
+
if (!canClickLine(item.line.id)) return;
|
|
55039
55107
|
trackCoreEvent("Leaderboard Line Clicked", {
|
|
55040
55108
|
line_id: item.line.id,
|
|
55041
55109
|
line_name: item.line.line_name,
|
|
@@ -55047,7 +55115,7 @@ var LinesLeaderboard = ({
|
|
|
55047
55115
|
supervisor_name: item.supervisorName || "Unassigned"
|
|
55048
55116
|
});
|
|
55049
55117
|
onLineClick(item.line);
|
|
55050
|
-
}, [onLineClick, timeRange]);
|
|
55118
|
+
}, [canClickLine, onLineClick, timeRange]);
|
|
55051
55119
|
const viewLoadedTrackedRef = React26__namespace.default.useRef(null);
|
|
55052
55120
|
const leaderboardData = React26__namespace.default.useMemo(() => {
|
|
55053
55121
|
const loading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
|
|
@@ -55201,11 +55269,12 @@ var LinesLeaderboard = ({
|
|
|
55201
55269
|
const isFirst = item.rank === 1;
|
|
55202
55270
|
const isSecond = item.rank === 2;
|
|
55203
55271
|
item.rank === 3;
|
|
55272
|
+
const isClickable = canClickLine(item.line.id);
|
|
55204
55273
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
55205
55274
|
"div",
|
|
55206
55275
|
{
|
|
55207
55276
|
onClick: () => handleLeaderboardLineClick(item, "podium"),
|
|
55208
|
-
className: `relative flex flex-col items-center
|
|
55277
|
+
className: `relative flex flex-col items-center z-10 transition-transform ${isClickable ? "cursor-pointer hover:scale-105" : "cursor-not-allowed"}`,
|
|
55209
55278
|
children: [
|
|
55210
55279
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative -mb-4 z-20 ${isFirst ? "animate-float-slow" : isSecond ? "animate-float-medium" : "animate-float-fast"}`, children: [
|
|
55211
55280
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 rounded-full blur-xl opacity-40 ${isFirst ? "bg-yellow-400" : isSecond ? "bg-gray-400" : "bg-orange-400"}` }),
|
|
@@ -55266,11 +55335,12 @@ var LinesLeaderboard = ({
|
|
|
55266
55335
|
] }) }),
|
|
55267
55336
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
|
|
55268
55337
|
const isTopThree = item.rank <= 3;
|
|
55338
|
+
const isClickable = canClickLine(item.line.id);
|
|
55269
55339
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
55270
55340
|
"tr",
|
|
55271
55341
|
{
|
|
55272
55342
|
onClick: () => handleLeaderboardLineClick(item, isTopThree ? "podium" : "table"),
|
|
55273
|
-
className: `
|
|
55343
|
+
className: `transition-colors ${isTopThree ? "sm:hidden" : ""} ${isClickable ? "hover:bg-gray-50 cursor-pointer group" : "cursor-not-allowed"}`,
|
|
55274
55344
|
children: [
|
|
55275
55345
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 text-gray-600 font-bold text-sm", children: item.rank }) }),
|
|
55276
55346
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
@@ -55465,6 +55535,7 @@ var KPIsOverviewView = ({
|
|
|
55465
55535
|
lineIds
|
|
55466
55536
|
}) => {
|
|
55467
55537
|
const [lines, setLines] = React26.useState([]);
|
|
55538
|
+
const [leaderboardLines, setLeaderboardLines] = React26.useState([]);
|
|
55468
55539
|
const [activeTab, setActiveTab] = React26.useState("today");
|
|
55469
55540
|
const [timeRange, setTimeRange] = React26.useState("today");
|
|
55470
55541
|
const [loading, setLoading] = React26.useState(true);
|
|
@@ -55480,11 +55551,16 @@ var KPIsOverviewView = ({
|
|
|
55480
55551
|
});
|
|
55481
55552
|
const [topPerformerLoading, setTopPerformerLoading] = React26.useState(true);
|
|
55482
55553
|
const [topPerformerImageError, setTopPerformerImageError] = React26.useState(false);
|
|
55554
|
+
const [todayEfficiencyByLineId, setTodayEfficiencyByLineId] = React26.useState(/* @__PURE__ */ new Map());
|
|
55555
|
+
const [dailyLoading, setDailyLoading] = React26.useState(false);
|
|
55556
|
+
const [dailyError, setDailyError] = React26.useState(null);
|
|
55483
55557
|
const [monthlyEfficiencyByLineId, setMonthlyEfficiencyByLineId] = React26.useState(/* @__PURE__ */ new Map());
|
|
55484
55558
|
const [monthlyLoading, setMonthlyLoading] = React26.useState(false);
|
|
55485
55559
|
const [monthlyError, setMonthlyError] = React26.useState(null);
|
|
55560
|
+
const dailyRequestKeyRef = React26.useRef(null);
|
|
55486
55561
|
const monthlyRequestKeyRef = React26.useRef(null);
|
|
55487
55562
|
const supabase = useSupabase();
|
|
55563
|
+
const { user } = useAuth();
|
|
55488
55564
|
const dashboardConfig = useDashboardConfig();
|
|
55489
55565
|
const entityConfig = useEntityConfig();
|
|
55490
55566
|
const configCompanyId = (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0) || dashboardConfig.customConfig?.companyId || dashboardConfig.customConfig?.company_id;
|
|
@@ -55499,6 +55575,16 @@ var KPIsOverviewView = ({
|
|
|
55499
55575
|
const dbTimezone = useAppTimezone();
|
|
55500
55576
|
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
55501
55577
|
const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
|
|
55578
|
+
const isSupervisor = user?.role_level === "supervisor";
|
|
55579
|
+
const assignedLineIdsForLeaderboard = isSupervisor ? lineIds : void 0;
|
|
55580
|
+
const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
|
|
55581
|
+
const currentShiftDate = currentShiftDetails.date;
|
|
55582
|
+
const currentShiftId = currentShiftDetails.shiftId;
|
|
55583
|
+
const shiftEndDate = React26__namespace.default.useMemo(
|
|
55584
|
+
() => getShiftEndDate(currentShiftDetails, configuredTimezone),
|
|
55585
|
+
[currentShiftDetails, configuredTimezone]
|
|
55586
|
+
);
|
|
55587
|
+
const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
|
|
55502
55588
|
const factoryViewId = entityConfig.factoryViewId || "factory";
|
|
55503
55589
|
const {
|
|
55504
55590
|
lineMetrics,
|
|
@@ -55516,19 +55602,14 @@ var KPIsOverviewView = ({
|
|
|
55516
55602
|
});
|
|
55517
55603
|
return map;
|
|
55518
55604
|
}, [lineMetrics]);
|
|
55519
|
-
const
|
|
55520
|
-
|
|
55521
|
-
|
|
55522
|
-
|
|
55523
|
-
|
|
55524
|
-
|
|
55525
|
-
|
|
55526
|
-
|
|
55527
|
-
return map;
|
|
55528
|
-
}, [kpisByLineId]);
|
|
55529
|
-
const visibleLineIds = React26__namespace.default.useMemo(() => lines.map((l) => l.id), [lines]);
|
|
55530
|
-
const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
|
|
55531
|
-
enabled: supervisorEnabled && visibleLineIds.length > 0
|
|
55605
|
+
const supervisorLineIds = React26__namespace.default.useMemo(
|
|
55606
|
+
() => (leaderboardLines.length > 0 ? leaderboardLines : lines).map((l) => l.id),
|
|
55607
|
+
[leaderboardLines, lines]
|
|
55608
|
+
);
|
|
55609
|
+
const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(supervisorLineIds, {
|
|
55610
|
+
enabled: supervisorEnabled && supervisorLineIds.length > 0,
|
|
55611
|
+
companyId: resolvedCompanyId,
|
|
55612
|
+
useBackend: true
|
|
55532
55613
|
});
|
|
55533
55614
|
React26.useEffect(() => {
|
|
55534
55615
|
let isMounted = true;
|
|
@@ -55589,9 +55670,41 @@ var KPIsOverviewView = ({
|
|
|
55589
55670
|
};
|
|
55590
55671
|
fetchLines();
|
|
55591
55672
|
}, [supabase, dashboardConfig, lineIds]);
|
|
55673
|
+
React26.useEffect(() => {
|
|
55674
|
+
let isMounted = true;
|
|
55675
|
+
const fetchLeaderboardLines = async () => {
|
|
55676
|
+
if (!supabase || !resolvedCompanyId) {
|
|
55677
|
+
setLeaderboardLines(lines);
|
|
55678
|
+
return;
|
|
55679
|
+
}
|
|
55680
|
+
try {
|
|
55681
|
+
const linesService2 = new LinesService(supabase);
|
|
55682
|
+
const companyLines = await linesService2.getLinesByCompanyId(resolvedCompanyId);
|
|
55683
|
+
if (!isMounted) return;
|
|
55684
|
+
const transformed = companyLines.map((line) => ({
|
|
55685
|
+
id: line.id,
|
|
55686
|
+
line_name: line.name,
|
|
55687
|
+
factory_id: line.factoryId || "",
|
|
55688
|
+
factory_name: "N/A",
|
|
55689
|
+
company_id: line.companyId,
|
|
55690
|
+
company_name: "",
|
|
55691
|
+
enable: line.isActive ?? true
|
|
55692
|
+
}));
|
|
55693
|
+
setLeaderboardLines(transformed);
|
|
55694
|
+
} catch (err) {
|
|
55695
|
+
console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
|
|
55696
|
+
if (!isMounted) return;
|
|
55697
|
+
setLeaderboardLines(lines);
|
|
55698
|
+
}
|
|
55699
|
+
};
|
|
55700
|
+
fetchLeaderboardLines();
|
|
55701
|
+
return () => {
|
|
55702
|
+
isMounted = false;
|
|
55703
|
+
};
|
|
55704
|
+
}, [supabase, resolvedCompanyId, lines]);
|
|
55592
55705
|
const fetchMonthlyLeaderboard = React26.useCallback(async () => {
|
|
55593
|
-
if (!supabase || !resolvedCompanyId ||
|
|
55594
|
-
const targetLineIds =
|
|
55706
|
+
if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
|
|
55707
|
+
const targetLineIds = leaderboardLines.map((line) => line.id);
|
|
55595
55708
|
const lineIdsKey = targetLineIds.slice().sort().join(",");
|
|
55596
55709
|
const requestKey = `${resolvedCompanyId}|${monthStartDate}|${monthEndDateKey}|${lineIdsKey}`;
|
|
55597
55710
|
if (monthlyRequestKeyRef.current === requestKey) return;
|
|
@@ -55605,8 +55718,7 @@ var KPIsOverviewView = ({
|
|
|
55605
55718
|
const entries = await lineLeaderboardService.getLineLeaderboard(supabase, {
|
|
55606
55719
|
companyId: resolvedCompanyId,
|
|
55607
55720
|
startDate: monthStartDate,
|
|
55608
|
-
endDate: monthEndDateKey
|
|
55609
|
-
lineIds: targetLineIds
|
|
55721
|
+
endDate: monthEndDateKey
|
|
55610
55722
|
});
|
|
55611
55723
|
const nextMap = /* @__PURE__ */ new Map();
|
|
55612
55724
|
targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
|
|
@@ -55639,11 +55751,47 @@ var KPIsOverviewView = ({
|
|
|
55639
55751
|
} finally {
|
|
55640
55752
|
setMonthlyLoading(false);
|
|
55641
55753
|
}
|
|
55642
|
-
}, [supabase, resolvedCompanyId,
|
|
55754
|
+
}, [supabase, resolvedCompanyId, leaderboardLines, monthStartDate, monthEndDateKey]);
|
|
55755
|
+
const fetchDailyLeaderboard = React26.useCallback(async () => {
|
|
55756
|
+
if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
|
|
55757
|
+
if (!currentShiftDate) return;
|
|
55758
|
+
const targetLineIds = leaderboardLines.map((line) => line.id);
|
|
55759
|
+
const lineIdsKey = targetLineIds.slice().sort().join(",");
|
|
55760
|
+
const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
|
|
55761
|
+
if (dailyRequestKeyRef.current === requestKey) return;
|
|
55762
|
+
dailyRequestKeyRef.current = requestKey;
|
|
55763
|
+
setDailyLoading(true);
|
|
55764
|
+
setDailyError(null);
|
|
55765
|
+
try {
|
|
55766
|
+
const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
|
|
55767
|
+
companyId: resolvedCompanyId,
|
|
55768
|
+
date: currentShiftDate,
|
|
55769
|
+
shiftId: currentShiftId
|
|
55770
|
+
});
|
|
55771
|
+
const nextMap = /* @__PURE__ */ new Map();
|
|
55772
|
+
entries.forEach((entry) => {
|
|
55773
|
+
const value = Number(entry.avg_efficiency);
|
|
55774
|
+
if (Number.isFinite(value)) {
|
|
55775
|
+
nextMap.set(entry.line_id, value);
|
|
55776
|
+
}
|
|
55777
|
+
});
|
|
55778
|
+
setTodayEfficiencyByLineId(nextMap);
|
|
55779
|
+
} catch (err) {
|
|
55780
|
+
console.error("[KPIsOverviewView] Failed to load daily leaderboard:", err);
|
|
55781
|
+
setDailyError("Failed to load daily leaderboard");
|
|
55782
|
+
dailyRequestKeyRef.current = null;
|
|
55783
|
+
} finally {
|
|
55784
|
+
setDailyLoading(false);
|
|
55785
|
+
}
|
|
55786
|
+
}, [supabase, resolvedCompanyId, leaderboardLines, currentShiftDate, currentShiftId]);
|
|
55643
55787
|
React26.useEffect(() => {
|
|
55644
55788
|
if (activeTab !== "leaderboard") return;
|
|
55645
55789
|
fetchMonthlyLeaderboard();
|
|
55646
55790
|
}, [activeTab, timeRange, fetchMonthlyLeaderboard]);
|
|
55791
|
+
React26.useEffect(() => {
|
|
55792
|
+
if (activeTab !== "leaderboard" || timeRange !== "today") return;
|
|
55793
|
+
fetchDailyLeaderboard();
|
|
55794
|
+
}, [activeTab, timeRange, fetchDailyLeaderboard]);
|
|
55647
55795
|
const formatTopPerformerWeek = (periodStart, periodEnd) => {
|
|
55648
55796
|
if (!periodStart || !periodEnd) return "Last Week";
|
|
55649
55797
|
const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
|
|
@@ -55739,14 +55887,15 @@ var KPIsOverviewView = ({
|
|
|
55739
55887
|
}, [onBackClick, backLinkUrl, navigation]);
|
|
55740
55888
|
const handleTabChange = React26.useCallback((newTab) => {
|
|
55741
55889
|
if (newTab === activeTab) return;
|
|
55890
|
+
const trackedLineCount = newTab === "leaderboard" ? leaderboardLines.length : lines.length;
|
|
55742
55891
|
trackCoreEvent("Leaderboard Tab Switched", {
|
|
55743
55892
|
from_tab: activeTab,
|
|
55744
55893
|
to_tab: newTab,
|
|
55745
55894
|
from_page: "kpis_overview",
|
|
55746
|
-
lines_count:
|
|
55895
|
+
lines_count: trackedLineCount
|
|
55747
55896
|
});
|
|
55748
55897
|
setActiveTab(newTab);
|
|
55749
|
-
}, [activeTab, lines.length]);
|
|
55898
|
+
}, [activeTab, leaderboardLines.length, lines.length]);
|
|
55750
55899
|
const formatLocalDate2 = (date) => {
|
|
55751
55900
|
const options = {
|
|
55752
55901
|
year: "numeric",
|
|
@@ -55764,12 +55913,6 @@ var KPIsOverviewView = ({
|
|
|
55764
55913
|
return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
|
|
55765
55914
|
};
|
|
55766
55915
|
const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
|
|
55767
|
-
const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
|
|
55768
|
-
const shiftEndDate = React26__namespace.default.useMemo(
|
|
55769
|
-
() => getShiftEndDate(currentShiftDetails, configuredTimezone),
|
|
55770
|
-
[currentShiftDetails, configuredTimezone]
|
|
55771
|
-
);
|
|
55772
|
-
const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
|
|
55773
55916
|
const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
|
|
55774
55917
|
typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
|
|
55775
55918
|
topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
|
|
@@ -56122,15 +56265,16 @@ var KPIsOverviewView = ({
|
|
|
56122
56265
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
56123
56266
|
LinesLeaderboard,
|
|
56124
56267
|
{
|
|
56125
|
-
lines,
|
|
56268
|
+
lines: leaderboardLines,
|
|
56126
56269
|
onLineClick: (line) => handleLineClick(line),
|
|
56270
|
+
assignedLineIds: assignedLineIdsForLeaderboard,
|
|
56127
56271
|
timeRange,
|
|
56128
56272
|
setTimeRange,
|
|
56129
56273
|
todayEfficiencyByLineId,
|
|
56130
56274
|
monthlyEfficiencyByLineId,
|
|
56131
56275
|
supervisorsByLineId,
|
|
56132
56276
|
supervisorNamesByLineId,
|
|
56133
|
-
isLoadingToday:
|
|
56277
|
+
isLoadingToday: dailyLoading,
|
|
56134
56278
|
isLoadingMonthly: monthlyLoading,
|
|
56135
56279
|
shiftEndDate,
|
|
56136
56280
|
monthEndDate
|
package/dist/index.mjs
CHANGED
|
@@ -7725,6 +7725,23 @@ var lineLeaderboardService = {
|
|
|
7725
7725
|
`/api/dashboard/line-leaderboard?${searchParams.toString()}`
|
|
7726
7726
|
);
|
|
7727
7727
|
return data?.entries ?? [];
|
|
7728
|
+
},
|
|
7729
|
+
async getDailyLineLeaderboard(supabase, params) {
|
|
7730
|
+
const searchParams = new URLSearchParams();
|
|
7731
|
+
searchParams.set("company_id", params.companyId);
|
|
7732
|
+
searchParams.set("date", params.date);
|
|
7733
|
+
searchParams.set("shift_id", params.shiftId.toString());
|
|
7734
|
+
if (params.lineIds && params.lineIds.length > 0) {
|
|
7735
|
+
searchParams.set("line_ids", params.lineIds.join(","));
|
|
7736
|
+
}
|
|
7737
|
+
if (typeof params.limit === "number") {
|
|
7738
|
+
searchParams.set("limit", params.limit.toString());
|
|
7739
|
+
}
|
|
7740
|
+
const data = await fetchBackendJson(
|
|
7741
|
+
supabase,
|
|
7742
|
+
`/api/dashboard/line-leaderboard-daily?${searchParams.toString()}`
|
|
7743
|
+
);
|
|
7744
|
+
return data?.entries ?? [];
|
|
7728
7745
|
}
|
|
7729
7746
|
};
|
|
7730
7747
|
var SupabaseContext = createContext(void 0);
|
|
@@ -15950,7 +15967,10 @@ function useLineSupervisor(lineId) {
|
|
|
15950
15967
|
}
|
|
15951
15968
|
var useSupervisorsByLineIds = (lineIds, options) => {
|
|
15952
15969
|
const supabase = useSupabase();
|
|
15970
|
+
const entityConfig = useEntityConfig();
|
|
15953
15971
|
const enabled = options?.enabled ?? true;
|
|
15972
|
+
const useBackend = options?.useBackend ?? false;
|
|
15973
|
+
const resolvedCompanyId = options?.companyId || entityConfig.companyId;
|
|
15954
15974
|
const lineIdsKey = useMemo(() => (lineIds || []).slice().sort().join(","), [lineIds]);
|
|
15955
15975
|
const targetLineIdSet = useMemo(() => new Set(lineIds || []), [lineIdsKey]);
|
|
15956
15976
|
const [supervisorsByLineId, setSupervisorsByLineId] = useState(/* @__PURE__ */ new Map());
|
|
@@ -15966,37 +15986,72 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
15966
15986
|
try {
|
|
15967
15987
|
setIsLoading(true);
|
|
15968
15988
|
setError(null);
|
|
15969
|
-
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
|
|
15970
|
-
if (fetchError) {
|
|
15971
|
-
throw fetchError;
|
|
15972
|
-
}
|
|
15973
15989
|
const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
|
|
15974
15990
|
const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
|
|
15975
|
-
(
|
|
15976
|
-
const
|
|
15977
|
-
|
|
15978
|
-
let displayName;
|
|
15979
|
-
if (row.first_name) {
|
|
15980
|
-
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
15981
|
-
} else {
|
|
15982
|
-
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
15983
|
-
}
|
|
15984
|
-
const supervisor = {
|
|
15985
|
-
userId: row.user_id,
|
|
15986
|
-
email,
|
|
15987
|
-
displayName,
|
|
15988
|
-
profilePhotoUrl: row.profile_photo_url
|
|
15989
|
-
};
|
|
15990
|
-
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
15991
|
-
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
15992
|
-
assignedLineIds.forEach((lineId) => {
|
|
15993
|
-
if (typeof lineId !== "string") return;
|
|
15994
|
-
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
15995
|
-
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
15996
|
-
existing.push(supervisor);
|
|
15997
|
-
nextSupervisorsByLineId.set(lineId, existing);
|
|
15991
|
+
if (useBackend && resolvedCompanyId) {
|
|
15992
|
+
const searchParams = new URLSearchParams({
|
|
15993
|
+
company_id: resolvedCompanyId
|
|
15998
15994
|
});
|
|
15999
|
-
|
|
15995
|
+
if (lineIdsKey) {
|
|
15996
|
+
searchParams.set("line_ids", lineIdsKey);
|
|
15997
|
+
}
|
|
15998
|
+
const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
|
|
15999
|
+
(data?.supervisors || []).forEach((row) => {
|
|
16000
|
+
const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
|
|
16001
|
+
if (!assignedLineIds.length) return;
|
|
16002
|
+
const email = row.email || "";
|
|
16003
|
+
let displayName;
|
|
16004
|
+
if (row.first_name) {
|
|
16005
|
+
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
16006
|
+
} else {
|
|
16007
|
+
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
16008
|
+
}
|
|
16009
|
+
const supervisor = {
|
|
16010
|
+
userId: row.user_id,
|
|
16011
|
+
email,
|
|
16012
|
+
displayName,
|
|
16013
|
+
profilePhotoUrl: row.profile_photo_url ?? null
|
|
16014
|
+
};
|
|
16015
|
+
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
16016
|
+
assignedLineIds.forEach((lineId) => {
|
|
16017
|
+
if (typeof lineId !== "string") return;
|
|
16018
|
+
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
16019
|
+
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
16020
|
+
existing.push(supervisor);
|
|
16021
|
+
nextSupervisorsByLineId.set(lineId, existing);
|
|
16022
|
+
});
|
|
16023
|
+
});
|
|
16024
|
+
} else {
|
|
16025
|
+
const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
|
|
16026
|
+
if (fetchError) {
|
|
16027
|
+
throw fetchError;
|
|
16028
|
+
}
|
|
16029
|
+
(data || []).forEach((row) => {
|
|
16030
|
+
const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
|
|
16031
|
+
const email = row.email || "";
|
|
16032
|
+
let displayName;
|
|
16033
|
+
if (row.first_name) {
|
|
16034
|
+
displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
|
|
16035
|
+
} else {
|
|
16036
|
+
displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
|
|
16037
|
+
}
|
|
16038
|
+
const supervisor = {
|
|
16039
|
+
userId: row.user_id,
|
|
16040
|
+
email,
|
|
16041
|
+
displayName,
|
|
16042
|
+
profilePhotoUrl: row.profile_photo_url
|
|
16043
|
+
};
|
|
16044
|
+
nextAllSupervisorsMap.set(supervisor.userId, supervisor);
|
|
16045
|
+
if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
|
|
16046
|
+
assignedLineIds.forEach((lineId) => {
|
|
16047
|
+
if (typeof lineId !== "string") return;
|
|
16048
|
+
if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
|
|
16049
|
+
const existing = nextSupervisorsByLineId.get(lineId) || [];
|
|
16050
|
+
existing.push(supervisor);
|
|
16051
|
+
nextSupervisorsByLineId.set(lineId, existing);
|
|
16052
|
+
});
|
|
16053
|
+
});
|
|
16054
|
+
}
|
|
16000
16055
|
const nextSupervisorNamesByLineId = /* @__PURE__ */ new Map();
|
|
16001
16056
|
nextSupervisorsByLineId.forEach((supervisors, lineId) => {
|
|
16002
16057
|
nextSupervisorNamesByLineId.set(lineId, supervisors.map((s) => s.displayName).join(", "));
|
|
@@ -16012,7 +16067,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
|
|
|
16012
16067
|
} finally {
|
|
16013
16068
|
setIsLoading(false);
|
|
16014
16069
|
}
|
|
16015
|
-
}, [enabled, supabase, targetLineIdSet]);
|
|
16070
|
+
}, [enabled, supabase, targetLineIdSet, useBackend, resolvedCompanyId, lineIdsKey]);
|
|
16016
16071
|
useEffect(() => {
|
|
16017
16072
|
fetchSupervisors();
|
|
16018
16073
|
}, [fetchSupervisors, lineIdsKey]);
|
|
@@ -54983,6 +55038,7 @@ var LeaderboardCountdown = ({ targetDate, format: format7, finishedLabel = "Fini
|
|
|
54983
55038
|
var LinesLeaderboard = ({
|
|
54984
55039
|
lines,
|
|
54985
55040
|
onLineClick,
|
|
55041
|
+
assignedLineIds,
|
|
54986
55042
|
timeRange,
|
|
54987
55043
|
setTimeRange,
|
|
54988
55044
|
todayEfficiencyByLineId,
|
|
@@ -54995,6 +55051,17 @@ var LinesLeaderboard = ({
|
|
|
54995
55051
|
monthEndDate
|
|
54996
55052
|
}) => {
|
|
54997
55053
|
const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
|
|
55054
|
+
const assignedLineIdSet = React26__default.useMemo(
|
|
55055
|
+
() => new Set(assignedLineIds || []),
|
|
55056
|
+
[assignedLineIds]
|
|
55057
|
+
);
|
|
55058
|
+
const canClickLine = React26__default.useCallback(
|
|
55059
|
+
(lineId) => {
|
|
55060
|
+
if (!assignedLineIds) return true;
|
|
55061
|
+
return assignedLineIdSet.has(lineId);
|
|
55062
|
+
},
|
|
55063
|
+
[assignedLineIds, assignedLineIdSet]
|
|
55064
|
+
);
|
|
54998
55065
|
const handleTimeRangeChange = React26__default.useCallback((newRange) => {
|
|
54999
55066
|
if (newRange === timeRange) return;
|
|
55000
55067
|
trackCoreEvent("Leaderboard Time Range Changed", {
|
|
@@ -55007,6 +55074,7 @@ var LinesLeaderboard = ({
|
|
|
55007
55074
|
setTimeRange(newRange);
|
|
55008
55075
|
}, [timeRange, lines.length, monthlyEfficiencyByLineId, setTimeRange]);
|
|
55009
55076
|
const handleLeaderboardLineClick = React26__default.useCallback((item, clickSource) => {
|
|
55077
|
+
if (!canClickLine(item.line.id)) return;
|
|
55010
55078
|
trackCoreEvent("Leaderboard Line Clicked", {
|
|
55011
55079
|
line_id: item.line.id,
|
|
55012
55080
|
line_name: item.line.line_name,
|
|
@@ -55018,7 +55086,7 @@ var LinesLeaderboard = ({
|
|
|
55018
55086
|
supervisor_name: item.supervisorName || "Unassigned"
|
|
55019
55087
|
});
|
|
55020
55088
|
onLineClick(item.line);
|
|
55021
|
-
}, [onLineClick, timeRange]);
|
|
55089
|
+
}, [canClickLine, onLineClick, timeRange]);
|
|
55022
55090
|
const viewLoadedTrackedRef = React26__default.useRef(null);
|
|
55023
55091
|
const leaderboardData = React26__default.useMemo(() => {
|
|
55024
55092
|
const loading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
|
|
@@ -55172,11 +55240,12 @@ var LinesLeaderboard = ({
|
|
|
55172
55240
|
const isFirst = item.rank === 1;
|
|
55173
55241
|
const isSecond = item.rank === 2;
|
|
55174
55242
|
item.rank === 3;
|
|
55243
|
+
const isClickable = canClickLine(item.line.id);
|
|
55175
55244
|
return /* @__PURE__ */ jsxs(
|
|
55176
55245
|
"div",
|
|
55177
55246
|
{
|
|
55178
55247
|
onClick: () => handleLeaderboardLineClick(item, "podium"),
|
|
55179
|
-
className: `relative flex flex-col items-center
|
|
55248
|
+
className: `relative flex flex-col items-center z-10 transition-transform ${isClickable ? "cursor-pointer hover:scale-105" : "cursor-not-allowed"}`,
|
|
55180
55249
|
children: [
|
|
55181
55250
|
/* @__PURE__ */ jsxs("div", { className: `relative -mb-4 z-20 ${isFirst ? "animate-float-slow" : isSecond ? "animate-float-medium" : "animate-float-fast"}`, children: [
|
|
55182
55251
|
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 rounded-full blur-xl opacity-40 ${isFirst ? "bg-yellow-400" : isSecond ? "bg-gray-400" : "bg-orange-400"}` }),
|
|
@@ -55237,11 +55306,12 @@ var LinesLeaderboard = ({
|
|
|
55237
55306
|
] }) }),
|
|
55238
55307
|
/* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
|
|
55239
55308
|
const isTopThree = item.rank <= 3;
|
|
55309
|
+
const isClickable = canClickLine(item.line.id);
|
|
55240
55310
|
return /* @__PURE__ */ jsxs(
|
|
55241
55311
|
"tr",
|
|
55242
55312
|
{
|
|
55243
55313
|
onClick: () => handleLeaderboardLineClick(item, isTopThree ? "podium" : "table"),
|
|
55244
|
-
className: `
|
|
55314
|
+
className: `transition-colors ${isTopThree ? "sm:hidden" : ""} ${isClickable ? "hover:bg-gray-50 cursor-pointer group" : "cursor-not-allowed"}`,
|
|
55245
55315
|
children: [
|
|
55246
55316
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 text-gray-600 font-bold text-sm", children: item.rank }) }),
|
|
55247
55317
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
@@ -55436,6 +55506,7 @@ var KPIsOverviewView = ({
|
|
|
55436
55506
|
lineIds
|
|
55437
55507
|
}) => {
|
|
55438
55508
|
const [lines, setLines] = useState([]);
|
|
55509
|
+
const [leaderboardLines, setLeaderboardLines] = useState([]);
|
|
55439
55510
|
const [activeTab, setActiveTab] = useState("today");
|
|
55440
55511
|
const [timeRange, setTimeRange] = useState("today");
|
|
55441
55512
|
const [loading, setLoading] = useState(true);
|
|
@@ -55451,11 +55522,16 @@ var KPIsOverviewView = ({
|
|
|
55451
55522
|
});
|
|
55452
55523
|
const [topPerformerLoading, setTopPerformerLoading] = useState(true);
|
|
55453
55524
|
const [topPerformerImageError, setTopPerformerImageError] = useState(false);
|
|
55525
|
+
const [todayEfficiencyByLineId, setTodayEfficiencyByLineId] = useState(/* @__PURE__ */ new Map());
|
|
55526
|
+
const [dailyLoading, setDailyLoading] = useState(false);
|
|
55527
|
+
const [dailyError, setDailyError] = useState(null);
|
|
55454
55528
|
const [monthlyEfficiencyByLineId, setMonthlyEfficiencyByLineId] = useState(/* @__PURE__ */ new Map());
|
|
55455
55529
|
const [monthlyLoading, setMonthlyLoading] = useState(false);
|
|
55456
55530
|
const [monthlyError, setMonthlyError] = useState(null);
|
|
55531
|
+
const dailyRequestKeyRef = useRef(null);
|
|
55457
55532
|
const monthlyRequestKeyRef = useRef(null);
|
|
55458
55533
|
const supabase = useSupabase();
|
|
55534
|
+
const { user } = useAuth();
|
|
55459
55535
|
const dashboardConfig = useDashboardConfig();
|
|
55460
55536
|
const entityConfig = useEntityConfig();
|
|
55461
55537
|
const configCompanyId = (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0) || dashboardConfig.customConfig?.companyId || dashboardConfig.customConfig?.company_id;
|
|
@@ -55470,6 +55546,16 @@ var KPIsOverviewView = ({
|
|
|
55470
55546
|
const dbTimezone = useAppTimezone();
|
|
55471
55547
|
const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
|
|
55472
55548
|
const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
|
|
55549
|
+
const isSupervisor = user?.role_level === "supervisor";
|
|
55550
|
+
const assignedLineIdsForLeaderboard = isSupervisor ? lineIds : void 0;
|
|
55551
|
+
const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
|
|
55552
|
+
const currentShiftDate = currentShiftDetails.date;
|
|
55553
|
+
const currentShiftId = currentShiftDetails.shiftId;
|
|
55554
|
+
const shiftEndDate = React26__default.useMemo(
|
|
55555
|
+
() => getShiftEndDate(currentShiftDetails, configuredTimezone),
|
|
55556
|
+
[currentShiftDetails, configuredTimezone]
|
|
55557
|
+
);
|
|
55558
|
+
const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
|
|
55473
55559
|
const factoryViewId = entityConfig.factoryViewId || "factory";
|
|
55474
55560
|
const {
|
|
55475
55561
|
lineMetrics,
|
|
@@ -55487,19 +55573,14 @@ var KPIsOverviewView = ({
|
|
|
55487
55573
|
});
|
|
55488
55574
|
return map;
|
|
55489
55575
|
}, [lineMetrics]);
|
|
55490
|
-
const
|
|
55491
|
-
|
|
55492
|
-
|
|
55493
|
-
|
|
55494
|
-
|
|
55495
|
-
|
|
55496
|
-
|
|
55497
|
-
|
|
55498
|
-
return map;
|
|
55499
|
-
}, [kpisByLineId]);
|
|
55500
|
-
const visibleLineIds = React26__default.useMemo(() => lines.map((l) => l.id), [lines]);
|
|
55501
|
-
const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
|
|
55502
|
-
enabled: supervisorEnabled && visibleLineIds.length > 0
|
|
55576
|
+
const supervisorLineIds = React26__default.useMemo(
|
|
55577
|
+
() => (leaderboardLines.length > 0 ? leaderboardLines : lines).map((l) => l.id),
|
|
55578
|
+
[leaderboardLines, lines]
|
|
55579
|
+
);
|
|
55580
|
+
const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(supervisorLineIds, {
|
|
55581
|
+
enabled: supervisorEnabled && supervisorLineIds.length > 0,
|
|
55582
|
+
companyId: resolvedCompanyId,
|
|
55583
|
+
useBackend: true
|
|
55503
55584
|
});
|
|
55504
55585
|
useEffect(() => {
|
|
55505
55586
|
let isMounted = true;
|
|
@@ -55560,9 +55641,41 @@ var KPIsOverviewView = ({
|
|
|
55560
55641
|
};
|
|
55561
55642
|
fetchLines();
|
|
55562
55643
|
}, [supabase, dashboardConfig, lineIds]);
|
|
55644
|
+
useEffect(() => {
|
|
55645
|
+
let isMounted = true;
|
|
55646
|
+
const fetchLeaderboardLines = async () => {
|
|
55647
|
+
if (!supabase || !resolvedCompanyId) {
|
|
55648
|
+
setLeaderboardLines(lines);
|
|
55649
|
+
return;
|
|
55650
|
+
}
|
|
55651
|
+
try {
|
|
55652
|
+
const linesService2 = new LinesService(supabase);
|
|
55653
|
+
const companyLines = await linesService2.getLinesByCompanyId(resolvedCompanyId);
|
|
55654
|
+
if (!isMounted) return;
|
|
55655
|
+
const transformed = companyLines.map((line) => ({
|
|
55656
|
+
id: line.id,
|
|
55657
|
+
line_name: line.name,
|
|
55658
|
+
factory_id: line.factoryId || "",
|
|
55659
|
+
factory_name: "N/A",
|
|
55660
|
+
company_id: line.companyId,
|
|
55661
|
+
company_name: "",
|
|
55662
|
+
enable: line.isActive ?? true
|
|
55663
|
+
}));
|
|
55664
|
+
setLeaderboardLines(transformed);
|
|
55665
|
+
} catch (err) {
|
|
55666
|
+
console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
|
|
55667
|
+
if (!isMounted) return;
|
|
55668
|
+
setLeaderboardLines(lines);
|
|
55669
|
+
}
|
|
55670
|
+
};
|
|
55671
|
+
fetchLeaderboardLines();
|
|
55672
|
+
return () => {
|
|
55673
|
+
isMounted = false;
|
|
55674
|
+
};
|
|
55675
|
+
}, [supabase, resolvedCompanyId, lines]);
|
|
55563
55676
|
const fetchMonthlyLeaderboard = useCallback(async () => {
|
|
55564
|
-
if (!supabase || !resolvedCompanyId ||
|
|
55565
|
-
const targetLineIds =
|
|
55677
|
+
if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
|
|
55678
|
+
const targetLineIds = leaderboardLines.map((line) => line.id);
|
|
55566
55679
|
const lineIdsKey = targetLineIds.slice().sort().join(",");
|
|
55567
55680
|
const requestKey = `${resolvedCompanyId}|${monthStartDate}|${monthEndDateKey}|${lineIdsKey}`;
|
|
55568
55681
|
if (monthlyRequestKeyRef.current === requestKey) return;
|
|
@@ -55576,8 +55689,7 @@ var KPIsOverviewView = ({
|
|
|
55576
55689
|
const entries = await lineLeaderboardService.getLineLeaderboard(supabase, {
|
|
55577
55690
|
companyId: resolvedCompanyId,
|
|
55578
55691
|
startDate: monthStartDate,
|
|
55579
|
-
endDate: monthEndDateKey
|
|
55580
|
-
lineIds: targetLineIds
|
|
55692
|
+
endDate: monthEndDateKey
|
|
55581
55693
|
});
|
|
55582
55694
|
const nextMap = /* @__PURE__ */ new Map();
|
|
55583
55695
|
targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
|
|
@@ -55610,11 +55722,47 @@ var KPIsOverviewView = ({
|
|
|
55610
55722
|
} finally {
|
|
55611
55723
|
setMonthlyLoading(false);
|
|
55612
55724
|
}
|
|
55613
|
-
}, [supabase, resolvedCompanyId,
|
|
55725
|
+
}, [supabase, resolvedCompanyId, leaderboardLines, monthStartDate, monthEndDateKey]);
|
|
55726
|
+
const fetchDailyLeaderboard = useCallback(async () => {
|
|
55727
|
+
if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
|
|
55728
|
+
if (!currentShiftDate) return;
|
|
55729
|
+
const targetLineIds = leaderboardLines.map((line) => line.id);
|
|
55730
|
+
const lineIdsKey = targetLineIds.slice().sort().join(",");
|
|
55731
|
+
const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
|
|
55732
|
+
if (dailyRequestKeyRef.current === requestKey) return;
|
|
55733
|
+
dailyRequestKeyRef.current = requestKey;
|
|
55734
|
+
setDailyLoading(true);
|
|
55735
|
+
setDailyError(null);
|
|
55736
|
+
try {
|
|
55737
|
+
const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
|
|
55738
|
+
companyId: resolvedCompanyId,
|
|
55739
|
+
date: currentShiftDate,
|
|
55740
|
+
shiftId: currentShiftId
|
|
55741
|
+
});
|
|
55742
|
+
const nextMap = /* @__PURE__ */ new Map();
|
|
55743
|
+
entries.forEach((entry) => {
|
|
55744
|
+
const value = Number(entry.avg_efficiency);
|
|
55745
|
+
if (Number.isFinite(value)) {
|
|
55746
|
+
nextMap.set(entry.line_id, value);
|
|
55747
|
+
}
|
|
55748
|
+
});
|
|
55749
|
+
setTodayEfficiencyByLineId(nextMap);
|
|
55750
|
+
} catch (err) {
|
|
55751
|
+
console.error("[KPIsOverviewView] Failed to load daily leaderboard:", err);
|
|
55752
|
+
setDailyError("Failed to load daily leaderboard");
|
|
55753
|
+
dailyRequestKeyRef.current = null;
|
|
55754
|
+
} finally {
|
|
55755
|
+
setDailyLoading(false);
|
|
55756
|
+
}
|
|
55757
|
+
}, [supabase, resolvedCompanyId, leaderboardLines, currentShiftDate, currentShiftId]);
|
|
55614
55758
|
useEffect(() => {
|
|
55615
55759
|
if (activeTab !== "leaderboard") return;
|
|
55616
55760
|
fetchMonthlyLeaderboard();
|
|
55617
55761
|
}, [activeTab, timeRange, fetchMonthlyLeaderboard]);
|
|
55762
|
+
useEffect(() => {
|
|
55763
|
+
if (activeTab !== "leaderboard" || timeRange !== "today") return;
|
|
55764
|
+
fetchDailyLeaderboard();
|
|
55765
|
+
}, [activeTab, timeRange, fetchDailyLeaderboard]);
|
|
55618
55766
|
const formatTopPerformerWeek = (periodStart, periodEnd) => {
|
|
55619
55767
|
if (!periodStart || !periodEnd) return "Last Week";
|
|
55620
55768
|
const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
|
|
@@ -55710,14 +55858,15 @@ var KPIsOverviewView = ({
|
|
|
55710
55858
|
}, [onBackClick, backLinkUrl, navigation]);
|
|
55711
55859
|
const handleTabChange = useCallback((newTab) => {
|
|
55712
55860
|
if (newTab === activeTab) return;
|
|
55861
|
+
const trackedLineCount = newTab === "leaderboard" ? leaderboardLines.length : lines.length;
|
|
55713
55862
|
trackCoreEvent("Leaderboard Tab Switched", {
|
|
55714
55863
|
from_tab: activeTab,
|
|
55715
55864
|
to_tab: newTab,
|
|
55716
55865
|
from_page: "kpis_overview",
|
|
55717
|
-
lines_count:
|
|
55866
|
+
lines_count: trackedLineCount
|
|
55718
55867
|
});
|
|
55719
55868
|
setActiveTab(newTab);
|
|
55720
|
-
}, [activeTab, lines.length]);
|
|
55869
|
+
}, [activeTab, leaderboardLines.length, lines.length]);
|
|
55721
55870
|
const formatLocalDate2 = (date) => {
|
|
55722
55871
|
const options = {
|
|
55723
55872
|
year: "numeric",
|
|
@@ -55735,12 +55884,6 @@ var KPIsOverviewView = ({
|
|
|
55735
55884
|
return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
|
|
55736
55885
|
};
|
|
55737
55886
|
const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
|
|
55738
|
-
const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
|
|
55739
|
-
const shiftEndDate = React26__default.useMemo(
|
|
55740
|
-
() => getShiftEndDate(currentShiftDetails, configuredTimezone),
|
|
55741
|
-
[currentShiftDetails, configuredTimezone]
|
|
55742
|
-
);
|
|
55743
|
-
const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
|
|
55744
55887
|
const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
|
|
55745
55888
|
typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
|
|
55746
55889
|
topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
|
|
@@ -56093,15 +56236,16 @@ var KPIsOverviewView = ({
|
|
|
56093
56236
|
/* @__PURE__ */ jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsx(
|
|
56094
56237
|
LinesLeaderboard,
|
|
56095
56238
|
{
|
|
56096
|
-
lines,
|
|
56239
|
+
lines: leaderboardLines,
|
|
56097
56240
|
onLineClick: (line) => handleLineClick(line),
|
|
56241
|
+
assignedLineIds: assignedLineIdsForLeaderboard,
|
|
56098
56242
|
timeRange,
|
|
56099
56243
|
setTimeRange,
|
|
56100
56244
|
todayEfficiencyByLineId,
|
|
56101
56245
|
monthlyEfficiencyByLineId,
|
|
56102
56246
|
supervisorsByLineId,
|
|
56103
56247
|
supervisorNamesByLineId,
|
|
56104
|
-
isLoadingToday:
|
|
56248
|
+
isLoadingToday: dailyLoading,
|
|
56105
56249
|
isLoadingMonthly: monthlyLoading,
|
|
56106
56250
|
shiftEndDate,
|
|
56107
56251
|
monthEndDate
|