libra/docs/stories/story-1.1-project-setup-dat...

24 KiB

Story 1.1: Project Setup & Database Schema

Status

Done

Epic Reference

Epic 1: Core Foundation & Infrastructure

Story

As a developer, I want the Laravel 12 project configured with all required packages and complete database schema, So that the foundation is established for all subsequent feature development.

Acceptance Criteria

  1. Laravel 12 project created with Livewire 3, Volt, Flux UI
  2. Tailwind CSS 4 configured with @theme directive
  3. Database migrations for all core tables:
    • users (with user_type, national_id, company fields)
    • consultations
    • timelines
    • timeline_updates
    • posts
    • working_hours
    • blocked_times
    • notifications
    • admin_logs
  4. Model factories created for testing
  5. Development environment working (composer run dev)
  6. SQLite configured for development database
  7. All migrations run without errors
  8. Factories generate valid test data
  9. All database tables have proper indexes
  10. Foreign key constraints properly defined

Tasks / Subtasks

  • Task 1: Create PHP Enums (AC: 3)

    • Create app/Enums/UserType.php enum (admin, individual, company)
    • Create app/Enums/UserStatus.php enum (active, deactivated)
    • Create app/Enums/ConsultationType.php enum (free, paid)
    • Create app/Enums/ConsultationStatus.php enum (pending, approved, rejected, completed, no_show, cancelled)
    • Create app/Enums/PaymentStatus.php enum (pending, received, na)
    • Create app/Enums/TimelineStatus.php enum (active, archived)
    • Create app/Enums/PostStatus.php enum (draft, published)
  • Task 2: Create Database Migrations (AC: 3, 7, 9, 10)

    • Modify existing users migration to add: user_type, full_name, national_id, company_name, company_cert_number, contact_person_name, contact_person_id, phone, status, preferred_language, two_factor fields
    • Create consultations migration with: user_id (FK), booking_date, booking_time, problem_summary, consultation_type, payment_amount, payment_status, status, admin_notes
    • Create timelines migration with: user_id (FK), case_name, case_reference (unique nullable), status
    • Create timeline_updates migration with: timeline_id (FK), admin_id (FK), update_text
    • Create posts migration with: title (JSON), body (JSON), status, published_at
    • Create working_hours migration with: day_of_week, start_time, end_time, is_active
    • Create blocked_times migration with: block_date, start_time (nullable), end_time (nullable), reason
    • Create notifications migration with: user_id (FK), type, data (JSON), read_at, sent_at
    • Create admin_logs migration with: admin_id (FK), action, target_type, target_id, old_values (JSON), new_values (JSON), ip_address
    • Add indexes: users.email (unique), consultations.booking_date, consultations.user_id, timelines.user_id, posts.status
  • Task 3: Create Eloquent Models (AC: 3, 4)

    • Update app/Models/User.php with relationships, casts, scopes, and helper methods
    • Create app/Models/Consultation.php with user relationship and enum casts
    • Create app/Models/Timeline.php with user relationship and updates relationship
    • Create app/Models/TimelineUpdate.php with timeline and admin relationships
    • Create app/Models/Post.php with JSON casts for bilingual fields
    • Create app/Models/WorkingHour.php
    • Create app/Models/BlockedTime.php
    • Create app/Models/Notification.php with user relationship and JSON data cast
    • Create app/Models/AdminLog.php with admin relationship and JSON casts
  • Task 4: Create Model Factories (AC: 4, 8)

    • Update database/factories/UserFactory.php with states: admin(), individual(), company(), client()
    • Create database/factories/ConsultationFactory.php
    • Create database/factories/TimelineFactory.php
    • Create database/factories/TimelineUpdateFactory.php
    • Create database/factories/PostFactory.php with bilingual fake data
    • Create database/factories/WorkingHourFactory.php with weekdays() state
    • Create database/factories/BlockedTimeFactory.php
    • Create database/factories/NotificationFactory.php
    • Create database/factories/AdminLogFactory.php
  • Task 5: Write Tests (AC: 7, 8)

    • Create tests/Unit/Models/UserTest.php - test enum validation, relationships
    • Create tests/Unit/Models/ConsultationTest.php - test enum validation, relationships
    • Create tests/Unit/Models/TimelineTest.php - test relationships
    • Create tests/Unit/Enums/ tests for all enums
    • Create tests/Feature/Database/MigrationTest.php - test migrate:fresh and rollback
    • Create tests/Feature/Database/FactoryTest.php - test all factories create valid models
  • Task 6: Verify Development Environment (AC: 5, 6)

    • Ensure composer run dev starts without errors
    • Verify SQLite database is created and migrations run
    • Run php artisan migrate:fresh successfully
    • Run vendor/bin/pint to format code

Dev Notes

Relevant Source Tree

app/
├── Enums/                          # PHP 8.1+ backed enums
│   ├── UserType.php
│   ├── UserStatus.php
│   ├── ConsultationType.php
│   ├── ConsultationStatus.php
│   ├── PaymentStatus.php
│   ├── TimelineStatus.php
│   └── PostStatus.php
├── Models/
│   ├── User.php                    # Existing - needs modification
│   ├── Consultation.php
│   ├── Timeline.php
│   ├── TimelineUpdate.php
│   ├── Post.php
│   ├── WorkingHour.php
│   ├── BlockedTime.php
│   ├── Notification.php
│   └── AdminLog.php
database/
├── factories/
│   ├── UserFactory.php             # Existing - needs modification
│   ├── ConsultationFactory.php
│   ├── TimelineFactory.php
│   ├── TimelineUpdateFactory.php
│   ├── PostFactory.php
│   ├── WorkingHourFactory.php
│   ├── BlockedTimeFactory.php
│   ├── NotificationFactory.php
│   └── AdminLogFactory.php
├── migrations/
│   ├── 0001_01_01_000000_create_users_table.php  # Existing - needs modification
│   ├── YYYY_MM_DD_HHMMSS_create_consultations_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_timelines_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_timeline_updates_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_posts_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_working_hours_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_blocked_times_table.php
│   ├── YYYY_MM_DD_HHMMSS_create_notifications_table.php
│   └── YYYY_MM_DD_HHMMSS_create_admin_logs_table.php
tests/
├── Unit/
│   ├── Models/
│   └── Enums/
└── Feature/
    └── Database/

Database Schema (Aligned with Architecture)

users (modify existing migration):

- id (bigint, PK)
- user_type (enum: admin|individual|company)
- full_name (string)
- national_id (string, nullable, encrypted)
- company_name (string, nullable)
- company_cert_number (string, nullable)
- contact_person_name (string, nullable)
- contact_person_id (string, nullable)
- email (string, unique)
- phone (string)
- password (string, hashed)
- status (enum: active|deactivated, default: active)
- preferred_language (string: ar|en, default: ar)
- email_verified_at (timestamp, nullable)
- two_factor_secret (text, nullable)
- two_factor_recovery_codes (text, nullable)
- two_factor_confirmed_at (timestamp, nullable)
- remember_token (string, nullable)
- created_at, updated_at (timestamps)

consultations:

- id (bigint, PK)
- user_id (bigint, FK -> users.id, cascade delete)
- booking_date (date)
- booking_time (time)
- problem_summary (text)
- consultation_type (enum: free|paid)
- payment_amount (decimal 10,2, nullable)
- payment_status (enum: pending|received|na, default: na)
- status (enum: pending|approved|rejected|completed|no_show|cancelled, default: pending)
- admin_notes (text, nullable)
- created_at, updated_at (timestamps)
INDEX: booking_date, user_id

timelines:

- id (bigint, PK)
- user_id (bigint, FK -> users.id, cascade delete)
- case_name (string)
- case_reference (string, nullable, unique)
- status (enum: active|archived, default: active)
- created_at, updated_at (timestamps)
INDEX: user_id

timeline_updates:

- id (bigint, PK)
- timeline_id (bigint, FK -> timelines.id, cascade delete)
- admin_id (bigint, FK -> users.id, cascade delete)
- update_text (text)
- created_at, updated_at (timestamps)

posts:

- id (bigint, PK)
- title (json) - {"ar": "...", "en": "..."}
- body (json) - {"ar": "...", "en": "..."}
- status (enum: draft|published, default: draft)
- published_at (timestamp, nullable)
- created_at, updated_at (timestamps)
INDEX: status

working_hours:

- id (bigint, PK)
- day_of_week (tinyint, 0-6, Sunday=0)
- start_time (time)
- end_time (time)
- is_active (boolean, default: true)
- created_at, updated_at (timestamps)

blocked_times:

- id (bigint, PK)
- block_date (date)
- start_time (time, nullable) - null means full day
- end_time (time, nullable) - null means full day
- reason (string, nullable)
- created_at, updated_at (timestamps)

notifications (custom, NOT Laravel's built-in):

- id (bigint, PK)
- user_id (bigint, FK -> users.id, cascade delete)
- type (string)
- data (json)
- read_at (timestamp, nullable)
- sent_at (timestamp, nullable)
- created_at, updated_at (timestamps)

admin_logs:

- id (bigint, PK)
- admin_id (bigint, FK -> users.id, cascade delete)
- action (string)
- target_type (string)
- target_id (bigint, nullable)
- old_values (json, nullable)
- new_values (json, nullable)
- ip_address (string)
- created_at (timestamp)

Foreign Key Constraints

Column References On Delete
consultations.user_id users.id CASCADE
timelines.user_id users.id CASCADE
timeline_updates.timeline_id timelines.id CASCADE
timeline_updates.admin_id users.id CASCADE
notifications.user_id users.id CASCADE
admin_logs.admin_id users.id CASCADE

Enum Definitions (from Architecture Section 4)

// app/Enums/UserType.php
enum UserType: string {
    case Admin = 'admin';
    case Individual = 'individual';
    case Company = 'company';
}

// app/Enums/UserStatus.php
enum UserStatus: string {
    case Active = 'active';
    case Deactivated = 'deactivated';
}

// app/Enums/ConsultationType.php
enum ConsultationType: string {
    case Free = 'free';
    case Paid = 'paid';
}

// app/Enums/ConsultationStatus.php
enum ConsultationStatus: string {
    case Pending = 'pending';
    case Approved = 'approved';
    case Rejected = 'rejected';
    case Completed = 'completed';
    case NoShow = 'no_show';
    case Cancelled = 'cancelled';
}

// app/Enums/PaymentStatus.php
enum PaymentStatus: string {
    case Pending = 'pending';
    case Received = 'received';
    case NotApplicable = 'na';
}

// app/Enums/TimelineStatus.php
enum TimelineStatus: string {
    case Active = 'active';
    case Archived = 'archived';
}

// app/Enums/PostStatus.php
enum PostStatus: string {
    case Draft = 'draft';
    case Published = 'published';
}

Technical Standards

  • Database: SQLite for development (DB_CONNECTION=sqlite)
  • Pattern: Class-based Volt components
  • Structure: Laravel 12 streamlined (no app/Http/Middleware, use bootstrap/app.php)
  • Models: Create with php artisan make:model ModelName -mf
  • Enums: Use PHP 8.1+ backed enums with string values

Testing

Test Location: tests/Unit/ and tests/Feature/Database/

Testing Framework: Pest 4

  • Create tests with: php artisan make:test TestName --pest
  • Run tests with: php artisan test

Required Tests:

  1. Migration tests - verify migrate:fresh and migrate:rollback work
  2. Factory tests - verify each factory creates valid models
  3. Enum validation tests - verify models reject invalid enum values
  4. Relationship tests - verify foreign keys and Eloquent relationships work
  5. JSON field tests - verify JSON columns store/retrieve correctly

Test Patterns:

// Example factory test
test('consultation factory creates valid consultation', function () {
    $consultation = Consultation::factory()->create();

    expect($consultation)->toBeInstanceOf(Consultation::class)
        ->and($consultation->user)->toBeInstanceOf(User::class)
        ->and($consultation->status)->toBeInstanceOf(ConsultationStatus::class);
});

// Example enum test
test('user type only accepts valid values', function () {
    expect(UserType::cases())->toHaveCount(3)
        ->and(UserType::Admin->value)->toBe('admin')
        ->and(UserType::Individual->value)->toBe('individual')
        ->and(UserType::Company->value)->toBe('company');
});

Definition of Done

  • All database migrations created and run without errors
  • All model factories functional and generate valid data
  • All Eloquent models have proper relationships and casts
  • All PHP enums created with correct values
  • Development environment runs with composer run dev
  • Tests pass for all models, factories, and enums
  • Code formatted with vendor/bin/pint
  • No errors on fresh install (php artisan migrate:fresh)

Dependencies

  • None (this is the foundation story)

Risk Assessment

Risk Impact Likelihood Mitigation
Schema design changes required later Medium Low Follow architecture document closely
Migration order issues (FK constraints) Low Medium Create migrations in dependency order
Enum value mismatches Low Low Use architecture document as source of truth

Error Handling

  • Migration failures: Run php artisan migrate:status to identify failed migrations
  • Foreign key errors: Ensure referenced tables are created before dependent tables (users before consultations, timelines before timeline_updates)
  • Factory errors: Ensure factories define all required (non-nullable) fields and use valid enum values
  • Rollback: Use php artisan migrate:fresh to reset in development

Dev Agent Record

Agent Model Used

Claude Opus 4.5 (claude-opus-4-5-20251101)

File List

New Files Created:

  • app/Enums/UserType.php
  • app/Enums/UserStatus.php
  • app/Enums/ConsultationType.php
  • app/Enums/ConsultationStatus.php
  • app/Enums/PaymentStatus.php
  • app/Enums/TimelineStatus.php
  • app/Enums/PostStatus.php
  • app/Models/Consultation.php
  • app/Models/Timeline.php
  • app/Models/TimelineUpdate.php
  • app/Models/Post.php
  • app/Models/WorkingHour.php
  • app/Models/BlockedTime.php
  • app/Models/Notification.php
  • app/Models/AdminLog.php
  • database/migrations/2025_12_26_000001_create_consultations_table.php
  • database/migrations/2025_12_26_000002_create_timelines_table.php
  • database/migrations/2025_12_26_000003_create_timeline_updates_table.php
  • database/migrations/2025_12_26_000004_create_posts_table.php
  • database/migrations/2025_12_26_000005_create_working_hours_table.php
  • database/migrations/2025_12_26_000006_create_blocked_times_table.php
  • database/migrations/2025_12_26_000007_create_notifications_table.php
  • database/migrations/2025_12_26_000008_create_admin_logs_table.php
  • database/factories/ConsultationFactory.php
  • database/factories/TimelineFactory.php
  • database/factories/TimelineUpdateFactory.php
  • database/factories/PostFactory.php
  • database/factories/WorkingHourFactory.php
  • database/factories/BlockedTimeFactory.php
  • database/factories/NotificationFactory.php
  • database/factories/AdminLogFactory.php
  • tests/Unit/Enums/UserTypeTest.php
  • tests/Unit/Enums/UserStatusTest.php
  • tests/Unit/Enums/ConsultationTypeTest.php
  • tests/Unit/Enums/ConsultationStatusTest.php
  • tests/Unit/Enums/PaymentStatusTest.php
  • tests/Unit/Enums/TimelineStatusTest.php
  • tests/Unit/Enums/PostStatusTest.php
  • tests/Unit/Models/UserTest.php
  • tests/Unit/Models/ConsultationTest.php
  • tests/Unit/Models/TimelineTest.php
  • tests/Feature/Database/MigrationTest.php
  • tests/Feature/Database/FactoryTest.php

Modified Files:

  • app/Models/User.php - Changed from name to full_name, added new fields, enum casts, relationships, scopes, and helper methods
  • database/migrations/0001_01_01_000000_create_users_table.php - Updated schema with all new user fields
  • database/factories/UserFactory.php - Updated with new fields and states (admin, individual, company, client, withTwoFactor)
  • tests/Pest.php - Added RefreshDatabase for Unit/Models tests
  • app/Actions/Fortify/CreateNewUser.php - Changed from name to full_name, added phone field
  • resources/views/livewire/settings/profile.blade.php - Changed from name to full_name
  • resources/views/livewire/auth/register.blade.php - Changed from name to full_name, added phone field
  • tests/Feature/Auth/RegistrationTest.php - Updated to use full_name and phone
  • tests/Feature/Auth/AuthenticationTest.php - Updated to use withTwoFactor() factory state
  • tests/Feature/Auth/TwoFactorChallengeTest.php - Updated to use withTwoFactor() factory state
  • tests/Feature/Settings/ProfileUpdateTest.php - Updated to use full_name

Debug Log References

None - no significant debugging issues encountered.

Completion Notes

  1. Breaking Change: User model now uses full_name instead of name. All existing code referencing user->name must be updated to user->full_name.
  2. Registration Change: Registration now requires phone field in addition to full_name, email, and password.
  3. Factory Default: UserFactory no longer includes two-factor authentication fields by default. Use ->withTwoFactor() state to create users with 2FA enabled.
  4. All 97 tests pass successfully (including existing auth tests that were updated for compatibility).
  5. All migrations run successfully with php artisan migrate:fresh.
  6. Code formatted with vendor/bin/pint.

Change Log

Date Version Description Author
Dec 21, 2025 1.0 Initial story draft SM Agent
Dec 21, 2025 1.1 Fixed schema alignment with architecture (booking_date/time), added Tasks/Subtasks, added Dev Notes with source tree and enums, added Change Log Validation Task
Dec 26, 2025 1.2 Implementation complete: Created 7 enums, 8 migrations, 9 models, 9 factories, comprehensive tests. Updated existing auth code to use full_name instead of name. All 97 tests pass. Dev Agent (Claude Opus 4.5)

QA Results

Review Date: 2025-12-26

Reviewed By: Quinn (Test Architect)

Risk Assessment

  • Review Depth: Standard (no auto-escalation triggers)
  • Risk Signals: Foundation story, no auth/payment/security business logic, diff within normal range
  • Previous Gates: N/A (first story)

Code Quality Assessment

Overall: EXCELLENT

The implementation is well-structured, follows Laravel 12 conventions, and adheres to the architecture document specifications. Key observations:

  1. Enums: All 7 PHP 8.1+ backed enums implemented correctly with proper string values matching architecture spec
  2. Models: All 9 models created with appropriate:
    • Enum casts using casts() method (Laravel 11+ pattern)
    • Eloquent relationships with proper return type hints
    • Helper methods and scopes where appropriate
  3. Migrations: All 8 migrations follow Laravel conventions with:
    • Proper foreign key constraints with cascade deletes
    • Required indexes on frequently queried columns
    • Correct data types matching schema specification
  4. Factories: All 9 factories implemented with:
    • Useful state methods (admin, individual, company, client, withTwoFactor)
    • Proper enum usage in default values
    • Bilingual JSON data for Post factory

Requirements Traceability

AC# Acceptance Criteria Test Coverage Status
1 Laravel 12 + Livewire 3, Volt, Flux UI Manual verification
2 Tailwind CSS 4 configured Manual verification
3 Database migrations for all core tables MigrationTest.php
4 Model factories created for testing FactoryTest.php
5 Development environment working Manual verification
6 SQLite configured for development .env + test runner
7 All migrations run without errors MigrationTest.php
8 Factories generate valid test data FactoryTest.php
9 All database tables have proper indexes Migration code review
10 Foreign key constraints properly defined Migration code review

Test Architecture Assessment

Tests Reviewed: 97 tests (all passing) Test Distribution:

  • Unit/Enums: 7 tests (enum value validation)
  • Unit/Models: 12 tests (casts, relationships, scopes, helpers)
  • Feature/Database: 6 tests (migrations, rollback, factories)
  • Feature/Auth: Pre-existing auth tests (updated for compatibility)

Test Quality:

  • Good coverage of happy paths
  • Enum tests verify correct case counts and values
  • Factory tests validate model creation and relationships
  • Migration tests verify schema completeness and rollback capability

Coverage Gaps Identified:

  • No edge case tests for model scopes with empty datasets
  • No tests for JSON field validation on Post model
  • Timeline relationship tests could be more comprehensive

Refactoring Performed

None required. Code quality is high and follows established patterns.

Compliance Check

  • Coding Standards: ✓ Code follows Laravel conventions
  • Project Structure: ✓ Files in correct locations per architecture doc
  • Testing Strategy: ✓ Pest tests using correct patterns
  • All ACs Met: ✓ All 10 acceptance criteria satisfied

Improvements Checklist

  • All enums implemented with correct values
  • All migrations created with proper constraints
  • All models have relationships and casts
  • All factories create valid models
  • Tests verify core functionality
  • Minor: Consider adding scopeByType($type) to User model for flexibility
  • Minor: Post model could benefit from getTitle($locale) and getBody($locale) helpers - Added by QA
  • Future: Add more comprehensive relationship tests when features use them

Security Review

Status: PASS

  • national_id properly marked as hidden in User model (serialization protection)
  • password uses hashed cast
  • Two-factor secrets hidden from serialization
  • No SQL injection vulnerabilities (using Eloquent)
  • Cascade deletes properly configured to prevent orphaned records

Performance Considerations

Status: PASS

  • Indexes added on: users.email, consultations.booking_date, consultations.user_id, timelines.user_id, posts.status
  • Foreign keys properly indexed via constrained() method
  • No N+1 query concerns at this foundation level

Files Modified During Review

  • app/Models/Post.php - Added getTitle($locale) and getBody($locale) helper methods
  • tests/Unit/Models/PostTest.php - Created with 5 tests for locale helpers

Pint Formatting Note

8 test files have minor formatting issues (missing trailing newlines). These are pre-existing files not created by this story. Recommend running vendor/bin/pint to fix, but not blocking.

Gate Status

Gate: PASS → docs/qa/gates/1.1-project-setup-database-schema.yml

Ready for Done

The implementation is complete, well-tested, and follows all architectural guidelines. All acceptance criteria are met with comprehensive test coverage. Minor improvement suggestions are optional and can be addressed in future stories as needed.