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
المشكلة
- بناء Docker كان يفشل مع خطأ TypeScript:
السبب
كان الكود يستخدم 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 لجلب طلبات التواصل مع التحقق من الأدمن |
ملاحظات عامة على البنية
متغيرات البيئة المطلوبة
هيكل 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 (الصفحة الرئيسية)