Files
LogisicsApp_Client/app/dashboard/page.tsx
2026-07-27 17:54:43 +03:00

159 lines
8.9 KiB
TypeScript
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.
"use client";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { clearAuth, getStoredUser } from "@/src/lib/auth";
import { Spinner, Alert } from "@/src/Components/UI";
import { useDashboardOverview } from "@/src/hooks/useDashboardOverview";
import {
KpiSection,
AlertsSection,
RecentActivityPanel,
TrendChartsPanel,
QuickAccessFooter,
} from "@/src/Components/Dashboard";
export default function DashboardPage() {
const router = useRouter();
const { data, error, loading } = useDashboardOverview();
useEffect(() => {
const storedUser = getStoredUser();
const role = typeof storedUser?.role === "string" ? storedUser.role : "";
if (!role || ["driver", "سائق"].includes(role)) {
clearAuth();
router.replace("/login");
}
}, [router]);
const activeTrips = data?.activeTrips || [];
return (
<section className="flex flex-col gap-6">
{/* ── Header ── */}
<header
style={{
background: "rgba(255,255,255,0.04)",
border: "1px solid var(--color-border-dark)",
borderRadius: "var(--radius-2xl)",
padding: "2rem",
boxShadow: "var(--shadow-overlay)",
backdropFilter: "blur(16px)",
}}
>
<p style={{ fontSize: 11, letterSpacing: "0.35em", textTransform: "uppercase", color: "#67E8F9", textAlign: "start" }}>
لوحة العمليات
</p>
<div className="mt-4 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<h1 style={{ fontSize: "clamp(1.5rem,3vw,2rem)", fontWeight: 600, color: "var(--color-text-dark-primary)", margin: 0, textAlign: "start" }}>
ذكاء الأسطول في لمحة سريعة
</h1>
<p style={{ marginTop: "0.75rem", maxWidth: 680, color: "var(--color-text-dark-muted)", fontSize: 14, lineHeight: 1.6, textAlign: "start" }}>
عرض واضح لكل وحدة من وحدات المنصة، مع تنبيهات فورية واتجاهات الأداء.
</p>
</div>
</div>
</header>
{/* ── Loading ── */}
{loading && !data && (
<div style={{ display: "flex", alignItems: "center", gap: 12, color: "var(--color-text-dark-muted)" }}>
<Spinner size="sm" className="text-cyan-400" />
<span style={{ fontSize: 14 }}>جارٍ تحميل البيانات</span>
</div>
)}
{/* ── Error ── */}
{error && (
<Alert type="error" message={error} className="border-rose-400/30 bg-rose-500/10 text-rose-100" />
)}
{/* ── KPI Section: total / active / pending per entity (9 cards) ── */}
<KpiSection entities={data?.entities ?? []} loading={loading && !data} />
{/* ── Alerts Section: urgent cross-entity issues ── */}
<AlertsSection alerts={data?.alerts ?? []} loading={loading && !data} />
{/* ── Insights Row: recent activity log + trend charts ── */}
<div className="grid gap-6 xl:grid-cols-[1.1fr_0.9fr]">
<RecentActivityPanel logs={data?.recentActivity ?? []} loading={loading && !data} />
<TrendChartsPanel series={data?.trends ?? []} loading={loading && !data} />
</div>
{/* ── Quick access to every entity page (minimizes navigation clicks) ── */}
<QuickAccessFooter />
{/* ── Active trips (kept from the previous layout — live operational detail) ── */}
<article style={{ borderRadius: "var(--radius-2xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-card)", padding: "1.5rem", boxShadow: "var(--shadow-card)" }}>
<div className="flex items-center justify-between">
<div>
<p style={{ fontSize: 11, letterSpacing: "0.25em", textTransform: "uppercase", color: "#67E8F9", textAlign: "start" }}>الرحلات النشطة</p>
<h2 style={{ fontSize: "1.25rem", fontWeight: 600, color: "var(--color-text-dark-primary)", marginTop: "0.5rem", textAlign: "start" }}>تقدم الرحلات</h2>
</div>
<span style={{ borderRadius: "var(--radius-full)", background: "rgba(52,211,153,0.10)", padding: "0.25rem 0.75rem", fontSize: 11, color: "#008000" }}>
مباشر
</span>
</div>
<div style={{ marginTop: "1.5rem", display: "flex", flexDirection: "column", gap: "1rem" }}>
{activeTrips.length ? activeTrips.map((trip) => (
<div key={trip.id} style={{ borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-raised)", padding: "1rem" }}>
<div className="flex items-center justify-between gap-3">
<div>
<p style={{ fontSize: 13, fontWeight: 600, color: "var(--color-text-dark-primary)", textAlign: "start" }}>
<span className="ltr-embed">{trip.tripNumber}</span>
</p>
<p style={{ fontSize: 13, color: "var(--color-text-dark-muted)", textAlign: "start" }}>{trip.title}</p>
</div>
<span style={{ borderRadius: "var(--radius-full)", background: "rgba(103,232,249,0.10)", padding: "0.25rem 0.75rem", fontSize: 11, color: "#CFFAFE" }}>
{trip.progress}%
</span>
</div>
<div style={{ marginTop: "0.75rem", height: 6, borderRadius: 999, background: "rgba(255,255,255,0.08)" }}>
<div style={{ height: 6, borderRadius: 999, width: `${trip.progress}%`, background: "linear-gradient(90deg,#06B6D4,#34D399)" }} />
</div>
</div>
)) : (
<p style={{ fontSize: 13, color: "var(--color-text-dark-muted)", textAlign: "start" }}>لا توجد رحلات نشطة حالياً.</p>
)}
</div>
</article>
{/* ── Security + endpoints ── */}
<div className="grid gap-6 lg:grid-cols-2">
<article style={{ borderRadius: "var(--radius-2xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-card)", padding: "1.5rem", boxShadow: "var(--shadow-card)" }}>
<p style={{ fontSize: 11, letterSpacing: "0.25em", textTransform: "uppercase", color: "#C4B5FD", textAlign: "start" }}>الأمان</p>
<h2 style={{ fontSize: "1.25rem", fontWeight: 600, color: "var(--color-text-dark-primary)", marginTop: "0.5rem", textAlign: "start" }}>لمحة الحساب</h2>
<div style={{ marginTop: "1.5rem", display: "flex", flexDirection: "column", gap: "0.75rem" }}>
<div style={{ borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-raised)", padding: "0.875rem 1rem", fontSize: 13, color: "var(--color-text-dark-muted)", textAlign: "start" }}>
آخر تسجيل دخول: <span className="ltr-embed">{data?.accountSecurity?.lastLogin || "—"}</span>
</div>
<div style={{ borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-raised)", padding: "0.875rem 1rem", fontSize: 13, color: "var(--color-text-dark-muted)", textAlign: "start" }}>
بيانات الجهاز: <span className="ltr-embed">{data?.accountSecurity?.requestMeta ? JSON.stringify(data.accountSecurity.requestMeta) : "—"}</span>
</div>
</div>
</article>
<article style={{ borderRadius: "var(--radius-2xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-card)", padding: "1.5rem", boxShadow: "var(--shadow-card)" }}>
<p style={{ fontSize: 11, letterSpacing: "0.25em", textTransform: "uppercase", color: "#6EE7B7", textAlign: "start" }}>خريطة الخلفية</p>
<h2 style={{ fontSize: "1.25rem", fontWeight: 600, color: "var(--color-text-dark-primary)", marginTop: "0.5rem", textAlign: "start" }}>النقاط النهائية</h2>
<ul style={{ marginTop: "1.5rem", display: "flex", flexDirection: "column", gap: "0.75rem", listStyle: "none", padding: 0 }}>
{[
"/v1/dashboard/overview — نظرة عامة تشغيلية",
"/v1/client — إدارة العملاء",
"/v1/orders — دورة شحن الطلبات",
"/v1/trip — تنظيم الرحلات",
].map((ep) => (
<li key={ep} style={{ borderRadius: "var(--radius-xl)", border: "1px solid var(--color-border-dark)", background: "var(--color-surface-dark-raised)", padding: "0.875rem 1rem", fontSize: 12, fontFamily: "var(--font-mono)", color: "var(--color-text-dark-muted)", textAlign: "start" }}>
<span className="ltr-embed">{ep.split(" — ")[0]}</span>
{" — "}
{ep.split(" — ")[1]}
</li>
))}
</ul>
</article>
</div>
</section>
);
}