create user pages crud also update pages ui build order and car ,driver first page interfase create model layer to componants

This commit is contained in:
m7amedez5511
2026-06-10 16:58:17 +03:00
parent 53c03e9867
commit e0e38dc87a
34 changed files with 2963 additions and 862 deletions

View File

@@ -4,38 +4,32 @@ import Link from "next/link";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { loginUser } from "../../lib/auth";
import Logo from "@/utils/logo";
import { Spinner } from "@/Components/UI";
import Logo from "../../utils/logo";
import { Button } from "../../Components/UI";
import { Input } from "../../Components/UI";
import { Alert } from "../../Components/UI";
export default function LoginPage() {
const router = useRouter();
const [identity, setIdentity] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError(null);
setLoading(true);
try {
await loginUser(identity, password);
window.location.href = "/dashboard"; // Use full page reload to ensure all client state is reset after login
console.log("Login successful", { identity });
window.location.href = "/dashboard";
} catch (err) {
// FIX: Surface specific error messages from auth.ts (invalid credentials,
// role denial, network failure) directly to the user.
if (err instanceof Error) {
// Network/fetch failure signals
if (
err.message.includes("fetch") ||
err.message.includes("network") ||
err.message.includes("Failed to fetch")
) {
setError(
"Unable to connect. Please check your internet connection and try again later.",
);
const msg = err.message.toLowerCase();
if (msg.includes("fetch") || msg.includes("network") || msg.includes("failed to fetch")) {
setError("Unable to connect. Please check your internet connection and try again later.");
} else {
setError(err.message);
}
@@ -50,45 +44,37 @@ export default function LoginPage() {
return (
<main className="min-h-screen bg-slate-100 text-slate-900">
<section className="grid min-h-screen w-full lg:grid-cols-[1fr_1fr]">
{/* ── Left panel ─────────────────────────────────── */}
<aside className="relative overflow-hidden bg-linear-to-br from-blue-900 via-blue-600 to-blue-700 px-8 py-10 text-white lg:px-12 lg:py-14">
<div
aria-hidden="true"
className="absolute inset-0 opacity-30"
style={{
backgroundImage:
"radial-gradient(circle, rgba(255,255,255,0.18) 1px, transparent 1px)",
backgroundImage: "radial-gradient(circle, rgba(255,255,255,0.18) 1px, transparent 1px)",
backgroundSize: "40px 40px",
}}
/>
<div className="relative z-10 flex h-full flex-col justify-between">
<div>
<Logo />
<Logo white={true} />
<h1 className="mt-10 max-w-md text-[36px] font-bold leading-tight tracking-[-0.5px]">
اللوجستيات ببساطة، والتسليم بثقة.
</h1>
<p className="mt-4 max-w-md text-[15px] leading-7 text-white/75">
راقب عمليات التسليم، وقم بتنظيم السائقين، وحافظ على رؤية كل طلب
من خلال تسجيل دخول آمن واحد.
راقب عمليات التسليم، وقم بتنظيم السائقين، وحافظ على رؤية كل طلب من خلال تسجيل دخول آمن واحد.
</p>
<div className="mt-8 space-y-3">
{[
[
"التنسيق اللحظي",
"تتبع كل طلب من الاستلام إلى التسليم في مكان واحد.",
],
[
"وصول آمن",
"الجلسات المعتمدة على الصلاحيات تبقي مسارات العميل والإدارة منفصلة.",
],
[
"عمليات سريعة",
"استخدم لوحة الإدارة لمراجعة الحالة والتنبيهات وحركة الرحلات.",
],
["التنسيق اللحظي", "تتبع كل طلب من الاستلام إلى التسليم في مكان واحد."],
["وصول آمن", "الجلسات المعتمدة على الصلاحيات تبقي مسارات العميل والإدارة منفصلة."],
["عمليات سريعة", "استخدم لوحة الإدارة لمراجعة الحالة والتنبيهات وحركة الرحلات."],
].map(([title, desc]) => (
<article
key={title}
className="flex items-start gap-3 rounded-[10px] border border-white/15 bg-white/10 p-3"
>
<div className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-white/15 text-sm">
<div aria-hidden="true" className="mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-white/15 text-sm">
</div>
<div>
@@ -99,71 +85,61 @@ export default function LoginPage() {
))}
</div>
</div>
<p className="text-[12px] text-white/50">
مصمم لفرق العمليات التي تحتاج إلى الوضوح والسرعة والثقة.
</p>
<p className="text-[12px] text-white/50">مصمم لفرق العمليات التي تحتاج إلى الوضوح والسرعة والثقة.</p>
</div>
</aside>
{/* ── Right panel ────────────────────────────────── */}
<section className="flex items-center justify-center bg-slate-50 px-6 py-10 lg:px-8">
<form
onSubmit={handleSubmit}
className="w-full max-w-100 rounded-[14px] border border-slate-200 bg-white p-8 shadow-sm"
className="w-full max-w-[400px] rounded-[14px] border border-slate-200 bg-white p-8 shadow-sm"
noValidate
>
<p className="text-[28px] font-bold text-slate-900">
مرحبًا بعودتك
</p>
<p className="mt-1 text-[14px] text-slate-500">
سجل الدخول لإدارة عمليات التسليم
</p>
<p className="text-[28px] font-bold text-slate-900">مرحبًا بعودتك</p>
<p className="mt-1 text-[14px] text-slate-500">سجل الدخول لإدارة عمليات التسليم</p>
<label className="mt-6 block text-[11px] font-bold uppercase tracking-[0.5px] text-slate-500">
البريد الإلكتروني أو رقم الهاتف أو اسم المستخدم
<input
<div className="mt-6 flex flex-col gap-4">
<Input
label="البريد الإلكتروني أو رقم الهاتف أو اسم المستخدم"
autoComplete="username"
className="mt-2 h-11 w-full rounded-[9px] border border-slate-200 bg-white px-3 text-[14px] text-slate-900 outline-none transition focus:border-blue-500 focus:ring-4 focus:ring-blue-100"
value={identity}
onChange={(event) => setIdentity(event.target.value)}
onChange={(e) => setIdentity(e.target.value)}
placeholder="name@company.com / 05xxxxxxxx / username"
required
/>
</label>
<label className="mt-4 block text-[11px] font-bold uppercase tracking-[0.5px] text-slate-500">
كلمة المرور
<input
<Input
label="كلمة المرور"
type="password"
autoComplete="current-password"
className="mt-2 h-11 w-full rounded-[9px] border border-slate-200 bg-white px-3 text-[14px] text-slate-900 outline-none transition focus:border-blue-500 focus:ring-4 focus:ring-blue-100"
value={password}
onChange={(event) => setPassword(event.target.value)}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
required
/>
</label>
</div>
<div className="mt-3 flex justify-end">
<Link
href="/forgot-password"
className="text-[12px] text-blue-600 hover:underline"
>
<Link href="/forgot-password" className="text-[12px] text-blue-600 hover:underline">
هل نسيت كلمة المرور؟
</Link>
</div>
{error ? (
<p className="mt-4 rounded-[9px] border border-red-200 bg-red-50 p-3 text-[13px] text-red-600">
{error}
</p>
) : null}
{error && (
<div className="mt-4">
<Alert type="error" message={error} onClose={() => setError(null)} />
</div>
)}
<button
<Button
type="submit"
disabled={loading}
className="mt-6 inline-flex h-11.5 w-full items-center justify-center rounded-[9px] bg-blue-600 text-[15px] font-semibold text-white transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-70"
loading={loading}
fullWidth
className="mt-6 h-11"
>
{loading ? <Spinner /> : "تسجيل الدخول"}
</button>
{loading ? "جاري تسجيل الدخول…" : "تسجيل الدخول"}
</Button>
<p className="mt-4 text-center text-[13px] text-slate-500">
ليس لديك حساب؟{" "}
@@ -176,4 +152,4 @@ export default function LoginPage() {
</section>
</main>
);
}
}