18 KiB
Story 2.1: Individual Client Account Management
Epic Reference
Epic 2: User Management System
User Story
As an admin, I want to create, view, edit, and search individual client accounts, So that I can manage client information and provide them platform access.
Story Context
Existing System Integration
- Integrates with: Users table, Fortify authentication
- Technology: Livewire Volt, Flux UI forms
- Follows pattern: Admin CRUD patterns, class-based Volt components
- Touch points: User model, admin dashboard
- PRD Reference: Section 5.3 (User Management System), Section 16.1 (Database Schema)
Prerequisites from Epic 1
- Users table with fields:
name,email,password,user_type,national_id,phone,preferred_language,status - AdminLog model and
admin_logstable for audit logging - Bilingual infrastructure (lang files,
__()helper)
Acceptance Criteria
Create Individual Client
- Form with required fields:
- Full Name (required)
- National ID Number (required, unique)
- Email Address (required, unique)
- Phone Number (required)
- Password (admin-set, required)
- Preferred Language (Arabic/English dropdown)
- Validation for all required fields
- Duplicate email/National ID prevention with clear error message
- Password strength indicator (optional)
- Success message on creation
List View
- Display all individual clients (user_type = 'individual')
- Columns: Name, Email, National ID, Phone, Status, Created Date
- Pagination (10/25/50 per page)
- Default sort by created date (newest first)
Search & Filter
- Search by name, email, or National ID
- Filter by status (active/deactivated/all)
- Real-time search with debounce (300ms)
- Clear filters button
Edit Client
- Edit all client information
- Cannot change user_type from this form
- Validation same as create
- Success message on update
View Client Profile
- Display all client information
- Show consultation history summary
- Show timeline history summary
- Quick links to related records
Quality Requirements
- Bilingual form labels and messages
- Proper form validation with error display
- Audit log entries for all operations
- Tests for CRUD operations
Technical Notes
Files to Create
resources/views/livewire/admin/clients/individual/
├── index.blade.php # List view with search/filter/pagination
├── create.blade.php # Create form
├── edit.blade.php # Edit form
└── show.blade.php # View profile page
User Model Scope
// In User model
public function scopeIndividual($query)
{
return $query->where('user_type', 'individual');
}
Volt Component Structure
<?php
use App\Models\User;
use Livewire\Volt\Component;
use Livewire\WithPagination;
new class extends Component {
use WithPagination;
public string $search = '';
public string $statusFilter = '';
public function updatedSearch()
{
$this->resetPage();
}
public function with(): array
{
return [
'clients' => User::individual()
->when($this->search, fn($q) => $q->where(function($q) {
$q->where('name', 'like', "%{$this->search}%")
->orWhere('email', 'like', "%{$this->search}%")
->orWhere('national_id', 'like', "%{$this->search}%");
}))
->when($this->statusFilter, fn($q) => $q->where('status', $this->statusFilter))
->latest()
->paginate(10),
];
}
};
Validation Rules
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'national_id' => ['required', 'string', 'unique:users,national_id'],
'email' => ['required', 'email', 'unique:users,email'],
'phone' => ['required', 'string'],
'password' => ['required', 'string', 'min:8'],
'preferred_language' => ['required', 'in:ar,en'],
];
}
Admin Logging
// After creating user
AdminLog::create([
'admin_id' => auth()->id(),
'action_type' => 'create',
'target_type' => 'user',
'target_id' => $user->id,
'new_values' => $user->only(['name', 'email', 'national_id']),
'ip_address' => request()->ip(),
]);
Edge Cases & Error Handling
- Validation failure: Display inline field errors using Flux UI error states
- Duplicate email: Show "Email already exists" error on email field
- Duplicate National ID: Show "National ID already registered" error
- Empty search results: Display "No clients found" message with clear filters option
Testing Requirements
Test File Location
tests/Feature/Admin/IndividualClientTest.php
Test Scenarios
Create Client Tests
- Can create individual client with all valid data
- Cannot create client without required name field
- Cannot create client without required email field
- Cannot create client without required national_id field
- Cannot create client without required phone field
- Cannot create client with invalid email format
- Cannot create client with duplicate email (existing user)
- Cannot create client with duplicate national_id
- Cannot create client with password less than 8 characters
- Created client has user_type set to 'individual'
- AdminLog entry created on successful creation
List View Tests
- Index page displays only individual clients (not company/admin)
- Pagination works with 10/25/50 per page options
- Clients sorted by created_at desc by default
Search & Filter Tests
- Can search clients by name (partial match)
- Can search clients by email (partial match)
- Can search clients by national_id (partial match)
- Can filter clients by active status
- Can filter clients by deactivated status
- Clear filters resets search and filter
Edit Client Tests
- Can edit existing client information
- Edit form pre-populates with current values
- Validation rules apply on edit (except unique for own record)
- AdminLog entry created on successful update
- Cannot change user_type via edit form
View Profile Tests
- Profile page displays all client information
- Profile shows consultation count/summary
- Profile shows timeline count/summary
Testing Approach
use Livewire\Volt\Volt;
test('admin can create individual client', function () {
$admin = User::factory()->admin()->create();
Volt::actingAs($admin)
->test('admin.clients.individual.create')
->set('name', 'Test Client')
->set('email', 'client@example.com')
->set('national_id', '123456789')
->set('phone', '+970599123456')
->set('password', 'password123')
->set('preferred_language', 'ar')
->call('create')
->assertHasNoErrors();
expect(User::where('email', 'client@example.com')->exists())->toBeTrue();
expect(AdminLog::where('action_type', 'create')->exists())->toBeTrue();
});
Definition of Done
- Create individual client form works
- List view displays all individual clients
- Search and filter functional
- Edit client works with validation
- View profile shows complete information
- Duplicate prevention works
- Audit logging implemented
- Bilingual support complete
- Tests pass for all CRUD operations
- Code formatted with Pint
Dependencies
- Story 1.1: Database schema with
userstable (user_type, national_id, status fields) andadmin_logstable - Story 1.2: Authentication system with admin role, AdminLog model
- Story 1.3: Bilingual infrastructure (translation files,
__()helper) - Story 1.4: Base admin layout and navigation
Risk Assessment
- Primary Risk: Duplicate National ID from different sources
- Mitigation: Database unique constraint + form validation
- Rollback: Remove user and notify if duplicate discovered
Estimation
Complexity: Medium Estimated Effort: 4-5 hours
Dev Agent Record
Status
Done
Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
File List
Created:
resources/views/livewire/admin/clients/individual/index.blade.php- List view with search/filter/paginationresources/views/livewire/admin/clients/individual/create.blade.php- Create individual client formresources/views/livewire/admin/clients/individual/edit.blade.php- Edit individual client formresources/views/livewire/admin/clients/individual/show.blade.php- Client profile/view pagelang/en/clients.php- English translations for clients modulelang/ar/clients.php- Arabic translations for clients moduletests/Feature/Admin/IndividualClientTest.php- 32 comprehensive tests for CRUD operations
Modified:
app/Models/User.php- AddedscopeIndividual()andscopeCompanies()scopesroutes/web.php- Added admin routes for individual clients CRUDresources/views/components/layouts/app/sidebar.blade.php- Added User Management navigation for adminslang/en/navigation.php- Added navigation translations for user managementlang/ar/navigation.php- Added Arabic navigation translations for user management
Change Log
| Date | Change | Files |
|---|---|---|
| 2025-12-26 | Added User model scopes for individual and company clients | app/Models/User.php |
| 2025-12-26 | Created bilingual translation files for clients module | lang/en/clients.php, lang/ar/clients.php |
| 2025-12-26 | Created individual clients CRUD Volt components | resources/views/livewire/admin/clients/individual/*.blade.php |
| 2025-12-26 | Added admin routes for individual clients management | routes/web.php |
| 2025-12-26 | Added sidebar navigation for User Management | resources/views/components/layouts/app/sidebar.blade.php |
| 2025-12-26 | Added navigation translations | lang/en/navigation.php, lang/ar/navigation.php |
| 2025-12-26 | Created comprehensive test suite (32 tests) | tests/Feature/Admin/IndividualClientTest.php |
Completion Notes
- All CRUD operations implemented with class-based Volt components following existing patterns
- Bilingual support complete for all form labels, messages, and navigation
- Search supports partial match on name, email, and national_id
- Filter by status (active/deactivated/all) with clear filters functionality
- Pagination with 10/25/50 per page options, sorted by created_at desc by default
- Audit logging (AdminLog) implemented for create and update operations
- Client profile page shows consultation and timeline counts with summary stats
- All 32 tests pass covering create, list, search, filter, edit, view, and authorization
- Full test suite (200 tests) passes with no regressions
- Code formatted with Laravel Pint
Definition of Done Checklist
- Create individual client form works
- List view displays all individual clients
- Search and filter functional
- Edit client works with validation
- View profile shows complete information
- Duplicate prevention works (email and national_id unique validation)
- Audit logging implemented (AdminLog entries for create/update)
- Bilingual support complete
- Tests pass for all CRUD operations (32 tests)
- Code formatted with Pint
QA Results
Review Date: 2025-12-26
Reviewed By: Quinn (Test Architect)
Risk Assessment
Risk Level: LOW-MEDIUM
- Not a security-critical feature (no auth/payment handling)
- 32 tests added covering all CRUD operations
- Diff is moderate (~700 lines across all new files)
- First gate for this story (no previous FAIL)
- 6 acceptance criteria sections with clear requirements
Code Quality Assessment
Overall: EXCELLENT
The implementation follows Laravel/Livewire best practices consistently:
-
Architecture & Patterns
- Class-based Volt components used correctly throughout
- Proper use of
WithPaginationtrait for list view - Clean separation of concerns with PHP logic in class block
- Follows existing project patterns for admin components
-
Code Structure
- Consistent file organization in
resources/views/livewire/admin/clients/individual/ - User model scopes (
scopeIndividual(),scopeCompanies()) properly implemented - Clean route definitions using Volt::route()
- Consistent file organization in
-
Validation
- Comprehensive rules for all form fields
- Proper handling of unique constraints on edit (using
Rule::unique()->ignore()) - Custom error messages for duplicate email/national_id
-
Audit Logging
- AdminLog entries created for both create and update operations
- Old and new values captured appropriately
- IP address tracking implemented
Refactoring Performed
None required. Code is clean and follows project conventions.
Compliance Check
- Coding Standards: ✓ Pint passes with --dirty flag
- Project Structure: ✓ Files in correct locations per story spec
- Testing Strategy: ✓ 32 tests covering all acceptance criteria
- All ACs Met: ✓ See traceability matrix below
Requirements Traceability
| Acceptance Criteria | Test Coverage | Status |
|---|---|---|
| AC1: Create form with all required fields | admin can create individual client with all valid data, field validation tests |
✓ |
| AC2: Validation for all required fields | 5 tests for missing required fields | ✓ |
| AC3: Duplicate email/National ID prevention | cannot create client with duplicate email/national_id |
✓ |
| AC4: Password strength indicator | Optional per story - not implemented | ⚠️ Optional |
| AC5: Success message on creation | Tested via redirect assertion | ✓ |
| AC6: List view individual clients only | index page displays only individual clients |
✓ |
| AC7: Columns display | Verified via component assertions | ✓ |
| AC8: Pagination 10/25/50 | Component property perPage tested |
✓ |
| AC9: Default sort by created_at desc | clients sorted by created_at desc by default |
✓ |
| AC10: Search by name/email/National ID | 3 search tests with partial match | ✓ |
| AC11: Filter by status | can filter clients by active/deactivated status |
✓ |
| AC12: Real-time search with debounce | wire:model.live.debounce.300ms in template |
✓ |
| AC13: Clear filters button | clear filters resets search and filter |
✓ |
| AC14: Edit all client information | can edit existing client information |
✓ |
| AC15: Cannot change user_type | Edit form doesn't expose user_type field | ✓ |
| AC16: Edit validation same as create | validation rules apply on edit |
✓ |
| AC17: Success message on update | Tested via session flash + redirect | ✓ |
| AC18: View client profile | profile page displays all client information |
✓ |
| AC19: Consultation history summary | profile shows consultation count |
✓ |
| AC20: Timeline history summary | profile shows timeline count |
✓ |
| AC21: Bilingual form labels | Translation files complete (en/ar) | ✓ |
| AC22: Proper form validation display | Flux:error components used | ✓ |
| AC23: Audit log entries | Tests verify AdminLog entries | ✓ |
| AC24: Tests for CRUD operations | 32 tests pass | ✓ |
Improvements Checklist
All items completed by developer - no action required:
- Proper validation rules with custom messages
- Clean route structure with named routes
- Bilingual translations complete
- Sidebar navigation for admin users
- AdminLog entries for audit trail
- Proper eager loading with loadCount() for profile stats
Security Review
Status: PASS
- Authorization: Routes properly protected by
adminmiddleware - Authentication: Tests verify non-admin and unauthenticated access blocked
- Data Validation: All inputs validated server-side before processing
- Password Handling: Uses
Hash::make()for password storage (proper bcrypt) - Sensitive Data: national_id marked as hidden in User model
Performance Considerations
Status: PASS
- N+1 Prevention: Profile page uses
loadCount()for relationship counts - Pagination: Implemented with configurable per-page options
- Search: Uses database-level filtering, not PHP array filtering
- Eager Loading: Properly scoped queries with when() clauses
Maintainability Assessment
Status: PASS
- Single Responsibility: Each component handles one view/action
- DRY: Translation keys reused across components
- Testability: All operations fully testable via Volt::test()
- Documentation: Code is self-documenting with clear method names
Files Modified During Review
None. No refactoring was necessary.
Gate Status
Gate: PASS → docs/qa/gates/2.1-individual-client-account-management.yml
Recommended Status
✓ Ready for Done - All acceptance criteria met, comprehensive test coverage, no blocking issues found.
Future Recommendations for Dev Agent
The following items are not blocking but should be considered for future implementation:
-
Password Strength Indicator (Optional)
- File:
resources/views/livewire/admin/clients/individual/create.blade.php - Description: AC4 in the story marked this as optional. Consider adding a visual password strength indicator (e.g., weak/medium/strong) using JavaScript or a Livewire reactive property.
- Priority: Low
- File:
-
Delete Client Functionality
- File:
resources/views/livewire/admin/clients/individual/index.blade.php - Description: The current story scope covers create, view, edit, and search only. Translation keys for delete already exist (
clients.delete,clients.client_deleted). When requirements are clarified, add a delete action with confirmation modal and appropriate AdminLog entry. - Priority: Medium - implement when a future story requires it
- Considerations:
- Soft delete vs hard delete decision needed
- Handle cascading relationships (consultations, timelines)
- Add authorization check before deletion
- Create test coverage for delete operation
- File: