import React, { useState, useMemo } from "react";
import { User, Users, Baby, MapPin, FileWarning, Shield } from "lucide-react";
export default function DeglaInternalSystem() {
const [step, setStep] = useState(1);
const [error, setError] = useState("");
const [data, setData] = useState({
processType: "",
mainMember: "",
dependentMember: "",
totalChildren: "",
over16: "",
under16: ""
});
const update = (fields) => setData((p) => ({ ...p, ...fields }));
const childrenValid =
parseInt(data.totalChildren || 0) ===
parseInt(data.over16 || 0) + parseInt(data.under16 || 0);
const next = () => {
setError("");
if (step === 3 && data.totalChildren !== "") {
if (!childrenValid) {
setError("عدد الأبناء غير متطابق مع التوزيع العمري.");
return;
}
}
setStep((s) => s + 1);
};
const requirements = useMemo(() => {
const list = [];
if (!data.mainMember) return list;
const main =
data.mainMember === "husband"
? "الزوج"
: data.mainMember === "wife"
? "الزوجة"
: "العضو";
list.push(`أصل بطاقة رقم قومي سارية لـ (${main}).`);
list.push(`أصل المؤهل الدراسي لـ (${main}).`);
if (data.mainMember !== "single") {
if (data.dependentMember === "spouse") {
const dep = main === "الزوج" ? "الزوجة" : "الزوج";
list.push(`أصل بطاقة رقم قومي سارية لـ (${dep}).`);
list.push(`أصل المؤهل الدراسي لـ (${dep}).`);
list.push(`أصل قسيمة الزواج المميكنة.`);
}
if (data.dependentMember === "notIncluded") {
list.push(`صورة بطاقة الطرف غير المنضم.`);
list.push(`في حالة السفر: صورة جواز السفر + الإقامة.`);
}
}
if (parseInt(data.totalChildren) > 0) {
if (parseInt(data.under16) > 0)
list.push(`أصل شهادة ميلاد مميكنة لعدد (${data.under16}).`);
if (parseInt(data.over16) > 0)
list.push(`بطاقة رقم قومي لعدد (${data.over16}).`);
}
if (data.processType === "governorate") {
list.push("أصل إيصال مرافق حديث (مياه/غاز/كهرباء).");
}
return list;
}, [data]);
return (
<div dir="rtl" className="min-h-screen bg-black text-white font-['Cairo'] p-6 flex justify-center">
<div className="w-full max-w-xl space-y-8">
<div className="text-center space-y-2">
<h1 className="text-3xl font-black text-[#FFD700]">
نظام العضويات الداخلي
</h1>
<p className="text-xs tracking-widest text-[#FFD700]/60">
Wadi Degla Clubs
</p>
</div>
<div className="h-2 bg-[#FFD700]/20 rounded-full">
<div
className="h-2 bg-[#FFD700] rounded-full transition-all"
style={{ width: `${(step / 5) * 100}%` }}
/>
</div>
{step === 1 && (
<div className="space-y-4">
<Option
icon={<Shield />}
label="تسجيل"
onClick={() => {
update({ processType: "regular" });
next();
}}
/>
<Option
icon={<MapPin />}
label="تسجيل محافظات"
onClick={() => {
update({ processType: "governorate" });
next();
}}
/>
</div>
)}
{step === 2 && (
<div className="space-y-4">
<Option icon={<User />} label="الزوج" onClick={() => { update({ mainMember: "husband" }); next(); }} />
<Option icon={<User />} label="الزوجة" onClick={() => { update({ mainMember: "wife" }); next(); }} />
<Option icon={<Users />} label="أعزب / عزباء" onClick={() => { update({ mainMember: "single" }); setStep(4); }} />
</div>
)}
{step === 3 && data.mainMember !== "single" && (
<div className="space-y-4">
<Option label="ضم الزوج / الزوجة" onClick={() => { update({ dependentMember: "spouse" }); next(); }} />
<Option label="عدم ضم الزوج" onClick={() => { update({ dependentMember: "notIncluded" }); next(); }} />
<Option label="بدون" onClick={() => { update({ dependentMember: "none" }); next(); }} />
</div>
)}
{step === 4 && (
<div className="space-y-4">
<input
type="number"
placeholder="إجمالي عدد الأبناء"
className="w-full p-4 bg-[#111] border border-[#FFD700]/30 rounded-xl text-center text-xl"
value={data.totalChildren}
onChange={(e) => update({ totalChildren: e.target.value })}
/>
{parseInt(data.totalChildren) > 0 && (
<div className="grid grid-cols-2 gap-4">
<input
type="number"
placeholder="فوق 16"
className="p-3 bg-[#111] border border-[#FFD700]/30 rounded-xl text-center"
value={data.over16}
onChange={(e) => update({ over16: e.target.value })}
/>
<input
type="number"
placeholder="تحت 16"
className="p-3 bg-[#111] border border-[#FFD700]/30 rounded-xl text-center"
value={data.under16}
onChange={(e) => update({ under16: e.target.value })}
/>
</div>
)}
{error && (
<div className="bg-yellow-600 text-black p-3 rounded-xl font-bold flex items-center gap-2">
<FileWarning /> {error}
</div>
)}
<button onClick={next} className="w-full bg-[#FFD700] text-black font-black p-4 rounded-xl">
المتابعة
</button>
</div>
)}
{step === 5 && (
<div className="space-y-6 bg-[#111] p-6 rounded-2xl border border-[#FFD700]/40">
<h3 className="text-[#FFD700] font-black text-lg">
المستندات المطلوبة
</h3>
<ul className="space-y-3">
{requirements.map((r, i) => (
<li key={i} className="flex gap-3 items-start">
<span className="w-3 h-3 bg-[#FFD700] rounded-full mt-2" />
{r}
</li>
))}
</ul>
<div className="bg-yellow-500 text-black p-4 rounded-xl font-bold text-sm">
⚠️ لا يتم إضافة الابن إذا كانت وظيفته مثبتة بالبطاقة.
</div>
</div>
)}
</div>
</div>
);
}
function Option({ icon, label, onClick }) {
return (
<button
onClick={onClick}
className="w-full flex items-center justify-between bg-[#111] border border-[#FFD700]/40 p-5 rounded-2xl hover:bg-[#FFD700] hover:text-black transition-all"
>
<span className="flex items-center gap-3 font-black">
{icon && (
<span className="w-10 h-10 bg-[#FFD700] text-black rounded-xl flex items-center justify-center">
{icon}
</span>
)}
{label}
</span>
</button>
);
}
Now let me read the existing project files and save the logo image.
Now let me save the logo image and build the app. Let me create all the files:
Now let me update the layout and create the components:
Now let me create the components:
Now let me create the main membership wizard component and the page:
Now let me also create a PWA manifest and make sure the fonts are properly configured:
Now let me verify the globals.css looks correct:
Everything looks good. Here's what I built for your Wadi Degla Clubs internal membership system:
Structure: The app is split into clean, modular components under components/membership/ — a header with the Wadi Degla logo, a step progress bar, reusable option buttons, and the requirements results view, all orchestrated by the main MembershipWizard client component.
Design: The theme uses the club's signature black and gold palette, with oklch-based design tokens for consistent theming. All interactive elements have minimum 44px+ touch targets, inputs use inputMode="numeric" for proper mobile keyboards, and the viewport is locked to prevent iOS auto-zoom. The RTL layout with Cairo Arabic font ensures proper right-to-left reading flow.
Flow: The 5-step wizard walks users through selecting registration type, main member, dependent member, children count (with age validation), and finally displays the personalized list of required documents — including a back button and full reset capability.
import React, { useState, useMemo } from "react";
export default function SmartMembershipAssistant() {
const [form, setForm] = useState({
registrationType: "regular",
mainMember: "",
dependentStatus: "none",
under16: 0,
over16: 0,
});
const [birthDate, setBirthDate] = useState("");
const [ageResult, setAgeResult] = useState("");
const update = (field, value) => {
setForm((prev) => ({ ...prev, [field]: value }));
};
// ===== Age Validator Logic =====
const calculateAge = (date) => {
if (!date) return "";
const today = new Date();
const birth = new Date(date);
let age = today.getFullYear() - birth.getFullYear();
const m = today.getMonth() - birth.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) {
age--;
}
if (age < 16) {
return `العمر ${age} سنة — يحتاج شهادة ميلاد`;
} else if (age >= 16 && age < 21) {
return `العمر ${age} سنة — يحتاج بطاقة رقم قومي`;
} else if (age >= 21 && age <= 25) {
return `العمر ${age} سنة — يحتاج برنت تأميني`;
} else {
return `العمر ${age} سنة — غير مسموح بالإضافة`;
}
};
const documents = useMemo(() => {
const list = [];
const notices = [];
const declarations = [];
if (!form.mainMember) return { list, notices, declarations };
if (form.mainMember === "single") {
list.push("أصل بطاقة رقم قومي سارية فقط");
list.push("أصل المؤهل الدراسي");
} else {
list.push("أصل بطاقة رقم قومي سارية (للزوج والزوجة) أو أصل جواز سفر ساري");
list.push("أصل المؤهل الدراسي");
list.push("أصل قسيمة الزواج المميكنة");
list.push("أصل شهادات ميلاد مميكنة للأبناء");
if (form.dependentStatus === "notIncluded") {
declarations.push(
"يقوم العضو الرئيسي بالتوقيع على إقرار رسمي بعدم ضم الزوج / الزوجة إلى ملف العضوية."
);
}
if (form.dependentStatus === "abroad") {
declarations.push(
"يقوم العضو الرئيسي بالتوقيع على إقرار يفيد بعدم ضم العضو التابع لوجوده خارج الدولة."
);
}
}
if (form.mainMember !== "single") {
if (form.under16 > 0) {
list.push("أصل شهادة ميلاد مميكنة أو أصل جواز سفر ساري (أقل من 16 سنة)");
}
if (form.over16 > 0) {
list.push("أصل بطاقة رقم قومي سارية أو أصل جواز سفر ساري (16 سنة فأكثر)");
declarations.push(
"يقوم العضو الرئيسي بالتوقيع على إقرار يفيد بصحة بيانات وسن العضو التابع."
);
notices.push("لا يُعتد بالكارنيه بدءًا من 1/7 من كل عام.");
notices.push("لا يتم إضافة الابن إذا كانت وظيفته مثبتة بالبطاقة.");
}
}
if (form.registrationType === "governorate") {
list.push("أصل إيصال مرافق حديث");
notices.push("(حديث – عنوان واضح – اسم – استهلاك فعلي)");
notices.push("معدل الاستهلاك:");
notices.push("• أكثر من 5 متر مكعب (مياه / غاز)");
notices.push("• أكثر من 5 كيلووات كهرباء");
notices.push("• أو شحن كارت بحد أدنى 50 جنيه");
}
return { list, notices, declarations };
}, [form]);
return (
<div dir="rtl" className="min-h-screen bg-gray-50 p-8 font-['Cairo']">
{/* Header */}
<div className="flex items-center justify-between border-b-2 border-yellow-500 pb-4 mb-8">
<h1 className="text-2xl font-bold">Smart Membership Assistant</h1>
<img src="/logo.png" alt="Club Logo" className="h-16 object-contain" />
</div>
<div className="grid grid-cols-3 gap-8">
{/* Controls */}
<div className="col-span-1 bg-white p-6 rounded-xl shadow-sm border space-y-6">
<div>
<label className="font-semibold block mb-2">نوع التسجيل</label>
<select
className="w-full border p-3 rounded-lg"
onChange={(e) => update("registrationType", e.target.value)}
>
<option value="regular">عادي</option>
<option value="governorate">محافظات</option>
</select>
</div>
<div>
<label className="font-semibold block mb-2">العضو الرئيسي</label>
<select
className="w-full border p-3 rounded-lg"
onChange={(e) => update("mainMember", e.target.value)}
>
<option value="">اختر</option>
<option value="husband">الزوج</option>
<option value="wife">الزوجة</option>
<option value="single">أعزب</option>
</select>
</div>
{form.mainMember && form.mainMember !== "single" && (
<>
<div>
<label className="font-semibold block mb-2">ضم الطرف الآخر</label>
<select
className="w-full border p-3 rounded-lg"
onChange={(e) => update("dependentStatus", e.target.value)}
>
<option value="none">ضم</option>
<option value="notIncluded">عدم ضم</option>
<option value="abroad">خارج البلاد</option>
</select>
</div>
<div>
<label className="font-semibold block mb-2">عدد الأبناء أقل من 16</label>
<input
type="number"
min="0"
className="w-full border p-3 rounded-lg"
onChange={(e) => update("under16", Number(e.target.value))}
/>
</div>
<div>
<label className="font-semibold block mb-2">عدد الأبناء 16 فأكثر</label>
<input
type="number"
min="0"
className="w-full border p-3 rounded-lg"
onChange={(e) => update("over16", Number(e.target.value))}
/>
</div>
</>
)}
</div>
{/* Output */}
<div className="col-span-1 bg-white p-6 rounded-xl shadow-sm border space-y-6">
<h2 className="font-bold text-lg">المستندات المطلوبة</h2>
<ul className="space-y-2">
{documents.list.map((doc, i) => (
<li key={i}>• {doc}</li>
))}
</ul>
{documents.declarations.length > 0 && (
<>
<h2 className="font-bold text-lg mt-4">إقرارات وتعهدات</h2>
<ul className="space-y-2">
{documents.declarations.map((d, i) => (
<li key={i}>• {d}</li>
))}
</ul>
</>
)}
{documents.notices.length > 0 && (
<div className="bg-yellow-50 border border-yellow-300 p-4 rounded-lg text-sm">
<h3 className="font-bold mb-2">تنبيهات</h3>
{documents.notices.map((n, i) => (
<div key={i}>{n}</div>
))}
</div>
)}
</div>
{/* Age Validator */}
<div className="col-span-1 bg-white p-6 rounded-xl shadow-sm border">
<h2 className="font-bold text-lg mb-4">حاسبة السن القانوني</h2>
<input
type="date"
className="w-full border p-3 rounded-lg"
value={birthDate}
onChange={(e) => {
setBirthDate(e.target.value);
setAgeResult(calculateAge(e.target.value));
}}
/>
{ageResult && (
<div className="mt-4 bg-gray-100 p-4 rounded-lg text-sm font-medium">
{ageResult}
</div>
)}
</div>
</div>
</div>
);
}
Now I understand the full picture. The user wants to replace the step-by-step wizard with a new dashboard-style layout: a 3-column grid with controls (left), documents output (center), and age validator (right), plus new features like dependent "abroad" status, declarations/notices sections, and the age calculator. Let me restructure the components.
Now let me rewrite the main wizard as the dashboard and update the header:
Now update the header to better fit the dashboard style:
Now let me clean up the old components that are no longer needed:
I've completely restructured the app from a step-by-step wizard to a dashboard-style layout matching your new code. The new design features a 3-column grid on desktop (controls, documents, age calculator) that stacks vertically on mobile. The controls panel uses dropdowns and inputs for instant interaction, the documents panel reactively shows required documents/declarations/notices based on selections, and the age calculator provides color-coded feedback for the four age categories (birth cert, ID card, insurance print, and blocked). All old wizard components (progress bar, option buttons, requirements list) have been removed and replaced with the new panel-based architecture.
import React, { useState } from "react";
export default function SmartMembershipAssistant() {
const initialState = {
registrationType: "",
mainMember: "",
spouseStatus: "",
children: [],
noChildren: false
};
const [form, setForm] = useState(initialState);
const [step, setStep] = useState(1);
const resetAll = () => {
setForm(initialState);
setStep(
You can send messages while the preview loads