From 959cc0e717d591b9e75e5c5d110f95567f422128 Mon Sep 17 00:00:00 2001 From: Naser Mansour Date: Fri, 9 Jan 2026 18:45:17 +0200 Subject: [PATCH] complete story 15.1 --- app/Enums/PotentialClientType.php | 19 ++++ app/Models/PotentialClient.php | 30 ++++++ database/factories/PotentialClientFactory.php | 41 ++++++++ ..._163956_create_potential_clients_table.php | 35 +++++++ docs/stories/story-15.1-database-model.md | 62 ++++++++++-- lang/ar/potential-clients.php | 21 ++++ lang/en/potential-clients.php | 21 ++++ tests/Unit/Enums/PotentialClientTypeTest.php | 19 ++++ tests/Unit/Models/PotentialClientTest.php | 99 +++++++++++++++++++ 9 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 app/Enums/PotentialClientType.php create mode 100644 app/Models/PotentialClient.php create mode 100644 database/factories/PotentialClientFactory.php create mode 100644 database/migrations/2026_01_09_163956_create_potential_clients_table.php create mode 100644 lang/ar/potential-clients.php create mode 100644 lang/en/potential-clients.php create mode 100644 tests/Unit/Enums/PotentialClientTypeTest.php create mode 100644 tests/Unit/Models/PotentialClientTest.php diff --git a/app/Enums/PotentialClientType.php b/app/Enums/PotentialClientType.php new file mode 100644 index 0000000..a468fca --- /dev/null +++ b/app/Enums/PotentialClientType.php @@ -0,0 +1,19 @@ + __('potential-clients.types.individual'), + self::Company => __('potential-clients.types.company'), + self::Agency => __('potential-clients.types.agency'), + }; + } +} diff --git a/app/Models/PotentialClient.php b/app/Models/PotentialClient.php new file mode 100644 index 0000000..51427cd --- /dev/null +++ b/app/Models/PotentialClient.php @@ -0,0 +1,30 @@ + PotentialClientType::class, + ]; + } +} diff --git a/database/factories/PotentialClientFactory.php b/database/factories/PotentialClientFactory.php new file mode 100644 index 0000000..3ab28f9 --- /dev/null +++ b/database/factories/PotentialClientFactory.php @@ -0,0 +1,41 @@ + fake()->randomElement(PotentialClientType::cases()), + 'name' => fake()->name(), + 'phone' => fake()->phoneNumber(), + 'email' => fake()->safeEmail(), + 'address' => fake()->address(), + 'social_media' => fake()->url(), + 'website' => fake()->url(), + 'notes' => fake()->optional()->sentence(), + ]; + } + + public function individual(): static + { + return $this->state(['type' => PotentialClientType::Individual]); + } + + public function company(): static + { + return $this->state(['type' => PotentialClientType::Company]); + } + + public function agency(): static + { + return $this->state(['type' => PotentialClientType::Agency]); + } +} diff --git a/database/migrations/2026_01_09_163956_create_potential_clients_table.php b/database/migrations/2026_01_09_163956_create_potential_clients_table.php new file mode 100644 index 0000000..1ec569d --- /dev/null +++ b/database/migrations/2026_01_09_163956_create_potential_clients_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('type'); + $table->string('name')->nullable(); + $table->string('phone', 50)->nullable(); + $table->string('email')->nullable(); + $table->text('address')->nullable(); + $table->string('social_media')->nullable(); + $table->string('website')->nullable(); + $table->text('notes')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('potential_clients'); + } +}; diff --git a/docs/stories/story-15.1-database-model.md b/docs/stories/story-15.1-database-model.md index 1584511..21dc544 100644 --- a/docs/stories/story-15.1-database-model.md +++ b/docs/stories/story-15.1-database-model.md @@ -246,15 +246,15 @@ return [ ## Dev Checklist -- [ ] Create migration file with proper schema -- [ ] Run migration successfully -- [ ] Create `PotentialClientType` enum with `label()` method -- [ ] Create `PotentialClient` model with fillable and casts -- [ ] Create factory with all state methods -- [ ] Create English translations -- [ ] Create Arabic translations -- [ ] Write model unit tests -- [ ] Verify factory generates valid data +- [x] Create migration file with proper schema +- [x] Run migration successfully +- [x] Create `PotentialClientType` enum with `label()` method +- [x] Create `PotentialClient` model with fillable and casts +- [x] Create factory with all state methods +- [x] Create English translations +- [x] Create Arabic translations +- [x] Write model unit tests +- [x] Verify factory generates valid data ## Estimation @@ -264,3 +264,47 @@ return [ ## Dependencies - None (foundation for other stories in this epic) + +--- + +## Dev Agent Record + +### Status + +Ready for Review + +### Agent Model Used + +Claude Opus 4.5 (claude-opus-4-5-20251101) + +### Completion Notes + +All acceptance criteria met: +- AC1: Migration created with all specified fields and proper schema +- AC2: PotentialClientType enum with Individual, Company, Agency cases and label() method +- AC3: PotentialClient model with fillable array and type casting to enum +- AC4: Factory with definition() and state methods (individual(), company(), agency()) +- AC5: English and Arabic translation files with all required keys + +### File List + +| File | Action | +|------|--------| +| `database/migrations/2026_01_09_163956_create_potential_clients_table.php` | Created | +| `app/Enums/PotentialClientType.php` | Created | +| `app/Models/PotentialClient.php` | Created | +| `database/factories/PotentialClientFactory.php` | Created | +| `lang/en/potential-clients.php` | Created | +| `lang/ar/potential-clients.php` | Created | +| `tests/Unit/Models/PotentialClientTest.php` | Created | +| `tests/Unit/Enums/PotentialClientTypeTest.php` | Created | + +### Change Log + +- 2026-01-09: Initial implementation of Story 15.1 + - Created potential_clients table migration with all specified columns + - Created PotentialClientType enum with label() method for bilingual support + - Created PotentialClient model with proper fillable and casts + - Created factory with realistic data and state methods + - Added English and Arabic translations + - Added comprehensive unit tests for model and enum (13 tests, 32 assertions) diff --git a/lang/ar/potential-clients.php b/lang/ar/potential-clients.php new file mode 100644 index 0000000..badf61a --- /dev/null +++ b/lang/ar/potential-clients.php @@ -0,0 +1,21 @@ + 'العملاء المحتملون', + 'singular' => 'عميل محتمل', + 'types' => [ + 'individual' => 'فرد', + 'company' => 'شركة', + 'agency' => 'وكالة', + ], + 'fields' => [ + 'type' => 'النوع', + 'name' => 'الاسم', + 'phone' => 'الهاتف', + 'email' => 'البريد الإلكتروني', + 'address' => 'العنوان', + 'social_media' => 'وسائل التواصل', + 'website' => 'الموقع الإلكتروني', + 'notes' => 'ملاحظات', + ], +]; diff --git a/lang/en/potential-clients.php b/lang/en/potential-clients.php new file mode 100644 index 0000000..fe4a60f --- /dev/null +++ b/lang/en/potential-clients.php @@ -0,0 +1,21 @@ + 'Potential Clients', + 'singular' => 'Potential Client', + 'types' => [ + 'individual' => 'Individual', + 'company' => 'Company', + 'agency' => 'Agency', + ], + 'fields' => [ + 'type' => 'Type', + 'name' => 'Name', + 'phone' => 'Phone', + 'email' => 'Email', + 'address' => 'Address', + 'social_media' => 'Social Media', + 'website' => 'Website', + 'notes' => 'Notes', + ], +]; diff --git a/tests/Unit/Enums/PotentialClientTypeTest.php b/tests/Unit/Enums/PotentialClientTypeTest.php new file mode 100644 index 0000000..544cdd5 --- /dev/null +++ b/tests/Unit/Enums/PotentialClientTypeTest.php @@ -0,0 +1,19 @@ +toHaveCount(3); +}); + +test('potential client type individual has correct value', function () { + expect(PotentialClientType::Individual->value)->toBe('individual'); +}); + +test('potential client type company has correct value', function () { + expect(PotentialClientType::Company->value)->toBe('company'); +}); + +test('potential client type agency has correct value', function () { + expect(PotentialClientType::Agency->value)->toBe('agency'); +}); diff --git a/tests/Unit/Models/PotentialClientTest.php b/tests/Unit/Models/PotentialClientTest.php new file mode 100644 index 0000000..2eb496e --- /dev/null +++ b/tests/Unit/Models/PotentialClientTest.php @@ -0,0 +1,99 @@ +create([ + 'type' => PotentialClientType::Individual, + ]); + + expect($client->type)->toBeInstanceOf(PotentialClientType::class) + ->and($client->type)->toBe(PotentialClientType::Individual); +}); + +test('all fields are fillable', function () { + $data = [ + 'type' => PotentialClientType::Company, + 'name' => 'Test Company', + 'phone' => '+1234567890', + 'email' => 'test@example.com', + 'address' => '123 Test Street', + 'social_media' => 'https://twitter.com/test', + 'website' => 'https://example.com', + 'notes' => 'Some notes here', + ]; + + $client = PotentialClient::create($data); + + expect($client->type)->toBe(PotentialClientType::Company) + ->and($client->name)->toBe('Test Company') + ->and($client->phone)->toBe('+1234567890') + ->and($client->email)->toBe('test@example.com') + ->and($client->address)->toBe('123 Test Street') + ->and($client->social_media)->toBe('https://twitter.com/test') + ->and($client->website)->toBe('https://example.com') + ->and($client->notes)->toBe('Some notes here'); +}); + +test('nullable fields can be null', function () { + $client = PotentialClient::factory()->create([ + 'type' => PotentialClientType::Individual, + 'name' => null, + 'phone' => null, + 'email' => null, + 'address' => null, + 'social_media' => null, + 'website' => null, + 'notes' => null, + ]); + + expect($client->name)->toBeNull() + ->and($client->phone)->toBeNull() + ->and($client->email)->toBeNull() + ->and($client->address)->toBeNull() + ->and($client->social_media)->toBeNull() + ->and($client->website)->toBeNull() + ->and($client->notes)->toBeNull(); +}); + +test('factory individual state creates individual type', function () { + $client = PotentialClient::factory()->individual()->create(); + + expect($client->type)->toBe(PotentialClientType::Individual); +}); + +test('factory company state creates company type', function () { + $client = PotentialClient::factory()->company()->create(); + + expect($client->type)->toBe(PotentialClientType::Company); +}); + +test('factory agency state creates agency type', function () { + $client = PotentialClient::factory()->agency()->create(); + + expect($client->type)->toBe(PotentialClientType::Agency); +}); + +test('factory generates valid data', function () { + $client = PotentialClient::factory()->create(); + + expect($client->type)->toBeInstanceOf(PotentialClientType::class) + ->and($client->id)->toBeInt(); +}); + +test('type enum returns english labels', function () { + app()->setLocale('en'); + + expect(PotentialClientType::Individual->label())->toBe('Individual') + ->and(PotentialClientType::Company->label())->toBe('Company') + ->and(PotentialClientType::Agency->label())->toBe('Agency'); +}); + +test('type enum returns arabic labels', function () { + app()->setLocale('ar'); + + expect(PotentialClientType::Individual->label())->toBe('فرد') + ->and(PotentialClientType::Company->label())->toBe('شركة') + ->and(PotentialClientType::Agency->label())->toBe('وكالة'); +});