Files
SS/FRONTEND.md

20 KiB
Raw Permalink Blame History

FRONTEND.md — شرح الواجهة الحالية + مواصفات التحسين (تصميم متجاوب + طرق شحن)

ملف مكمّل لـ AGENTS.md وWALLET.md. يشرح الواجهة الموجودة حالياً (public/index.html, public/app.js, public/styles.css) صفحة صفحة ودالة دالة، ثم يحدد مواصفات دقيقة لثلاث تحسينات مطلوبة: (1) نظام ألوان متناسق مع خيار تغيير اللون الأساسي، (2) تصميم متجاوب فعلي على الموبايل، (3) إصلاح طريقة شحن الرصيد لتدعم قنوات حقيقية (PayPal / شام كاش / USDT ... إلخ) بدل حقل نصي حر.


1. شرح الصفحات (كل صفحة شو وظيفتها)

الواجهة تطبيق صفحة واحدة (SPA) بدون أي framework — كل شي بملف app.js واحد، والتنقل بين "الصفحات" هو فعلياً تبديل محتوى #view حسب state.view.

شاشة الدخول (قبل تسجيل الدخول)

فورمين جنب بعض: تسجيل دخول وحساب جديد. عند نجاح الدخول تُخزَّن التوكنات وتنفتح الواجهة الرئيسية. لا رصيد ولا بيانات تُحمَّل قبل الدخول.

لوحة المستخدم (Dashboard)

نظرة سريعة: عدد الطلبات الكلي، عدد الطلبات قيد التنفيذ، عدد طلبات الشحن المعلّقة، وإجمالي المبلغ المصروف على الطلبات (محسوب من transactions نوع order). تحتها جدول بآخر 5 طلبات مع زر "عرض الكل" ينقلك لصفحة "طلباتي". الهدف: ملخص بلمحة، بدون أي إجراء — كله عرض فقط.

الخدمات والطلب (Services)

فورم إنشاء طلب بالأعلى (اختيار خدمة + رابط + كمية) وتحته كتالوج كامل بكل الخدمات النشطة (الاسم، التصنيف، السعر لكل 1000، الحد الأدنى والأعلى). السعر المعروض هو sell_price فقط — سعر المزوّد الحقيقي غير مرئي هون إطلاقاً (وهذا صحيح ومقصود، راجع AGENTS.md § 8). الهدف: تصفح + شراء.

طلباتي (Orders)

جدول بكل طلبات المستخدم نفسه فقط (الخدمة، الرابط، الكمية، المبلغ، المتبقي، الحالة). عرض فقط، لا إجراء — تحديث الحالة يصير تلقائياً من الـ job الدوري بالخلفية (order-status.scheduler.js)، فمجرد ما تعمل "تحديث" (زر أعلى الصفحة) بتشوف آخر حالة.

شحن الرصيد (Deposits) — ⚠️ هاي الصفحة اللي فيها المشكلة المطلوب حلها

فورم بسيط: مبلغ + حقل نصي حر لاسم الطريقة + مرجع التحويل. تحته جدول بطلبات الشحن السابقة وحالتها (معلّق/مقبول/مرفوض). المشكلة بالتفصيل بقسم 4 تحت.

كشف الحساب (Transactions)

جدول للقراءة فقط بكل حركة صارت على رصيد المستخدم (شحن، خصم طلب، استرجاع، تعديل أدمن) مع balance_after لكل صف — هاي هي "الحقيقة" اللي لازم تطابق الرصيد الظاهر بالأعلى دائماً (راجع WALLET.md § 2.4 التسوية الدورية).

لوحة الأدمن (Admin) — تظهر فقط لو role=admin

أربع بطاقات إحصائيات (عدد المستخدمين، طلبات معلّقة، إجمالي المبيعات، الربح)، فورم مزامنة خدمات من المزوّد، ثم أربع جداول إدارة: المستخدمون (حظر/تنشيط + تعديل رصيد يدوي عبر prompt())، طلبات الشحن (قبول/رفض)، الخدمات (تفعيل/إيقاف)، وكل الطلبات (عرض فقط).


2. شرح كل دالة بـ app.js (شو مهمتها)

الحالة والتخزين

  • state — كائن واحد مركزي فيه كل شي: التوكنات، الصفحة الحالية، وكل القوائم المحمّلة من الـ API (خدمات، طلبات، شحن، حركات، وبيانات الأدمن).
  • views — تعريف عناصر القائمة الجانبية (الآيدي، النص، هل هي للأدمن فقط).
  • el — كل عناصر الـ DOM المستخدمة كثيراً، محفوظة مرة وحدة بدل querySelector المتكرر.

أدوات مساعدة عامة

  • formatStatus(value) — يترجم قيم الحالة الإنجليزية (pending, completed...) لنص عربي للعرض.
  • showMessage(text, type) / clearMessage() — شريط رسالة واحد (نجاح/خطأ) يظهر إما بشاشة الدخول أو داخل التطبيق حسب أيّهم ظاهر حالياً.
  • friendlyMessage(text) — قاموس صغير يترجم رسائل خطأ إنجليزية معروفة من الـ API لعربي؛ أي رسالة مو موجودة بالقاموس تظهر زي ما هي بالإنجليزي (نقطة ضعف حالية — لازم القاموس يكبر مع الوقت).
  • getFormData(form) — يحوّل أي <form> لكائن JS بسيط.
  • api(path, options) — الغلاف الوحيد لكل نداء API: يحط توكن الدخول تلقائياً، يحوّل الرد لـ JSON، ويرمي Error برسالة الباك-إند لو الرد مو ناجح.
  • setSession(payload) / clearSession() — حفظ/مسح التوكنات بالـ localStorage وبالـ state.

البناء والعرض (Rendering)

  • renderNav() — يبني أزرار القائمة الجانبية من views، ويخفي رابط الأدمن عن غير الأدمن، ويعلّم الزر النشط.
  • table(headers, rows, emptyText) — دالة عامة تبني أي جدول HTML، وتعرض رسالة "لا توجد بيانات" لو القائمة فاضية.
  • statusBadge(status) — يغلّف formatStatus بوسم ملوّن (أخضر/أصفر/أحمر) حسب نوع الحالة.
  • renderDashboard / renderServices / renderOrders / renderDeposits / renderTransactions / renderAdmin — كل وحدة مسؤولة عن بناء HTML صفحتها فقط (مشروحة بالتفصيل بقسم 1)، وبتربط أحداث الفورم الخاصة فيها إذا وجدت.
  • render() — المايسترو: يحدّث العنوان واسم المستخدم والرصيد بالأعلى، يبني القائمة الجانبية، وينده دالة العرض المناسبة حسب state.view.

تحميل البيانات ودورة الحياة

  • loadUserData() — يجيب كل بيانات المستخدم بالتوازي (/me, /services, /orders, /deposits, /transactions)، وإذا كان أدمن يجيب بيانات لوحة الأدمن كمان بالتوازي.
  • boot() — نقطة الدخول عند تحميل الصفحة: لو ما في توكن يفتح شاشة الدخول، وإلا يحمّل البيانات ويفتح التطبيق؛ لو فشل التحميل (توكن منتهي مثلاً) يمسح الجلسة ويرجّع لشاشة الدخول مع رسالة خطأ.

إجراءات المستخدم العادي

  • submitOrder(event) — يمنع إعادة تحميل الصفحة، يرسل POST /orders، يعيد تحميل البيانات ويعرض رسالة نجاح/خطأ.
  • submitDeposit(event) — نفس الفكرة لـ POST /deposits.

إجراءات الأدمن

  • bindAdminActions() — ينده مرة وحدة بعد كل رسم للوحة الأدمن، ويربط كل الأزرار (مزامنة، حظر/تنشيط، تعديل رصيد، قبول/رفض شحن، تفعيل/إيقاف خدمة).
  • updateUser / reviewDeposit / updateService — أغلفة رفيعة تنده مسار PATCH المناسب بالباك-إند، وتعيد تحميل البيانات وتعرض رسالة.

الأحداث الرئيسية (أسفل الملف)

ربط فورم الدخول، فورم التسجيل، زر التحديث، زر الخروج، ثم نداء boot() مرة وحدة عند تحميل الملف.


3. نظام الألوان والتصميم المتجاوب (المطلوب تنفيذه)

3.1 المشكلة الحالية

الألوان بـ styles.css ثابتة بمتغيرات CSS (--accent, --accent-strong, --blue منفصلين عن بعض بدون علاقة منطقية بينهم)، وما في أي طريقة يغيّر فيها المستخدم اللون الأساسي. التجاوب موجود بس بسيط (breakpoint واحد بس عند 900px)، والجداول بتفيض أفقياً على الموبايل بدل ما تتكيّف.

3.2 نظام الألوان الجديد

وحّد كل متغيرات اللون الأساسي تحت اسم واحد مشتق:

:root {
  --primary-h: 174;      /* Hue */
  --primary-s: 64%;      /* Saturation */
  --primary-l: 30%;      /* Lightness — يتغيّر بالجافاسكربت */

  --primary: hsl(var(--primary-h) var(--primary-s) var(--primary-l));
  --primary-hover: hsl(var(--primary-h) var(--primary-s) calc(var(--primary-l) - 8%));
  --primary-soft: hsl(var(--primary-h) var(--primary-s) 94%);
  --primary-text-on: #ffffff;
}

كل مكان بالـ CSS كان يستخدم --accent / --accent-strong / --blue (الأزرار، الرابط النشط بالقائمة الجانبية، لون الرصيد بالأعلى، حدود التركيز :focus) لازم يتحول لـ --primary / --primary-hover / --primary-soft. الألوان الدلالية (أخضر=نجاح، أصفر=معلّق، أحمر=خطر) تبقى ثابتة ومنفصلة عن اللون الأساسي — ما تتأثر بتغيير اللون، لأنها تحمل معنى (حالة الطلب/الحظر) مو تزيين.

3.3 خيار تغيير اللون الأساسي (Theme Picker) — نسخة منقّحة

ملاحظة: النسخة الأولى (زر دائري صغير + قائمة نقاط 34px بدون تسميات) نُفّذت ووظيفياً شغّالة صح، لكن شكلها بدائي وما بتبين إنها "ميزة" حقيقية — أقرب لعنصر تجريبي. هاي المواصفات المحدّثة تحل هالموضوع شكلياً بدون ما تغيّر آلية الحفظ/التطبيق اللي شغّالة صح أصلاً.

الزر المُشغِّل (Trigger): بدل الدائرة الصغيرة، زر فيه أيقونة واضحة (SVG بسيط لقطارة ألوان أو لوحة رسام — inline SVG، بدون مكتبة أيقونات خارجية) + نص "المظهر" يظهر بجانبها من عرض التابلت فما فوق (أيقونة فقط على الموبايل لضيق المساحة). الهدف: المستخدم يفهم من أول نظرة إنه إعداد حقيقي مو زر تزييني.

اللوحة (Panel) بعد الضغط: بدل قائمة نقاط صغيرة، بطاقة منبثقة (Popover) واضحة:

  • عنوان أعلى اللوحة: "اختر اللون الأساسي" + زر إغلاق (×).
  • شبكة الألوان الجاهزة بحجم أكبر (44px بدل 34px)، وتحت كل لون اسمه (تركواز، أزرق، بنفسجي، أخضر، برتقالي، وردي) — مو لون بدون تسمية.
  • اللون المختار حالياً يبين عليه علامة واضحة (حلقة حدّ بلون داكن + ✓ صغيرة بالنص) — حالياً ما في أي إشارة لأي لون مفعّل.
  • قسم "لون مخصص" منفصل تحت الشبكة فيه: <input type="color"> بحجم أكبر + حقل نصي بجانبه يعرض/يقبل كود الـ HEX يدوياً (Bidirectional: تغيير أي وحدة يحدّث التانية) + شريحة معاينة حيّة صغيرة (زر تجريبي أو badge) تتلوّن فوراً أثناء السحب على منتقي اللون، قبل حتى ما يثبّت المستخدم اختياره.
  • اللوحة تنغلق: بالضغط خارجها، بزر ×، بمفتاح Escape، أو عند اختيار لون جاهز. انتقال فتح/إغلاق ناعم (transition على opacity/transform، مش ظهور مفاجئ).

آلية الحفظ والتطبيق تبقى كما هي بدون تغيير (شغّالة صح فعلاً):

  1. عند اختيار لون (Preset أو مخصص)، حوّل الـ HEX إلى HSL.
  2. حدّث المتغيرات الثلاث مباشرة على document.documentElement:
    document.documentElement.style.setProperty('--primary-h', h);
    document.documentElement.style.setProperty('--primary-s', `${s}%`);
    document.documentElement.style.setProperty('--primary-l', `${l}%`);
    
  3. احفظ الاختيار بـ localStorage.setItem('primaryColor', hex).
  4. بأول سطر تنفيذ بالملف (قبل أي رسم)، اقرأ القيمة المحفوظة وطبّقها فوراً — هذا موجود ومضبوط، لا تلمسه.

معيار القبول: الزر واضح إنه إعداد مو زخرفة (أيقونة + تسمية)، فتح اللوحة يبين أسماء الألوان وتعليم واضح على اللون المفعّل حالياً، تغيير اللون المخصص يعطي معاينة حيّة قبل التثبيت، وإعادة تحميل الصفحة (F5) بترجع لنفس اللون فوراً بدون وميض (زي ما هو مضبوط حالياً).

3.4 التجاوب (Responsive) — تفصيل الكسور (Breakpoints)

اعتمد ثلاث كسور بدل واحد:

  • ≥ 1024px (Desktop): القائمة الجانبية ثابتة زي الوضع الحالي.
  • 640–1024px (Tablet): القائمة الجانبية تتحول لصف علوي أفقي قابل للتمرير، أو تبقى جانبية بعرض أضيق مع أيقونات فقط.
  • < 640px (Mobile): القائمة الجانبية تختفي افتراضياً وتظهر كـ Drawer منزلق من فوق زر ☰ (Hamburger) يضاف بالـ topbar، مع خلفية شفافة (backdrop) تقفل القائمة عند الضغط عليها.

نقاط إضافية إلزامية:

  • الجداول: الجداول الكثيفة (الطلبات، كشف الحساب، طلبات الشحن) تتحول على الموبايل (< 640px) لعرض "بطاقات" (كل صف = بطاقة فيها أزواج تسمية/قيمة) بدل التمرير الأفقي — استخدم CSS فقط (display: block على tr/td مع data-label من الـ JS) بدون أي مكتبة خارجية.
  • الفورمات (.inline-form): تتكدس عمود واحد تحت 640px (مو 900px كما هو حالياً فقط — خليها تدريجية: عمودين بالتابلت، عمود واحد بالموبايل).
  • الأزرار وحقول الإدخال: ارتفاع لا يقل عن 44px بالموبايل لسهولة اللمس.
  • topbar: صندوق الرصيد والأزرار لازم يلتفوا (flex-wrap) بدل ما يفيضوا خارج الشاشة على الموبايل.

4. طرق شحن الرصيد — إعادة تصميم (المشكلة اللي لاحظها المالك)

4.1 المشكلة بالضبط

حقل "الطريقة" بفورم الشحن حالياً <input name="method" required placeholder="manual / usdt"> — نص حر بالكامل. هذا يعني:

  • المستخدم لازم يخمّن شو يكتب.
  • ما في أي تعليمات وين يحوّل المصاري فعلياً (رقم حساب PayPal؟ رقم محفظة USDT؟ حساب شام كاش؟) — الفورم ساكت تماماً عن هالمعلومة.
  • الأدمن ما عنده مكان يتحكم فيه بقنوات الدفع المتاحة من لوحة الأدمن — لازم كل قناة تنضاف بالكود.

4.2 الحل: جدول payment_methods قابل للتحكم من الأدمن

بدل تثبيت القنوات بالكود، أضف جدول يتحكم فيه الأدمن:

العمود النوع ملاحظات
id BIGINT PK
code VARCHAR(30) UNIQUE مثال: paypal, sham_cash, usdt_trc20, bank_transfer
label_ar VARCHAR(100) الاسم المعروض للمستخدم، مثال: "PayPal" أو "شام كاش"
instructions TEXT نص التعليمات (وين يحوّل، شو يكتب بالمرجع)
destination VARCHAR(255) NULL إيميل PayPal / رقم محفظة / رقم حساب — يُعرض جزء من التعليمات
is_active BOOLEAN DEFAULT true الأدمن يقدر يوقف قناة مؤقتاً
sort_order INT DEFAULT 0 ترتيب الظهور بالفورم
created_at / updated_at TIMESTAMP

مسارات جديدة:

GET   /payment-methods              → القنوات النشطة فقط (عام، يحتاج تسجيل دخول بس)
GET   /admin/payment-methods        → كل القنوات (نشطة وموقوفة)
POST  /admin/payment-methods        → إضافة قناة جديدة
PATCH /admin/payment-methods/:id    → تعديل/تفعيل/إيقاف قناة

تعديل على deposits: عمود method يصير يخزّن payment_methods.code بدل نص حر، والتحقق بالـ backend (zod) يرفض أي method مو موجود بقائمة القنوات النشطة — بدل قبول أي نص.

⚠️ هاي القنوات تبقى يدوية بالكامل (المستخدم يحوّل بنفسه ثم يدخل رقم العملية، والأدمن يوافق يدوياً) — تماماً متل الوضع الحالي، بس بقناة محددة وتعليمات واضحة بدل تخمين. ما في تكامل API حقيقي مع PayPal أو شام كاش بهالمرحلة (يحتاج توثيق واعتماد رسمي من المزوّد نفسه، وهذا قرار مفتوح لازم يُحسم لاحقاً إذا بدك أتمتة فعلية — راجع AGENTS.md § 12).

4.3 التعديل على فورم "شحن الرصيد" بالواجهة

  1. حمّل GET /payment-methods عند فتح الصفحة.
  2. بدّل حقل النص الحر بقائمة اختيار (<select>) بالأسماء العربية (label_ar).
  3. عند اختيار قناة، اعرض صندوق تعليمات بارز فوق حقل "مرجع التحويل" يعرض instructions وdestination (مثال: "حوّل عبر PayPal إلى: pay@panel.com، ثم انسخ رقم العملية بالأسفل").
  4. غيّر تسمية حقل "مرجع التحويل" ديناميكياً حسب القناة (مثال: "رقم عملية PayPal" / "رقم التحويل — شام كاش" / "TXID").
  5. (تحسين اختياري لاحقاً، مو إلزامي بهالمرحلة): إمكانية رفع صورة إثبات التحويل — يحتاج تخزين ملفات، أجّله لمرحلة لاحقة إذا بدك ياه.

معيار القبول: فتح فورم الشحن يعرض قنوات حقيقية (PayPal / شام كاش / USDT على الأقل) بدل حقل فاضي، واختيار أي قناة يعرض تعليمات واضحة، وإرسال طلب بقناة موقوفة أو غير موجودة يُرفض من الباك-إند مو بس من الواجهة.


5. ملاحظة للوكيل

هاي التحسينات الثلاث (الألوان/الثيمنج، التجاوب، طرق الشحن) موجودة كخطوات مرقّمة بـ TASKS.md (المرحلة 9 والمرحلة 10). نفّذها بنفس فلسفة بقية المشروع: كل تغيير بالباك-إند يمر بالتحقق (zod)، وكل تعديل على deposits.method ما لازم يكسر اختبارات WALLET.md § 4 الموجودة أصلاً — شغّلها بعد التعديل للتأكد.