Changelog — Lootah Robotics G1 Configurator
تاريخ التغييرات — 20 أبريل 2026
2ff21c5 — perf: compress GLBs 75%, add Draco decoder, loading spinner for attire
المشكلة
- الموبايلات القديمة كانت تعلّق لأوقات طويلة عند تحميل الأزياء الجديدة
- التبديل بين الأزياء (Robot Doctor / Security Guard) كان يعلّق بدون أي مؤشر تحميل
التغييرات
| الملف |
التغيير |
public/models/robot-doctor.glb |
ضغط Draco: 32 MB → 8.5 MB (توفير 74%) |
public/models/security-guard.glb |
ضغط Draco: 29 MB → 6.85 MB (توفير 77%) |
public/draco/ |
إضافة ملفات Draco decoder للمتصفح |
src/components/RobotModel.tsx |
تفعيل useGLTF.setDecoderPath('/draco/') |
src/components/RobotCanvas.tsx |
تفعيل Draco decoder + تخفيض DPR من 2x إلى 1.5x |
src/components/ScrollScene.tsx |
تفعيل Draco decoder |
src/components/ConfigPanel.tsx |
إضافة spinner عند تحميل زي جديد |
لماذا كانت المشكلة موجودة؟
الملفات كانت مضغوطة بـ Draco لكن المتصفح لم يكن عنده الـ decoder لفك الضغط، فكان يفشل بصمت ويرجع للروبوت الأساسي.
b2a484f — fix: dynamic attire buttons in ScrollOverlays + mobile touch support
المشكلة
- أزرار الأزياء في صفحة الـ Landing (Kandura, Vest, Suit) كانت مكتوبة بشكل ثابت (hardcoded)
- أي زي جديد يضاف من الأدمن لا يظهر في الصفحة الرئيسية
- الأزرار كانت تعمل بـ hover فقط (لا تعمل على الموبايل بالضغط)
التغييرات
| الملف |
التغيير |
src/components/ScrollOverlays.tsx |
جلب الأزياء ديناميكياً من /api/admin/pricing/ |
src/components/ScrollOverlays.tsx |
إضافة onClick بجانب onMouseEnter لدعم اللمس |
src/components/ScrollOverlays.tsx |
إضافة pointerEvents: 'auto' لأن الـ overlay كان pointerEvents: 'none' |
320b77b — fix: contacts API - use ADMIN_JWT_SECRET env var
المشكلة
- صفحة Contacts في الأدمن كانت ترجع خطأ 500
السبب
- Route الـ contacts كان يستخدم
JWT_SECRET بينما باقي الـ routes تستخدم ADMIN_JWT_SECRET
- أي JWT مولّد بـ
ADMIN_JWT_SECRET سيفشل التحقق عند استخدام متغير مختلف
التغييرات
| الملف |
التغيير |
src/app/api/admin/contacts/route.ts |
استخدام ADMIN_JWT_SECRET بدلاً من JWT_SECRET |
src/app/api/admin/contacts/route.ts |
إضافة رسالة خطأ واضحة إذا كان ADMIN_JWT_SECRET غير موجود |
25ffbf4 — feat: add favicon and app icons for PWA support
التغييرات
| الملف |
التغيير |
public/favicon.ico |
أيقونة المتصفح |
public/apple-touch-icon.png |
أيقونة iOS Home Screen |
public/icon-192.png |
أيقونة PWA 192px |
public/icon-192-maskable.png |
أيقونة PWA maskable 192px |
public/icon-512.png |
أيقونة PWA 512px |
public/icon-512-maskable.png |
أيقونة PWA maskable 512px |
src/app/layout.tsx |
إضافة <link rel="icon"> و <link rel="apple-touch-icon"> |
e686d41 — fix: use configStore.getState().setPersonaAttire in ScrollOverlays
المشكلة
السبب
كان الكود يستخدم useConfigStore.getState() لكن useConfigStore هو React hook (دالة عادية) وليس Zustand store. فقط configStore المُصدَّر من vanilla Zustand يملك .getState().
بالإضافة لذلك، اسم الدالة كان خاطئاً: setActivePersonaAttire بدلاً من setPersonaAttire.
التغييرات
| الملف |
التغيير |
src/components/ScrollOverlays.tsx |
useConfigStore.getState().setActivePersonaAttire() → configStore.getState().setPersonaAttire() |
e159965 — feat: add GET endpoint to retrieve contact requests with admin authentication
التغييرات
| الملف |
التغيير |
src/app/api/admin/contacts/route.ts |
إضافة GET endpoint لجلب طلبات التواصل مع التحقق من الأدمن |
ملاحظات عامة على البنية
متغيرات البيئة المطلوبة
ADMIN_JWT_SECRET= # مطلوب لجميع routes الأدمن
DATABASE_URL= # Prisma / SQLite
STRIPE_SECRET_KEY= # للمدفوعات
هيكل Stores
| Store |
الوصف |
configStore (vanilla Zustand) |
الألوان والزي النشط — يدعم .getState() |
useConfigStore (React hook) |
wrapper لـ configStore للاستخدام داخل components |
personaStore (vanilla Zustand) |
قائمة الأزياء — تُحمَّل من API عند التهيئة |
pricingStore |
أسعار العناصر — تُزامَن مع قاعدة البيانات |
تدفق الأزياء المرفوعة
- الأدمن يرفع
.glb من لوحة التحكم
- يُضغط تلقائياً بـ Draco عبر
upload-model route
- يُحفظ المسار في قاعدة البيانات (
PricingItem.modelPath)
- عند تحميل الصفحة،
personaStore.hydrate() يجلب الأزياء من /api/admin/pricing/
- تظهر تلقائياً في
ConfigPanel وفي ScrollOverlays (الصفحة الرئيسية)