# 01 — MVC Module Structure

This mirrors the existing HR module pattern (`app/Http/Controllers/Admin/HR/Employee/EmployeeController.php`
→ Service → Request → Model → Blade view) and the existing `employee`-guard Portal pattern
(`routes/portal.php`, `app/Http/Controllers/Portal/*`, `resources/views/portal/*`) for
everything Doctor-facing.

## Controllers

```
app/Http/Controllers/
├── Admin/Clinic/                         (guard: admin — Super Admin + Clinic Admin + Receptionist)
│   ├── ClinicController.php              Super Admin: create/edit a Branch + its ClinicProfile together
│   ├── SpecialtyController.php           Super Admin: manage the global specialty list
│   ├── DoctorController.php              Clinic Admin: doctor profile CRUD (branch-scoped)
│   ├── DoctorScheduleController.php      Clinic Admin: per-branch weekly schedule + slot duration + prices
│   ├── BookingController.php             Clinic Admin/Receptionist: list/confirm/cancel bookings (branch-scoped)
│   ├── PatientController.php             Clinic Admin/Receptionist: booking-linked patient list (NO medical fields)
│   ├── OfferController.php               Super Admin (platform-wide) + Clinic Admin (local, own branch only)
│   ├── Setup/
│   │   └── PolicyController.php          Super Admin: default cancellation/reschedule policy, commission %
│   └── Reports/
│       └── ClinicReportController.php    Bookings/revenue/performance/ratings, filterable by branch+period
│
├── Doctor/                               (guard: doctor — mirrors App\Http\Controllers\Portal\*)
│   ├── Auth/AuthenticatedSessionController.php   login/logout, mirrors Portal\Auth\AuthenticatedSessionController
│   ├── DashboardController.php
│   ├── ScheduleController.php            own schedule/bookings, all branches they work at
│   ├── PatientController.php             own patients: basic data + medical history (read)
│   ├── VisitController.php               visit/exam detail, diagnosis, prescription, report (+ edit)
│   ├── ConsultationController.php        starts/joins the Agora video room for an online booking
│   ├── ChatController.php                reply to patient inquiries
│   └── RatingController.php              read-only: own ratings
│
├── Site/Clinic/                          (guard: web — mirrors existing App\Http\Controllers\Site\*)
│   ├── SearchController.php              search by specialty + branch (public, no auth required)
│   ├── DoctorProfileController.php       public doctor/clinic detail page
│   ├── BookingController.php             create a booking (auth required), choose visit/online + slot
│   ├── BookingPaymentController.php      creates a clinic_payments row, calls PaymentGatewayManager, handles return/webhook
│   ├── MyBookingController.php           patient's bookings: list, cancel/reschedule within policy
│   ├── MedicalRecordController.php       patient's own prescriptions/reports (read-only)
│   ├── ChatController.php                patient side of the chat
│   ├── ReviewController.php              two-step post-booking rating
│   └── PatientProfileController.php      medical-profile form (history/surgical/medication/allergies)
│
└── Api/Clinic/                           (guard: sanctum — Phase 5, mobile app)
    mirrors Site/Clinic/* and Doctor/* one-for-one as JSON endpoints once Sanctum is added.
```

## Services (business logic — controllers stay thin, per existing convention)

```
app/Services/Clinic/
├── ClinicService.php            create/update a Branch + ClinicProfile pair atomically
├── SpecialtyService.php
├── DoctorService.php
├── DoctorScheduleService.php    slot-generation logic (given weekly schedule + slot duration → bookable slots)
├── BookingService.php           availability check, create booking, cancellation/reschedule policy enforcement
├── BookingPaymentService.php    wraps PaymentGatewayManager for a Booking's clinic_payments row
├── MedicalRecordService.php     create/update diagnosis+prescription+report, logs edits to ActivityLog
├── ChatService.php              send message, broadcast via Reverb, notify if recipient offline
├── ReviewService.php            two-step rating capture, recompute ClinicProfile.avg_rating
└── OfferService.php
```

## Form Requests (validation — per-module namespace, per existing convention)

```
app/Http/Requests/Admin/Clinic/     Store/UpdateClinicRequest, Store/UpdateSpecialtyRequest,
                                     Store/UpdateDoctorRequest, Store/UpdateDoctorScheduleRequest, ...
app/Http/Requests/Site/Clinic/      StoreBookingRequest, StorePatientProfileRequest,
                                     StoreReviewRequest, StoreChatMessageRequest, ...
app/Http/Requests/Doctor/           StoreMedicalRecordRequest, UpdateMedicalRecordRequest, ...
```

## Models

```
app/Models/Clinic/
├── ClinicProfile.php     belongsTo Core\Branch
├── Specialty.php
├── Doctor.php             guard 'doctor' Authenticatable
├── DoctorClinic.php       pivot: Doctor × Core\Branch
├── DoctorSchedule.php
├── PatientProfile.php     belongsTo App\Models\User
├── Booking.php
├── Payment.php             table clinic_payments
├── MedicalRecord.php
├── Review.php
├── ChatMessage.php
└── Offer.php
```

Full field/relationship definitions for each: see `models/*.md`.

## Views

```
resources/views/dashboard/admin/clinic/     Admin-side, extends dashboard.admin.layouts.master
  clinics/{index,create,edit}.blade.php
  specialties/{index,create,edit}.blade.php
  doctors/{index,create,edit}.blade.php     (edit has schedule/price tabs, one per branch)
  bookings/index.blade.php
  patients/index.blade.php
  offers/{index,create,edit}.blade.php
  reports/index.blade.php

resources/views/doctor/                     Doctor portal, mirrors resources/views/portal/ structure
  layouts/app.blade.php                     full-document layout, NOT a Blade component (matches portal's approach)
  auth/login.blade.php
  dashboard.blade.php
  schedule/index.blade.php
  patients/{index,show}.blade.php
  visits/{show,edit}.blade.php
  consultations/show.blade.php              Agora video room
  chat/index.blade.php
  ratings/index.blade.php

resources/views/web_site/clinic/            Patient-facing, mirrors existing resources/views/web_site/* Site pages
  search.blade.php
  doctor-profile.blade.php
  booking/{create,payment}.blade.php
  my-bookings/{index,show}.blade.php
  medical-records/index.blade.php
  chat/index.blade.php
  review/create.blade.php
  profile/medical.blade.php
```

## Route registration

```
routes/admin.php     add `use` statements for Admin\Clinic\* controllers, then inside the existing
                      Route::prefix('admin')->as('admin.') → dashboard group, add:
                      Route::prefix('clinic')->as('clinic.')->group(function () { ...
                          every route ->middleware('permission:clinic.<resource>.<action>')
                      });

routes/web.php       inside the existing localized Route::group (same one that has Site\* routes),
                      add Site\Clinic\* routes for search/booking/payment/my-bookings/chat/review,
                      guarded by `auth:web` where the brief requires a logged-in patient (booking,
                      payment, my-bookings, chat, review) and public where it doesn't (search, doctor
                      profile page).

routes/doctor.php    NEW FILE. Copy the structure of routes/portal.php verbatim, swapping
                      guard employee → doctor, prefix portal → doctor, controllers Portal\* → Doctor\*.
                      Require it from routes/web.php the same way routes/portal.php is required
                      (inside the localized group, next to `require __DIR__.'/portal.php';`).

routes/api.php        NEW FILE (Phase 5). Register in bootstrap/app.php withRouting(api: ...).
```

## Middleware (new)

```
app/Http/Middleware/EnsureDoctorActive.php   copy of EnsureEmployeeActive.php, checks Auth::guard('doctor')
app/Http/Middleware/ScopeToAdminBranch.php   NEW CONCEPT — see 03-permissions-and-roles.md
                                              (no existing branch-scoping middleware to copy from)
```

Both aliased in `bootstrap/app.php`'s `$middleware->alias([...])` block, next to `'employee.active'`.

## Sidebar menu

Add a "Clinic" section to `resources/views/dashboard/admin/layouts/main-sidebar.blade.php`,
following the exact pattern already used for every other module there:

```php
$isClinic = str_starts_with($currentRouteName, 'admin.dashboard.clinic.');
```

...then a `menu-item menu-accordion` block (`show` bound to `$isClinic`) with links wrapped in
`@can('clinic.<resource>.view')`, placed alongside the existing Finance/Inventory/Sales sections.
