# Story 3.1: Working Hours Configuration ## Epic Reference **Epic 3:** Booking & Consultation System ## User Story As an **admin**, I want **to configure available working hours for each day of the week**, So that **clients can only book consultations during my available times**. ## Story Context ### Existing System Integration - **Integrates with:** working_hours table, availability calendar - **Technology:** Livewire Volt, Flux UI forms - **Follows pattern:** Admin settings pattern - **Touch points:** Booking availability calculation ## Acceptance Criteria ### Working Hours Management - [ ] Set available days (enable/disable each day of week) - [ ] Set start time for each enabled day - [ ] Set end time for each enabled day - [ ] Support different hours for different days - [ ] 15-minute buffer automatically applied between appointments - [ ] 12-hour time format display (AM/PM) ### Configuration Interface - [ ] Visual weekly schedule view - [ ] Toggle for each day (Sunday-Saturday) - [ ] Time pickers for start/end times - [ ] Preview of available slots per day - [ ] Save button with confirmation ### Behavior - [ ] Changes take effect immediately for new bookings - [ ] Existing approved bookings NOT affected by changes - [ ] Warning if changing hours that have pending bookings - [ ] Validation: end time must be after start time ### Quality Requirements - [ ] Bilingual labels and messages - [ ] Default working hours on initial setup - [ ] Audit log entry on changes - [ ] Tests for configuration logic ## Technical Notes ### Database Schema ```php // working_hours table Schema::create('working_hours', function (Blueprint $table) { $table->id(); $table->tinyInteger('day_of_week'); // 0=Sunday, 6=Saturday $table->time('start_time'); $table->time('end_time'); $table->boolean('is_active')->default(true); $table->timestamps(); }); ``` ### Model ```php 'boolean', ]; public static function getDayName(int $dayOfWeek, string $locale = null): string { $locale = $locale ?? app()->getLocale(); $days = [ 'en' => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 'ar' => ['الأحد', 'الإثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], ]; return $days[$locale][$dayOfWeek] ?? $days['en'][$dayOfWeek]; } public function getSlots(int $duration = 60): array { $slots = []; $start = Carbon::parse($this->start_time); $end = Carbon::parse($this->end_time); while ($start->copy()->addMinutes($duration)->lte($end)) { $slots[] = $start->format('H:i'); $start->addMinutes($duration); } return $slots; } } ``` ### Volt Component ```php first(); $this->schedule[$day] = [ 'is_active' => $workingHour?->is_active ?? false, 'start_time' => $workingHour?->start_time ?? '09:00', 'end_time' => $workingHour?->end_time ?? '17:00', ]; } } public function save(): void { foreach ($this->schedule as $day => $config) { WorkingHour::updateOrCreate( ['day_of_week' => $day], [ 'is_active' => $config['is_active'], 'start_time' => $config['start_time'], 'end_time' => $config['end_time'], ] ); } // Log action AdminLog::create([ 'admin_id' => auth()->id(), 'action_type' => 'update', 'target_type' => 'working_hours', 'new_values' => $this->schedule, 'ip_address' => request()->ip(), ]); session()->flash('success', __('messages.working_hours_saved')); } }; ``` ### Blade Template ```blade
{{ __('admin.working_hours') }} @foreach(range(0, 6) as $day)
{{ \App\Models\WorkingHour::getDayName($day) }} @if($schedule[$day]['is_active']) {{ __('common.to') }} @else {{ __('admin.closed') }} @endif
@endforeach {{ __('common.save') }}
``` ### Slot Calculation Service ```php dayOfWeek; $workingHour = WorkingHour::where('day_of_week', $dayOfWeek) ->where('is_active', true) ->first(); if (!$workingHour) { return []; } // Get all slots for the day $slots = $workingHour->getSlots(60); // 1 hour slots (45min + 15min buffer) // Remove already booked slots $bookedSlots = Consultation::where('scheduled_date', $date->toDateString()) ->whereIn('status', ['pending', 'approved']) ->pluck('scheduled_time') ->map(fn($time) => Carbon::parse($time)->format('H:i')) ->toArray(); // Remove blocked times $blockedSlots = $this->getBlockedSlots($date); return array_diff($slots, $bookedSlots, $blockedSlots); } } ``` ## Definition of Done - [ ] Can enable/disable each day of week - [ ] Can set start/end times per day - [ ] Changes save correctly to database - [ ] Existing bookings not affected - [ ] Preview shows available slots - [ ] 12-hour time format displayed - [ ] Audit log created on save - [ ] Bilingual support complete - [ ] Tests for configuration - [ ] Code formatted with Pint ## Dependencies - **Epic 1:** Database schema, admin authentication ## Risk Assessment - **Primary Risk:** Changing hours affects availability incorrectly - **Mitigation:** Clear separation between existing bookings and new availability - **Rollback:** Restore previous working hours from audit log ## Estimation **Complexity:** Medium **Estimated Effort:** 3-4 hours