feat: multi-tenant SaaS implementation - admin interface, tenant isolation, and UI updates

This commit is contained in:
jeremy bayse
2026-03-28 18:38:22 +01:00
parent 7d94be7a8c
commit f53d5770df
20 changed files with 757 additions and 34 deletions

View File

@@ -9,10 +9,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
#[Fillable(['user_id', 'job_position_id', 'phone', 'linkedin_url', 'status', 'notes', 'cv_score', 'motivation_score', 'interview_score', 'ai_analysis'])]
use App\Traits\BelongsToTenant;
#[Fillable(['user_id', 'job_position_id', 'phone', 'linkedin_url', 'status', 'notes', 'cv_score', 'motivation_score', 'interview_score', 'ai_analysis', 'tenant_id'])]
class Candidate extends Model
{
use HasFactory;
use HasFactory, BelongsToTenant;
protected $casts = [
'ai_analysis' => 'array',

View File

@@ -7,10 +7,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
#[Fillable(['title', 'description', 'requirements', 'ai_prompt'])]
use App\Traits\BelongsToTenant;
#[Fillable(['title', 'description', 'requirements', 'ai_prompt', 'tenant_id'])]
class JobPosition extends Model
{
use HasFactory;
use HasFactory, BelongsToTenant;
protected $casts = [
'requirements' => 'array',

View File

@@ -8,10 +8,12 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
#[Fillable(['title', 'description', 'duration_minutes'])]
use App\Traits\BelongsToTenant;
#[Fillable(['title', 'description', 'duration_minutes', 'tenant_id'])]
class Quiz extends Model
{
use HasFactory;
use HasFactory, BelongsToTenant;
public function questions(): HasMany
{

31
app/Models/Tenant.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Fillable;
#[Fillable(['name'])]
class Tenant extends Model
{
public function users()
{
return $this->hasMany(User::class);
}
public function candidates()
{
return $this->hasMany(Candidate::class);
}
public function quizzes()
{
return $this->hasMany(Quiz::class);
}
public function jobPositions()
{
return $this->hasMany(JobPosition::class);
}
}

View File

@@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
#[Fillable(['name', 'email', 'password', 'role'])]
#[Fillable(['name', 'email', 'password', 'role', 'tenant_id'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable
{
@@ -19,7 +19,12 @@ class User extends Authenticatable
public function isAdmin(): bool
{
return $this->role === 'admin';
return in_array($this->role, ['admin', 'super_admin']);
}
public function isSuperAdmin(): bool
{
return $this->role === 'super_admin';
}
public function isCandidate(): bool
@@ -32,6 +37,11 @@ class User extends Authenticatable
return $this->hasOne(Candidate::class);
}
public function tenant()
{
return $this->belongsTo(Tenant::class);
}
/**
* Get the attributes that should be cast.
*