completed story 1.1 with QA tests and fixes

This commit is contained in:
Naser Mansour
2025-12-26 13:46:39 +02:00
parent 028ce573b9
commit 84d9c2f66a
57 changed files with 2193 additions and 85 deletions
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\AdminLog>
*/
class AdminLogFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'admin_id' => User::factory()->admin(),
'action' => fake()->randomElement(['create', 'update', 'delete', 'login', 'logout']),
'target_type' => fake()->randomElement(['User', 'Consultation', 'Timeline', 'Post']),
'target_id' => fake()->optional()->numberBetween(1, 100),
'old_values' => fake()->optional()->randomElements(['status' => 'pending', 'name' => 'Old Name'], null),
'new_values' => fake()->optional()->randomElements(['status' => 'approved', 'name' => 'New Name'], null),
'ip_address' => fake()->ipv4(),
'created_at' => fake()->dateTimeBetween('-30 days', 'now'),
];
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\BlockedTime>
*/
class BlockedTimeFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'block_date' => fake()->dateTimeBetween('now', '+30 days'),
'start_time' => null,
'end_time' => null,
'reason' => fake()->optional()->sentence(),
];
}
/**
* Create a full day block.
*/
public function fullDay(): static
{
return $this->state(fn (array $attributes) => [
'start_time' => null,
'end_time' => null,
]);
}
/**
* Create a partial day block with specific times.
*/
public function partialDay(): static
{
return $this->state(fn (array $attributes) => [
'start_time' => '09:00:00',
'end_time' => '12:00:00',
]);
}
}
@@ -0,0 +1,94 @@
<?php
namespace Database\Factories;
use App\Enums\ConsultationStatus;
use App\Enums\ConsultationType;
use App\Enums\PaymentStatus;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Consultation>
*/
class ConsultationFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$consultationType = fake()->randomElement(ConsultationType::cases());
$paymentStatus = $consultationType === ConsultationType::Paid
? fake()->randomElement([PaymentStatus::Pending, PaymentStatus::Received])
: PaymentStatus::NotApplicable;
return [
'user_id' => User::factory(),
'booking_date' => fake()->dateTimeBetween('now', '+30 days'),
'booking_time' => fake()->time('H:i:s'),
'problem_summary' => fake()->paragraph(),
'consultation_type' => $consultationType,
'payment_amount' => $consultationType === ConsultationType::Paid ? fake()->randomFloat(2, 50, 500) : null,
'payment_status' => $paymentStatus,
'status' => fake()->randomElement(ConsultationStatus::cases()),
'admin_notes' => fake()->optional()->paragraph(),
];
}
/**
* Create a free consultation.
*/
public function free(): static
{
return $this->state(fn (array $attributes) => [
'consultation_type' => ConsultationType::Free,
'payment_amount' => null,
'payment_status' => PaymentStatus::NotApplicable,
]);
}
/**
* Create a paid consultation.
*/
public function paid(): static
{
return $this->state(fn (array $attributes) => [
'consultation_type' => ConsultationType::Paid,
'payment_amount' => fake()->randomFloat(2, 50, 500),
'payment_status' => fake()->randomElement([PaymentStatus::Pending, PaymentStatus::Received]),
]);
}
/**
* Create a pending consultation.
*/
public function pending(): static
{
return $this->state(fn (array $attributes) => [
'status' => ConsultationStatus::Pending,
]);
}
/**
* Create an approved consultation.
*/
public function approved(): static
{
return $this->state(fn (array $attributes) => [
'status' => ConsultationStatus::Approved,
]);
}
/**
* Create a completed consultation.
*/
public function completed(): static
{
return $this->state(fn (array $attributes) => [
'status' => ConsultationStatus::Completed,
]);
}
}
@@ -0,0 +1,61 @@
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Notification>
*/
class NotificationFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'type' => fake()->randomElement(['consultation_approved', 'consultation_rejected', 'timeline_update', 'new_post']),
'data' => [
'message' => fake()->sentence(),
'action_url' => fake()->url(),
],
'read_at' => null,
'sent_at' => fake()->optional()->dateTimeBetween('-7 days', 'now'),
];
}
/**
* Create a read notification.
*/
public function read(): static
{
return $this->state(fn (array $attributes) => [
'read_at' => fake()->dateTimeBetween('-7 days', 'now'),
]);
}
/**
* Create an unread notification.
*/
public function unread(): static
{
return $this->state(fn (array $attributes) => [
'read_at' => null,
]);
}
/**
* Create a sent notification.
*/
public function sent(): static
{
return $this->state(fn (array $attributes) => [
'sent_at' => fake()->dateTimeBetween('-7 days', 'now'),
]);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Database\Factories;
use App\Enums\PostStatus;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'title' => [
'ar' => fake('ar_SA')->sentence(),
'en' => fake()->sentence(),
],
'body' => [
'ar' => fake('ar_SA')->paragraphs(3, true),
'en' => fake()->paragraphs(3, true),
],
'status' => PostStatus::Draft,
'published_at' => null,
];
}
/**
* Create a published post.
*/
public function published(): static
{
return $this->state(fn (array $attributes) => [
'status' => PostStatus::Published,
'published_at' => fake()->dateTimeBetween('-30 days', 'now'),
]);
}
/**
* Create a draft post.
*/
public function draft(): static
{
return $this->state(fn (array $attributes) => [
'status' => PostStatus::Draft,
'published_at' => null,
]);
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Database\Factories;
use App\Enums\TimelineStatus;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Timeline>
*/
class TimelineFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'case_name' => fake()->sentence(3),
'case_reference' => fake()->boolean(70) ? fake()->unique()->regexify('[A-Z]{2}-[0-9]{4}-[0-9]{3}') : null,
'status' => TimelineStatus::Active,
];
}
/**
* Create an active timeline.
*/
public function active(): static
{
return $this->state(fn (array $attributes) => [
'status' => TimelineStatus::Active,
]);
}
/**
* Create an archived timeline.
*/
public function archived(): static
{
return $this->state(fn (array $attributes) => [
'status' => TimelineStatus::Archived,
]);
}
/**
* Create a timeline with a case reference.
*/
public function withReference(): static
{
return $this->state(fn (array $attributes) => [
'case_reference' => fake()->unique()->regexify('[A-Z]{2}-[0-9]{4}-[0-9]{3}'),
]);
}
}
@@ -0,0 +1,27 @@
<?php
namespace Database\Factories;
use App\Models\Timeline;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\TimelineUpdate>
*/
class TimelineUpdateFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'timeline_id' => Timeline::factory(),
'admin_id' => User::factory()->admin(),
'update_text' => fake()->paragraph(),
];
}
}
+84 -5
View File
@@ -2,6 +2,8 @@
namespace Database\Factories;
use App\Enums\UserStatus;
use App\Enums\UserType;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
@@ -24,14 +26,16 @@ class UserFactory extends Factory
public function definition(): array
{
return [
'name' => fake()->name(),
'user_type' => UserType::Individual,
'full_name' => fake()->name(),
'national_id' => fake()->numerify('#########'),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'phone' => fake()->phoneNumber(),
'password' => static::$password ??= Hash::make('password'),
'status' => UserStatus::Active,
'preferred_language' => fake()->randomElement(['ar', 'en']),
'email_verified_at' => now(),
'remember_token' => Str::random(10),
'two_factor_secret' => Str::random(10),
'two_factor_recovery_codes' => Str::random(10),
'two_factor_confirmed_at' => now(),
];
}
@@ -45,6 +49,81 @@ class UserFactory extends Factory
]);
}
/**
* Create an admin user.
*/
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'user_type' => UserType::Admin,
'company_name' => null,
'company_cert_number' => null,
'contact_person_name' => null,
'contact_person_id' => null,
]);
}
/**
* Create an individual client user.
*/
public function individual(): static
{
return $this->state(fn (array $attributes) => [
'user_type' => UserType::Individual,
'company_name' => null,
'company_cert_number' => null,
'contact_person_name' => null,
'contact_person_id' => null,
]);
}
/**
* Create a company client user.
*/
public function company(): static
{
return $this->state(fn (array $attributes) => [
'user_type' => UserType::Company,
'national_id' => null,
'company_name' => fake()->company(),
'company_cert_number' => fake()->numerify('CR-######'),
'contact_person_name' => fake()->name(),
'contact_person_id' => fake()->numerify('#########'),
]);
}
/**
* Create a client user (individual or company).
*/
public function client(): static
{
return fake()->boolean()
? $this->individual()
: $this->company();
}
/**
* Create a deactivated user.
*/
public function deactivated(): static
{
return $this->state(fn (array $attributes) => [
'status' => UserStatus::Deactivated,
]);
}
/**
* Indicate that the model has two-factor authentication configured.
*/
public function withTwoFactor(): static
{
return $this->state(fn (array $attributes) => [
'two_factor_secret' => Str::random(10),
'two_factor_recovery_codes' => Str::random(10),
'two_factor_confirmed_at' => now(),
]);
}
/**
* Indicate that the model does not have two-factor authentication configured.
*/
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\WorkingHour>
*/
class WorkingHourFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'day_of_week' => fake()->numberBetween(0, 6),
'start_time' => '09:00:00',
'end_time' => '17:00:00',
'is_active' => true,
];
}
/**
* Create working hours for weekdays (Sunday-Thursday, 0-4).
*/
public function weekdays(): static
{
return $this->state(fn (array $attributes) => [
'day_of_week' => fake()->numberBetween(0, 4),
]);
}
/**
* Create inactive working hours.
*/
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
}