feat: complete Epic 1 — team management & permission system
- Story 1.1: Permission enum, config, AuthorizesPermissions & HasWorkspaceScope traits, member→worker migration - Story 1.2: Team page with member list, invitation system with queued email - Story 1.3: Role assignment (Manager/Worker) and member removal with activity logging - Story 1.4: Owner-only permission toggle matrix for Managers (manage team, view logs, configure portal) - Story 1.5: Role-based access enforcement — Workers see only assigned declarations/clients, sidebar scoping - Story 1.6: Workspace switcher dropdown for multi-workspace users with session-based switching - 83 new/modified files, 182 tests passing with zero regressions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->foreignId('workspace_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('role')->default(WorkspaceUserRole::Member);
|
||||
$table->string('role')->default(WorkspaceUserRole::Worker);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['workspace_id', 'user_id']);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
DB::table('workspace_user')
|
||||
->where('role', 'member')
|
||||
->update(['role' => 'worker']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
DB::table('workspace_user')
|
||||
->where('role', 'worker')
|
||||
->update(['role' => 'member']);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('team_invitations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('workspace_id')->constrained()->on('workspaces')->cascadeOnDelete();
|
||||
$table->string('email');
|
||||
$table->string('role');
|
||||
$table->uuid('token')->unique();
|
||||
$table->foreignId('invited_by')->constrained()->on('users')->cascadeOnDelete();
|
||||
$table->timestamp('accepted_at')->nullable();
|
||||
$table->timestamp('expires_at');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['workspace_id', 'email']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('team_invitations');
|
||||
}
|
||||
};
|
||||
@@ -68,8 +68,8 @@ class DatabaseSeeder extends Seeder
|
||||
// Attach users to workspaces
|
||||
$wsCasa->users()->attach($admin->id, ['role' => WorkspaceUserRole::Owner]);
|
||||
$wsCasa->users()->attach($fatima->id, ['role' => WorkspaceUserRole::Manager]);
|
||||
$wsCasa->users()->attach($youssef->id, ['role' => WorkspaceUserRole::Member]);
|
||||
$wsCasa->users()->attach($khadija->id, ['role' => WorkspaceUserRole::Member]);
|
||||
$wsCasa->users()->attach($youssef->id, ['role' => WorkspaceUserRole::Worker]);
|
||||
$wsCasa->users()->attach($khadija->id, ['role' => WorkspaceUserRole::Worker]);
|
||||
|
||||
$wsRabat->users()->attach($admin->id, ['role' => WorkspaceUserRole::Owner]);
|
||||
$wsRabat->users()->attach($omar->id, ['role' => WorkspaceUserRole::Manager]);
|
||||
|
||||
Reference in New Issue
Block a user