` - Consultation details
+
+### Status Badge Colors
+| Status | Color | Variant |
+|--------|-------|---------|
+| approved | green | `variant="success"` |
+| pending | yellow | `variant="warning"` |
+| completed | gray | `variant="subtle"` |
+| cancelled | red | `variant="danger"` |
+| no_show | red | `variant="danger"` |
+
+### Payment Status Display
+- For paid consultations, show payment status:
+ - "Payment Pending" (yellow badge)
+ - "Payment Received" (green badge)
+- For free consultations, show "Free" (blue badge)
+
+## References
+- `docs/epics/epic-3-booking-consultation.md` - Consultation model, statuses, payment handling
+- `docs/epics/epic-3-booking-consultation.md#story-36` - .ics calendar file generation
+- `docs/stories/story-7.1-client-dashboard-overview.md` - Dashboard navigation context
+
+## Test Scenarios
+
+### Access Control
+- [ ] Unauthenticated users redirected to login
+- [ ] User sees only their own consultations (not other users')
+
+### Upcoming Section
+- [ ] Shows approved consultations with `scheduled_date >= today`
+- [ ] Sorted by date ascending (soonest first)
+- [ ] Displays type, status, payment status correctly
+- [ ] Calendar download button triggers .ics download
+
+### Pending Section
+- [ ] Shows consultations with status='pending'
+- [ ] Displays submission date and problem summary
+- [ ] Summary truncated if too long
+
+### Past Section
+- [ ] Shows completed, cancelled, no_show consultations
+- [ ] Shows approved consultations with `scheduled_date < today`
+- [ ] Sorted by date descending (newest first)
+- [ ] Pagination works correctly (10 per page)
+
+### Empty States
+- [ ] Empty upcoming shows appropriate message
+- [ ] Empty pending shows appropriate message
+- [ ] Empty past shows appropriate message
+
+### Calendar Download
+- [ ] Download generates valid .ics file
+- [ ] File contains correct consultation details
+- [ ] Only available for approved upcoming consultations
+
## Definition of Done
- [ ] All sections display correctly
- [ ] Calendar download works
-- [ ] Status indicators clear
-- [ ] Read-only (no actions)
-- [ ] Pagination works
+- [ ] Status indicators clear and color-coded
+- [ ] Read-only (no actions except download)
+- [ ] Pagination works for past consultations
+- [ ] Empty states display appropriately
+- [ ] Mobile responsive
+- [ ] Bilingual support
- [ ] Tests pass
## Estimation
diff --git a/docs/stories/story-7.3-my-cases-timelines-view.md b/docs/stories/story-7.3-my-cases-timelines-view.md
index 77b15db..a2fa813 100644
--- a/docs/stories/story-7.3-my-cases-timelines-view.md
+++ b/docs/stories/story-7.3-my-cases-timelines-view.md
@@ -1,73 +1,245 @@
-# Story 7.3: My Cases/Timelines View
+# Story 7.3: My Cases/Timelines View (Dashboard Integration)
## Epic Reference
**Epic 7:** Client Dashboard
## User Story
As a **client**,
-I want **to view my case timelines and their updates**,
-So that **I can track the progress of my legal matters**.
+I want **to access my case timelines from the dashboard navigation**,
+So that **I can easily track the progress of my legal matters from one central location**.
+
+## Story Context
+
+### Relationship to Story 4.5
+Story 4.5 (`docs/stories/story-4.5-client-timeline-view.md`) already implements the **complete timeline viewing functionality**:
+- Routes: `client.timelines.index` and `client.timelines.show`
+- Components: `pages/client/timelines/index.blade.php` and `show.blade.php`
+- Active/archived separation with visual distinction
+- Individual timeline detail view with chronological updates
+- Authorization, tests, and translations
+
+**This story (7.3) focuses solely on dashboard navigation integration** - ensuring clients can access the existing timeline views from the Epic 7 client dashboard structure.
+
+### Prerequisites
+- **Story 4.5:** Client Timeline View - MUST be complete (provides all timeline components)
+- **Story 7.1:** Client Dashboard Overview - MUST be complete (provides dashboard layout and navigation)
+
+### What This Story Does NOT Do
+- Does NOT recreate timeline list or detail views (use Story 4.5's components)
+- Does NOT add new timeline functionality
+- Does NOT modify existing timeline components
## Acceptance Criteria
-### Active Cases Section
-- [ ] List of active timelines
-- [ ] Case name and reference
-- [ ] Last update date
-- [ ] Update count
-- [ ] "View" button
+### Navigation Integration
+- [ ] "My Cases" navigation item added to client dashboard sidebar/nav
+- [ ] Navigation links to `route('client.timelines.index')`
+- [ ] Active state shown when on timeline routes
+- [ ] Icon: folder or briefcase icon for cases
-### Archived Cases Section
-- [ ] Clearly separated from active
-- [ ] Different visual styling (muted)
-- [ ] Still accessible for viewing
+### Dashboard Widget (on Story 7.1's dashboard)
+- [ ] "My Cases" widget card displays:
+ - Count of active cases
+ - Latest update preview (case name + date)
+ - "View All" link to timeline index
+- [ ] Widget shows empty state if no cases exist
-### Individual Timeline View
-- [ ] Case name and reference
-- [ ] Status badge (active/archived)
-- [ ] All updates in chronological order
-- [ ] Each update shows:
- - Date and time
- - Update content
-- [ ] Read-only (no interactions)
+### Layout Consistency
+- [ ] Timeline pages use client dashboard layout (consistent header/nav)
+- [ ] Breadcrumbs: Dashboard > My Cases (on index)
+- [ ] Breadcrumbs: Dashboard > My Cases > [Case Name] (on show)
-### Navigation
-- [ ] Back to cases list
-- [ ] Responsive layout
+### Bilingual Support
+- [ ] Navigation label translated (AR/EN)
+- [ ] Widget content translated (AR/EN)
## Technical Notes
-Reuse components from Story 4.5.
+### File Structure
+```
+Files to Modify:
+ resources/views/components/layouts/client.blade.php (add nav item)
+ OR resources/views/livewire/pages/client/dashboard.blade.php (add widget)
+
+Files from Story 4.5 (DO NOT MODIFY - just ensure they exist):
+ resources/views/livewire/pages/client/timelines/index.blade.php
+ resources/views/livewire/pages/client/timelines/show.blade.php
+
+Tests to Create:
+ tests/Feature/Client/DashboardTimelineIntegrationTest.php
+```
+
+### Navigation Item Addition
+Add to client dashboard navigation (location depends on Story 7.1's implementation):
```php
-new class extends Component {
- public function with(): array
- {
- return [
- 'activeTimelines' => auth()->user()
- ->timelines()
- ->active()
- ->withCount('updates')
- ->latest('updated_at')
- ->get(),
- 'archivedTimelines' => auth()->user()
- ->timelines()
- ->archived()
- ->withCount('updates')
- ->latest('updated_at')
- ->get(),
- ];
- }
-};
+{{-- In client layout/navigation component --}}
+
+ {{ __('client.my_cases') }}
+
+```
+
+### Dashboard Widget Component
+Add to Story 7.1's dashboard view:
+
+```php
+{{-- My Cases Widget --}}
+
+
+
{{ __('client.my_cases') }}
+ {{ $activeTimelinesCount }} {{ __('client.active') }}
+
+
+ @if($latestTimelineUpdate)
+
+
{{ $latestTimelineUpdate->timeline->case_name }}
+
{{ __('client.last_update') }}: {{ $latestTimelineUpdate->created_at->diffForHumans() }}
+
+ @else
+
{{ __('client.no_cases_yet') }}
+ @endif
+
+
+ {{ __('client.view_all_cases') }}
+
+
+```
+
+### Data for Widget (add to Story 7.1's dashboard component)
+```php
+// In dashboard component's with() method
+'activeTimelinesCount' => auth()->user()->timelines()->active()->count(),
+'latestTimelineUpdate' => TimelineUpdate::whereHas('timeline',
+ fn($q) => $q->where('user_id', auth()->id())->active()
+)
+ ->with('timeline:id,case_name')
+ ->latest()
+ ->first(),
+```
+
+### Required Translation Keys
+```php
+// Add to resources/lang/en/client.php (if not already from 4.5)
+'view_all_cases' => 'View All Cases',
+
+// Add to resources/lang/ar/client.php
+'view_all_cases' => 'عرض جميع القضايا',
+```
+
+## Test Scenarios
+
+```php
+create(['user_type' => 'individual']);
+
+ $this->actingAs($client)
+ ->get(route('client.dashboard'))
+ ->assertOk()
+ ->assertSee(__('client.my_cases'))
+ ->assertSee(route('client.timelines.index'));
+});
+
+test('client dashboard shows active cases count in widget', function () {
+ $client = User::factory()->create(['user_type' => 'individual']);
+ Timeline::factory()->count(3)->create([
+ 'user_id' => $client->id,
+ 'status' => 'active',
+ ]);
+ Timeline::factory()->create([
+ 'user_id' => $client->id,
+ 'status' => 'archived',
+ ]);
+
+ $this->actingAs($client)
+ ->get(route('client.dashboard'))
+ ->assertSee('3'); // Only active cases counted
+});
+
+test('client dashboard shows latest timeline update', function () {
+ $client = User::factory()->create(['user_type' => 'individual']);
+ $timeline = Timeline::factory()->create([
+ 'user_id' => $client->id,
+ 'case_name' => 'Property Dispute Case',
+ ]);
+ TimelineUpdate::factory()->create([
+ 'timeline_id' => $timeline->id,
+ 'created_at' => now(),
+ ]);
+
+ $this->actingAs($client)
+ ->get(route('client.dashboard'))
+ ->assertSee('Property Dispute Case');
+});
+
+test('client dashboard shows empty state when no cases', function () {
+ $client = User::factory()->create(['user_type' => 'individual']);
+
+ $this->actingAs($client)
+ ->get(route('client.dashboard'))
+ ->assertSee(__('client.no_cases_yet'));
+});
+
+test('my cases navigation is active on timeline routes', function () {
+ $client = User::factory()->create(['user_type' => 'individual']);
+ $timeline = Timeline::factory()->create(['user_id' => $client->id]);
+
+ // Test on index
+ $this->actingAs($client)
+ ->get(route('client.timelines.index'))
+ ->assertOk();
+
+ // Test on show
+ $this->actingAs($client)
+ ->get(route('client.timelines.show', $timeline))
+ ->assertOk();
+});
+
+test('timeline pages use client dashboard layout', function () {
+ $client = User::factory()->create(['user_type' => 'individual']);
+
+ $this->actingAs($client)
+ ->get(route('client.timelines.index'))
+ ->assertSee(__('client.my_cases')); // Nav item visible = layout applied
+});
```
## Definition of Done
-- [ ] Active cases display correctly
-- [ ] Archived cases separated
-- [ ] Timeline detail view works
-- [ ] Updates display chronologically
-- [ ] Read-only enforced
-- [ ] Tests pass
+- [ ] "My Cases" navigation item added to client dashboard
+- [ ] Navigation links to existing `client.timelines.index` route
+- [ ] Active state shows on timeline routes
+- [ ] Dashboard widget displays active case count
+- [ ] Dashboard widget shows latest update preview
+- [ ] Dashboard widget links to timeline index
+- [ ] Empty state handled in widget
+- [ ] Translation keys added for new strings
+- [ ] Timeline pages render within client dashboard layout
+- [ ] All tests pass
+- [ ] Code formatted with Pint
+
+## Dependencies
+
+- **Story 4.5:** Client Timeline View (REQUIRED - provides timeline components and routes)
+- **Story 7.1:** Client Dashboard Overview (REQUIRED - provides dashboard layout)
+
+## Notes
+
+This story is intentionally minimal because the heavy lifting was done in Story 4.5. The developer should:
+1. Verify Story 4.5 is complete and routes work
+2. Add navigation item to client layout
+3. Add widget to dashboard
+4. Ensure layout consistency
+5. Write integration tests
+
+Do NOT duplicate or recreate the timeline components from Story 4.5.
## Estimation
-**Complexity:** Medium | **Effort:** 3-4 hours
+**Complexity:** Low | **Effort:** 1-2 hours
diff --git a/docs/stories/story-7.4-my-profile-view.md b/docs/stories/story-7.4-my-profile-view.md
index 1185a52..f9cac96 100644
--- a/docs/stories/story-7.4-my-profile-view.md
+++ b/docs/stories/story-7.4-my-profile-view.md
@@ -3,40 +3,79 @@
## Epic Reference
**Epic 7:** Client Dashboard
+## Dependencies
+- **Epic 2:** User Management (user model with all client profile fields must exist)
+- **Story 7.1:** Client Dashboard Overview (layout and navigation context)
+
## User Story
As a **client**,
I want **to view my profile information**,
So that **I can verify my account details are correct**.
+## Prerequisites
+
+### Required User Model Fields
+The `users` table must include these columns (from Epic 2):
+
+| Column | Type | Description |
+|--------|------|-------------|
+| `user_type` | enum('individual','company') | Distinguishes client type |
+| `name` | string | Full name (individual) or company name |
+| `national_id` | string | National ID for individuals |
+| `email` | string | Email address |
+| `phone` | string | Phone number |
+| `preferred_language` | enum('ar','en') | User's language preference |
+| `company_name` | string, nullable | Company name (company clients) |
+| `company_cert_number` | string, nullable | Company registration number |
+| `contact_person_name` | string, nullable | Contact person (company clients) |
+| `contact_person_id` | string, nullable | Contact person's ID |
+| `created_at` | timestamp | Account creation date |
+
## Acceptance Criteria
### Individual Client Profile
-- [ ] Full name
-- [ ] National ID
-- [ ] Email address
-- [ ] Phone number
-- [ ] Preferred language
-- [ ] Account created date
+- [ ] Full name displayed
+- [ ] National ID displayed
+- [ ] Email address displayed
+- [ ] Phone number displayed
+- [ ] Preferred language displayed
+- [ ] Account created date displayed
### Company Client Profile
-- [ ] Company name
-- [ ] Registration number
-- [ ] Contact person name
-- [ ] Contact person ID
-- [ ] Email address
-- [ ] Phone number
-- [ ] Preferred language
-- [ ] Account created date
+- [ ] Company name displayed
+- [ ] Company certificate/registration number displayed
+- [ ] Contact person name displayed
+- [ ] Contact person ID displayed
+- [ ] Email address displayed
+- [ ] Phone number displayed
+- [ ] Preferred language displayed
+- [ ] Account created date displayed
### Features
-- [ ] Account type indicator
-- [ ] No edit capabilities (read-only)
+- [ ] Account type indicator (Individual/Company badge)
+- [ ] No edit capabilities (read-only view)
- [ ] Message: "Contact admin to update your information"
-- [ ] Logout button
+- [ ] Logout button with confirmation redirect
## Technical Notes
+### File Location
+```
+resources/views/livewire/client/profile.blade.php
+```
+
+### Route
```php
+// In routes/web.php (client authenticated routes)
+Route::get('/client/profile', \Livewire\Volt\Volt::route('client.profile'))->name('client.profile');
+```
+
+### Component Implementation
+```php
+
{{ __('client.my_profile') }}
+
+
+ {{ $user->user_type === 'individual' ? __('profile.individual_account') : __('profile.company_account') }}
+
+
@if($user->user_type === 'individual')
@@ -69,10 +113,58 @@ new class extends Component {
- {{ __('profile.national_id') }}
- {{ $user->national_id }}
-
+
+
{{ __('profile.email') }}
+ {{ $user->email }}
+
+
+
{{ __('profile.phone') }}
+ {{ $user->phone }}
+
+
+
{{ __('profile.preferred_language') }}
+ {{ $user->preferred_language === 'ar' ? __('profile.arabic') : __('profile.english') }}
+
+
+
{{ __('profile.member_since') }}
+ {{ $user->created_at->translatedFormat('F j, Y') }}
+
@else
-
+
+
+
- {{ __('profile.company_name') }}
+ - {{ $user->company_name }}
+
+
+
- {{ __('profile.registration_number') }}
+ - {{ $user->company_cert_number }}
+
+
+
- {{ __('profile.contact_person') }}
+ - {{ $user->contact_person_name }}
+
+
+
- {{ __('profile.contact_person_id') }}
+ - {{ $user->contact_person_id }}
+
+
+
- {{ __('profile.email') }}
+ - {{ $user->email }}
+
+
+
- {{ __('profile.phone') }}
+ - {{ $user->phone }}
+
+
+
- {{ __('profile.preferred_language') }}
+ - {{ $user->preferred_language === 'ar' ? __('profile.arabic') : __('profile.english') }}
+
+
+
- {{ __('profile.member_since') }}
+ - {{ $user->created_at->translatedFormat('F j, Y') }}
+
+
@endif
@@ -86,13 +178,188 @@ new class extends Component {
```
+### Required Translation Keys
+
+Add to `lang/en/client.php`:
+```php
+'my_profile' => 'My Profile',
+'contact_admin_to_update' => 'Contact admin to update your information',
+```
+
+Add to `lang/en/profile.php`:
+```php
+'full_name' => 'Full Name',
+'national_id' => 'National ID',
+'email' => 'Email Address',
+'phone' => 'Phone Number',
+'preferred_language' => 'Preferred Language',
+'member_since' => 'Member Since',
+'company_name' => 'Company Name',
+'registration_number' => 'Registration Number',
+'contact_person' => 'Contact Person',
+'contact_person_id' => 'Contact Person ID',
+'individual_account' => 'Individual Account',
+'company_account' => 'Company Account',
+'arabic' => 'Arabic',
+'english' => 'English',
+```
+
+Add to `lang/en/auth.php`:
+```php
+'logout' => 'Logout',
+```
+
+Create corresponding Arabic translations in `lang/ar/` files.
+
+## Test Scenarios
+
+### Unit/Feature Tests
+Create `tests/Feature/Client/ProfileTest.php`:
+
+```php
+use App\Models\User;
+use Livewire\Volt\Volt;
+
+test('client can view individual profile page', function () {
+ $user = User::factory()->individual()->create();
+
+ $this->actingAs($user)
+ ->get(route('client.profile'))
+ ->assertOk()
+ ->assertSeeLivewire('client.profile');
+});
+
+test('individual profile displays all required fields', function () {
+ $user = User::factory()->individual()->create([
+ 'name' => 'Test User',
+ 'national_id' => '123456789',
+ 'email' => 'test@example.com',
+ 'phone' => '+970599999999',
+ 'preferred_language' => 'en',
+ ]);
+
+ Volt::test('client.profile')
+ ->actingAs($user)
+ ->assertSee('Test User')
+ ->assertSee('123456789')
+ ->assertSee('test@example.com')
+ ->assertSee('+970599999999')
+ ->assertSee('English');
+});
+
+test('company profile displays all required fields', function () {
+ $user = User::factory()->company()->create([
+ 'company_name' => 'Test Company',
+ 'company_cert_number' => 'REG-12345',
+ 'contact_person_name' => 'John Doe',
+ 'contact_person_id' => '987654321',
+ 'email' => 'company@example.com',
+ 'phone' => '+970599888888',
+ 'preferred_language' => 'ar',
+ ]);
+
+ Volt::test('client.profile')
+ ->actingAs($user)
+ ->assertSee('Test Company')
+ ->assertSee('REG-12345')
+ ->assertSee('John Doe')
+ ->assertSee('987654321')
+ ->assertSee('company@example.com');
+});
+
+test('profile page shows correct account type badge', function () {
+ $individual = User::factory()->individual()->create();
+ $company = User::factory()->company()->create();
+
+ Volt::test('client.profile')
+ ->actingAs($individual)
+ ->assertSee(__('profile.individual_account'));
+
+ Volt::test('client.profile')
+ ->actingAs($company)
+ ->assertSee(__('profile.company_account'));
+});
+
+test('profile page has no edit functionality', function () {
+ $user = User::factory()->individual()->create();
+
+ Volt::test('client.profile')
+ ->actingAs($user)
+ ->assertDontSee('Edit')
+ ->assertDontSee('Update')
+ ->assertDontSee('wire:model');
+});
+
+test('profile page shows contact admin message', function () {
+ $user = User::factory()->individual()->create();
+
+ Volt::test('client.profile')
+ ->actingAs($user)
+ ->assertSee(__('client.contact_admin_to_update'));
+});
+
+test('logout button logs out user and redirects to login', function () {
+ $user = User::factory()->individual()->create();
+
+ Volt::test('client.profile')
+ ->actingAs($user)
+ ->call('logout')
+ ->assertRedirect(route('login'));
+
+ $this->assertGuest();
+});
+
+test('unauthenticated users cannot access profile', function () {
+ $this->get(route('client.profile'))
+ ->assertRedirect(route('login'));
+});
+```
+
+### User Factory States Required
+Ensure `database/factories/UserFactory.php` has these states:
+
+```php
+public function individual(): static
+{
+ return $this->state(fn (array $attributes) => [
+ 'user_type' => 'individual',
+ 'national_id' => fake()->numerify('#########'),
+ 'company_name' => null,
+ 'company_cert_number' => null,
+ 'contact_person_name' => null,
+ 'contact_person_id' => null,
+ ]);
+}
+
+public function company(): static
+{
+ return $this->state(fn (array $attributes) => [
+ 'user_type' => 'company',
+ 'company_name' => fake()->company(),
+ 'company_cert_number' => fake()->numerify('REG-#####'),
+ 'contact_person_name' => fake()->name(),
+ 'contact_person_id' => fake()->numerify('#########'),
+ 'national_id' => null,
+ ]);
+}
+```
+
## Definition of Done
-- [ ] Individual profile displays correctly
-- [ ] Company profile displays correctly
-- [ ] No edit functionality
-- [ ] Contact admin message shown
-- [ ] Logout works
-- [ ] Tests pass
+- [ ] Individual profile displays all fields correctly
+- [ ] Company profile displays all fields correctly
+- [ ] Account type badge shows correctly for both types
+- [ ] No edit functionality present (read-only)
+- [ ] Contact admin message displayed
+- [ ] Logout button works and redirects to login
+- [ ] All test scenarios pass
+- [ ] Bilingual support (AR/EN) working
+- [ ] Responsive design on mobile
+- [ ] Code formatted with Pint
## Estimation
-**Complexity:** Low | **Effort:** 2 hours
+**Complexity:** Low | **Effort:** 2-3 hours
+
+## Notes
+- Date formatting uses `translatedFormat()` for locale-aware display
+- Ensure the User model has `$casts` for `created_at` as datetime
+- The `bg-cream` and `text-charcoal` classes should be defined in Tailwind config per project design system
diff --git a/docs/stories/story-7.6-booking-limit-indicator.md b/docs/stories/story-7.6-booking-limit-indicator.md
index a0d4cad..b701e6a 100644
--- a/docs/stories/story-7.6-booking-limit-indicator.md
+++ b/docs/stories/story-7.6-booking-limit-indicator.md
@@ -3,6 +3,10 @@
## Epic Reference
**Epic 7:** Client Dashboard
+## Dependencies
+- **Story 7.5:** New Booking Interface (provides booking page where indicator displays)
+- **Story 3.3:** Availability Calendar (calendar component to integrate with)
+
## User Story
As a **client**,
I want **to see my booking status and limits clearly**,
@@ -11,25 +15,46 @@ So that **I understand when I can book consultations**.
## Acceptance Criteria
### Display Locations
-- [ ] Dashboard widget
-- [ ] Booking page
+- [ ] Dashboard widget showing booking status
+- [ ] Booking page status banner
### Status Messages
-- [ ] "You can book a consultation today"
-- [ ] "You already have a booking for today"
-- [ ] "You have a pending request for [date]"
+- [ ] "You can book a consultation today" (when no booking exists for today)
+- [ ] "You already have a booking for today" (when pending/approved booking exists for today)
+- [ ] "You have a pending request for [date]" (shows first pending request date)
### Calendar Integration
-- [ ] Calendar shows booked days as unavailable
-- [ ] Visual indicator for user's booked dates
+- [ ] Pass `bookedDates` array to availability calendar component
+- [ ] Calendar marks user's booked dates as unavailable (distinct styling)
+- [ ] Visual indicator differentiates "user already booked" from "no slots available"
### Information
- [ ] Clear messaging about 1-per-day limit
-- [ ] Bilingual messages
+- [ ] Bilingual messages (Arabic/English)
+
+### Edge Cases
+- [ ] Handle multiple pending requests (show count or list)
+- [ ] Handle cancelled bookings (should not block new booking)
+- [ ] Loading state while fetching booking status
## Technical Notes
+### Files to Create
+- `resources/views/livewire/client/booking-status.blade.php` - Reusable status component
+
+### Files to Modify
+- `resources/views/livewire/client/dashboard.blade.php` - Add booking status widget
+- `resources/views/livewire/client/booking.blade.php` - Add status banner (from Story 7.5)
+- `resources/lang/en/booking.php` - Add translation keys
+- `resources/lang/ar/booking.php` - Add translation keys
+
+### Component Implementation
+
```php
+first();
$pendingRequests = $user->consultations()
- ->pending()
+ ->where('status', 'pending')
+ ->where('scheduled_date', '>=', today())
+ ->orderBy('scheduled_date')
->get();
$upcomingApproved = $user->consultations()
- ->approved()
+ ->where('status', 'approved')
->where('scheduled_date', '>=', today())
->get();
@@ -62,43 +89,136 @@ new class extends Component {
->toArray(),
];
}
-};
-```
-### Template
-```blade
-
- @if($canBookToday)
-
-
- {{ __('booking.can_book_today') }}
-
- @else
-
-
- {{ __('booking.already_booked_today') }}
-
- @endif
+ public function with(): array
+ {
+ return $this->getBookingStatus();
+ }
+}; ?>
- @if($pendingRequests->isNotEmpty())
-
- {{ __('booking.pending_for_date', ['date' => $pendingRequests->first()->scheduled_date->format('d/m/Y')]) }}
-
- @endif
-
-
- {{ __('booking.limit_message') }}
-
+
+
```
+### Template
+
+```blade
+
+ {{-- Loading State --}}
+
+
+
+ @if($canBookToday)
+
+
+ {{ __('booking.can_book_today') }}
+
+ @else
+
+
+ {{ __('booking.already_booked_today') }}
+
+ @endif
+
+ @if($pendingRequests->isNotEmpty())
+
+ @if($pendingRequests->count() === 1)
+
{{ __('booking.pending_for_date', ['date' => $pendingRequests->first()->scheduled_date->format('d/m/Y')]) }}
+ @else
+
{{ __('booking.pending_count', ['count' => $pendingRequests->count()]) }}
+
+ @foreach($pendingRequests->take(3) as $request)
+ - {{ $request->scheduled_date->format('d/m/Y') }}
+ @endforeach
+
+ @endif
+
+ @endif
+
+
+ {{ __('booking.limit_message') }}
+
+
+
+```
+
+### Translation Keys
+
+```php
+// resources/lang/en/booking.php
+return [
+ 'can_book_today' => 'You can book a consultation today',
+ 'already_booked_today' => 'You already have a booking for today',
+ 'pending_for_date' => 'You have a pending request for :date',
+ 'pending_count' => 'You have :count pending requests',
+ 'limit_message' => 'Note: You can book a maximum of 1 consultation per day.',
+];
+
+// resources/lang/ar/booking.php
+return [
+ 'can_book_today' => 'يمكنك حجز استشارة اليوم',
+ 'already_booked_today' => 'لديك حجز بالفعل لهذا اليوم',
+ 'pending_for_date' => 'لديك طلب معلق بتاريخ :date',
+ 'pending_count' => 'لديك :count طلبات معلقة',
+ 'limit_message' => 'ملاحظة: يمكنك حجز استشارة واحدة كحد أقصى في اليوم.',
+];
+```
+
+### Calendar Integration
+
+Pass `bookedDates` to the availability calendar from Story 3.3:
+
+```blade
+{{-- In booking page --}}
+
+```
+
+The calendar should style user's booked dates differently (e.g., with a badge or distinct color) from dates with no available slots.
+
+## Test Scenarios
+
+### Unit Tests
+- `getBookingStatus()` returns correct structure
+- `canBookToday` is true when no booking exists for today
+- `canBookToday` is false when pending booking exists for today
+- `canBookToday` is false when approved booking exists for today
+- Cancelled bookings do not affect `canBookToday`
+- `bookedDates` only includes pending and approved bookings
+
+### Feature Tests
+- Status displays "can book today" message for new users
+- Status displays "already booked" when user has today's booking
+- Status displays pending request date correctly
+- Multiple pending requests display count and dates
+- Component renders on dashboard page
+- Component renders on booking page
+- Messages display correctly in Arabic locale
+- Messages display correctly in English locale
+
+### Browser Tests (optional)
+- Calendar visually shows user's booked dates as unavailable
+- Status updates after successful booking submission
+
+## References
+- **PRD Section 5.8:** Client Dashboard - Booking limit status requirement
+- **PRD Section 5.4:** "Maximum 1 consultation per client per day"
+- **Epic 7 Success Criteria:** "Booking limit enforcement (1 per day)"
+
## Definition of Done
+- [ ] Booking status component created
- [ ] Status displays on dashboard
- [ ] Status displays on booking page
-- [ ] Calendar highlights booked dates
-- [ ] Messages are accurate
-- [ ] Bilingual support
-- [ ] Tests pass
+- [ ] Calendar highlights user's booked dates
+- [ ] Messages are accurate for all states
+- [ ] Bilingual support (AR/EN)
+- [ ] Loading state implemented
+- [ ] Edge cases handled (multiple pending, cancelled)
+- [ ] Unit tests pass
+- [ ] Feature tests pass
+- [ ] Code formatted with Pint
## Estimation
-**Complexity:** Low | **Effort:** 2 hours
+**Complexity:** Low | **Effort:** 2-3 hours