supersam/src/pages/DashboardPage.jsx

203 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import { Navigate, useNavigate, useSearchParams, useLocation } from "react-router-dom";
import { DriverDeliveryPlanner } from "../components/driver/DriverDeliveryPlanner";
import { LogisticsReadinessBoard } from "../components/logistics/LogisticsReadinessBoard";
import { OrdersTable } from "../components/orders/OrdersTable";
import { AdminDashboard } from "../components/admin/AdminDashboard";
import UserManagementPanel from "../components/admin/UserManagementPanel";
import ErrorLogPanel from "../components/admin/ErrorLogPanel";
import { StopWordsPanel } from "../components/admin/StopWordsPanel";
import { ActionLogPanel } from "../components/admin/ActionLogPanel";
import { Panel } from "../components/UI/Panel";
import { useAuth } from "../context/AuthContext";
import { useNotifications } from "../hooks/useNotifications";
import { usePushNotifications } from "../hooks/usePushNotifications";
import { usePwaStatus } from "../hooks/usePwaStatus";
import { useOrderGroups } from "../hooks/useOrderGroups";
import { AppShell } from "../layouts/AppShell";
const MEGA_ADMIN_NAV = [
{ key: "analytics", label: "Аналитика", description: "Статистика доставки, графики и показатели.", badge: null },
{ key: "orders", label: "Группы", description: "Реестр групп доставки.", badge: null },
{ key: "users", label: "Пользователи", description: "Управление пользователями и ролями.", badge: null },
{ key: "errors", label: "Ошибки", description: "Журнал ошибок приложения.", badge: null },
{ key: "stop_words", label: "Стоп-слова", description: "Слова, исключаемые из клиентской карточки.", badge: null },
{ key: "action_log", label: "Журнал", description: "Журнал действий сотрудников.", badge: null },
];
const ROLE_SECTION = {
mega_admin: { key: "analytics", label: "Аналитика" },
admin: { key: "analytics", label: "Аналитика", description: "Статистика доставки." },
manager: { key: "orders", label: "Группы", description: "Реестр групп доставки, поиск и просмотр карточки." },
logistician: { key: "logistics", label: "Логистика", description: "Группы доставки по готовности к уведомлению." },
driver: { key: "deliveries", label: "Мои доставки", description: "Группы доставки по датам и статусам." },
};
export const DashboardPage = () => {
const { user, signOut, isSessionLoading } = useAuth();
const location = useLocation();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const userRole = user?.role;
const isMegaAdmin = userRole === "mega_admin";
const isAdmin = userRole === "admin" || isMegaAdmin;
const section = ROLE_SECTION[userRole] || ROLE_SECTION.manager;
// Active section from URL, fallback to role default
const activeSection = searchParams.get("tab") || section.key;
const setActiveSection = (key) => {
if (key === section.key) {
setSearchParams({}, { replace: true }); // default tab → clean URL
} else {
setSearchParams({ tab: key }, { replace: true });
}
};
const {
notifications,
unreadCount,
isLoading: notifLoading,
markAsRead: markNotificationRead,
markAllAsRead: markAllNotificationsRead,
} = useNotifications(user?.id);
const { isSupported, isSubscribed, subscribe } = usePushNotifications(user?.id);
React.useEffect(() => {
if (isSupported && !isSubscribed && user?.id && Notification.permission === "granted") {
subscribe();
}
}, [isSupported, isSubscribed, user?.id, subscribe]);
const { isInstalled, isInstallAvailable, installApp: onInstallApp } = usePwaStatus();
const {
orderGroups,
allOrderGroups,
filteredOrderGroups,
selectedOrderGroupId,
setSelectedOrderGroupId,
filters,
setFilters,
statusOptions,
isLoading,
loadError,
} = useOrderGroups();
const cities = React.useMemo(() => {
const set = new Set();
for (const g of allOrderGroups) {
if (g.city) set.add(g.city);
}
return [...set].sort();
}, [allOrderGroups]);
const openGroupPage = React.useCallback((groupId) => {
navigate("/dashboard/group/" + groupId);
}, [navigate]);
const navItems = isMegaAdmin
? MEGA_ADMIN_NAV
: userRole === "admin"
? [
{ key: "analytics", label: "Аналитика", description: "Статистика доставки.", badge: null },
{ key: "orders", label: "Группы", description: "Реестр групп доставки.", badge: String(allOrderGroups.length || orderGroups.length || 0) },
{ key: "users", label: "Пользователи", description: "Управление пользователями.", badge: null },
{ key: "stop_words", label: "Стоп-слова", description: "Слова, исключаемые из карточки.", badge: null },
{ key: "errors", label: "Ошибки", description: "Журнал ошибок приложения.", badge: null },
{ key: "action_log", label: "Журнал", description: "Журнал действий сотрудников.", badge: null },
]
: userRole === "logistician"
? [
{ key: "logistics", label: "Логистика", description: "Группы доставки по готовности к уведомлению.", badge: String(allOrderGroups.length || orderGroups.length || 0) },
]
: [
{ key: section.key, label: section.label, description: section.description, badge: String(allOrderGroups.length || orderGroups.length || 0) },
];
const activeSectionMeta = navItems.find((n) => n.key === activeSection) || navItems[0];
const isGuideOpen = false;
const ALLOWED_DASHBOARD_ROLES = ["admin", "mega_admin", "manager", "logistician", "driver"];
// Wait for session restore before deciding redirect
if (isSessionLoading) {
return null;
}
if (!user) {
return <Navigate to={`/login?redirect=${encodeURIComponent(location.pathname + location.search)}`} replace />;
}
if (!ALLOWED_DASHBOARD_ROLES.includes(userRole)) {
return <Navigate to="/forbidden" replace />;
}
const renderActiveSection = () => {
if (activeSection === "analytics") return <div className="space-y-6 xl:space-y-8"><AdminDashboard /></div>;
if (activeSection === "users") return <div className="space-y-6 xl:space-y-8"><UserManagementPanel /></div>;
if (activeSection === "stop_words") return <div className="space-y-6 xl:space-y-8"><StopWordsPanel /></div>;
if (activeSection === "errors") return <div className="space-y-6 xl:space-y-8"><ErrorLogPanel /></div>;
if (activeSection === "action_log") return <div className="space-y-6 xl:space-y-8"><ActionLogPanel /></div>;
if (userRole === "driver") {
return (
<div className="space-y-6 xl:space-y-8">
<DriverDeliveryPlanner orderGroups={allOrderGroups} onOpenOrder={openGroupPage} currentUser={user} />
</div>
);
}
if (userRole === "logistician") {
if (activeSection === "orders") {
return (
<div className="space-y-6 xl:space-y-8">
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} cities={cities} />
</div>
);
}
return (
<div className="space-y-6 xl:space-y-8">
<LogisticsReadinessBoard orderGroups={allOrderGroups} onSelectSet={openGroupPage} statusOptions={statusOptions} />
</div>
);
}
return (
<div className="space-y-6 xl:space-y-8">
<OrdersTable orderGroups={filteredOrderGroups} selectedOrderGroupId={selectedOrderGroupId} onOpenOrder={openGroupPage} filters={filters} setFilters={setFilters} statusOptions={statusOptions} cities={cities} />
</div>
);
};
return (
<AppShell
user={user}
onSignOut={signOut}
onInstallApp={onInstallApp}
isInstalled={isInstalled}
isInstallAvailable={isInstallAvailable}
onOpenGuide={undefined}
isGuideOpen={false}
navItems={navItems}
activeSection={activeSection}
onSectionChange={setActiveSection}
sectionMeta={activeSectionMeta}
notifications={notifications}
unreadCount={unreadCount}
onMarkNotificationRead={markNotificationRead}
onMarkAllNotificationsRead={markAllNotificationsRead}
>
{isLoading && (
<Panel className="border border-dashed border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4 text-sm text-[var(--color-text-muted)]">
Загружаем данные...
</Panel>
)}
{loadError && (
<Panel className="border border-dashed border-[var(--color-danger)] bg-[var(--color-surface-strong)] p-4 text-sm text-[var(--color-danger)]">
Не удалось загрузить данные. Обратитесь к администратору.
</Panel>
)}
{renderActiveSection()}
</AppShell>
);
};