feat: multi-tenant SaaS implementation - admin interface, tenant isolation, and UI updates
This commit is contained in:
@@ -15,10 +15,14 @@ class CandidateController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$candidates = Candidate::with(['user', 'documents', 'attempts'])->latest()->get();
|
||||
$candidates = Candidate::with(['user', 'documents', 'attempts', 'tenant', 'jobPosition'])->latest()->get();
|
||||
$jobPositions = \App\Models\JobPosition::orderBy('title')->get();
|
||||
$tenants = \App\Models\Tenant::orderBy('name')->get();
|
||||
|
||||
return \Inertia\Inertia::render('Admin/Candidates/Index', [
|
||||
'candidates' => $candidates
|
||||
'candidates' => $candidates,
|
||||
'jobPositions' => $jobPositions,
|
||||
'tenants' => $tenants
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -42,8 +46,10 @@ class CandidateController extends Controller
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'linkedin_url' => 'nullable|url|max:255',
|
||||
'cv' => 'nullable|file|mimes:pdf|max:5120',
|
||||
'cover_letter' => 'nullable|file|mimes:pdf|max:5120',
|
||||
'cv' => 'nullable|mimes:pdf|max:5120',
|
||||
'cover_letter' => 'nullable|mimes:pdf|max:5120',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
'job_position_id' => 'nullable|exists:job_positions,id',
|
||||
]);
|
||||
|
||||
$password = Str::random(10);
|
||||
@@ -51,15 +57,17 @@ class CandidateController extends Controller
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($password),
|
||||
'password' => Hash::make(Str::random(12)),
|
||||
'role' => 'candidate',
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
]);
|
||||
|
||||
$candidate = Candidate::create([
|
||||
'user_id' => $user->id,
|
||||
$candidate = $user->candidate()->create([
|
||||
'phone' => $request->phone,
|
||||
'linkedin_url' => $request->linkedin_url,
|
||||
'status' => 'en_attente',
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
'job_position_id' => $request->job_position_id,
|
||||
]);
|
||||
|
||||
$this->storeDocument($candidate, $request->file('cv'), 'cv');
|
||||
|
||||
@@ -13,7 +13,8 @@ class JobPositionController extends Controller
|
||||
$this->authorizeAdmin();
|
||||
|
||||
return Inertia::render('Admin/JobPositions/Index', [
|
||||
'jobPositions' => JobPosition::all()
|
||||
'jobPositions' => JobPosition::with('tenant')->get(),
|
||||
'tenants' => \App\Models\Tenant::orderBy('name')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -26,6 +27,7 @@ class JobPositionController extends Controller
|
||||
'description' => 'required|string',
|
||||
'requirements' => 'nullable|array',
|
||||
'ai_prompt' => 'nullable|string',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
]);
|
||||
|
||||
JobPosition::create([
|
||||
@@ -33,6 +35,7 @@ class JobPositionController extends Controller
|
||||
'description' => $request->description,
|
||||
'requirements' => $request->requirements,
|
||||
'ai_prompt' => $request->ai_prompt,
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Fiche de poste créée avec succès.');
|
||||
@@ -47,6 +50,7 @@ class JobPositionController extends Controller
|
||||
'description' => 'required|string',
|
||||
'requirements' => 'nullable|array',
|
||||
'ai_prompt' => 'nullable|string',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
]);
|
||||
|
||||
$jobPosition->update([
|
||||
@@ -54,6 +58,7 @@ class JobPositionController extends Controller
|
||||
'description' => $request->description,
|
||||
'requirements' => $request->requirements,
|
||||
'ai_prompt' => $request->ai_prompt,
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Fiche de poste mise à jour.');
|
||||
|
||||
64
app/Http/Controllers/TenantController.php
Normal file
64
app/Http/Controllers/TenantController.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class TenantController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$tenants = Tenant::orderBy('name')->get();
|
||||
|
||||
return Inertia::render('Admin/Tenants/Index', [
|
||||
'tenants' => $tenants
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255|unique:tenants,name',
|
||||
]);
|
||||
|
||||
Tenant::create($request->only('name'));
|
||||
|
||||
return back()->with('success', 'Structure créée avec succès.');
|
||||
}
|
||||
|
||||
public function update(Request $request, Tenant $tenant)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255|unique:tenants,name,' . $tenant->id,
|
||||
]);
|
||||
|
||||
$tenant->update($request->only('name'));
|
||||
|
||||
return back()->with('success', 'Structure mise à jour.');
|
||||
}
|
||||
|
||||
public function destroy(Tenant $tenant)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$tenant->delete();
|
||||
|
||||
return back()->with('success', 'Structure supprimée.');
|
||||
}
|
||||
}
|
||||
97
app/Http/Controllers/UserController.php
Normal file
97
app/Http/Controllers/UserController.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$users = User::whereIn('role', ['admin', 'super_admin'])
|
||||
->with('tenant')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
$tenants = Tenant::orderBy('name')->get();
|
||||
|
||||
return Inertia::render('Admin/Users/Index', [
|
||||
'users' => $users,
|
||||
'tenants' => $tenants
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'role' => ['required', Rule::in(['admin', 'super_admin'])],
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
]);
|
||||
|
||||
$password = Str::random(10);
|
||||
|
||||
User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($password),
|
||||
'role' => $request->role,
|
||||
'tenant_id' => $request->role === 'super_admin' ? null : $request->tenant_id,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Administrateur créé avec succès. Mot de passe généré : ' . $password);
|
||||
}
|
||||
|
||||
public function update(Request $request, User $user)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
|
||||
'role' => ['required', Rule::in(['admin', 'super_admin'])],
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
]);
|
||||
|
||||
$user->update([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'role' => $request->role,
|
||||
'tenant_id' => $request->role === 'super_admin' ? null : $request->tenant_id,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Administrateur mis à jour.');
|
||||
}
|
||||
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if (!auth()->user()->isSuperAdmin()) {
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
|
||||
if ($user->id === auth()->id()) {
|
||||
return back()->with('error', 'Vous ne pouvez pas supprimer votre propre compte.');
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
return back()->with('success', 'Administrateur supprimé.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user