# Story 6.1: Dashboard Overview & Statistics ## Epic Reference **Epic 6:** Admin Dashboard ## User Story As an **admin**, I want **to see real-time metrics and key statistics at a glance**, So that **I can understand the current state of my practice**. ## Prerequisites / Dependencies This story requires the following to be completed first: | Dependency | Required From | What's Needed | |------------|---------------|---------------| | User Model | Epic 2 | `status` (active/deactivated), `user_type` (individual/company) fields | | Consultation Model | Epic 3 | `consultations` table with `status`, `consultation_type`, `scheduled_date` | | Timeline Model | Epic 4 | `timelines` table with `status` (active/archived) | | Timeline Updates | Epic 4 | `timeline_updates` table with `created_at` | | Post Model | Epic 5 | `posts` table with `status` (published/draft), `created_at` | | Admin Layout | Epic 1 | Admin authenticated layout with navigation | **References:** - Epic 6 details: `docs/epics/epic-6-admin-dashboard.md` - PRD Dashboard Section: `docs/prd.md` Section 5.7 (Admin Dashboard) - Database Schema: `docs/prd.md` Section 16.1 ## Acceptance Criteria ### User Metrics Card - [ ] Total active clients (individual + company with status = active) - [ ] Individual vs company breakdown (count each type) - [ ] Deactivated clients count - [ ] New clients this month (created_at in current month) ### Booking Metrics Card - [ ] Pending requests count (highlighted with warning color) - [ ] Today's consultations (scheduled for today, approved status) - [ ] This week's consultations - [ ] This month's consultations - [ ] Free vs paid breakdown (consultation_type field) - [ ] No-show rate percentage (no-show / total completed * 100) ### Timeline Metrics Card - [ ] Active case timelines (status = active) - [ ] Archived timelines (status = archived) - [ ] Updates added this week (timeline_updates created in last 7 days) ### Posts Metrics Card - [ ] Total published posts (status = published) - [ ] Posts published this month ### Design - [ ] Clean card-based layout using Flux UI components - [ ] Color-coded status indicators (gold for highlights, success green, warning colors) - [ ] Responsive grid (2 columns on tablet, 1 on mobile, 4 on desktop) - [ ] Navy blue and gold color scheme per PRD Section 7.1 ## Technical Implementation ### Files to Create/Modify | File | Purpose | |------|---------| | `resources/views/livewire/admin/dashboard.blade.php` | Main Volt component | | `routes/web.php` | Add admin dashboard route | ### Route Definition ```php Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () { Route::get('/dashboard', function () { return view('livewire.admin.dashboard'); })->name('admin.dashboard'); }); ``` ### Component Structure (Volt Class-Based) ```php $this->getUserMetrics(), 'bookingMetrics' => $this->getBookingMetrics(), 'timelineMetrics' => $this->getTimelineMetrics(), 'postMetrics' => $this->getPostMetrics(), ]; } private function getUserMetrics(): array { return Cache::remember('admin.metrics.users', 300, fn() => [ 'total_active' => User::where('status', 'active') ->whereIn('user_type', ['individual', 'company'])->count(), 'individual' => User::where('user_type', 'individual') ->where('status', 'active')->count(), 'company' => User::where('user_type', 'company') ->where('status', 'active')->count(), 'deactivated' => User::where('status', 'deactivated')->count(), 'new_this_month' => User::whereMonth('created_at', now()->month) ->whereYear('created_at', now()->year)->count(), ]); } private function getBookingMetrics(): array { return Cache::remember('admin.metrics.bookings', 300, function () { $total = Consultation::whereIn('status', ['completed', 'no-show'])->count(); $noShows = Consultation::where('status', 'no-show')->count(); return [ 'pending' => Consultation::where('status', 'pending')->count(), 'today' => Consultation::whereDate('scheduled_date', today()) ->where('status', 'approved')->count(), 'this_week' => Consultation::whereBetween('scheduled_date', [ now()->startOfWeek(), now()->endOfWeek() ])->count(), 'this_month' => Consultation::whereMonth('scheduled_date', now()->month) ->whereYear('scheduled_date', now()->year)->count(), 'free' => Consultation::where('consultation_type', 'free')->count(), 'paid' => Consultation::where('consultation_type', 'paid')->count(), 'no_show_rate' => $total > 0 ? round(($noShows / $total) * 100, 1) : 0, ]; }); } private function getTimelineMetrics(): array { return Cache::remember('admin.metrics.timelines', 300, fn() => [ 'active' => Timeline::where('status', 'active')->count(), 'archived' => Timeline::where('status', 'archived')->count(), 'updates_this_week' => TimelineUpdate::where('created_at', '>=', now()->subWeek())->count(), ]); } private function getPostMetrics(): array { return Cache::remember('admin.metrics.posts', 300, fn() => [ 'total_published' => Post::where('status', 'published')->count(), 'this_month' => Post::where('status', 'published') ->whereMonth('created_at', now()->month) ->whereYear('created_at', now()->year)->count(), ]); } }; ?>