Initial SMM panel implementation
This commit is contained in:
+275
@@ -0,0 +1,275 @@
|
||||
# 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 نظام الألوان الجديد
|
||||
وحّد كل متغيرات اللون الأساسي تحت اسم واحد مشتق:
|
||||
|
||||
```css
|
||||
: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`:
|
||||
```js
|
||||
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 الموجودة أصلاً — شغّلها بعد التعديل للتأكد.
|
||||
Reference in New Issue
Block a user