completed story 1.1 with QA tests and fixes
This commit is contained in:
@@ -19,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
public function create(array $input): User
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'full_name' => ['required', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
@@ -27,12 +27,14 @@ class CreateNewUser implements CreatesNewUsers
|
||||
'max:255',
|
||||
Rule::unique(User::class),
|
||||
],
|
||||
'phone' => ['required', 'string', 'max:20'],
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
return User::create([
|
||||
'name' => $input['name'],
|
||||
'full_name' => $input['full_name'],
|
||||
'email' => $input['email'],
|
||||
'phone' => $input['phone'],
|
||||
'password' => $input['password'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ConsultationStatus: string
|
||||
{
|
||||
case Pending = 'pending';
|
||||
case Approved = 'approved';
|
||||
case Rejected = 'rejected';
|
||||
case Completed = 'completed';
|
||||
case NoShow = 'no_show';
|
||||
case Cancelled = 'cancelled';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum ConsultationType: string
|
||||
{
|
||||
case Free = 'free';
|
||||
case Paid = 'paid';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum PaymentStatus: string
|
||||
{
|
||||
case Pending = 'pending';
|
||||
case Received = 'received';
|
||||
case NotApplicable = 'na';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum PostStatus: string
|
||||
{
|
||||
case Draft = 'draft';
|
||||
case Published = 'published';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum TimelineStatus: string
|
||||
{
|
||||
case Active = 'active';
|
||||
case Archived = 'archived';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum UserStatus: string
|
||||
{
|
||||
case Active = 'active';
|
||||
case Deactivated = 'deactivated';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum UserType: string
|
||||
{
|
||||
case Admin = 'admin';
|
||||
case Individual = 'individual';
|
||||
case Company = 'company';
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AdminLog extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'admin_id',
|
||||
'action',
|
||||
'target_type',
|
||||
'target_id',
|
||||
'old_values',
|
||||
'new_values',
|
||||
'ip_address',
|
||||
'created_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'old_values' => 'array',
|
||||
'new_values' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin that created the log.
|
||||
*/
|
||||
public function admin(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'admin_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BlockedTime extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'block_date',
|
||||
'start_time',
|
||||
'end_time',
|
||||
'reason',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'block_date' => 'date',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a full day block.
|
||||
*/
|
||||
public function isFullDay(): bool
|
||||
{
|
||||
return is_null($this->start_time) && is_null($this->end_time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ConsultationStatus;
|
||||
use App\Enums\ConsultationType;
|
||||
use App\Enums\PaymentStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Consultation extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'booking_date',
|
||||
'booking_time',
|
||||
'problem_summary',
|
||||
'consultation_type',
|
||||
'payment_amount',
|
||||
'payment_status',
|
||||
'status',
|
||||
'admin_notes',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'booking_date' => 'date',
|
||||
'consultation_type' => ConsultationType::class,
|
||||
'payment_status' => PaymentStatus::class,
|
||||
'status' => ConsultationStatus::class,
|
||||
'payment_amount' => 'decimal:2',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the consultation.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Notification extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'type',
|
||||
'data',
|
||||
'read_at',
|
||||
'sent_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'data' => 'array',
|
||||
'read_at' => 'datetime',
|
||||
'sent_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the notification.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the notification as read.
|
||||
*/
|
||||
public function markAsRead(): void
|
||||
{
|
||||
$this->update(['read_at' => now()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter unread notifications.
|
||||
*/
|
||||
public function scopeUnread($query)
|
||||
{
|
||||
return $query->whereNull('read_at');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PostStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'body',
|
||||
'status',
|
||||
'published_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'array',
|
||||
'body' => 'array',
|
||||
'status' => PostStatus::class,
|
||||
'published_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter published posts.
|
||||
*/
|
||||
public function scopePublished($query)
|
||||
{
|
||||
return $query->where('status', PostStatus::Published);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter draft posts.
|
||||
*/
|
||||
public function scopeDraft($query)
|
||||
{
|
||||
return $query->where('status', PostStatus::Draft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title in the specified locale, with fallback to Arabic.
|
||||
*/
|
||||
public function getTitle(?string $locale = null): string
|
||||
{
|
||||
$locale ??= app()->getLocale();
|
||||
|
||||
return $this->title[$locale] ?? $this->title['ar'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body in the specified locale, with fallback to Arabic.
|
||||
*/
|
||||
public function getBody(?string $locale = null): string
|
||||
{
|
||||
$locale ??= app()->getLocale();
|
||||
|
||||
return $this->body[$locale] ?? $this->body['ar'] ?? '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\TimelineStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Timeline extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'case_name',
|
||||
'case_reference',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => TimelineStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the timeline.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the updates for the timeline.
|
||||
*/
|
||||
public function updates(): HasMany
|
||||
{
|
||||
return $this->hasMany(TimelineUpdate::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TimelineUpdate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'timeline_id',
|
||||
'admin_id',
|
||||
'update_text',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the timeline that owns the update.
|
||||
*/
|
||||
public function timeline(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Timeline::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin that created the update.
|
||||
*/
|
||||
public function admin(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'admin_id');
|
||||
}
|
||||
}
|
||||
+115
-4
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use App\Enums\UserStatus;
|
||||
use App\Enums\UserType;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -20,9 +22,18 @@ class User extends Authenticatable
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'user_type',
|
||||
'full_name',
|
||||
'national_id',
|
||||
'company_name',
|
||||
'company_cert_number',
|
||||
'contact_person_name',
|
||||
'contact_person_id',
|
||||
'email',
|
||||
'phone',
|
||||
'password',
|
||||
'status',
|
||||
'preferred_language',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -32,6 +43,7 @@ class User extends Authenticatable
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'national_id',
|
||||
'two_factor_secret',
|
||||
'two_factor_recovery_codes',
|
||||
'remember_token',
|
||||
@@ -45,20 +57,119 @@ class User extends Authenticatable
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'user_type' => UserType::class,
|
||||
'status' => UserStatus::class,
|
||||
'email_verified_at' => 'datetime',
|
||||
'two_factor_confirmed_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's initials
|
||||
* Get the user's initials.
|
||||
*/
|
||||
public function initials(): string
|
||||
{
|
||||
return Str::of($this->name)
|
||||
return Str::of($this->full_name)
|
||||
->explode(' ')
|
||||
->take(2)
|
||||
->map(fn ($word) => Str::substr($word, 0, 1))
|
||||
->implode('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is an admin.
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->user_type === UserType::Admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is an individual client.
|
||||
*/
|
||||
public function isIndividual(): bool
|
||||
{
|
||||
return $this->user_type === UserType::Individual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is a company client.
|
||||
*/
|
||||
public function isCompany(): bool
|
||||
{
|
||||
return $this->user_type === UserType::Company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is a client (individual or company).
|
||||
*/
|
||||
public function isClient(): bool
|
||||
{
|
||||
return $this->isIndividual() || $this->isCompany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter admin users.
|
||||
*/
|
||||
public function scopeAdmins($query)
|
||||
{
|
||||
return $query->where('user_type', UserType::Admin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter client users (individual or company).
|
||||
*/
|
||||
public function scopeClients($query)
|
||||
{
|
||||
return $query->whereIn('user_type', [UserType::Individual, UserType::Company]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter active users.
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('status', UserStatus::Active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the consultations for the user.
|
||||
*/
|
||||
public function consultations(): HasMany
|
||||
{
|
||||
return $this->hasMany(Consultation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timelines for the user.
|
||||
*/
|
||||
public function timelines(): HasMany
|
||||
{
|
||||
return $this->hasMany(Timeline::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notifications for the user.
|
||||
*/
|
||||
public function customNotifications(): HasMany
|
||||
{
|
||||
return $this->hasMany(Notification::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin logs for the user.
|
||||
*/
|
||||
public function adminLogs(): HasMany
|
||||
{
|
||||
return $this->hasMany(AdminLog::class, 'admin_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timeline updates created by this admin.
|
||||
*/
|
||||
public function timelineUpdates(): HasMany
|
||||
{
|
||||
return $this->hasMany(TimelineUpdate::class, 'admin_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WorkingHour extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'day_of_week',
|
||||
'start_time',
|
||||
'end_time',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'day_of_week' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to filter active working hours.
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user