From a377ce3bde77ed6abbdc05443b1889b80f495cbb Mon Sep 17 00:00:00 2001 From: ryanmueller24 Date: Tue, 13 May 2025 23:01:33 -0500 Subject: [PATCH 01/21] folder restructure --- .../Crud/ClientAdmin/UserController.php | 6 +++--- .../{tasks.tsx => admin/admin-task-list.tsx} | 0 .../admin-user-details.tsx} | 0 .../user-list.tsx => admin/admin-user-list.tsx} | 0 routes/web.php | 15 +++++++-------- 5 files changed, 10 insertions(+), 11 deletions(-) rename resources/js/pages/{tasks.tsx => admin/admin-task-list.tsx} (100%) rename resources/js/pages/{Users/user-details.tsx => admin/admin-user-details.tsx} (100%) rename resources/js/pages/{Users/user-list.tsx => admin/admin-user-list.tsx} (100%) diff --git a/app/Http/Controllers/Crud/ClientAdmin/UserController.php b/app/Http/Controllers/Crud/ClientAdmin/UserController.php index 02c4c74..0b21db8 100644 --- a/app/Http/Controllers/Crud/ClientAdmin/UserController.php +++ b/app/Http/Controllers/Crud/ClientAdmin/UserController.php @@ -16,7 +16,7 @@ class UserController extends Controller public function index() { $users = User::where('client_id', auth()->user()->client_id)->get(); - return Inertia::render('Users/user-list', [ + return Inertia::render('admin/admin-user-list', [ 'users' => $users, 'roles' => Role::all(['id', 'name'])->except(1)//exclude the system admin role ]); @@ -75,7 +75,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 +83,7 @@ public function show(User $user) public function destroy(User $user) { $user->delete(); - return redirect()->route('users.index'); + return redirect()->route('admin.users.index'); } diff --git a/resources/js/pages/tasks.tsx b/resources/js/pages/admin/admin-task-list.tsx similarity index 100% rename from resources/js/pages/tasks.tsx rename to resources/js/pages/admin/admin-task-list.tsx diff --git a/resources/js/pages/Users/user-details.tsx b/resources/js/pages/admin/admin-user-details.tsx similarity index 100% rename from resources/js/pages/Users/user-details.tsx rename to resources/js/pages/admin/admin-user-details.tsx diff --git a/resources/js/pages/Users/user-list.tsx b/resources/js/pages/admin/admin-user-list.tsx similarity index 100% rename from resources/js/pages/Users/user-list.tsx rename to resources/js/pages/admin/admin-user-list.tsx diff --git a/routes/web.php b/routes/web.php index 2f9396a..454c276 100644 --- a/routes/web.php +++ b/routes/web.php @@ -15,17 +15,16 @@ })->name('dashboard'); Route::get('tasks', function () { - return Inertia::render('tasks'); + return Inertia::render('admin/admin-task-list'); })->name('tasks'); - - + // Admin Routes Route::middleware(['role:Client-Admin'])->group(function () { - Route::get('users', [ClientAdminUserController::class, 'index'])->name('users.index'); - Route::post('users', [ClientAdminUserController::class, 'store'])->name('users.store'); - Route::get('users/{user}', [ClientAdminUserController::class, 'show'])->name('users.show'); - Route::put('users/{user}', [ClientAdminUserController::class, 'update'])->name('users.update'); - Route::delete('users/{user}', [ClientAdminUserController::class, 'destroy'])->name('users.destroy'); + Route::get('users', [ClientAdminUserController::class, 'index'])->name('admin.users.index'); + Route::post('users', [ClientAdminUserController::class, 'store'])->name('admin.users.store'); + Route::get('users/{user}', [ClientAdminUserController::class, 'show'])->name('admin.users.show'); + Route::put('users/{user}', [ClientAdminUserController::class, 'update'])->name('admin.users.update'); + Route::delete('users/{user}', [ClientAdminUserController::class, 'destroy'])->name('admin.users.destroy'); }); }); From 9c778df8ec7f4dff5c7500f3e67627335f13046d Mon Sep 17 00:00:00 2001 From: ryanmueller24 Date: Fri, 23 May 2025 00:07:04 -0500 Subject: [PATCH 02/21] user changes --- .../Crud/ClientAdmin/UserController.php | 4 +- app/Models/Role.php | 3 + app/Models/Task.php | 1 + app/Models/User.php | 3 +- .../0001_01_01_000000_create_users_table.php | 1 + .../js/components/ui/create-user-dialog.tsx | 143 ---------------- .../components/users/create-user-dialog.tsx | 156 ++++++++++++++++++ resources/js/pages/admin/admin-user-list.tsx | 51 +++--- routes/web.php | 12 +- 9 files changed, 201 insertions(+), 173 deletions(-) delete mode 100644 resources/js/components/ui/create-user-dialog.tsx create mode 100644 resources/js/components/users/create-user-dialog.tsx diff --git a/app/Http/Controllers/Crud/ClientAdmin/UserController.php b/app/Http/Controllers/Crud/ClientAdmin/UserController.php index 0b21db8..c3257ae 100644 --- a/app/Http/Controllers/Crud/ClientAdmin/UserController.php +++ b/app/Http/Controllers/Crud/ClientAdmin/UserController.php @@ -15,7 +15,9 @@ class UserController extends Controller { public function index() { - $users = User::where('client_id', auth()->user()->client_id)->get(); + $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 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..6bd1386 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class Task extends Model { diff --git a/app/Models/User.php b/app/Models/User.php index 3727e95..cb84df9 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. 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/resources/js/components/ui/create-user-dialog.tsx b/resources/js/components/ui/create-user-dialog.tsx deleted file mode 100644 index 6295ada..0000000 --- a/resources/js/components/ui/create-user-dialog.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useForm, usePage } from '@inertiajs/react'; -import { FormEventHandler } from 'react'; -import { Plus, LoaderCircle } from 'lucide-react'; -import type { PageProps as InertiaPageProps } from '@inertiajs/core'; - -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Button } from '@/components/ui/button'; -import InputError from '@/components/input-error'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; - -interface PageProps extends InertiaPageProps { - auth: { - user: { - client_id: number; - }; - }; - roles: Array<{ - id: number; - name: string; - }>; -} - -export default function CreateUserDialog() { - const { auth, roles } = usePage().props; - const clientId = auth.user.client_id; - - const { data, setData, post, processing, errors, reset } = useForm({ - name: '', - email: '', - password: '', - password_confirmation: '', - client_id: clientId, - role_id: roles.find(r => r.name === 'member')?.id || '', - }); - - const submit: FormEventHandler = (e) => { - e.preventDefault(); - post(route('users.store'), { - onSuccess: () => { - reset(); - window.location.href = route('users.index'); - }, - }); - }; - - return ( - - - - - - - Create New User - - Add a new user to the system - - -
-
-
- - setData('name', e.target.value)} - placeholder="Full name" - /> - -
- -
- - setData('email', e.target.value)} - placeholder="email@example.com" - /> - -
- -
- - setData('password', e.target.value)} - placeholder="Password" - /> - -
- -
- - setData('password_confirmation', e.target.value)} - placeholder="Confirm password" - /> - -
- -
- - - -
-
- - - - -
-
-
- ); -} \ No newline at end of file diff --git a/resources/js/components/users/create-user-dialog.tsx b/resources/js/components/users/create-user-dialog.tsx new file mode 100644 index 0000000..5e81e44 --- /dev/null +++ b/resources/js/components/users/create-user-dialog.tsx @@ -0,0 +1,156 @@ +import { useForm, usePage } from '@inertiajs/react'; +import { FormEventHandler } from 'react'; +import { Plus, LoaderCircle } from 'lucide-react'; +import type { PageProps as InertiaPageProps } from '@inertiajs/core'; + +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Button } from '@/components/ui/button'; +import InputError from '@/components/input-error'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; +import { Card, CardContent } from '@/components/ui/card'; + +interface PageProps extends InertiaPageProps { + auth: { + user: { + client_id: number; + }; + }; + roles: Array<{ + id: number; + name: string; + }>; +} + +export default function CreateUserDialog() { + const { auth, roles } = usePage().props; + const clientId = auth.user.client_id; + + const { data, setData, post, processing, errors, reset } = useForm({ + name: '', + email: '', + password: '', + password_confirmation: '', + client_id: clientId, + role_id: roles.find(r => r.name === 'member')?.id || '', + }); + + const submit: FormEventHandler = (e) => { + e.preventDefault(); + post(route('admin.users.store'), { + onSuccess: () => { + reset(); + window.location.href = route('admin.users.index'); + }, + }); + }; + + const formatRoleName = (name: string) => { + return name === "Client-Admin" ? "Admin" : name; + }; + + return ( + + + + + + + Create New User + + Add a new user to the system + + +
+ + +
+
+ + setData('name', e.target.value)} + placeholder="Full name" + className="w-full" + /> + +
+ +
+ + setData('email', e.target.value)} + placeholder="email@example.com" + className="w-full" + /> + +
+ +
+ + setData('password', e.target.value)} + placeholder="Password" + className="w-full" + /> + +
+ +
+ + setData('password_confirmation', e.target.value)} + placeholder="Confirm password" + className="w-full" + /> + +
+ +
+ + + +
+
+
+
+ + + + +
+
+
+ ); +} \ No newline at end of file diff --git a/resources/js/pages/admin/admin-user-list.tsx b/resources/js/pages/admin/admin-user-list.tsx index d19ee9d..ce6d7a2 100644 --- a/resources/js/pages/admin/admin-user-list.tsx +++ b/resources/js/pages/admin/admin-user-list.tsx @@ -10,7 +10,7 @@ import { Plus, ArrowUpDown } from 'lucide-react'; import { DataTable } from '@/components/data-table'; import { ColumnDef } from '@tanstack/react-table'; import { PageProps } from '@/types'; -import CreateUserDialog from '@/components/ui/create-user-dialog'; +import CreateUserDialog from '@/components/users/create-user-dialog'; const breadcrumbs: BreadcrumbItem[] = [ @@ -21,36 +21,19 @@ const breadcrumbs: BreadcrumbItem[] = [ }, ]; - - interface User { id: number; name: string; email: string; + role: { + id: number; + name: string; + }; } + + const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - }, + { accessorKey: "name", header: ({ column }) => { @@ -79,6 +62,24 @@ const columns: ColumnDef[] = [ ) }, }, + { + accessorKey: "role.name", + header: ({ column }) => { + return ( + + ) + }, + cell: ({ row }) => { + const roleName = row.original.role.name; + return roleName === "Client-Admin" ? "Admin" : roleName; + } + }, ]; export default function Users({ users }: PageProps<{ users: User[] }>) { diff --git a/routes/web.php b/routes/web.php index 454c276..fd87089 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Http\Controllers\Crud\ClientAdmin\UserController as ClientAdminUserController; +use App\Http\Controllers\Crud\ClientAdmin\TaskController as ClientAdminTaskController; use App\Http\Middleware\CheckRole; Route::get('/', function () { @@ -14,9 +15,14 @@ return Inertia::render('dashboard'); })->name('dashboard'); - Route::get('tasks', function () { - return Inertia::render('admin/admin-task-list'); - })->name('tasks'); + // Task routes + Route::middleware(['role:Client-Admin'])->group(function () { + Route::get('tasks', [ClientAdminTaskController::class, 'index'])->name('admin.tasks.index'); + Route::post('tasks', [ClientAdminTaskController::class, 'store'])->name('admin.tasks.store'); + Route::get('tasks/{task}', [ClientAdminTaskController::class, 'show'])->name('admin.tasks.show'); + Route::put('tasks/{task}', [ClientAdminTaskController::class, 'update'])->name('admin.tasks.update'); + Route::delete('tasks/{task}', [ClientAdminTaskController::class, 'destroy'])->name('admin.tasks.destroy'); + }); // Admin Routes Route::middleware(['role:Client-Admin'])->group(function () { From 6b45bfde5ac4be0c6ea5325c9813f1bb543df12c Mon Sep 17 00:00:00 2001 From: ryanmueller24 Date: Fri, 23 May 2025 03:41:21 -0500 Subject: [PATCH 03/21] task and user backend -> frontend working --- .../Crud/ClientAdmin/TaskController.php | 124 ++ app/Models/Task.php | 15 +- app/Models/User.php | 1 + .../2025_04_29_063856_create_tasks_table.php | 1 + ..._04_30_181719_create_client_task_table.php | 31 - database/seeders/ClientSeeder.php | 2 +- database/seeders/DatabaseSeeder.php | 1 + database/seeders/TaskSeeder.php | 75 ++ database/seeders/UserSeeder.php | 37 +- package-lock.json | 1094 +++++++++++++++-- package.json | 6 +- resources/js/components/data-table.tsx | 31 +- .../js/components/tasks/admin-task-stats.tsx | 12 + .../components/tasks/create-task-dialog.tsx | 252 ++++ .../js/components/tasks/task-progress.tsx | 25 + .../js/components/tasks/task-statistics.tsx | 64 + resources/js/components/ui/command.tsx | 175 +++ resources/js/components/ui/data-table.tsx | 75 ++ resources/js/components/ui/input.tsx | 37 +- resources/js/components/ui/popover.tsx | 46 + resources/js/components/ui/progress.tsx | 29 + resources/js/components/ui/textarea.tsx | 28 + .../components/users/create-user-dialog.tsx | 6 +- .../js/pages/admin/admin-task-details.tsx | 281 +++++ resources/js/pages/admin/admin-task-list.tsx | 207 ++-- .../js/pages/admin/admin-user-details.tsx | 35 +- resources/js/pages/admin/admin-user-list.tsx | 47 +- resources/js/types/task.ts | 18 + 28 files changed, 2369 insertions(+), 386 deletions(-) create mode 100644 app/Http/Controllers/Crud/ClientAdmin/TaskController.php delete mode 100644 database/migrations/2025_04_30_181719_create_client_task_table.php create mode 100644 database/seeders/TaskSeeder.php create mode 100644 resources/js/components/tasks/admin-task-stats.tsx create mode 100644 resources/js/components/tasks/create-task-dialog.tsx create mode 100644 resources/js/components/tasks/task-progress.tsx create mode 100644 resources/js/components/tasks/task-statistics.tsx create mode 100644 resources/js/components/ui/command.tsx create mode 100644 resources/js/components/ui/data-table.tsx create mode 100644 resources/js/components/ui/popover.tsx create mode 100644 resources/js/components/ui/progress.tsx create mode 100644 resources/js/components/ui/textarea.tsx create mode 100644 resources/js/pages/admin/admin-task-details.tsx create mode 100644 resources/js/types/task.ts diff --git a/app/Http/Controllers/Crud/ClientAdmin/TaskController.php b/app/Http/Controllers/Crud/ClientAdmin/TaskController.php new file mode 100644 index 0000000..5f6b578 --- /dev/null +++ b/app/Http/Controllers/Crud/ClientAdmin/TaskController.php @@ -0,0 +1,124 @@ +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); + } + + 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); + } + + 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(); + return redirect()->route('admin.tasks.index'); + } + + + +} \ No newline at end of file diff --git a/app/Models/Task.php b/app/Models/Task.php index 6bd1386..bf07704 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -4,6 +4,8 @@ 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 { @@ -15,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 cb84df9..67d495d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -70,4 +70,5 @@ public function role(): BelongsTo { return $this->belongsTo(Role::class); } + } 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/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..72e39a0 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,6 +16,7 @@ public function run(): void ClientSeeder::class, SystemAdminSeeder::class, UserSeeder::class, + TaskSeeder::class, ]); } } diff --git a/database/seeders/TaskSeeder.php b/database/seeders/TaskSeeder.php new file mode 100644 index 0000000..10192e3 --- /dev/null +++ b/database/seeders/TaskSeeder.php @@ -0,0 +1,75 @@ +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..e40ee8a 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -17,7 +17,7 @@ public function run() # client admin 'name' => 'Ryan Mueller', 'password' => Hash::make('password'), - 'email' => 'ryan@starplot.ai', + 'email' => 'ryan@equippers.com', 'password_set' => true, 'email_verified_at' => now(), 'client_id' => 1, @@ -29,7 +29,40 @@ public function run() # client member 'name' => 'Andrew McDowell', 'password' => Hash::make('password'), - 'email' => 'andrew@starplot.ai', + 'email' => 'andrew@equippers.com', + 'password_set' => true, + 'email_verified_at' => now(), + 'client_id' => 1, + 'role_id' => 3, + ]); + + User::create([ + # client member + 'name' => 'Allison Port', + 'password' => Hash::make('password'), + 'email' => 'allison@equippers.com', + 'password_set' => true, + 'email_verified_at' => now(), + 'client_id' => 1, + 'role_id' => 3, + ]); + + User::create([ + # client member + 'name' => 'George Kalafatis', + 'password' => Hash::make('password'), + 'email' => 'george@equippers.com', + 'password_set' => true, + 'email_verified_at' => now(), + 'client_id' => 1, + 'role_id' => 3, + ]); + + User::create([ + # client member + 'name' => 'John Doe', + 'password' => Hash::make('password'), + 'email' => 'john@equippers.com', 'password_set' => true, 'email_verified_at' => now(), 'client_id' => 1, 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/data-table.tsx b/resources/js/components/data-table.tsx index d704a70..3ac1bd2 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}
}
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..2305f33 --- /dev/null +++ b/resources/js/components/tasks/create-task-dialog.tsx @@ -0,0 +1,252 @@ +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 { Check, ChevronsUpDown } from "lucide-react" +import { cn } from "@/lib/utils" +import { useState, useEffect, useCallback } from "react" +import { useForm } from "@inertiajs/react" +import { TaskStatus, TaskPriority, User } from "@/types/task" +import { Badge } from "@/components/ui/badge" + +interface CreateTaskDialogProps { + users: User[]; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function CreateTaskDialog({ users, open, onOpenChange }: CreateTaskDialogProps) { + const [userPopoverOpen, setUserPopoverOpen] = useState(false); + const [filteredUsers, setFilteredUsers] = useState(users); + + const { data, setData, post, processing, errors, reset } = useForm({ + name: '', + description: '', + status: 'pending' as TaskStatus, + priority: 'medium' as TaskPriority, + due_date: '', + users: [] as number[] + }); + + // Update filtered users when users prop changes + useEffect(() => { + setFilteredUsers(users); + }, [users]); + + const handleUserSearch = useCallback((searchTerm: string) => { + const filtered = users.filter(user => + user.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setFilteredUsers(filtered); + }, [users]); + + const handleUserSelect = useCallback((userId: number) => { + const currentUsers = [...data.users]; + const index = currentUsers.indexOf(userId); + + if (index === -1) { + currentUsers.push(userId); + } else { + currentUsers.splice(index, 1); + } + + setData('users', currentUsers); + }, [data.users, setData]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + post('/tasks', { + onSuccess: () => { + reset(); + onOpenChange(false); + } + }); + }; + + const handleDropdownToggle = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setUserPopoverOpen(prev => !prev); + }; + + const handleDropdownClick = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + return ( + + + + Create New Task + + Add a new task to your project. Fill in the details below. + + +
+
+
+
+ + setData('name', e.target.value)} + error={errors.name} + /> +
+ +
+ +