diff --git a/app/Http/Controllers/Crud/ClientAdmin/MeetingController.php b/app/Http/Controllers/Crud/ClientAdmin/MeetingController.php new file mode 100644 index 0000000..a626a07 --- /dev/null +++ b/app/Http/Controllers/Crud/ClientAdmin/MeetingController.php @@ -0,0 +1,182 @@ +user()->client->id; + + $meetings = Meeting::where('client_id', $clientId) + ->with(['users' => function ($query) use ($clientId) { + $query->where('users.client_id', $clientId) + ->select('users.id', 'users.name', 'users.email'); + }]) + ->with(['tasks' => function ($query) use ($clientId) { + $query->where('tasks.client_id', $clientId) + ->select('tasks.id', 'tasks.name', 'tasks.status') + ->withPivot('meeting_id'); + }]) + ->get(); + + $availableUsers = User::where('client_id', $clientId) + ->select('id', 'name', 'email') + ->get(); + + $availableTasks = Task::where('client_id', $clientId) + ->select('id', 'name', 'status') + ->get(); + + return Inertia::render('admin/admin-meeting-list', [ + 'meetings' => $meetings, + 'users' => $availableUsers, + 'tasks' => $availableTasks + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'title' => 'required|string|max:255', + 'date' => 'required|date', + 'type' => 'required|string|max:255', + 'agenda_text' => 'required|string', + 'users' => 'nullable|array', + 'users.*' => 'exists:users,id', + 'tasks' => 'nullable|array', + 'tasks.*' => 'exists:tasks,id' + ]); + + $clientId = auth()->user()->client->id; + + // Create agenda first + $agenda = Agenda::create([ + 'text' => $request->agenda_text, + 'client_id' => $clientId, + ]); + + $meeting = Meeting::create([ + 'title' => $request->title, + 'date' => $request->date, + 'type' => $request->type, + 'agenda_id' => $agenda->id, + 'client_id' => $clientId, + ]); + + if ($request->has('users')) { + $meeting->users()->attach($request->users); + } + + if ($request->has('tasks')) { + $meeting->tasks()->attach($request->tasks); + } + + session()->flash('success', 'Meeting created successfully!'); + return back(); + } + + public function show(Meeting $meeting) + { + $clientId = auth()->user()->client->id; + + // Ensure the meeting belongs to the client + if ($meeting->client_id !== $clientId) { + abort(403); + } + + // Load the meeting with its relationships and agenda + $meeting->load(['users' => function ($query) use ($clientId) { + $query->where('users.client_id', $clientId) + ->select('users.id', 'users.name', 'users.email'); + }]) + ->load(['tasks' => function ($query) use ($clientId) { + $query->where('tasks.client_id', $clientId) + ->select('tasks.id', 'tasks.name', 'tasks.status'); + }]) + ->load('agenda'); + + // Add agenda_text to the meeting data + $meeting->agenda_text = $meeting->agenda->text; + + // Get all available users and tasks for the form + $availableUsers = User::where('client_id', $clientId) + ->select('id', 'name', 'email') + ->get(); + + $availableTasks = Task::where('client_id', $clientId) + ->select('id', 'name', 'status') + ->get(); + + return Inertia::render('admin/admin-meeting-details', [ + 'meeting' => $meeting, + 'users' => $availableUsers, + 'tasks' => $availableTasks + ]); + } + + public function update(Request $request, Meeting $meeting) + { + // Ensure the meeting belongs to the client + if ($meeting->client_id !== auth()->user()->client->id) { + abort(403, 'Unauthorized'); + } + + $request->validate([ + 'title' => 'required|string|max:255', + 'date' => 'required|date', + 'type' => 'required|string|max:255', + 'agenda_text' => 'required|string', + 'users' => 'nullable|array', + 'users.*' => 'exists:users,id', + 'tasks' => 'nullable|array', + 'tasks.*' => 'exists:tasks,id' + ]); + + // Update the meeting + $meeting->update([ + 'title' => $request->title, + 'date' => $request->date, + 'type' => $request->type, + ]); + + // Update the agenda + $meeting->agenda()->update([ + 'text' => $request->agenda_text + ]); + + // Sync users and tasks if they are provided + if ($request->has('users')) { + $meeting->users()->sync($request->users); + } + + if ($request->has('tasks')) { + $meeting->tasks()->sync($request->tasks); + } + + session()->flash('success', 'Meeting updated successfully!'); + return back(); + } + + public function destroy(Meeting $meeting) + { + // Ensure the meeting belongs to the client + if ($meeting->client_id !== auth()->user()->client->id) { + abort(403, 'Unauthorized'); + } + + // Delete the meeting (this will cascade delete the agenda due to the relationship) + $meeting->delete(); + + session()->flash('success', 'Meeting deleted successfully!'); + return redirect()->route('admin.meetings.index'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Crud/ClientAdmin/TaskController.php b/app/Http/Controllers/Crud/ClientAdmin/TaskController.php new file mode 100644 index 0000000..1302bed --- /dev/null +++ b/app/Http/Controllers/Crud/ClientAdmin/TaskController.php @@ -0,0 +1,126 @@ +user()->client->id; + + $tasks = Task::where('client_id', $clientId) + ->with(['users' => function ($query) use ($clientId) { + $query->where('users.client_id', $clientId) + ->select('users.id', 'users.name', 'users.email'); + }]) + ->get(); + + $users = User::where('client_id', $clientId) + ->select('id', 'name', 'email') + ->get(); + + return Inertia::render('admin/admin-task-list', [ + 'tasks' => $tasks, + 'users' => $users + ]); + } + + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'required|string|max:255', + 'status' => 'required|in:pending,in_progress,completed', + 'priority' => 'required|in:low,medium,high', + 'due_date' => 'nullable|date', + 'users' => 'array', + 'users.*' => 'exists:users,id' + ]); + + $task = Task::create([ + 'name' => $request->name, + 'description' => $request->description, + 'status' => $request->status, + 'priority' => $request->priority, + 'due_date' => $request->due_date, + 'client_id' => auth()->user()->client->id + ]); + + if ($request->has('users')) { + $task->users()->attach($request->users); + } + session()->flash('success', 'Task created successfully!'); + return back(); + } + + public function show(Task $task) + { + // Ensure the task belongs to the user's client + if ($task->client_id !== auth()->user()->client->id) { + abort(403, 'Unauthorized'); + } + + $clientId = auth()->user()->client->id; + + // Get all users for the client, for the task details page (to assign users to the task) + $all_users = User::where('client_id', $clientId) + ->select('id', 'name', 'email') + ->get(); + + return Inertia::render('admin/admin-task-details', [ + 'task' => $task->load(['users' => function ($query) use ($clientId) { + $query->where('users.client_id', $clientId) + ->select('users.id', 'users.name', 'users.email'); + }]), + 'all_users' => $all_users + ]); + } + + public function update(Request $request, Task $task) + { + // Ensure the task belongs to the user's client + if ($task->client_id !== auth()->user()->client->id) { + abort(403, 'Unauthorized'); + } + + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'required|string|max:255', + 'status' => 'required|in:pending,in_progress,completed', + 'priority' => 'required|in:low,medium,high', + 'due_date' => 'nullable|date', + 'users' => 'array', + 'users.*' => 'exists:users,id' + ]); + + $task->update($request->only(['name', 'description', 'status', 'priority', 'due_date'])); + + if ($request->has('users')) { + $task->users()->sync($request->users); + } + + session()->flash('success', 'Task updated successfully!'); + return back(); + } + + public function destroy(Task $task) + { + // Ensure the task belongs to the user's client + if ($task->client_id !== auth()->user()->client->id) { + abort(403, 'Unauthorized'); + } + + $task->delete(); + session()->flash('success', 'Task deleted successfully!'); + return redirect()->route('admin.tasks.index'); + } + + + +} \ No newline at end of file diff --git a/app/Http/Controllers/Crud/ClientAdmin/UserController.php b/app/Http/Controllers/Crud/ClientAdmin/UserController.php index 02c4c74..46a13c1 100644 --- a/app/Http/Controllers/Crud/ClientAdmin/UserController.php +++ b/app/Http/Controllers/Crud/ClientAdmin/UserController.php @@ -15,8 +15,10 @@ class UserController extends Controller { public function index() { - $users = User::where('client_id', auth()->user()->client_id)->get(); - return Inertia::render('Users/user-list', [ + $users = User::where('client_id', auth()->user()->client_id) + ->with('role:id,name') + ->get(); + return Inertia::render('admin/admin-user-list', [ 'users' => $users, 'roles' => Role::all(['id', 'name'])->except(1)//exclude the system admin role ]); @@ -43,7 +45,7 @@ public function store(Request $request) 'client_id' => $request->client_id, 'role_id' => $request->role_id, ]); - + session()->flash('success', 'User created successfully!'); return back(); } @@ -66,6 +68,7 @@ public function update(Request $request, User $user) $user->update($validated); + session()->flash('success', 'User updated successfully!'); return back(); } @@ -75,7 +78,7 @@ public function show(User $user) abort(403, 'Unauthorized'); } - return Inertia::render('Users/user-details', [ + return Inertia::render('admin/admin-user-details', [ 'user' => $user ]); } @@ -83,7 +86,8 @@ public function show(User $user) public function destroy(User $user) { $user->delete(); - return redirect()->route('users.index'); + session()->flash('success', 'User deleted successfully!'); + return redirect()->route('admin.users.index'); } diff --git a/app/Models/Role.php b/app/Models/Role.php index 20c2b46..2da315e 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -3,9 +3,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class Role extends Model { + use SoftDeletes; + protected $fillable = ['name', 'description']; public function users() diff --git a/app/Models/Task.php b/app/Models/Task.php index 8f6819b..bf07704 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -3,6 +3,9 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Task extends Model { @@ -14,20 +17,21 @@ class Task extends Model 'status', 'priority', 'due_date', + 'client_id' ]; - public function users() + public function client(): BelongsTo { - return $this->belongsToMany(User::class); + return $this->belongsTo(Client::class); } - public function meetings() + public function users(): BelongsToMany { - return $this->belongsToMany(Meeting::class); + return $this->belongsToMany(User::class); } - public function clients() + public function meetings() { - return $this->belongsToMany(Client::class); + return $this->belongsToMany(Meeting::class); } } diff --git a/app/Models/User.php b/app/Models/User.php index 3727e95..67d495d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -8,11 +8,12 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\SoftDeletes; class User extends Authenticatable { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable; + use HasFactory, Notifiable, SoftDeletes; /** * The attributes that are mass assignable. @@ -69,4 +70,5 @@ public function role(): BelongsTo { return $this->belongsTo(Role::class); } + } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..eaf988a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use Inertia\Inertia; class AppServiceProvider extends ServiceProvider { @@ -19,6 +20,12 @@ public function register(): void */ public function boot(): void { - // + Inertia::share([ + 'flash' => function () { + return [ + 'success' => session('success'), + ]; + }, + ]); } } diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index e273a0c..3dad911 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -19,6 +19,7 @@ public function up(): void $table->string('password'); $table->boolean('password_set')->default(false); $table->rememberToken(); + $table->softDeletes(); $table->timestamps(); }); diff --git a/database/migrations/2025_04_29_063856_create_tasks_table.php b/database/migrations/2025_04_29_063856_create_tasks_table.php index 7eaf60b..42f88b8 100644 --- a/database/migrations/2025_04_29_063856_create_tasks_table.php +++ b/database/migrations/2025_04_29_063856_create_tasks_table.php @@ -13,6 +13,7 @@ public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); + $table->foreignId('client_id')->constrained()->onDelete('cascade'); $table->string('name'); $table->string('description'); $table->enum('status', ['pending', 'in_progress', 'completed']); diff --git a/database/migrations/2025_04_30_181719_create_client_task_table.php b/database/migrations/2025_04_30_181719_create_client_task_table.php deleted file mode 100644 index dae705f..0000000 --- a/database/migrations/2025_04_30_181719_create_client_task_table.php +++ /dev/null @@ -1,31 +0,0 @@ -id(); - $table->foreignId('client_id')->constrained('clients'); - $table->foreignId('task_id')->constrained('tasks'); - $table->timestamps(); - $table->softDeletes(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('client_task'); - } -}; diff --git a/database/seeders/AdditionalUsersSeeder.php b/database/seeders/AdditionalUsersSeeder.php new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/database/seeders/AdditionalUsersSeeder.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/database/seeders/AgendaSeeder.php b/database/seeders/AgendaSeeder.php new file mode 100644 index 0000000..5cdf1bd --- /dev/null +++ b/database/seeders/AgendaSeeder.php @@ -0,0 +1,26 @@ + 'Agenda 1', + ]); + + Agenda::create([ + 'text' => 'Agenda 2', + ]); + + Agenda::create([ + 'text' => 'Agenda 3', + ]); + } +} diff --git a/database/seeders/ClientSeeder.php b/database/seeders/ClientSeeder.php index 94c401e..0f90a4f 100644 --- a/database/seeders/ClientSeeder.php +++ b/database/seeders/ClientSeeder.php @@ -12,7 +12,7 @@ class ClientSeeder extends Seeder public function run() { Client::create([ - 'name' => 'Starplot', + 'name' => 'Equippers', ]); } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 680f325..de6af81 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,6 +16,9 @@ public function run(): void ClientSeeder::class, SystemAdminSeeder::class, UserSeeder::class, + TaskSeeder::class, + AgendaSeeder::class, + MeetingSeeder::class, ]); } } diff --git a/database/seeders/MeetingSeeder.php b/database/seeders/MeetingSeeder.php new file mode 100644 index 0000000..1dcbeca --- /dev/null +++ b/database/seeders/MeetingSeeder.php @@ -0,0 +1,60 @@ +take(5)->get(); + + // Get some tasks to assign to meetings + $tasks = Task::where('client_id', 1)->take(5)->get(); + + $meetings = [ + [ + 'title' => 'Meeting 1', + 'date' => Carbon::now(), + 'type' => 'Online Meeting ', + 'agenda_id' => 1, + 'client_id' => 1, + ], + [ + 'title' => 'Meeting 2', + 'date' => Carbon::now(), + 'type' => 'In-Person Meeting', + 'agenda_id' => 2, + 'client_id' => 1, + ], + [ + 'title' => 'Meeting 3', + 'date' => Carbon::now(), + 'type' => 'In-Person Meeting', + 'agenda_id' => 3, + 'client_id' => 1, + ], + ]; + + foreach ($meetings as $meeting) { + $meeting = Meeting::create($meeting); + + $meeting->users()->attach( + $users->random(2)->pluck('id')->toArray(), + ['created_at' => now(), 'updated_at' => now()] + ); + + $meeting->tasks()->attach( + $tasks->random(2)->pluck('id')->toArray(), + ['created_at' => now(), 'updated_at' => now()] + ); + } + } +} diff --git a/database/seeders/TaskSeeder.php b/database/seeders/TaskSeeder.php new file mode 100644 index 0000000..0b49895 --- /dev/null +++ b/database/seeders/TaskSeeder.php @@ -0,0 +1,73 @@ +take(3)->get(); + + // Create 5 tasks for client 1 + $tasks = [ + [ + 'name' => 'Implement User Authentication', + 'description' => 'Set up secure user authentication system with JWT tokens', + 'status' => 'in_progress', + 'priority' => 'high', + 'due_date' => now()->addDays(7), + 'client_id' => 1, + ], + [ + 'name' => 'Design Database Schema', + 'description' => 'Create and optimize database schema for the application', + 'status' => 'completed', + 'priority' => 'high', + 'due_date' => now()->subDays(2), + 'client_id' => 1, + ], + [ + 'name' => 'Create API Documentation', + 'description' => 'Write comprehensive API documentation for all endpoints', + 'status' => 'pending', + 'priority' => 'medium', + 'due_date' => now()->addDays(14), + 'client_id' => 1, + ], + [ + 'name' => 'Implement Task Management', + 'description' => 'Build task management system with CRUD operations', + 'status' => 'in_progress', + 'priority' => 'high', + 'due_date' => now()->addDays(5), + 'client_id' => 1, + ], + [ + 'name' => 'Setup CI/CD Pipeline', + 'description' => 'Configure continuous integration and deployment pipeline', + 'status' => 'pending', + 'priority' => 'medium', + 'due_date' => now()->addDays(10), + 'client_id' => 1, + ], + ]; + + foreach ($tasks as $taskData) { + $task = Task::create($taskData); + + // Assign 2 random users to each task + $task->users()->attach( + $users->random(2)->pluck('id')->toArray(), + ['created_at' => now(), 'updated_at' => now()] + ); + } + } +} \ No newline at end of file diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index fef625f..65ed2ab 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -12,28 +12,119 @@ class UserSeeder extends Seeder { public function run() { - - User::create([ - # client admin - 'name' => 'Ryan Mueller', - 'password' => Hash::make('password'), - 'email' => 'ryan@starplot.ai', - 'password_set' => true, - 'email_verified_at' => now(), - 'client_id' => 1, - 'role_id' => 2, + $users = [ + [ + 'name' => 'Ryan Mueller', + 'email' => 'ryan@equippers.com', + 'role_id' => 2, // Client-Admin + ], + [ + 'name' => 'Andrew McDowell', + 'email' => 'andrew@equippers.com', + 'role_id' => 3, // Member + ], + [ + 'name' => 'Allison Port', + 'email' => 'allison@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'George Kalafatis', + 'email' => 'george@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'John Doe', + 'email' => 'john@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Sarah Johnson', + 'email' => 'sarah@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Michael Chen', + 'email' => 'michael@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Emma Wilson', + 'email' => 'emma@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'David Brown', + 'email' => 'david@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Lisa Anderson', + 'email' => 'lisa@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'James Taylor', + 'email' => 'james@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Rachel Martinez', + 'email' => 'rachel@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Thomas White', + 'email' => 'thomas@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Olivia Lee', + 'email' => 'olivia@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Daniel Kim', + 'email' => 'daniel@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Sophia Garcia', + 'email' => 'sophia@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'William Clark', + 'email' => 'william@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Ava Rodriguez', + 'email' => 'ava@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Ethan Moore', + 'email' => 'ethan@equippers.com', + 'role_id' => 3, + ], + [ + 'name' => 'Isabella Thompson', + 'email' => 'isabella@equippers.com', + 'role_id' => 3, + ], + ]; - ]); - - User::create([ - # client member - 'name' => 'Andrew McDowell', - 'password' => Hash::make('password'), - 'email' => 'andrew@starplot.ai', - 'password_set' => true, - 'email_verified_at' => now(), - 'client_id' => 1, - 'role_id' => 3, - ]); + foreach ($users as $userData) { + User::create([ + 'name' => $userData['name'], + 'email' => $userData['email'], + 'password' => Hash::make('password'), + 'password_set' => true, + 'email_verified_at' => now(), + 'client_id' => 1, + 'role_id' => $userData['role_id'], + ]); + } } } diff --git a/package-lock.json b/package-lock.json index ddc58af..58dd788 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-collapsible": "^1.1.3", - "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", @@ -27,7 +29,9 @@ "@vitejs/plugin-react": "^4.3.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "concurrently": "^9.0.1", + "date-fns": "^4.1.0", "globals": "^15.14.0", "laravel-vite-plugin": "^1.0", "lucide-react": "^0.475.0", @@ -1302,23 +1306,23 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", - "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.1.0", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -1337,6 +1341,283 @@ } } }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -1352,17 +1633,464 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", - "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", + "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", + "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-navigation-menu": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.5.tgz", + "integrity": "sha512-myMHHQUZ3ZLTi8W381/Vu43Ia0NqakkQZ2vzynMmTUtQQ9kNkjzhOwkZC9TAM5R07OZUVIQyHC06f/9JZJpvvA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -1379,19 +2107,13 @@ } } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", - "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.6", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1408,11 +2130,14 @@ } } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1423,38 +2148,29 @@ } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", - "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -1466,105 +2182,81 @@ } } }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", - "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.2" + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { "optional": true } } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", - "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.2", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.2", - "@radix-ui/react-portal": "1.1.4", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-roving-focus": "1.1.2", - "@radix-ui/react-slot": "1.1.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.5.tgz", - "integrity": "sha512-myMHHQUZ3ZLTi8W381/Vu43Ia0NqakkQZ2vzynMmTUtQQ9kNkjzhOwkZC9TAM5R07OZUVIQyHC06f/9JZJpvvA==", + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.2", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.5", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.2", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", @@ -1668,6 +2360,101 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", @@ -1904,6 +2691,39 @@ } } }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", @@ -3429,6 +4249,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3572,6 +4408,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", diff --git a/package.json b/package.json index 693fd0e..1656df4 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,12 @@ "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-collapsible": "^1.1.3", - "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", @@ -45,7 +47,9 @@ "@vitejs/plugin-react": "^4.3.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "concurrently": "^9.0.1", + "date-fns": "^4.1.0", "globals": "^15.14.0", "laravel-vite-plugin": "^1.0", "lucide-react": "^0.475.0", diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index 05ba0e8..298b41a 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -3,7 +3,7 @@ import { NavUser } from '@/components/nav-user'; import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar'; import { type NavItem } from '@/types'; import { Link } from '@inertiajs/react'; -import { LayoutGrid, SquareCheckBig, Users } from 'lucide-react'; +import { LayoutGrid, SquareCheckBig, Users, Calendar } from 'lucide-react'; import AppLogo from './app-logo'; const mainNavItems: NavItem[] = [ @@ -22,6 +22,12 @@ const mainNavItems: NavItem[] = [ href: '/users', icon: Users, }, + + { + title: 'Meetings', + href: '/meetings', + icon: Calendar, + }, ]; export function AppSidebar() { diff --git a/resources/js/components/data-table.tsx b/resources/js/components/data-table.tsx index d704a70..d8d19a7 100644 --- a/resources/js/components/data-table.tsx +++ b/resources/js/components/data-table.tsx @@ -46,6 +46,7 @@ interface DataTableProps { searchPlaceholder?: string searchColumn?: string onRowClick?: (row: TData) => void; // allow for row click to lead to action + addButton?: React.ReactNode // optional add button } export function DataTable({ @@ -54,6 +55,7 @@ export function DataTable({ searchPlaceholder = "Filter...", searchColumn = "email", onRowClick, + addButton, }: DataTableProps) { const [sorting, setSorting] = React.useState([]) const [columnFilters, setColumnFilters] = React.useState([]) @@ -81,7 +83,7 @@ export function DataTable({ return (
-
+
({ } className="max-w-sm" /> - - - - - - {table - .getAllColumns() - .filter((column) => column.getCanHide()) - .map((column) => { - return ( - - column.toggleVisibility(!!value) - } - > - {column.id} - - ) - })} - - + {addButton &&
{addButton}
}
@@ -180,6 +157,7 @@ export function DataTable({ size="sm" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} + className="cursor-pointer" > Previous @@ -188,6 +166,7 @@ export function DataTable({ size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()} + className="cursor-pointer" > Next diff --git a/resources/js/components/meetings/create-meeting-dialog.tsx b/resources/js/components/meetings/create-meeting-dialog.tsx new file mode 100644 index 0000000..32eb988 --- /dev/null +++ b/resources/js/components/meetings/create-meeting-dialog.tsx @@ -0,0 +1,120 @@ +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { useForm } from "@inertiajs/react" +import { User, Task } from "@/types/task" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { MultiSelect } from "@/components/ui/multi-select" + +interface CreateMeetingDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + users: User[] + tasks: Task[] +} + +export function CreateMeetingDialog({ open, onOpenChange, users, tasks }: CreateMeetingDialogProps) { + const { data, setData, post, processing, errors, reset } = useForm({ + title: '', + date: '', + type: '', + agenda_text: '', + users: [] as number[], + tasks: [] as number[], + }) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + post('/meetings', { + onSuccess: () => { + reset() + onOpenChange(false) + } + }) + } + + return ( + + +
+ + Create New Meeting + + Add a new meeting and assign attendees and tasks. + + +
+
+
+ + setData('title', e.target.value)} + error={errors.title} + /> +
+
+ + setData('date', e.target.value)} + error={errors.date} + /> +
+
+ + setData('type', e.target.value)} + error={errors.type} + placeholder="Type (e.g. Internal, Client, etc.)" + /> +
+
+ + setData('agenda_text', e.target.value)} + error={errors.agenda_text} + placeholder="Meeting agenda or description" + /> +
+
+
+ setData('users', ids)} + placeholder="Select users..." + label="Attendees" + error={errors.users} + /> + setData('tasks', ids)} + placeholder="Select tasks..." + label="Related Tasks" + error={errors.tasks} + /> +
+
+ + + + + +
+
+ ) +} \ No newline at end of file diff --git a/resources/js/components/meetings/meeting-summary.tsx b/resources/js/components/meetings/meeting-summary.tsx new file mode 100644 index 0000000..1ba02b5 --- /dev/null +++ b/resources/js/components/meetings/meeting-summary.tsx @@ -0,0 +1,66 @@ +import { Meeting } from "@/types/meeting" +import { Card } from "@/components/ui/card" +import { Users, Calendar, Clock } from "lucide-react" + +interface MeetingSummaryProps { + meetings: Meeting[] +} + +export function MeetingSummary({ meetings }: MeetingSummaryProps) { + const now = new Date() + const upcomingCount = meetings.filter(m => new Date(m.date) > now).length + const pastCount = meetings.filter(m => new Date(m.date) <= now).length + const totalCount = meetings.length + + const totalAttendees = meetings.reduce((acc, meeting) => acc + meeting.users.length, 0) + const averageAttendees = totalCount > 0 ? Math.round(totalAttendees / totalCount) : 0 + + return ( + +
+
+
+
+ + + {upcomingCount} + +
+ Upcoming +
+ +
+
+ + + {pastCount} + +
+ Past +
+ +
+
+ + + {averageAttendees} + +
+ Avg. Attendees +
+ +
+ +
+
+ + {totalCount} + +
+ Total Meetings +
+
+
+ + ) +} \ No newline at end of file diff --git a/resources/js/components/tasks/admin-task-stats.tsx b/resources/js/components/tasks/admin-task-stats.tsx new file mode 100644 index 0000000..145985a --- /dev/null +++ b/resources/js/components/tasks/admin-task-stats.tsx @@ -0,0 +1,12 @@ +import { Task } from "@/types/task" +import { TaskSummary } from "@/components/tasks/task-summary" + +interface AdminTaskStatsProps { + tasks: Task[] +} + +export function AdminTaskStats({ tasks }: AdminTaskStatsProps) { + return ( + + ) +} \ No newline at end of file diff --git a/resources/js/components/tasks/create-task-dialog.tsx b/resources/js/components/tasks/create-task-dialog.tsx new file mode 100644 index 0000000..445ba97 --- /dev/null +++ b/resources/js/components/tasks/create-task-dialog.tsx @@ -0,0 +1,147 @@ +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { useForm } from "@inertiajs/react" +import { TaskStatus, TaskPriority, User } from "@/types/task" +import { MultiSelect } from "@/components/ui/multi-select" + +interface CreateTaskDialogProps { + users: User[]; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function CreateTaskDialog({ users, open, onOpenChange }: CreateTaskDialogProps) { + const { data, setData, post, processing, errors, reset } = useForm({ + name: '', + description: '', + status: 'pending' as TaskStatus, + priority: 'medium' as TaskPriority, + due_date: '', + users: [] as number[] + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + post('/tasks', { + onSuccess: () => { + reset(); + onOpenChange(false); + } + }); + }; + + return ( + + + + Create New Task + + Add a new task to your project. Fill in the details below. + + +
+
+
+
+ + setData('name', e.target.value)} + error={errors.name} + /> +
+ +
+ +