الباك-إند (موديول WebStore) جاهز وناضج ومختبَر — API كامل للمتجر (كتالوج، سلة، تشيك أوت، طلبات، روشتات، ولاء). المطلوب: بناء واجهة العميل من ثيم theme.zip («صيدليتي») وربطها بالـAPI. هذا تحليل الفجوة الكامل قبل كتابة أي كود.
Backend WebStore API is mature & tested. Goal: build the customer storefront FE from the theme and wire it to the API. Gap analysis before any code.
moon-erp-angular، مش ريبو جديد — عشان البيئات المتعددة، راجع §١٠). باقي القرارات المفتوحة: النطاق (MVP)، الدفع (COD أولًا)، اللغة — في §٨.
المالك عنده ثيم HTML كامل (theme.zip — صيدلية إلكترونية «صيدليتي»، ٢٩ صفحة، Tailwind + RTL عربي) وباك-إند مبني بالفعل (موديول WebStore — API متجر متكامل ومختبَر). عايز نعمل «العرض بتاع الاستور» = نحوّل الثيم الثابت إلى واجهة متجر حيّة تستهلك الـAPI الموجود: عرض المنتجات، السلة، التشيك أوت، الطلبات، الحساب، الروشتات، الولاء.
WebStore ناضج ومختبَر ✅٤ شهور تطوير (مارس→يوليو ٢٠٢٦)، ٤٤ كوميت (STORE-001..066)، اتبنى ليطابق عقد تطبيق موبايل («100% mobile app API parity»). مؤخّرًا اتصلّب كتير (typed-defaults، image URLs، company-pin) + إضافتان: تحليلات العملاء والولاء.
| الطبقة | موجود | مرجع |
|---|---|---|
| Auth | Sanctum PAT · تسجيل/دخول (username=email أو موبايل) · OTP · social-login (google/apple/facebook) · forgot/verify/reset · refresh-token | StoreAuthService · Storefront/AuthController |
| كتالوج | منتجات (فلترة/فرز/صفحات) · تفاصيل + صور + variants + tiers · تقييمات · فئات + tree · مصنّعين · tags | CatalogProductController · CatalogCategoryController |
| سلة | get/add/update/remove/clear · reorder · coupon apply/remove (auth-only) | CartController · CartService |
| تشيك أوت | validate · calculate (subtotal/discount/shipping) · place-order → طلب حقيقي + حدث محاسبي | CheckoutController · CheckoutService |
| طلبات | list/detail · track (سجل حالات) · cancel · rate · حالات per-company (لا تُثبّت في الواجهة) | OrderController |
| روشتات | list/detail/create/update/delete (صورة عبر upload ثم مسار) | PrescriptionController |
| حساب | profile · addresses (+governorates/cities cascade، الشحن per-city) · wishlist · loyalty summary/preview/points | Profile/Address/Wishlist/LoyaltyController |
| محتوى/إعدادات | settings (لوجو/شحن/عنوان/تواصل/سوشيال/معدّل نقاط) · sliders · ads · offers · boardings · pages (CMS) · payment-methods · branches · coupons | StoreSettingsController … |
| رفع ملفات | upload/multi/delete (≤10MB، قرص public) | FileUploadController |
Base path: https://moonui.elbaset.com/moon-erp-be/api/store/... · Header: X-Authorization: Bearer <token> (يُفضّل على هذا الاستضافة) · Token في response.token · i18n عبر Accept-Language: ar|en · Tenancy: single-tenant مثبّت على company_id=4 (الواجهة لا ترسل أي شيء) · CORS: افتراضي * — لا يحجب دومين منفصل.
| البُعد | الحالة |
|---|---|
| الصفحات | ٢٩ صفحة HTML مكتملة التصميم (رئيسية، كتالوج، تفاصيل منتج، سلة، تشيك أوت، طلبات، حساب، روشتات، auth، about/contact) |
| ربط API | صفر — app.js (٣٦٦ سطر) مفيهوش أي fetch/axios/XHR. كله سلوك عرض (سلايدر، منيو، toasts). عدّاد السلة نص ثابت "5"، مفيش localStorage، مفيش حالة auth |
| القالب المشترك | الهيدر/الفوتر/المنيو/الـmodals مكرّرة نسخ-لصق في كل الـ٢٩ صفحة — مفيش include/template |
| i18n/RTL | عربي فقط (dir="rtl" ثابت). style-en.css موجود بس غير مستخدَم. مفيش data-i18n. زر «اللغة» بيفتح منيو بس مش بيبدّل لغة |
| Tailwind | عبر CDN (cdn.tailwindcss.com) — dev-only، مش production-grade (غير مُنقّى، runtime) |
| مكتبات JS | لا شيء (لا Swiper/jQuery). السلايدر vanilla يدوي. مفيش HTTP client/state/router |
| الصور | روابط Unsplash خارجية عيّنة + Material Icons من CDN |
username + branch — وده يطابق الباك-إند (username=email/موبايل، والتسجيل بيتطلب branch_id). التصميم متوافق مع العقد، مش هيحتاج تعديل جذري في نموذج الدخول.| القدرة | الثيم (الآن) | الباك-إند | اللي لازم يتعمل | الحجم |
|---|---|---|---|---|
| عرض المنتجات | كروت ثابتة data-* | GET products جاهز | render ديناميكي + فلترة/فرز/صفحات + بحث | متوسط |
| تفاصيل منتج | معرض + تابات ثابتة | products/{id} + reviews | ربط الجاليري/السعر/المخزون/التقييمات/ذات صلة | متوسط |
| السلة | عدّاد DOM ثابت، بلا حفظ | سلة auth-only، بلا guest cart | سلة عميل client-side (localStorage) + مزامنة بعد الدخول | كبير |
| التشيك أوت | راديو دفع: كاش/بطاقة/InstaPay/محفظة | calculate + place-order (COD/pending)، بلا بوابة دفع | ربط الحساب/العنوان/الشحن + قرار الدفع (راجع ٨) | كبير |
| Auth | فورمز action="x.html" (reload) | Sanctum كامل + OTP | AJAX + تخزين توكن + OTP concat + branch select | متوسط |
| الطلبات/التتبّع | كروت ثابتة | orders + track جاهز | ربط القوائم + الحالات (من الـAPI مش ثابتة) | صغير |
| الحساب | فورمز ثابتة | profile/addresses/wishlist/coupons جاهز | ربط + cascade محافظة→مدينة | متوسط |
| الروشتات | رفع + قوائم ثابتة | upload ثم POST prescriptions | خطوتين رفع + ربط الحالة | صغير |
| الولاء | checkbox «استخدم النقاط» | loyalty/* + redeem inline | ربط الرصيد/المعاينة/الاستخدام في التشيك أوت | صغير |
| الهيدر المشترك | مكرّر ×٢٩، ثابت | — | partial JS واحد يُحقن (سلة/دخول/فئات/بحث) | متوسط |
| i18n (عربي/إنجليزي) | عربي فقط، en ميّت | Accept-Language جاهز | طبقة ترجمة كاملة لو الإنجليزي مطلوب | متوسط |
| Tailwind production | CDN dev-only | — | build حقيقي (purge) بدل CDN | صغير |
| الإشعارات | صفحة كاملة | stub (يرجّع فاضي) | إخفاء/تأجيل لحد ما يتبني نظام إشعارات | لاحقًا |
place-order بيعمل طلب Pending فقط (زي الدفع عند الاستلام) — مفيش بوابة دفع/redirect/webhook. لو نطلق بخيارات دفع إلكتروني بدون باك-إند دفع = وعد كاذب للعميل. لازم قرار (قسم ٨).api-client.js (base URL + X-Authorization + Accept-Language + 401)، store-state.js (سلة/wishlist/auth في localStorage)، config.js (API base + عملة).header.js/footer.js — يُحقن الهيدر الديناميكي بدل تعديل ٢٩ ملف.store.currency / store.name / ألوان (غير موجودة في settings).otp_bypass_enabled (افتراضي ON، كود 123456) + توصيل مُرسِل SMS (الـOTP حاليًا يُسجّل فقط).StoreCouponController::index لا يستخدم trait الـtenancy).المسارات الأساسية: Modules/WebStore/routes/{public,customer}.php · Storefront/*Controller.php · Http/Resources/* · Concerns/ResolvesStoreCompany.php · config/config.php · core app/Models/Product.php.
| الحالة | المعالجة |
|---|---|
| سلة الزائر (غير مسجّل) | لا يوجد guest cart بالباك-إند → سلة client-side في localStorage، تُدمج (replay POST cart/items) بعد الدخول. |
| منتج نفد/تعطّل بين الإضافة والدفع | checkout/validate + place-order يرجّعوا 422 → عرض رسالة وتحديث السلة. |
| توكن منتهٍ (401) | interceptor: مسح الجلسة + توجيه login (زي بورتال B2B). لا يوجد logout endpoint → الخروج = حذف التوكن محليًا. |
| OTP لا يصل فعليًا | حاليًا يُسجّل فقط (لا SMS). قبل الإطلاق: توصيل مُرسِل، وإلا forgot-password لا يعمل لمستخدم حقيقي. |
| الحالات (طلب/دفع) | per-company DB rows — تُقرأ من كائن الطلب (status.color/name)، ممنوع تثبيتها في الواجهة. |
| الشحن | per-city (city.shipping_cost) → يظهر فقط بعد اختيار عنوان في checkout/calculate. لا ضريبة (tax=0 ثابت). |
| اختيار الفرع عند التسجيل | التسجيل يتطلب branch_id → لازم GET branches واختيار إجباري (احتكاك UX — راجع ٨). |
| الصور | معظم الـresources ترجّع image_url مطلق؛ التحقق من image في قائمة المنتجات (accessor) — fallback placeholder. |
| العملة/اسم المتجر/الألوان | غير موجودة في settings → إما ثابتة في الواجهة أو نضيف إعدادات (راجع ٨). |
مرتّبة بالتسلسل. النطاق المقترح = MVP أولًا ثم توسعة (راجع قرار ٨-ب). كل WP = نطاق + ملفات + تحقّق. اختبارات الباك-إند خضراء بالفعل (٤٦ ملف)؛ اختبار الواجهة = يدوي/e2e على المسار الحقيقي.
سكافولد: build (Tailwind production + Vite)، api-client.js (base + X-Authorization + Accept-Language + 401 interceptor)، store-state.js (سلة/wishlist/auth localStorage)، header.js/footer.js مُحقَن، config.js. نشر skeleton.
تحقّق: صفحة تجيب GET settings + GET products وتعرضهم.
كتالوج: index (سلايدر/فئات/منتجات) · products (فلترة/فرز/صفحات/بحث) · product-detail (جاليري/سعر/مخزون/تقييمات/ذات صلة) · categories.
سلة+تشيك أوت: cart (client-side + coupon) · checkout (calculate + place-order) · order-success · order-tracking.
Auth: login · register (+branch) · verify-otp (٦ خانات) · forgot/reset.
تحقّق: طلب حقيقي كامل يظهر في لوحة ERP + يتتبّع. [FIN] — التشيك أوت يمرّ على Codex + مراجعة عناية (فلوس).
profile · addresses (+ cascade محافظة→مدينة + set default) · wishlist · coupons · orders + order-detail (reorder/cancel/rate). تحقّق: CRUD عنوان + إعادة طلب.
prescription-upload (upload ثم POST prescriptions) · prescriptions (قائمة+حالة) · prescription-detail. تحقّق: رفع صورة → تظهر بالحالة الصحيحة.
ربط sliders/ads/offers في الرئيسية · loyalty (رصيد/معاينة/استخدام في التشيك أوت) · about/contact/pages (CMS) · account-coupons/notifications (الإشعارات stub → تُخفى/تُؤجّل). تحقّق: استخدام نقاط يخصم فعليًا في الطلب.
إغلاق otp_bypass + توصيل SMS · قرار/تنفيذ بوابة الدفع · إصلاح تسريب الكوبونات · Tailwind production نهائي · i18n إنجليزي (لو مطلوب) · CORS lockdown (اختياري). [FIN]/أمان — Codex + مراجعة قبل الإطلاق العام.
خلّي الـstorefront ينزل لكل العملاء كابديت عادي (تفصيل §١٠):
(١) إضافة تطبيق storefront للـworkspace الحالي (ng generate application storefront في moon-erp-angular) ببناء --base-href /store/.
(٢) توسعة /fullpush + moonstack-release-from-git.sh ليبنوا التطبيقين (أدمن → /app، متجر → /store).
(٣) تسجيل dist المتجر كـexternal frontend ثانٍ في الحزمة (package.frontend2 → public/store) + إضافة 'store' لـmanifest.frontend_paths (استبدال نظيف بلا chunks قديمة).
(٤) كاتب config.json للمتجر (apiUrl per-client، يُصان عبر الابديتات — نمط /app/assets/config.json). تحقّق: إصدار تجريبي → العميل ياخد /store في ابديت delta بلا build عنده.
توضيح مهم: «مستقل» ≠ بدون Angular. «مستقل» = منفصل عن تطبيق الإدارة moon-erp في الكود والنشر. وده يتعمل بـAngular أو بالثيم كما هو — الاتنين ينفع «مستقل».
تطبيق Angular 21 جديد ومنفصل للمتجر (مش جوّه moon-erp)، نحتفظ بتصميم الثيم بالكامل (نفس HTML وكلاسات Tailwind، بس مقسّمة لمكوّنات وبياناتها من الـAPI). Tailwind يشتغل في Angular.
ليه: متسق مع خبرة الفريق (Angular shop، عندكم Angular 21 ضخم) · مكوّنات قابلة لإعادة الاستخدام · state/router/i18n جاهزين · صيانة أفضل. عيب: شغل أولي أكبر شوية (نقل الماركب لمكوّنات — التصميم متحفظ ١٠٠٪).
نحافظ على الـ٢٩ صفحة HTML بالظبط + طبقة JS خفيفة تكلّم الـAPI + build إنتاجي، ننشره كدومين مستقل.
ليه: أسرع إطلاق، أقل شغل، الثيم بلا لمس. عيب: vanilla عبر ٢٩ صفحة = صيانة أصعب، بلا إعادة استخدام مكوّنات، state/routing يدوي.
موديول متجر جوّه تطبيق الإدارة. يخلط موقع العميل بلوحة الإدارة (auth/routing/bundle مختلطين). غير الخيار الأول (Angular منفصل). كذلك Blade داخل الموديول يربط الواجهة بالمونوليث ويضيّع طبيعة الـSPA.
الموصى به: MVP أولًا (WP1: الرحلة الأساسية) ثم توسعة بالـWPs — أأمن وأسرع للتحقّق. البديل: كل الـ٢٩ صفحة دفعة واحدة (أشمل، أبطأ، مخاطرة أعلى).
الباك-إند مفيهوش بوابة دفع. الخيارات: (1) إطلاق COD/دفع-عند-الاستلام فقط الآن (نخفي بطاقة/محفظة) — أسرع وصادق. (2) دمج بوابة (Paymob/Fawry/InstaPay) — شغل باك-إند إضافي (redirect + webhook + تحديث حالة الدفع). توصيتي: ابدأ COD، وبوابة الدفع WP لاحق.
المتجر كما هو يبيع منتجات Inventory فقط (بوابة status=Active & is_active). تحاليل LIS وخدمات العيادة مش قابلة للبيع حاليًا. لو المالك عايز صيدلية تبيع تحاليل أونلاين = فيتشر باك-إند منفصل (خارج نطاق ربط الثيم).
التصميم لا يتغيّر — الثيم هو المعاينة البصرية. اللي يتغيّر = البيانات والسلوك (من ثابت → حيّ). المحاكيات أدناه تقارب شكل الثيم (برتقالي، Tajawal) وتوضّح الحالة قبل/بعد.
عدّاد 5 نص ثابت · منتج عيّنة · «أضف» بتحرّك DOM بلا حفظ
GET products · العدّاد = طول السلة الفعلية · «أضف» → localStorage/POST cart/items + toast
header.js)دايمًا «تسجيل الدخول» — مفيش فرع لحالة الدخول
مسجّل → «حسابي» + عدّادات حيّة · غير مسجّل → «تسجيل الدخول»
٤ خيارات — لكن ٣ منها بلا باك-إند دفع
نُظهر المتاح فعلًا فقط؛ الباقي «قريبًا» لحد بوابة الدفع (قرار ٨-ج)
التسجيل يتطلب branch_id إجباري — الثيم أصلًا فيه حقل «الفرع» فالتطابق موجود.
الهدف: العميل يعمل ابديت من MoonStack زي أي مرة → الاستور ينزل معاه تلقائيًا. الفحص أكّد إن ده ممكن ومباشر — MoonStack أصلًا نظام توصيل واجهات مبنية جاهزة.
public/app) + vendor → zip موقّع (RSA) + versions.json (١٤٧ إصدار، آخره 5.1.34).manifest.frontend_paths=['app','angular']).ng build عند العميل إطلاقًا (حد LVE PMEM يمنعه) — العميل ياخد ملفات مبنية بس..htaccess بيوجّه أي مسار /xxx/ جوّه public/xxx/ تلقائيًا.public/store»| السؤال | الإجابة المؤكّدة |
|---|---|
| ينزل كابديت MoonStack؟ | ✅ نعم — dist المتجر يُحزَم كـfrontend ثانٍ، ويُنسخ لدوكروت العميل زي /app بالظبط، عبر نفس مسار التوقيع/الـrollback/الـdelta. |
| build عند العميل؟ | ❌ لا — يُبنى على سيرفر الإصدار، ويُشحن مبني (يحترم حد PMEM). |
| يظهر عند العميل فين؟ | https://<client>/store/ (base-href /store/) — تلقائيًا بلا تعديل .htaccess. أو subdomain عبر vhost على public/store. |
| رابط الـAPI لكل عميل؟ | config.json خاص بالمتجر (apiUrl per-client) يُكتب وقت التثبيت ويُصان عبر الابديتات — نفس نمط /app/assets/config.json. |
moon-erp-angular). ريبو تالت جديد = بيئات عمياء عنه (مش هتكلونه ولا تبنيه ولا تنشره) + كل تولينج/onboarding/fullpush لازم يتعلّم عنه. التكلفة التشغيلية عبر ٣+ بيئات أكبر من مكسب «العزل النظيف».✅ القرار: الاستور تطبيق ثانٍ داخل ريبو الـFE الحالي (moon-erp-angular) — Angular multi-project workspace، مش ريبو جديد. الـangular.json أصلًا workspace (newProjectRoot: projects) فيه مشروع moon-erp؛ نضيف تطبيق storefront بـng generate application storefront → projects/storefront/ ببناء مستقل (--base-href /store/).
| البُعد | ريبو جديد ❌ | تطبيق ثانٍ في FE workspace ✅ |
|---|---|---|
| البيئات القائمة (moon2/3) | عمياء عنه (تعليمات قديمة) | تاخده مجانًا مع كلون الـFE |
| onboarding بيئة جديدة | «اكلون ريبو تالت» | صفر تغيير — جاي مع الـFE |
/fullpush | مسار ثالث كامل | سطر ng build storefront + deploy /store واحد |
| MoonStack release-from-git | كلون + كريدنشال ثانٍ | نفس الكلون، ng build ثانٍ بس |
| الفصل أدمن/متجر | ريبو | مشروعان منفصلان (سورس/راوتنج/dist مستقل) — فصل نظيف بردو |
التنازل المقبول: قطار إصدار مشترك (كوميت المتجر والأدمن على نفس الفرع/الميرج — طبيعي لفريقكم) + وقت بناء أطول شوية (تطبيقان). مقابل: صفر بيئة عمياء، أقل تولينج، مسار توزيع واحد.
ghp_…) — يُنصح بتدويرها (rotate).