# Mobile App Plan — Client App (Guardians & Participants)

## Vision
A visually stunning, data-driven mobile app for guardians and participants. Push notifications that work even when app is closed. Not a business management app — it's the client-facing experience.

## Target Users
- **Guardians (parents)** — see their children's progress, pay invoices, get notified
- **Participants (players)** — see their schedule, attendance, achievements

---

## Per-Instance Architecture (ONE APP PER CLIENT)

### The Model
Each client (academy) gets their own app uploaded to Play Store / App Store under their brand. The Flutter source code is IDENTICAL across all builds — the only difference is a single config file that points to the instance URL.

### How It Works

```
flutter_app/
├── lib/
│   ├── config/
│   │   └── instance.dart        ← Config: URL + package ID
│   ├── core/
│   ├── features/
│   └── main.dart
├── android/
│   ├── app/src/main/
│   │   ├── google-services.json  ← Per-client Firebase config (Android)
│   │   └── res/
│   │       ├── mipmap-hdpi/ic_launcher.png    ← App icon
│   │       ├── mipmap-xhdpi/ic_launcher.png
│   │       ├── mipmap-xxhdpi/ic_launcher.png
│   │       └── mipmap-xxxhdpi/ic_launcher.png
│   └── ...
├── ios/
│   ├── Runner/
│   │   ├── GoogleService-Info.plist  ← Per-client Firebase config (iOS)
│   │   └── Assets.xcassets/AppIcon.appiconset/  ← iOS app icon
│   └── ...
└── assets/
    └── branding/
        ├── logo.png              ← Academy logo (used in splash + login)
        ├── logo_dark.png         ← Logo variant for dark backgrounds
        └── splash.png            ← Full splash screen image (1080x1920)
```

### What You Edit Per Client (4 things only)

| File | What |
|------|------|
| `lib/config/instance.dart` | Instance URL + package ID + app name |
| `assets/branding/logo.png` + `splash.png` | Academy logo + splash screen |
| `android/app/google-services.json` | Firebase config (Android) |
| Android `ic_launcher.png` (all sizes) | App icon on home screen |

### instance.dart — The Single Source of Truth

```dart
class InstanceConfig {
  // ===== CHANGE THESE PER CLIENT =====
  static const String baseUrl = 'https://oc-sport.caprover.al-arcade.com';
  static const String appName = 'OC Sport';
  static const String appNameAr = 'او سي سبورت';
  static const String packageId = 'com.alcaptain.ocsport';  // unique per Play Store listing
  // ====================================

  // These are fetched from the API at runtime — no need to hardcode
  // Logo, colors, branding → GET /api/v1/app/config
}
```

### What Happens at App Startup

```
1. App reads instance.dart → knows the base URL
2. App calls GET {baseUrl}/api/v1/app/config (public, no auth)
3. Server returns:
   - academy name (ar + en)
   - logo URL
   - primary color / accent color / theme
   - feature flags (shop enabled? events enabled? chat enabled?)
   - minimum app version (for forced updates)
   - maintenance mode flag
4. App caches this config locally (refreshes every 24h or on app open)
5. App renders splash + login screen with the academy's branding
```

### To Deploy for a New Client

1. Clone the Flutter repo
2. Edit `lib/config/instance.dart` → paste the instance URL + package ID + name
3. Replace `assets/branding/logo.png` + `splash.png` with the client's branding
4. Replace Android `ic_launcher.png` (use Android Studio Image Asset tool or https://icon.kitchen)
5. Drop in `google-services.json` (from their Firebase project)
6. Run `flutter build appbundle --release`
7. Upload to Play Store under the client's developer account
8. On the Laravel instance: add the Firebase service account JSON to system settings
9. Done — notifications flow from their instance to their app

---

## Authentication — Demo Mode (No SMS)

### The Problem
We don't have an SMS provider yet. We need the app to be testable and demo-able without real OTP delivery.

### Solution: Dual-Mode Auth

```
System Setting: `auth_otp_mode` = 'sms' | 'demo'

Mode: 'demo' (default for new instances)
  - OTP is always 123456
  - No SMS sent
  - Console/log shows the OTP
  - Works for prototyping, internal testing, demos
  - Yellow banner in app: "وضع تجريبي — رمز التحقق: 123456"

Mode: 'sms' (production)
  - Real OTP generated (6 random digits)
  - Sent via configured SMS provider
  - 5-minute TTL in cache
  - Rate limited: 3 attempts per phone per 10 min
```

### SMS Provider Integration (when ready)

```php
// config/services.php
'sms' => [
    'provider' => env('SMS_PROVIDER', 'log'),  // 'log', 'twilio', 'vonage', 'smsmisr', 'victorylink'
    'from' => env('SMS_FROM', 'ElCaptain'),
    // Provider-specific keys in .env
],

// Providers for Egypt:
// - SMS Misr (smsmisr.com) — cheapest local, Arabic support
// - Victory Link — popular in Egypt
// - Twilio — international, reliable, expensive
// - Vonage — international alternative
```

### Auth Flow (Complete)

```
Phone Input → POST /api/v1/auth/otp/request {phone: "+201012345678"}
  ├── demo mode: cache OTP as "123456", return {sent: true, demo: true}
  └── sms mode: generate random 6-digit, send SMS, cache 5min, return {sent: true}

OTP Input → POST /api/v1/auth/otp/verify {phone, otp}
  ├── verify against cache
  ├── find User by phone → find linked Participant/Guardian
  ├── issue Sanctum token with abilities ['mobile:*']
  └── return {token, user, participants: [...]}

Subsequent requests → Bearer {token} header
  ├── token valid 90 days
  ├── refreshed on each API call (sliding expiry)
  └── revoked on logout or password change
```

---

## Firebase & Push Notifications — Complete Setup

### Architecture: One Firebase Project Per Client

Each client gets their own Firebase project. This keeps:
- Push notifications branded per academy
- Analytics separated
- No cross-client data leakage
- Client can own their own Firebase project if they want

### What You Need Per Client (Firebase Console)

1. **Create Firebase Project** → name it after the academy (e.g., "OC Sport")
2. **Add Android App** → package name matches `instance.dart` packageId
3. **Download `google-services.json`** → goes in `android/app/`
4. **(iOS) Add iOS App** → bundle ID matches → download `GoogleService-Info.plist`
5. **Cloud Messaging** → enabled by default
6. **Create Service Account** for server-side sending:
   - Firebase Console → Project Settings → Service accounts
   - Click "Generate new private key"
   - Download the JSON file (this is what the Laravel instance needs)

### What The Laravel Instance Needs

```
System Settings (stored in DB, academy-level):
  - firebase_service_account_json: (the full JSON content from step 6 above)

OR stored as a file:
  - storage/app/firebase/service-account.json

Environment variable alternative:
  - FIREBASE_CREDENTIALS=/path/to/service-account.json
```

### How Notifications Flow

```
[Event in System]                    [Laravel Instance]              [FCM]                [Phone]
     │                                      │                         │                     │
     │  e.g., Attendance Marked             │                         │                     │
     ├─────────────────────────────────────>│                         │                     │
     │                                      │  PushNotificationService│                     │
     │                                      │  loads service account  │                     │
     │                                      │  gets device tokens     │                     │
     │                                      │  from device_tokens DB  │                     │
     │                                      ├────────────────────────>│                     │
     │                                      │  FCM HTTP v1 API call   │                     │
     │                                      │  POST https://fcm.googleapis.com/v1/          │
     │                                      │  projects/{project}/messages:send             │
     │                                      │                         ├────────────────────>│
     │                                      │                         │  Push delivered     │
     │                                      │                         │  (even if app closed)
```

### Firebase Configuration Checklist Per Client

| Step | Where | What |
|------|-------|------|
| 1 | Firebase Console | Create project |
| 2 | Firebase Console | Add Android app with correct package name |
| 3 | Firebase Console | Download `google-services.json` |
| 4 | Flutter repo | Place `google-services.json` in `android/app/` |
| 5 | Firebase Console | (iOS) Add iOS app, download plist |
| 6 | Flutter repo | (iOS) Place `GoogleService-Info.plist` in `ios/Runner/` |
| 7 | Firebase Console | Project Settings → Service Accounts → Generate private key |
| 8 | Laravel instance | Upload service account JSON via System Settings UI |
| 9 | Laravel instance | Done — system can now push to this client's app |

### Laravel Side: `PushNotificationService`

```php
// Uses: kreait/firebase-php (official Firebase Admin SDK)
// composer require kreait/firebase-php

class PushNotificationService
{
    private Messaging $messaging;

    public function __construct()
    {
        $credentials = $this->getCredentials(); // from system_settings or file
        $factory = (new Factory)->withServiceAccount($credentials);
        $this->messaging = $factory->createMessaging();
    }

    public function sendToUser(User $user, string $title, string $body, array $data = []): void
    {
        $tokens = DeviceToken::where('user_id', $user->id)
            ->where('is_active', true)
            ->pluck('device_token')
            ->toArray();

        if (empty($tokens)) return;

        $message = CloudMessage::new()
            ->withNotification(Notification::create($title, $body))
            ->withData($data);  // custom payload for in-app routing

        $report = $this->messaging->sendMulticast($message, $tokens);

        // Deactivate invalid tokens
        foreach ($report->invalidTokens() as $token) {
            DeviceToken::where('device_token', $token)->update(['is_active' => false]);
        }
    }

    public function sendToParticipantGuardians(Participant $participant, string $title, string $body, array $data = []): void
    {
        $guardianUserIds = $participant->guardians()->pluck('user_id')->filter();
        foreach ($guardianUserIds as $userId) {
            $this->sendToUser(User::find($userId), $title, $body, $data);
        }
    }
}
```

### FCM Message Payload Structure

```json
{
  "message": {
    "notification": {
      "title": "تم تسجيل الحضور",
      "body": "تم تسجيل حضور أحمد محمد في جلسة اليوم"
    },
    "data": {
      "type": "attendance_marked",
      "participant_uuid": "abc-123",
      "session_id": "456",
      "click_action": "OPEN_ATTENDANCE"
    },
    "android": {
      "priority": "high",
      "notification": {
        "channel_id": "attendance",
        "sound": "default"
      }
    },
    "apns": {
      "payload": {
        "aps": {
          "sound": "default",
          "badge": 1
        }
      }
    }
  }
}
```

### Flutter Side: FCM Setup

```dart
// pubspec.yaml
dependencies:
  firebase_core: ^latest
  firebase_messaging: ^latest

// main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Request notification permission (iOS)
  await FirebaseMessaging.instance.requestPermission();

  // Get FCM token → send to backend
  final token = await FirebaseMessaging.instance.getToken();
  // POST /api/v1/devices/register {token, platform: 'android'}

  // Listen for token refresh
  FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
    // PATCH /api/v1/devices/refresh {old_token, new_token}
  });

  // Handle background messages
  FirebaseMessaging.onBackgroundMessage(_backgroundHandler);

  runApp(MyApp());
}

@pragma('vm:entry-point')
Future<void> _backgroundHandler(RemoteMessage message) async {
  // This runs even when app is terminated
  // Handle: badge count, local storage update, etc.
}
```

---

## Features (Client-Facing)

### 1. Authentication & Onboarding
- Phone number + OTP login (Egypt: +20)
- Demo mode: fixed OTP 123456, yellow banner
- Link guardian to existing account (by phone)
- Participant self-login (age 14+) via phone
- Biometric (fingerprint/face) for quick re-entry
- Academy branding on login screen (fetched from API, not hardcoded)

### 2. Dashboard (Home Screen)
- Upcoming sessions (today/this week) with countdown timers
- Outstanding balance / next payment due
- Recent attendance (last 5 sessions — present/absent indicators)
- Academy announcements
- Quick actions: pay now, report absence, contact academy

### 3. Schedule & Calendar
- Weekly/monthly view of all sessions
- Color-coded by group/program
- Session details: time, facility, trainer, group
- Cancelled/rescheduled sessions highlighted
- Add to phone calendar (ICS export)

### 4. Attendance & Progress
- Attendance history (calendar heatmap view)
- Attendance rate percentage (visual ring)
- Late arrivals tracked
- Streak tracking (consecutive present days)
- Monthly attendance summary

### 5. Financial
- Outstanding invoices list
- Payment history
- Pay now (online payment gateway integration)
- Installment plan view (paid/upcoming/overdue)
- Download receipt (PDF)
- Wallet balance (if applicable)

### 6. Profile & Children
- Guardian sees all linked children
- Switch between children
- Each child: photo, group, program, trainer, enrollment date
- Medical certificate expiry warning
- Documents (uploaded/pending)

### 7. Notifications (THE CORE FEATURE)
- Push notifications via FCM
- Works when app is closed (background/terminated)
- Types:
  - Session reminder (30 min before)
  - Attendance marked (your child was marked present/absent)
  - Invoice created / payment due
  - Payment confirmed
  - Session cancelled/rescheduled
  - Announcement from academy
  - Medical certificate expiring
  - Installment due tomorrow
  - Enrollment confirmed
  - Group transfer notification
- In-app notification center (history)
- Read/unread state
- Notification preferences (per type toggle)

### 8. Communication
- Direct message to academy (simple chat or form)
- Report absence in advance
- Request transfer/freeze
- Feedback after session (optional, academy-configurable)

### 9. Explore Academy
- Academy about/info (pulled from API)
- News feed (from website news module)
- Photo/video gallery
- Branches & locations (with map)
- Working hours
- Social links
- Contact info
- Programs offered (browse available programs)

### 10. Events
- Upcoming events list
- Event details + registration (join from app)
- Event gallery (photos/videos after event)
- Push notification when new event is published

### 11. Shop — Essential Products
- Browse essential products available for purchase
- See product details, photos, price
- Buy from app (online payment)
- Installment plans visible + selectable
- Order status tracking (pending delivery → delivered)
- Purchase history
- System warns if product already purchased this year

### 12. Kit / Uniform Ordering
- If academy sells kits with specific sizes
- Size selection
- Add to cart + pay or request from academy

---

## System-Side Preparations (Laravel Backend)

### A. API Layer — `routes/api.php`

All endpoints versioned under `/api/v1/`.

**App config (public, no auth):**
- `GET /api/v1/app/config` — returns academy branding, feature flags, min version

**Auth endpoints:**
- `POST /api/v1/auth/otp/request` — send OTP to phone
- `POST /api/v1/auth/otp/verify` — verify OTP, return Sanctum token
- `POST /api/v1/auth/logout` — revoke token
- `GET /api/v1/auth/me` — current user + linked participants

**Data endpoints:**
- `GET /api/v1/participants/{uuid}` — profile
- `GET /api/v1/participants/{uuid}/summary` — dashboard data (schedule + balance + attendance)
- `GET /api/v1/participants/{uuid}/schedule` — upcoming sessions
- `GET /api/v1/participants/{uuid}/attendance` — history + stats
- `GET /api/v1/participants/{uuid}/invoices` — outstanding + paid
- `GET /api/v1/participants/{uuid}/enrollments` — active programs
- `GET /api/v1/participants/{uuid}/documents` — uploaded docs
- `GET /api/v1/guardian/children` — all linked participants

**Action endpoints:**
- `POST /api/v1/payments/initiate` — start online payment
- `POST /api/v1/payments/callback` — payment gateway webhook
- `POST /api/v1/absences/report` — report planned absence
- `POST /api/v1/messages/send` — contact academy
- `POST /api/v1/notifications/preferences` — update notification prefs
- `PATCH /api/v1/notifications/{id}/read` — mark as read
- `GET /api/v1/notifications` — paginated notification history

**Device endpoints:**
- `POST /api/v1/devices/register` — register FCM token
- `PATCH /api/v1/devices/refresh` — update token on refresh
- `DELETE /api/v1/devices/{token}` — unregister on logout

**Shop endpoints:**
- `GET /api/v1/products` — available essential products (with `already_purchased_at` field)
- `POST /api/v1/orders/create` — purchase product from app
- `GET /api/v1/orders` — order history
- `GET /api/v1/orders/{uuid}` — order status/details

**Academy explore endpoints:**
- `GET /api/v1/academy/news` — news feed (paginated)
- `GET /api/v1/academy/gallery` — photo/video gallery
- `GET /api/v1/academy/programs` — available programs
- `GET /api/v1/academy/events` — events list
- `POST /api/v1/events/{uuid}/register` — join event

### B. Database Changes

```sql
-- device_tokens table
CREATE TABLE device_tokens (
    id bigserial PRIMARY KEY,
    academy_id bigint NOT NULL REFERENCES academies(id),
    user_id bigint NOT NULL REFERENCES users(id),
    device_token varchar(255) NOT NULL,
    platform varchar(10) NOT NULL CHECK (platform IN ('android', 'ios')),
    device_name varchar(100),
    app_version varchar(20),
    is_active boolean DEFAULT true,
    last_used_at timestamp,
    created_at timestamp,
    updated_at timestamp,
    UNIQUE(device_token)
);
CREATE INDEX idx_device_tokens_user ON device_tokens(user_id, is_active);

-- system_settings additions (per academy)
-- key: 'firebase_service_account_json' → value: full JSON content
-- key: 'auth_otp_mode' → value: 'demo' or 'sms'
-- key: 'sms_provider' → value: 'log' | 'smsmisr' | 'victorylink' | 'twilio'
-- key: 'app_min_version' → value: '1.0.0'
-- key: 'app_maintenance_mode' → value: 'false'
-- key: 'app_features_shop' → value: 'true'
-- key: 'app_features_events' → value: 'true'
-- key: 'app_features_chat' → value: 'false'
```

### C. App Config Endpoint Response

```json
// GET /api/v1/app/config (public, no auth needed)
{
  "academy": {
    "name": "OC Sport",
    "name_ar": "او سي سبورت",
    "logo_url": "https://oc-sport.caprover.al-arcade.com/storage/logos/logo.png",
    "cover_url": "https://...",
    "primary_color": "#1B5E20",
    "accent_color": "#4CAF50",
    "phone": "+201234567890",
    "whatsapp": "+201234567890"
  },
  "features": {
    "shop": true,
    "events": true,
    "chat": false,
    "online_payment": true,
    "gallery": true
  },
  "auth": {
    "mode": "demo",
    "demo_otp": "123456"
  },
  "app": {
    "min_version": "1.0.0",
    "maintenance": false,
    "maintenance_message": null
  }
}
```

### D. New System Settings UI Section

Add a "Mobile App" section in system settings:
- Toggle: OTP mode (demo/sms)
- Upload: Firebase service account JSON
- Toggle: Shop enabled
- Toggle: Events enabled
- Toggle: Chat enabled
- Input: Minimum app version
- Toggle: Maintenance mode
- Input: Maintenance message

---

## New Client Deployment Checklist

### Firebase Setup (5 min)
- [ ] Create Firebase project at console.firebase.google.com
- [ ] Add Android app → package: `com.alcaptain.{clientslug}`
- [ ] Download `google-services.json`
- [ ] (If iOS) Add iOS app → bundle ID same → download plist
- [ ] Project Settings → Service Accounts → Generate new private key
- [ ] Save the private key JSON

### Flutter Build (5 min)
- [ ] Edit `lib/config/instance.dart`:
  - `baseUrl` = client's instance URL
  - `appName` / `appNameAr` = client's name
  - `packageId` = `com.alcaptain.{clientslug}`
- [ ] Replace `assets/branding/logo.png` with client's logo
- [ ] Replace `assets/branding/splash.png` with client's splash (1080x1920)
- [ ] Generate app icons from logo (use icon.kitchen or Android Studio)
- [ ] Replace `android/app/google-services.json`
- [ ] (iOS) Replace `ios/Runner/GoogleService-Info.plist`
- [ ] Update `android/app/build.gradle` → `applicationId` matches packageId
- [ ] `flutter build appbundle --release`

### Laravel Instance Config (2 min)
- [ ] System Settings → Mobile App → upload Firebase service account JSON
- [ ] Set OTP mode to 'demo' for testing, 'sms' for production
- [ ] Enable/disable features (shop, events, chat)

### Play Store Upload
- [ ] Client's Google Play Console → Create new app
- [ ] Upload AAB
- [ ] Store listing: use academy's name, logo, screenshots
- [ ] Submit for review

---

## Build Order (System Side)

### Phase 1: API Foundation + App Config
1. `routes/api.php` with `/api/v1/` prefix
2. `GET /api/v1/app/config` endpoint (public)
3. Sanctum token auth setup
4. OTP auth flow with demo mode
5. Device token registration endpoint
6. Guardian/participant read endpoints
7. API Resource classes

### Phase 2: Push Notifications
8. `device_tokens` migration
9. `composer require kreait/firebase-php`
10. `PushNotificationService` (FCM HTTP v1)
11. System Settings UI for Firebase JSON upload
12. Extend event listeners → push triggers
13. Session reminder scheduled job
14. Installment due reminder job

### Phase 3: Explore + Shop
15. Academy info/news/gallery API endpoints
16. Events API + registration
17. Products API (with `already_purchased_at` check)
18. Order creation + payment endpoint

### Phase 4: Financial API
19. Invoice list/detail endpoints
20. Payment gateway integration (Paymob)
21. Payment initiation + webhook callback
22. Installment tracking endpoints

### Phase 5: Communication + Polish
23. Report absence endpoint
24. Notification preferences endpoint
25. Contact/message endpoint
26. Rate limiting middleware
27. Error response standardization (Arabic messages)

---

## Decisions Made

| Decision | Choice | Reason |
|----------|--------|--------|
| App per client | Separate build per client | Clients paid for their own app |
| Config approach | Single `instance.dart` file | One URL change = new client app |
| Firebase | One project per client | Isolation, client ownership |
| Auth without SMS | Demo mode (OTP = 123456) | Allows prototyping immediately |
| Branding | Fetched from API at runtime | No need to rebuild for logo/color changes |
| API versioning | `/api/v1/` prefix | Future-proof without breaking old apps |

## Decisions Still Needed

1. **Payment gateway** — Paymob? Fawry? Both?
2. **SMS provider** — SMS Misr? Victory Link? Twilio?
3. **iOS** — Are we building for iOS too or Android-only initially?
4. **Offline mode** — How much data cached locally? How stale is acceptable?
5. **Language** — Arabic only? Or bilingual toggle in app?
