@optifye/dashboard-core 6.11.36 → 6.11.37
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -195
- package/dist/index.mjs +87 -195
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -9530,7 +9530,7 @@ interface UserManagementTableProps {
|
|
|
9530
9530
|
availableLines?: Line[];
|
|
9531
9531
|
availableFactories?: Factory[];
|
|
9532
9532
|
currentUserId?: string;
|
|
9533
|
-
/**
|
|
9533
|
+
/** Average daily active usage in milliseconds keyed by user_id */
|
|
9534
9534
|
avgDailyUsage?: Record<string, number>;
|
|
9535
9535
|
/** Whether usage data is loading */
|
|
9536
9536
|
isUsageLoading?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -9530,7 +9530,7 @@ interface UserManagementTableProps {
|
|
|
9530
9530
|
availableLines?: Line[];
|
|
9531
9531
|
availableFactories?: Factory[];
|
|
9532
9532
|
currentUserId?: string;
|
|
9533
|
-
/**
|
|
9533
|
+
/** Average daily active usage in milliseconds keyed by user_id */
|
|
9534
9534
|
avgDailyUsage?: Record<string, number>;
|
|
9535
9535
|
/** Whether usage data is loading */
|
|
9536
9536
|
isUsageLoading?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -58812,6 +58812,50 @@ var UserUsageStats = ({
|
|
|
58812
58812
|
] })
|
|
58813
58813
|
] });
|
|
58814
58814
|
};
|
|
58815
|
+
|
|
58816
|
+
// src/lib/utils/teamUsage.ts
|
|
58817
|
+
var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
58818
|
+
function formatLocalDateKey(date) {
|
|
58819
|
+
const year = date.getFullYear();
|
|
58820
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58821
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
58822
|
+
return `${year}-${month}-${day}`;
|
|
58823
|
+
}
|
|
58824
|
+
function parseUsageDate(dateLike) {
|
|
58825
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) {
|
|
58826
|
+
const [year, month, day] = dateLike.split("-").map(Number);
|
|
58827
|
+
return new Date(year, month - 1, day);
|
|
58828
|
+
}
|
|
58829
|
+
return new Date(dateLike);
|
|
58830
|
+
}
|
|
58831
|
+
function normalizeUsageDateKey(dateLike) {
|
|
58832
|
+
if (!dateLike) return void 0;
|
|
58833
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
|
|
58834
|
+
const parsed = parseUsageDate(dateLike);
|
|
58835
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
58836
|
+
return dateLike.split("T")[0] || dateLike;
|
|
58837
|
+
}
|
|
58838
|
+
return formatLocalDateKey(parsed);
|
|
58839
|
+
}
|
|
58840
|
+
function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
|
|
58841
|
+
const normalizedToday = new Date(today);
|
|
58842
|
+
normalizedToday.setHours(0, 0, 0, 0);
|
|
58843
|
+
const dayOfWeek = normalizedToday.getDay();
|
|
58844
|
+
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
58845
|
+
const monday = new Date(normalizedToday);
|
|
58846
|
+
monday.setDate(normalizedToday.getDate() - daysSinceMonday);
|
|
58847
|
+
return {
|
|
58848
|
+
startDate: formatLocalDateKey(monday),
|
|
58849
|
+
endDate: formatLocalDateKey(normalizedToday),
|
|
58850
|
+
daysElapsed: daysSinceMonday + 1
|
|
58851
|
+
};
|
|
58852
|
+
}
|
|
58853
|
+
function calculateAverageDailyDuration(durationMs, dayCount) {
|
|
58854
|
+
if (!Number.isFinite(durationMs) || dayCount <= 0) {
|
|
58855
|
+
return 0;
|
|
58856
|
+
}
|
|
58857
|
+
return Math.round(durationMs / dayCount);
|
|
58858
|
+
}
|
|
58815
58859
|
var UserUsageDetailModal = ({
|
|
58816
58860
|
userId,
|
|
58817
58861
|
userName,
|
|
@@ -58821,7 +58865,7 @@ var UserUsageDetailModal = ({
|
|
|
58821
58865
|
endDate
|
|
58822
58866
|
}) => {
|
|
58823
58867
|
const [currentWeekStart, setCurrentWeekStart] = React143.useState(() => {
|
|
58824
|
-
if (startDate) return
|
|
58868
|
+
if (startDate) return parseUsageDate(startDate);
|
|
58825
58869
|
const { start } = getCurrentWeekRange();
|
|
58826
58870
|
return start;
|
|
58827
58871
|
});
|
|
@@ -58831,8 +58875,8 @@ var UserUsageDetailModal = ({
|
|
|
58831
58875
|
return end;
|
|
58832
58876
|
}, [currentWeekStart]);
|
|
58833
58877
|
const { data, isLoading, error } = useUserUsage(userId, {
|
|
58834
|
-
startDate: currentWeekStart
|
|
58835
|
-
endDate: currentWeekEnd
|
|
58878
|
+
startDate: formatLocalDateKey(currentWeekStart),
|
|
58879
|
+
endDate: formatLocalDateKey(currentWeekEnd)
|
|
58836
58880
|
});
|
|
58837
58881
|
const handlePrevWeek = () => {
|
|
58838
58882
|
setCurrentWeekStart((prev) => {
|
|
@@ -58925,8 +58969,8 @@ function UsageContent({
|
|
|
58925
58969
|
} else {
|
|
58926
58970
|
daysToCount = 7;
|
|
58927
58971
|
}
|
|
58928
|
-
const
|
|
58929
|
-
const weekAvgMs =
|
|
58972
|
+
const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
|
|
58973
|
+
const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
|
|
58930
58974
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
58931
58975
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
|
|
58932
58976
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -58938,7 +58982,7 @@ function UsageContent({
|
|
|
58938
58982
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
58939
58983
|
}
|
|
58940
58984
|
),
|
|
58941
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(currentWeekStart
|
|
58985
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
|
|
58942
58986
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
58943
58987
|
"button",
|
|
58944
58988
|
{
|
|
@@ -58970,7 +59014,7 @@ function DailyBarChart({
|
|
|
58970
59014
|
const weekData = React143.useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
|
|
58971
59015
|
const chartData = React143.useMemo(() => {
|
|
58972
59016
|
return weekData.map((day, index) => {
|
|
58973
|
-
const date =
|
|
59017
|
+
const date = parseUsageDate(day.date);
|
|
58974
59018
|
return {
|
|
58975
59019
|
...day,
|
|
58976
59020
|
index,
|
|
@@ -58980,7 +59024,7 @@ function DailyBarChart({
|
|
|
58980
59024
|
// green-500
|
|
58981
59025
|
passiveColor: "#fbbf24",
|
|
58982
59026
|
// amber-400
|
|
58983
|
-
isToday: day.date === (/* @__PURE__ */ new Date())
|
|
59027
|
+
isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
|
|
58984
59028
|
};
|
|
58985
59029
|
});
|
|
58986
59030
|
}, [weekData]);
|
|
@@ -59000,7 +59044,7 @@ function DailyBarChart({
|
|
|
59000
59044
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
59001
59045
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
|
|
59002
59046
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
|
|
59003
|
-
"Average Daily usage: ",
|
|
59047
|
+
"Average Daily active usage: ",
|
|
59004
59048
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
|
|
59005
59049
|
] })
|
|
59006
59050
|
] }),
|
|
@@ -59165,17 +59209,14 @@ function getCurrentWeekRange() {
|
|
|
59165
59209
|
function getDateStringsInRange(start, end) {
|
|
59166
59210
|
const result = [];
|
|
59167
59211
|
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
59168
|
-
|
|
59169
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
59170
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
59171
|
-
result.push(`${year}-${month}-${day}`);
|
|
59212
|
+
result.push(formatLocalDateKey(d));
|
|
59172
59213
|
}
|
|
59173
59214
|
return result;
|
|
59174
59215
|
}
|
|
59175
59216
|
function buildWeekData(dailyData, weekDates) {
|
|
59176
59217
|
const dataMap = new Map(
|
|
59177
59218
|
(dailyData || []).map((d) => {
|
|
59178
|
-
const key =
|
|
59219
|
+
const key = normalizeUsageDateKey(d.date);
|
|
59179
59220
|
return [key, { ...d, date: key }];
|
|
59180
59221
|
})
|
|
59181
59222
|
);
|
|
@@ -59190,22 +59231,10 @@ function buildWeekData(dailyData, weekDates) {
|
|
|
59190
59231
|
};
|
|
59191
59232
|
});
|
|
59192
59233
|
}
|
|
59193
|
-
function normalizeDateKey(dateLike) {
|
|
59194
|
-
if (!dateLike) return void 0;
|
|
59195
|
-
try {
|
|
59196
|
-
const date = new Date(dateLike);
|
|
59197
|
-
const year = date.getFullYear();
|
|
59198
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
59199
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
59200
|
-
return `${year}-${month}-${day}`;
|
|
59201
|
-
} catch {
|
|
59202
|
-
return dateLike.split("T")[0] || dateLike;
|
|
59203
|
-
}
|
|
59204
|
-
}
|
|
59205
59234
|
function formatDateRange(startDate, endDate) {
|
|
59206
59235
|
try {
|
|
59207
|
-
const start =
|
|
59208
|
-
const end =
|
|
59236
|
+
const start = parseUsageDate(startDate);
|
|
59237
|
+
const end = parseUsageDate(endDate);
|
|
59209
59238
|
const opts = { month: "short", day: "numeric" };
|
|
59210
59239
|
const yearOpts = { year: "numeric" };
|
|
59211
59240
|
return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
|
|
@@ -59216,7 +59245,7 @@ function formatDateRange(startDate, endDate) {
|
|
|
59216
59245
|
function formatDate(dateStr) {
|
|
59217
59246
|
if (!dateStr) return "";
|
|
59218
59247
|
try {
|
|
59219
|
-
const date =
|
|
59248
|
+
const date = parseUsageDate(dateStr);
|
|
59220
59249
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
59221
59250
|
} catch {
|
|
59222
59251
|
return dateStr;
|
|
@@ -59494,7 +59523,7 @@ var UserManagementTable = ({
|
|
|
59494
59523
|
}
|
|
59495
59524
|
),
|
|
59496
59525
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
|
|
59497
|
-
showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily
|
|
59526
|
+
showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
|
|
59498
59527
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
|
|
59499
59528
|
] }) }),
|
|
59500
59529
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
|
|
@@ -73791,39 +73820,8 @@ var TeamManagementView = ({
|
|
|
73791
73820
|
const [users, setUsers] = React143.useState([]);
|
|
73792
73821
|
const [availableLines, setAvailableLines] = React143.useState([]);
|
|
73793
73822
|
const [availableFactories, setAvailableFactories] = React143.useState([]);
|
|
73794
|
-
const [
|
|
73795
|
-
totalUsers: 0,
|
|
73796
|
-
owners: 0,
|
|
73797
|
-
it: 0,
|
|
73798
|
-
plantHeads: 0,
|
|
73799
|
-
industrialEngineers: 0,
|
|
73800
|
-
supervisors: 0,
|
|
73801
|
-
optifye: 0
|
|
73802
|
-
});
|
|
73823
|
+
const [usageSummaryByUser, setUsageSummaryByUser] = React143.useState({});
|
|
73803
73824
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = React143.useState(false);
|
|
73804
|
-
const normalizeIds = React143.useCallback((value) => {
|
|
73805
|
-
if (Array.isArray(value)) {
|
|
73806
|
-
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
73807
|
-
}
|
|
73808
|
-
if (typeof value === "string" && value.length > 0) {
|
|
73809
|
-
return [value];
|
|
73810
|
-
}
|
|
73811
|
-
return [];
|
|
73812
|
-
}, []);
|
|
73813
|
-
const factoryScopedRoleFactoryIds = React143.useMemo(() => {
|
|
73814
|
-
if (!isFactoryScopedRole(user?.role_level)) return [];
|
|
73815
|
-
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
73816
|
-
if (scopedFactoryIds.length > 0) {
|
|
73817
|
-
return Array.from(new Set(scopedFactoryIds));
|
|
73818
|
-
}
|
|
73819
|
-
const propertyFactoryIds = normalizeIds(
|
|
73820
|
-
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
73821
|
-
);
|
|
73822
|
-
if (propertyFactoryIds.length > 0) {
|
|
73823
|
-
return Array.from(new Set(propertyFactoryIds));
|
|
73824
|
-
}
|
|
73825
|
-
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
73826
|
-
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
73827
73825
|
const notifyScopeRefresh = React143.useCallback(() => {
|
|
73828
73826
|
if (typeof window !== "undefined") {
|
|
73829
73827
|
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
@@ -73832,37 +73830,17 @@ var TeamManagementView = ({
|
|
|
73832
73830
|
const canAddUsers = canRoleManageUsers(user?.role_level);
|
|
73833
73831
|
const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
|
|
73834
73832
|
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
73835
|
-
const
|
|
73836
|
-
const usageDateRange = React143.useMemo(() => {
|
|
73837
|
-
const today = /* @__PURE__ */ new Date();
|
|
73838
|
-
const dayOfWeek = today.getDay();
|
|
73839
|
-
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
73840
|
-
const monday = new Date(today);
|
|
73841
|
-
monday.setDate(today.getDate() - daysSinceMonday);
|
|
73842
|
-
return {
|
|
73843
|
-
startDate: monday.toISOString().slice(0, 10),
|
|
73844
|
-
endDate: today.toISOString().slice(0, 10),
|
|
73845
|
-
daysElapsed: daysSinceMonday + 1
|
|
73846
|
-
// Include today
|
|
73847
|
-
};
|
|
73848
|
-
}, []);
|
|
73849
|
-
const {
|
|
73850
|
-
data: usageData,
|
|
73851
|
-
isLoading: isUsageLoading
|
|
73852
|
-
} = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
|
|
73853
|
-
startDate: usageDateRange.startDate,
|
|
73854
|
-
endDate: usageDateRange.endDate
|
|
73855
|
-
});
|
|
73833
|
+
const usageDateRange = React143.useMemo(() => getCurrentWeekToDateUsageRange(), []);
|
|
73856
73834
|
const avgDailyUsageMap = React143.useMemo(() => {
|
|
73857
|
-
if (!
|
|
73835
|
+
if (!canViewUsageStats) {
|
|
73858
73836
|
return {};
|
|
73859
73837
|
}
|
|
73860
73838
|
const daysElapsed = usageDateRange.daysElapsed;
|
|
73861
|
-
return
|
|
73862
|
-
acc[
|
|
73839
|
+
return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
|
|
73840
|
+
acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
|
|
73863
73841
|
return acc;
|
|
73864
73842
|
}, {});
|
|
73865
|
-
}, [
|
|
73843
|
+
}, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
|
|
73866
73844
|
const pageTitle = "Team Management";
|
|
73867
73845
|
const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
73868
73846
|
const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
|
|
@@ -73886,7 +73864,6 @@ var TeamManagementView = ({
|
|
|
73886
73864
|
setIsLoading(true);
|
|
73887
73865
|
setError(void 0);
|
|
73888
73866
|
try {
|
|
73889
|
-
const userManagementService = createUserManagementService(supabase);
|
|
73890
73867
|
const { data: { session } } = await supabase.auth.getSession();
|
|
73891
73868
|
if (!session?.access_token) {
|
|
73892
73869
|
throw new Error("No authentication token available");
|
|
@@ -73896,116 +73873,31 @@ var TeamManagementView = ({
|
|
|
73896
73873
|
if (!backendUrl) {
|
|
73897
73874
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
73898
73875
|
}
|
|
73899
|
-
|
|
73900
|
-
|
|
73901
|
-
|
|
73902
|
-
|
|
73903
|
-
|
|
73904
|
-
|
|
73905
|
-
|
|
73906
|
-
});
|
|
73907
|
-
if (!linesResponse.ok) {
|
|
73908
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73909
|
-
}
|
|
73910
|
-
const linesData = await linesResponse.json();
|
|
73911
|
-
const lines = linesData.lines || [];
|
|
73912
|
-
setAvailableFactories(factories || []);
|
|
73913
|
-
setAvailableLines(lines);
|
|
73914
|
-
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
73915
|
-
} else if (isFactoryScopedRole(user?.role_level)) {
|
|
73916
|
-
if (factoryScopedRoleFactoryIds.length > 0) {
|
|
73917
|
-
if (companyId) {
|
|
73918
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73919
|
-
headers: {
|
|
73920
|
-
"Authorization": `Bearer ${token}`,
|
|
73921
|
-
"Content-Type": "application/json"
|
|
73922
|
-
}
|
|
73923
|
-
});
|
|
73924
|
-
if (!linesResponse.ok) {
|
|
73925
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73926
|
-
}
|
|
73927
|
-
const linesData = await linesResponse.json();
|
|
73928
|
-
const allLines = linesData.lines || [];
|
|
73929
|
-
const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
|
|
73930
|
-
setAvailableLines(lines);
|
|
73931
|
-
console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
|
|
73932
|
-
}
|
|
73933
|
-
} else if (entityConfig?.factoryId) {
|
|
73934
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
|
|
73935
|
-
headers: {
|
|
73936
|
-
"Authorization": `Bearer ${token}`,
|
|
73937
|
-
"Content-Type": "application/json"
|
|
73938
|
-
}
|
|
73939
|
-
});
|
|
73940
|
-
if (!linesResponse.ok) {
|
|
73941
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73942
|
-
}
|
|
73943
|
-
const linesData = await linesResponse.json();
|
|
73944
|
-
const lines = linesData.lines || [];
|
|
73945
|
-
setAvailableLines(lines);
|
|
73946
|
-
console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
|
|
73947
|
-
} else {
|
|
73948
|
-
setAvailableLines([]);
|
|
73949
|
-
console.warn("[TeamManagementView] Plant Head has no factory assignments");
|
|
73950
|
-
}
|
|
73951
|
-
setAvailableFactories([]);
|
|
73952
|
-
} else if (companyId) {
|
|
73953
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73876
|
+
const params = new URLSearchParams({
|
|
73877
|
+
start_date: usageDateRange.startDate,
|
|
73878
|
+
end_date: usageDateRange.endDate
|
|
73879
|
+
});
|
|
73880
|
+
const bootstrapResponse = await fetch(
|
|
73881
|
+
`${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
|
|
73882
|
+
{
|
|
73954
73883
|
headers: {
|
|
73955
73884
|
"Authorization": `Bearer ${token}`,
|
|
73956
73885
|
"Content-Type": "application/json"
|
|
73957
73886
|
}
|
|
73958
|
-
});
|
|
73959
|
-
if (!linesResponse.ok) {
|
|
73960
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73961
73887
|
}
|
|
73962
|
-
|
|
73963
|
-
|
|
73964
|
-
|
|
73965
|
-
|
|
73966
|
-
|
|
73967
|
-
|
|
73968
|
-
|
|
73969
|
-
|
|
73970
|
-
|
|
73971
|
-
|
|
73972
|
-
|
|
73973
|
-
|
|
73974
|
-
|
|
73975
|
-
const successful = results.filter(
|
|
73976
|
-
(result) => result.status === "fulfilled"
|
|
73977
|
-
).flatMap((result) => result.value || []);
|
|
73978
|
-
if (successful.length > 0) {
|
|
73979
|
-
const byUserId = /* @__PURE__ */ new Map();
|
|
73980
|
-
successful.forEach((u) => {
|
|
73981
|
-
if (u?.user_id) {
|
|
73982
|
-
byUserId.set(u.user_id, u);
|
|
73983
|
-
}
|
|
73984
|
-
});
|
|
73985
|
-
return Array.from(byUserId.values());
|
|
73986
|
-
}
|
|
73987
|
-
const firstRejected = results.find(
|
|
73988
|
-
(result) => result.status === "rejected"
|
|
73989
|
-
);
|
|
73990
|
-
if (firstRejected) {
|
|
73991
|
-
throw firstRejected.reason;
|
|
73992
|
-
}
|
|
73993
|
-
return [];
|
|
73994
|
-
})() : userManagementService.getCompanyUsers(companyId);
|
|
73995
|
-
const [usersData, userStats] = await Promise.all([
|
|
73996
|
-
usersPromise,
|
|
73997
|
-
userManagementService.getUserStats(companyId)
|
|
73998
|
-
]);
|
|
73999
|
-
setUsers(usersData);
|
|
74000
|
-
setStats({
|
|
74001
|
-
totalUsers: userStats.total,
|
|
74002
|
-
owners: userStats.owners,
|
|
74003
|
-
it: userStats.it,
|
|
74004
|
-
plantHeads: userStats.plant_heads,
|
|
74005
|
-
industrialEngineers: userStats.industrial_engineers,
|
|
74006
|
-
supervisors: userStats.supervisors,
|
|
74007
|
-
optifye: 0
|
|
74008
|
-
});
|
|
73888
|
+
);
|
|
73889
|
+
if (!bootstrapResponse.ok) {
|
|
73890
|
+
const errorData = await bootstrapResponse.json().catch(() => ({}));
|
|
73891
|
+
throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
|
|
73892
|
+
}
|
|
73893
|
+
const bootstrapData = await bootstrapResponse.json();
|
|
73894
|
+
setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
|
|
73895
|
+
id: factory.id,
|
|
73896
|
+
factory_name: factory.factory_name
|
|
73897
|
+
})));
|
|
73898
|
+
setAvailableLines(bootstrapData.lines || []);
|
|
73899
|
+
setUsers(bootstrapData.users || []);
|
|
73900
|
+
setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
|
|
74009
73901
|
} catch (err) {
|
|
74010
73902
|
console.error("Error loading team management data:", err);
|
|
74011
73903
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -74013,7 +73905,7 @@ var TeamManagementView = ({
|
|
|
74013
73905
|
} finally {
|
|
74014
73906
|
setIsLoading(false);
|
|
74015
73907
|
}
|
|
74016
|
-
}, [supabase, user, pageCompanyId,
|
|
73908
|
+
}, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
|
|
74017
73909
|
React143.useEffect(() => {
|
|
74018
73910
|
const companyId = pageCompanyId;
|
|
74019
73911
|
const canLoad = hasAccess && user && !!companyId;
|
|
@@ -74194,7 +74086,7 @@ var TeamManagementView = ({
|
|
|
74194
74086
|
availableLines,
|
|
74195
74087
|
availableFactories,
|
|
74196
74088
|
avgDailyUsage: avgDailyUsageMap,
|
|
74197
|
-
isUsageLoading,
|
|
74089
|
+
isUsageLoading: false,
|
|
74198
74090
|
showUsageStats: canViewUsageStats
|
|
74199
74091
|
}
|
|
74200
74092
|
) }),
|
package/dist/index.mjs
CHANGED
|
@@ -58783,6 +58783,50 @@ var UserUsageStats = ({
|
|
|
58783
58783
|
] })
|
|
58784
58784
|
] });
|
|
58785
58785
|
};
|
|
58786
|
+
|
|
58787
|
+
// src/lib/utils/teamUsage.ts
|
|
58788
|
+
var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
58789
|
+
function formatLocalDateKey(date) {
|
|
58790
|
+
const year = date.getFullYear();
|
|
58791
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58792
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
58793
|
+
return `${year}-${month}-${day}`;
|
|
58794
|
+
}
|
|
58795
|
+
function parseUsageDate(dateLike) {
|
|
58796
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) {
|
|
58797
|
+
const [year, month, day] = dateLike.split("-").map(Number);
|
|
58798
|
+
return new Date(year, month - 1, day);
|
|
58799
|
+
}
|
|
58800
|
+
return new Date(dateLike);
|
|
58801
|
+
}
|
|
58802
|
+
function normalizeUsageDateKey(dateLike) {
|
|
58803
|
+
if (!dateLike) return void 0;
|
|
58804
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
|
|
58805
|
+
const parsed = parseUsageDate(dateLike);
|
|
58806
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
58807
|
+
return dateLike.split("T")[0] || dateLike;
|
|
58808
|
+
}
|
|
58809
|
+
return formatLocalDateKey(parsed);
|
|
58810
|
+
}
|
|
58811
|
+
function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
|
|
58812
|
+
const normalizedToday = new Date(today);
|
|
58813
|
+
normalizedToday.setHours(0, 0, 0, 0);
|
|
58814
|
+
const dayOfWeek = normalizedToday.getDay();
|
|
58815
|
+
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
58816
|
+
const monday = new Date(normalizedToday);
|
|
58817
|
+
monday.setDate(normalizedToday.getDate() - daysSinceMonday);
|
|
58818
|
+
return {
|
|
58819
|
+
startDate: formatLocalDateKey(monday),
|
|
58820
|
+
endDate: formatLocalDateKey(normalizedToday),
|
|
58821
|
+
daysElapsed: daysSinceMonday + 1
|
|
58822
|
+
};
|
|
58823
|
+
}
|
|
58824
|
+
function calculateAverageDailyDuration(durationMs, dayCount) {
|
|
58825
|
+
if (!Number.isFinite(durationMs) || dayCount <= 0) {
|
|
58826
|
+
return 0;
|
|
58827
|
+
}
|
|
58828
|
+
return Math.round(durationMs / dayCount);
|
|
58829
|
+
}
|
|
58786
58830
|
var UserUsageDetailModal = ({
|
|
58787
58831
|
userId,
|
|
58788
58832
|
userName,
|
|
@@ -58792,7 +58836,7 @@ var UserUsageDetailModal = ({
|
|
|
58792
58836
|
endDate
|
|
58793
58837
|
}) => {
|
|
58794
58838
|
const [currentWeekStart, setCurrentWeekStart] = useState(() => {
|
|
58795
|
-
if (startDate) return
|
|
58839
|
+
if (startDate) return parseUsageDate(startDate);
|
|
58796
58840
|
const { start } = getCurrentWeekRange();
|
|
58797
58841
|
return start;
|
|
58798
58842
|
});
|
|
@@ -58802,8 +58846,8 @@ var UserUsageDetailModal = ({
|
|
|
58802
58846
|
return end;
|
|
58803
58847
|
}, [currentWeekStart]);
|
|
58804
58848
|
const { data, isLoading, error } = useUserUsage(userId, {
|
|
58805
|
-
startDate: currentWeekStart
|
|
58806
|
-
endDate: currentWeekEnd
|
|
58849
|
+
startDate: formatLocalDateKey(currentWeekStart),
|
|
58850
|
+
endDate: formatLocalDateKey(currentWeekEnd)
|
|
58807
58851
|
});
|
|
58808
58852
|
const handlePrevWeek = () => {
|
|
58809
58853
|
setCurrentWeekStart((prev) => {
|
|
@@ -58896,8 +58940,8 @@ function UsageContent({
|
|
|
58896
58940
|
} else {
|
|
58897
58941
|
daysToCount = 7;
|
|
58898
58942
|
}
|
|
58899
|
-
const
|
|
58900
|
-
const weekAvgMs =
|
|
58943
|
+
const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
|
|
58944
|
+
const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
|
|
58901
58945
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
58902
58946
|
/* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
|
|
58903
58947
|
/* @__PURE__ */ jsx(
|
|
@@ -58909,7 +58953,7 @@ function UsageContent({
|
|
|
58909
58953
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
|
|
58910
58954
|
}
|
|
58911
58955
|
),
|
|
58912
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(currentWeekStart
|
|
58956
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
|
|
58913
58957
|
/* @__PURE__ */ jsx(
|
|
58914
58958
|
"button",
|
|
58915
58959
|
{
|
|
@@ -58941,7 +58985,7 @@ function DailyBarChart({
|
|
|
58941
58985
|
const weekData = useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
|
|
58942
58986
|
const chartData = useMemo(() => {
|
|
58943
58987
|
return weekData.map((day, index) => {
|
|
58944
|
-
const date =
|
|
58988
|
+
const date = parseUsageDate(day.date);
|
|
58945
58989
|
return {
|
|
58946
58990
|
...day,
|
|
58947
58991
|
index,
|
|
@@ -58951,7 +58995,7 @@ function DailyBarChart({
|
|
|
58951
58995
|
// green-500
|
|
58952
58996
|
passiveColor: "#fbbf24",
|
|
58953
58997
|
// amber-400
|
|
58954
|
-
isToday: day.date === (/* @__PURE__ */ new Date())
|
|
58998
|
+
isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
|
|
58955
58999
|
};
|
|
58956
59000
|
});
|
|
58957
59001
|
}, [weekData]);
|
|
@@ -58971,7 +59015,7 @@ function DailyBarChart({
|
|
|
58971
59015
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
58972
59016
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
|
|
58973
59017
|
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
|
|
58974
|
-
"Average Daily usage: ",
|
|
59018
|
+
"Average Daily active usage: ",
|
|
58975
59019
|
/* @__PURE__ */ jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
|
|
58976
59020
|
] })
|
|
58977
59021
|
] }),
|
|
@@ -59136,17 +59180,14 @@ function getCurrentWeekRange() {
|
|
|
59136
59180
|
function getDateStringsInRange(start, end) {
|
|
59137
59181
|
const result = [];
|
|
59138
59182
|
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
59139
|
-
|
|
59140
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
59141
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
59142
|
-
result.push(`${year}-${month}-${day}`);
|
|
59183
|
+
result.push(formatLocalDateKey(d));
|
|
59143
59184
|
}
|
|
59144
59185
|
return result;
|
|
59145
59186
|
}
|
|
59146
59187
|
function buildWeekData(dailyData, weekDates) {
|
|
59147
59188
|
const dataMap = new Map(
|
|
59148
59189
|
(dailyData || []).map((d) => {
|
|
59149
|
-
const key =
|
|
59190
|
+
const key = normalizeUsageDateKey(d.date);
|
|
59150
59191
|
return [key, { ...d, date: key }];
|
|
59151
59192
|
})
|
|
59152
59193
|
);
|
|
@@ -59161,22 +59202,10 @@ function buildWeekData(dailyData, weekDates) {
|
|
|
59161
59202
|
};
|
|
59162
59203
|
});
|
|
59163
59204
|
}
|
|
59164
|
-
function normalizeDateKey(dateLike) {
|
|
59165
|
-
if (!dateLike) return void 0;
|
|
59166
|
-
try {
|
|
59167
|
-
const date = new Date(dateLike);
|
|
59168
|
-
const year = date.getFullYear();
|
|
59169
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
59170
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
59171
|
-
return `${year}-${month}-${day}`;
|
|
59172
|
-
} catch {
|
|
59173
|
-
return dateLike.split("T")[0] || dateLike;
|
|
59174
|
-
}
|
|
59175
|
-
}
|
|
59176
59205
|
function formatDateRange(startDate, endDate) {
|
|
59177
59206
|
try {
|
|
59178
|
-
const start =
|
|
59179
|
-
const end =
|
|
59207
|
+
const start = parseUsageDate(startDate);
|
|
59208
|
+
const end = parseUsageDate(endDate);
|
|
59180
59209
|
const opts = { month: "short", day: "numeric" };
|
|
59181
59210
|
const yearOpts = { year: "numeric" };
|
|
59182
59211
|
return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
|
|
@@ -59187,7 +59216,7 @@ function formatDateRange(startDate, endDate) {
|
|
|
59187
59216
|
function formatDate(dateStr) {
|
|
59188
59217
|
if (!dateStr) return "";
|
|
59189
59218
|
try {
|
|
59190
|
-
const date =
|
|
59219
|
+
const date = parseUsageDate(dateStr);
|
|
59191
59220
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
59192
59221
|
} catch {
|
|
59193
59222
|
return dateStr;
|
|
@@ -59465,7 +59494,7 @@ var UserManagementTable = ({
|
|
|
59465
59494
|
}
|
|
59466
59495
|
),
|
|
59467
59496
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
|
|
59468
|
-
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily
|
|
59497
|
+
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
|
|
59469
59498
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
|
|
59470
59499
|
] }) }),
|
|
59471
59500
|
/* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
|
|
@@ -73762,39 +73791,8 @@ var TeamManagementView = ({
|
|
|
73762
73791
|
const [users, setUsers] = useState([]);
|
|
73763
73792
|
const [availableLines, setAvailableLines] = useState([]);
|
|
73764
73793
|
const [availableFactories, setAvailableFactories] = useState([]);
|
|
73765
|
-
const [
|
|
73766
|
-
totalUsers: 0,
|
|
73767
|
-
owners: 0,
|
|
73768
|
-
it: 0,
|
|
73769
|
-
plantHeads: 0,
|
|
73770
|
-
industrialEngineers: 0,
|
|
73771
|
-
supervisors: 0,
|
|
73772
|
-
optifye: 0
|
|
73773
|
-
});
|
|
73794
|
+
const [usageSummaryByUser, setUsageSummaryByUser] = useState({});
|
|
73774
73795
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
|
|
73775
|
-
const normalizeIds = useCallback((value) => {
|
|
73776
|
-
if (Array.isArray(value)) {
|
|
73777
|
-
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
73778
|
-
}
|
|
73779
|
-
if (typeof value === "string" && value.length > 0) {
|
|
73780
|
-
return [value];
|
|
73781
|
-
}
|
|
73782
|
-
return [];
|
|
73783
|
-
}, []);
|
|
73784
|
-
const factoryScopedRoleFactoryIds = useMemo(() => {
|
|
73785
|
-
if (!isFactoryScopedRole(user?.role_level)) return [];
|
|
73786
|
-
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
73787
|
-
if (scopedFactoryIds.length > 0) {
|
|
73788
|
-
return Array.from(new Set(scopedFactoryIds));
|
|
73789
|
-
}
|
|
73790
|
-
const propertyFactoryIds = normalizeIds(
|
|
73791
|
-
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
73792
|
-
);
|
|
73793
|
-
if (propertyFactoryIds.length > 0) {
|
|
73794
|
-
return Array.from(new Set(propertyFactoryIds));
|
|
73795
|
-
}
|
|
73796
|
-
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
73797
|
-
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
73798
73796
|
const notifyScopeRefresh = useCallback(() => {
|
|
73799
73797
|
if (typeof window !== "undefined") {
|
|
73800
73798
|
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
@@ -73803,37 +73801,17 @@ var TeamManagementView = ({
|
|
|
73803
73801
|
const canAddUsers = canRoleManageUsers(user?.role_level);
|
|
73804
73802
|
const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
|
|
73805
73803
|
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
73806
|
-
const
|
|
73807
|
-
const usageDateRange = useMemo(() => {
|
|
73808
|
-
const today = /* @__PURE__ */ new Date();
|
|
73809
|
-
const dayOfWeek = today.getDay();
|
|
73810
|
-
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
73811
|
-
const monday = new Date(today);
|
|
73812
|
-
monday.setDate(today.getDate() - daysSinceMonday);
|
|
73813
|
-
return {
|
|
73814
|
-
startDate: monday.toISOString().slice(0, 10),
|
|
73815
|
-
endDate: today.toISOString().slice(0, 10),
|
|
73816
|
-
daysElapsed: daysSinceMonday + 1
|
|
73817
|
-
// Include today
|
|
73818
|
-
};
|
|
73819
|
-
}, []);
|
|
73820
|
-
const {
|
|
73821
|
-
data: usageData,
|
|
73822
|
-
isLoading: isUsageLoading
|
|
73823
|
-
} = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
|
|
73824
|
-
startDate: usageDateRange.startDate,
|
|
73825
|
-
endDate: usageDateRange.endDate
|
|
73826
|
-
});
|
|
73804
|
+
const usageDateRange = useMemo(() => getCurrentWeekToDateUsageRange(), []);
|
|
73827
73805
|
const avgDailyUsageMap = useMemo(() => {
|
|
73828
|
-
if (!
|
|
73806
|
+
if (!canViewUsageStats) {
|
|
73829
73807
|
return {};
|
|
73830
73808
|
}
|
|
73831
73809
|
const daysElapsed = usageDateRange.daysElapsed;
|
|
73832
|
-
return
|
|
73833
|
-
acc[
|
|
73810
|
+
return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
|
|
73811
|
+
acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
|
|
73834
73812
|
return acc;
|
|
73835
73813
|
}, {});
|
|
73836
|
-
}, [
|
|
73814
|
+
}, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
|
|
73837
73815
|
const pageTitle = "Team Management";
|
|
73838
73816
|
const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
73839
73817
|
const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
|
|
@@ -73857,7 +73835,6 @@ var TeamManagementView = ({
|
|
|
73857
73835
|
setIsLoading(true);
|
|
73858
73836
|
setError(void 0);
|
|
73859
73837
|
try {
|
|
73860
|
-
const userManagementService = createUserManagementService(supabase);
|
|
73861
73838
|
const { data: { session } } = await supabase.auth.getSession();
|
|
73862
73839
|
if (!session?.access_token) {
|
|
73863
73840
|
throw new Error("No authentication token available");
|
|
@@ -73867,116 +73844,31 @@ var TeamManagementView = ({
|
|
|
73867
73844
|
if (!backendUrl) {
|
|
73868
73845
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
73869
73846
|
}
|
|
73870
|
-
|
|
73871
|
-
|
|
73872
|
-
|
|
73873
|
-
|
|
73874
|
-
|
|
73875
|
-
|
|
73876
|
-
|
|
73877
|
-
});
|
|
73878
|
-
if (!linesResponse.ok) {
|
|
73879
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73880
|
-
}
|
|
73881
|
-
const linesData = await linesResponse.json();
|
|
73882
|
-
const lines = linesData.lines || [];
|
|
73883
|
-
setAvailableFactories(factories || []);
|
|
73884
|
-
setAvailableLines(lines);
|
|
73885
|
-
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
73886
|
-
} else if (isFactoryScopedRole(user?.role_level)) {
|
|
73887
|
-
if (factoryScopedRoleFactoryIds.length > 0) {
|
|
73888
|
-
if (companyId) {
|
|
73889
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73890
|
-
headers: {
|
|
73891
|
-
"Authorization": `Bearer ${token}`,
|
|
73892
|
-
"Content-Type": "application/json"
|
|
73893
|
-
}
|
|
73894
|
-
});
|
|
73895
|
-
if (!linesResponse.ok) {
|
|
73896
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73897
|
-
}
|
|
73898
|
-
const linesData = await linesResponse.json();
|
|
73899
|
-
const allLines = linesData.lines || [];
|
|
73900
|
-
const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
|
|
73901
|
-
setAvailableLines(lines);
|
|
73902
|
-
console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
|
|
73903
|
-
}
|
|
73904
|
-
} else if (entityConfig?.factoryId) {
|
|
73905
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
|
|
73906
|
-
headers: {
|
|
73907
|
-
"Authorization": `Bearer ${token}`,
|
|
73908
|
-
"Content-Type": "application/json"
|
|
73909
|
-
}
|
|
73910
|
-
});
|
|
73911
|
-
if (!linesResponse.ok) {
|
|
73912
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73913
|
-
}
|
|
73914
|
-
const linesData = await linesResponse.json();
|
|
73915
|
-
const lines = linesData.lines || [];
|
|
73916
|
-
setAvailableLines(lines);
|
|
73917
|
-
console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
|
|
73918
|
-
} else {
|
|
73919
|
-
setAvailableLines([]);
|
|
73920
|
-
console.warn("[TeamManagementView] Plant Head has no factory assignments");
|
|
73921
|
-
}
|
|
73922
|
-
setAvailableFactories([]);
|
|
73923
|
-
} else if (companyId) {
|
|
73924
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73847
|
+
const params = new URLSearchParams({
|
|
73848
|
+
start_date: usageDateRange.startDate,
|
|
73849
|
+
end_date: usageDateRange.endDate
|
|
73850
|
+
});
|
|
73851
|
+
const bootstrapResponse = await fetch(
|
|
73852
|
+
`${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
|
|
73853
|
+
{
|
|
73925
73854
|
headers: {
|
|
73926
73855
|
"Authorization": `Bearer ${token}`,
|
|
73927
73856
|
"Content-Type": "application/json"
|
|
73928
73857
|
}
|
|
73929
|
-
});
|
|
73930
|
-
if (!linesResponse.ok) {
|
|
73931
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73932
73858
|
}
|
|
73933
|
-
|
|
73934
|
-
|
|
73935
|
-
|
|
73936
|
-
|
|
73937
|
-
|
|
73938
|
-
|
|
73939
|
-
|
|
73940
|
-
|
|
73941
|
-
|
|
73942
|
-
|
|
73943
|
-
|
|
73944
|
-
|
|
73945
|
-
|
|
73946
|
-
const successful = results.filter(
|
|
73947
|
-
(result) => result.status === "fulfilled"
|
|
73948
|
-
).flatMap((result) => result.value || []);
|
|
73949
|
-
if (successful.length > 0) {
|
|
73950
|
-
const byUserId = /* @__PURE__ */ new Map();
|
|
73951
|
-
successful.forEach((u) => {
|
|
73952
|
-
if (u?.user_id) {
|
|
73953
|
-
byUserId.set(u.user_id, u);
|
|
73954
|
-
}
|
|
73955
|
-
});
|
|
73956
|
-
return Array.from(byUserId.values());
|
|
73957
|
-
}
|
|
73958
|
-
const firstRejected = results.find(
|
|
73959
|
-
(result) => result.status === "rejected"
|
|
73960
|
-
);
|
|
73961
|
-
if (firstRejected) {
|
|
73962
|
-
throw firstRejected.reason;
|
|
73963
|
-
}
|
|
73964
|
-
return [];
|
|
73965
|
-
})() : userManagementService.getCompanyUsers(companyId);
|
|
73966
|
-
const [usersData, userStats] = await Promise.all([
|
|
73967
|
-
usersPromise,
|
|
73968
|
-
userManagementService.getUserStats(companyId)
|
|
73969
|
-
]);
|
|
73970
|
-
setUsers(usersData);
|
|
73971
|
-
setStats({
|
|
73972
|
-
totalUsers: userStats.total,
|
|
73973
|
-
owners: userStats.owners,
|
|
73974
|
-
it: userStats.it,
|
|
73975
|
-
plantHeads: userStats.plant_heads,
|
|
73976
|
-
industrialEngineers: userStats.industrial_engineers,
|
|
73977
|
-
supervisors: userStats.supervisors,
|
|
73978
|
-
optifye: 0
|
|
73979
|
-
});
|
|
73859
|
+
);
|
|
73860
|
+
if (!bootstrapResponse.ok) {
|
|
73861
|
+
const errorData = await bootstrapResponse.json().catch(() => ({}));
|
|
73862
|
+
throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
|
|
73863
|
+
}
|
|
73864
|
+
const bootstrapData = await bootstrapResponse.json();
|
|
73865
|
+
setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
|
|
73866
|
+
id: factory.id,
|
|
73867
|
+
factory_name: factory.factory_name
|
|
73868
|
+
})));
|
|
73869
|
+
setAvailableLines(bootstrapData.lines || []);
|
|
73870
|
+
setUsers(bootstrapData.users || []);
|
|
73871
|
+
setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
|
|
73980
73872
|
} catch (err) {
|
|
73981
73873
|
console.error("Error loading team management data:", err);
|
|
73982
73874
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -73984,7 +73876,7 @@ var TeamManagementView = ({
|
|
|
73984
73876
|
} finally {
|
|
73985
73877
|
setIsLoading(false);
|
|
73986
73878
|
}
|
|
73987
|
-
}, [supabase, user, pageCompanyId,
|
|
73879
|
+
}, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
|
|
73988
73880
|
useEffect(() => {
|
|
73989
73881
|
const companyId = pageCompanyId;
|
|
73990
73882
|
const canLoad = hasAccess && user && !!companyId;
|
|
@@ -74165,7 +74057,7 @@ var TeamManagementView = ({
|
|
|
74165
74057
|
availableLines,
|
|
74166
74058
|
availableFactories,
|
|
74167
74059
|
avgDailyUsage: avgDailyUsageMap,
|
|
74168
|
-
isUsageLoading,
|
|
74060
|
+
isUsageLoading: false,
|
|
74169
74061
|
showUsageStats: canViewUsageStats
|
|
74170
74062
|
}
|
|
74171
74063
|
) }),
|