دليل مبرمج الواجهة (فلاتر)

Frontend engineer guide (Flutter)

شرح هيكل التطبيقين، طبقة الشبكة، الشاشات، الأدوار، واللغة. كتالوج المسارات: frontend-api.html.

How the two apps are wired: packages, HTTP, screens, roles, locale. Endpoint catalog: frontend-api.html.

1. الهيكل — لا تبنِ شاشات من الصفر مرتين

المستودع مونوريبو. التطبيقان رفيعان؛ المنطق والشاشات مشتركة.

apps/farmer_app     org.nidaa.user     AppKind.farmer
apps/admin_app      org.nidaa.admin    AppKind.admin
packages/shared_ui  BridgeApp + كل الشاشات
packages/core       HttpBridgeRepository + النماذج

main.dart في كل تطبيق ينشئ HttpBridgeRepository() ثم BridgeApp(kind: ..., repository: ...).

عنوان المختبر: packages/core/lib/src/lab_url.dart → http://174.138.29.61:18080/v1.

البناء الميداني يستخدم --dart-define=USE_HTTP=true وGOOGLE_SERVER_CLIENT_ID=....

العقد الوحيد مع الخادم

كل الطلبات تمر من HttpBridgeRepository الذي ينفّذ BridgeRepository. لا تستدعِ http من الشاشة مباشرة.

2. المصادقة — اختلاف التطبيقين

تطبيقحقل الدخولمن يُقبل
مزارعهاتف + كلمة مروردور farmer فقط. ارفض مشرف/مالك محلياً.
إدارةبريد + كلمة مروردور admin أو owner فقط. ارفض مزارع محلياً.

login() يرسل phone إن لم يحتوِ المعرّف على @، وإلا email.

POST /v1/auth/login
{ "phone": "0120000001", "password": "LabBridge#2026" }
{ "email": "admin.lab@nidaa.local", "password": "LabBridge#2026" }

الاستجابة إما جلسة status: ok أو otp_required أو phone_required (بعد قوقل/فيسبوك بلا هاتف).

حسابات @nidaa.local تتجاوز OTP في المختبر. فاطمة 0120000004 هاتفها غير موثّق لكنها تدخل لأن البريد مختبري؛ المشرف يوثّق الرقم من تفاصيل المزارع.

موقوف (عمر 0120000005): الخادم يرفض الدخول. لا تحاول «إصلاح» ذلك في الواجهة.

بعد الجلسة

3. خريطة الشاشات → المستودع

التبويبات تُبنى في ShellPage حسب AppKind وuser.role وuser.supportEligible.

الشاشةمتى تظهراستدعاءات أساسية
الرئيسيةدائماًcontent()
الأسعاردائماًcropPrices() — الكتابة: createCropPrice / updateCropPrice
الإرشاد / المعلوماتدائماًcontent(stage:, kind:) — الكتابة: createContent / publishContent
الدفعاتمزارع + أهليةmyPayments() myBank() submitBank() myQr() getPayment()
المزارعونإدارةsearchFarmers getFarmer reviewPhone reviewBank setEligibility setPaymentStatus reopenPayment resetFarmerPayments suspendFarmer deleteFarmer
المحاصيلإدارةlookups() createCrop createMarket
مسح QRإدارة (أيقونة العنوان)verifyQr(token)
لوحة المالكowner فقطownerDashboard listBackups createBackup restoreBackup backupSchedule
الإشعاراتدائماًnotifications markRead — إن وُجد paymentId افتح PaymentDetailPage
الملفدائماًupdateProfile setLang

قواعد أعمال يجب احترامها في الواجهة

4. اللغة والوضع الداكن

packages/shared_ui/lib/src/l10n.dart — الدالة t(context, ar, en). اللغة الافتراضية عربية حتى تنجح اختبارات الويدجت.

5. كيف تضيف ميزة

  1. أضف المسار في Nest ثم الدالة في BridgeRepository وHttpBridgeRepository.
  2. حدّث النموذج في models.dart مع fromJson يتحمّل النصوص للأرقام.
  3. أضف الشاشة في shared_ui واستخدم t() لكل نص واجهة.
  4. اربط التبويب في ShellPage بشرط الدور/الأهلية.
  5. لا تبنِ APK المزارع والإدارة معاً (OOM). ابنِ واحداً ثم انسخ إلى infra/caddy/apk/.
حسابات التجريب الحالية
مزارع أحمد   0120000001     LabBridge#2026
مزارعة فاطمة 0120000004     LabBridge#2026
مزارع عمر    0120000005     موقوف — لا يدخل
مشرف         admin.lab@nidaa.local
مالك         owner.lab@nidaa.local

تقرير المنتج بالشاشات: report.html. كتالوج REST: frontend-api.html.

1. Architecture — do not duplicate screens

Monorepo. Both apps are thin; UI and networking are shared.

apps/farmer_app     org.nidaa.user     AppKind.farmer
apps/admin_app      org.nidaa.admin    AppKind.admin
packages/shared_ui  BridgeApp + every screen
packages/core       HttpBridgeRepository + models

Each main.dart builds HttpBridgeRepository() then BridgeApp(kind: ..., repository: ...).

Lab origin is packages/core/lib/src/lab_url.dart → http://174.138.29.61:18080/v1.

Field APKs are built with --dart-define=USE_HTTP=true and the Google web client id.

Talk to the server only through BridgeRepository. Never call http from a widget. IDs are strings. Tokens refresh on 401. Show messageAr/messageEn from errors. Use mediaAccessUrl for files.

2. Auth differs by app

Farmer: phone + password, role farmer only. Admin: email + password, admin or owner only. Reject the wrong role locally even if the token is valid.

@nidaa.local skips OTP in the lab. Omar 0120000005 is banned — login must fail. Persist tokens in secure storage; restore on launch via /auth/me.

3. Screen map

ShellPage builds tabs from kind, role, and supportEligible. Payments tab is farmer-only and eligibility-gated. Owner dashboard is owner-only. QR scan is an app-bar action on admin.

4. Locale

t(context, ar, en) in l10n.dart. Default locale is Arabic (widget tests). Published posts stay as authored. Notifications pick English fields when the UI is English. Dark mode is local.

5. Adding a feature

  1. Nest route → BridgeRepository + HttpBridgeRepository.
  2. Model fromJson that stringifies bigints.
  3. Screen in shared_ui with t() on every chrome string.
  4. Gate the tab in ShellPage.
  5. Do not build both APKs in parallel (OOM). Copy the APK to infra/caddy/apk/.
Farmer Ahmed    0120000001     LabBridge#2026
Farmer Fatima   0120000004     LabBridge#2026
Farmer Omar     0120000005     banned
Admin           admin.lab@nidaa.local
Owner           owner.lab@nidaa.local

Product report: report.html. REST catalog: frontend-api.html.