From 561750dc831d456866b961427ae2bad7247cfd0e Mon Sep 17 00:00:00 2001 From: myrmidex Date: Sun, 16 Aug 2026 17:29:19 +0200 Subject: [PATCH] FOSS pivot: retire SaaS, demo, and APP_MODE plumbing (#37 #38 #39 #40 #41) Remove Stripe/Cashier subscription billing, demo mode, and APP_MODE enum/helpers. Delete frontend-old/ and stale bin scripts. Adopt AGPL-3.0 (LICENSE.md, README, composer.json). Point Docker image, prod scripts, and README at forge.lvl0.xyz/lvl0/dishplanner. --- .dockerignore | 3 - LICENSE.md | 313 +- README.md | 12 +- .../Commands/PurgeDemoAccountsCommand.php | 28 - app/Enums/AppModeEnum.php | 40 - app/Http/Controllers/Auth/LoginController.php | 4 - .../Controllers/SubscriptionController.php | 112 - app/Http/Middleware/DemoMiddleware.php | 43 - app/Http/Middleware/RequireSaasMode.php | 19 - app/Http/Middleware/RequireSubscription.php | 26 - app/Models/Planner.php | 3 +- app/Providers/AppServiceProvider.php | 4 - app/helpers.php | 31 - bin/build_and_push.sh | 4 +- bin/start-dev | 159 - bin/update.sh | 4 +- bootstrap/app.php | 18 - composer.json | 6 +- config/app.php | 13 - config/services.php | 10 - ...6_01_06_000525_create_customer_columns.php | 34 - ...1_06_000526_create_subscriptions_table.php | 37 - ...000527_create_subscription_items_table.php | 34 - ...d_meter_id_to_subscription_items_table.php | 28 - ...event_name_to_subscription_items_table.php | 28 - docker-compose.prod.yml | 2 +- frontend-old/.dockerignore | 4 - frontend-old/.gitignore | 6 - frontend-old/Dockerfile | 22 - frontend-old/README.md | 87 - frontend-old/app/app.css | 18 - frontend-old/app/components/Spinner.tsx | 15 - .../components/features/OnboardingBanner.tsx | 48 - .../components/features/auth/LoginForm.tsx | 98 - .../components/features/auth/RegisterForm.tsx | 107 - .../features/dishes/AddUserToDishForm.tsx | 101 - .../features/dishes/CreateDishForm.tsx | 95 - .../app/components/features/dishes/Dish.tsx | 39 - .../components/features/dishes/DishCard.tsx | 22 - .../features/dishes/EditDishForm.tsx | 87 - .../dishes/EditDishUserCardEditForm.tsx | 119 - .../features/dishes/RecurrenceLabels.tsx | 53 - .../features/dishes/SyncUsersForm.tsx | 32 - .../features/dishes/UserDishCard.tsx | 83 - .../features/navbar/MobileDropdownMenu.tsx | 69 - .../features/schedule/HistoricalDishes.tsx | 44 - .../features/schedule/ScheduleCalendar.tsx | 72 - .../features/schedule/ScheduleEditForm.tsx | 142 - .../schedule/ScheduleRegenerateButton.tsx | 35 - .../schedule/ScheduleRegenerateForm.tsx | 78 - .../features/schedule/UpcomingDishes.tsx | 60 - .../features/schedule/UserDishEditCard.tsx | 79 - .../features/schedule/dayCard/DateBadge.tsx | 27 - .../schedule/dayCard/ScheduleDayCard.tsx | 50 - .../dayCard/ScheduleDayCardUserDish.tsx | 32 - .../features/users/EditUserForm.tsx | 63 - .../app/components/layout/AuthGuard.tsx | 26 - frontend-old/app/components/layout/Card.tsx | 15 - frontend-old/app/components/layout/NavBar.tsx | 80 - .../app/components/pages/PrivatePage.tsx | 9 - .../app/components/pages/PublicPage.tsx | 11 - frontend-old/app/components/ui/Alert.tsx | 34 - frontend-old/app/components/ui/Button.tsx | 62 - .../components/ui/Buttons/OutlineButton.tsx | 37 - .../ui/Buttons/OutlineLinkButton.tsx | 49 - .../app/components/ui/Buttons/SolidButton.tsx | 40 - .../components/ui/Buttons/SolidLinkButton.tsx | 46 - .../app/components/ui/Description.tsx | 17 - frontend-old/app/components/ui/Hr.tsx | 13 - frontend-old/app/components/ui/Label.tsx | 25 - frontend-old/app/components/ui/Modal.tsx | 60 - frontend-old/app/components/ui/PageTitle.tsx | 17 - .../app/components/ui/RecurrenceInput.tsx | 65 - .../app/components/ui/SectionTitle.tsx | 16 - frontend-old/app/components/ui/Toggle.tsx | 41 - frontend-old/app/context/AuthContext.tsx | 41 - frontend-old/app/helpers/Date.ts | 18 - frontend-old/app/hooks/useFetchDishes.ts | 22 - frontend-old/app/hooks/useFetchUsers.ts | 22 - frontend-old/app/hooks/useRoutes.ts | 32 - frontend-old/app/root.tsx | 84 - frontend-old/app/routes.ts | 23 - frontend-old/app/routes/dishes.$id.edit.tsx | 66 - frontend-old/app/routes/dishes.create.tsx | 13 - frontend-old/app/routes/dishes.tsx | 64 - frontend-old/app/routes/home.tsx | 13 - .../app/routes/schedule.$date.edit.tsx | 19 - .../routes/scheduled-user-dishes.history.tsx | 13 - frontend-old/app/routes/users.$id.edit.tsx | 32 - frontend-old/app/routes/users.create.tsx | 74 - frontend-old/app/routes/users.tsx | 77 - frontend-old/app/styles/base/globals.css | 19 - .../app/styles/components/buttons.css | 42 - frontend-old/app/styles/components/select.css | 0 frontend-old/app/styles/main.css | 6 - frontend-old/app/styles/theme/borders.css | 14 - frontend-old/app/styles/theme/colors.css | 10 - .../app/styles/theme/colors/background.css | 226 - .../app/styles/theme/colors/border.css | 286 - frontend-old/app/styles/theme/colors/root.css | 193 - frontend-old/app/styles/theme/colors/text.css | 216 - frontend-old/app/styles/theme/fonts.css | 93 - frontend-old/app/types/DishType.ts | 20 - frontend-old/app/types/RecurrenceType.ts | 4 - frontend-old/app/types/ScheduleType.ts | 22 - .../app/types/ScheduledUserDishType.ts | 15 - frontend-old/app/types/UserDishType.ts | 8 - .../app/types/UserDishWithoutUserType.ts | 8 - frontend-old/app/types/UserType.ts | 7 - frontend-old/app/utils/api/apiRequest.ts | 107 - frontend-old/app/utils/api/auth.ts | 28 - frontend-old/app/utils/api/dishApi.ts | 149 - frontend-old/app/utils/api/scheduleApi.ts | 112 - .../app/utils/api/scheduledUserDishesApi.ts | 77 - frontend-old/app/utils/api/userDishApi.ts | 18 - frontend-old/app/utils/api/usersApi.ts | 139 - frontend-old/app/utils/dateBuilder.ts | 24 - frontend-old/app/utils/scheduleBuilder.ts | 19 - frontend-old/app/welcome/logo-dark.svg | 23 - frontend-old/app/welcome/logo-light.svg | 23 - frontend-old/app/welcome/welcome.tsx | 89 - frontend-old/archive/.dockerignore | 1 - frontend-old/archive/.env.local.example | 2 - frontend-old/archive/.gitignore | 45 - frontend-old/archive/README.md | 2 - frontend-old/archive/bin/update.sh | 16 - frontend-old/archive/build_and_push.sh | 3 - frontend-old/archive/eslint.config.mjs | 16 - frontend-old/archive/next.config.ts | 15 - frontend-old/archive/package.json | 33 - frontend-old/archive/postcss.config.mjs | 8 - frontend-old/archive/public/dish-planner.webp | Bin 237142 -> 0 bytes frontend-old/archive/public/file.svg | 1 - frontend-old/archive/public/globe.svg | 1 - frontend-old/archive/public/next.svg | 1 - frontend-old/archive/public/vercel.svg | 1 - frontend-old/archive/public/window.svg | 1 - .../src/app/dishes/[id]/delete/page.tsx | 83 - .../archive/src/app/dishes/[id]/edit/page.tsx | 59 - .../archive/src/app/dishes/create/page.tsx | 7 - frontend-old/archive/src/app/dishes/page.tsx | 57 - frontend-old/archive/src/app/favicon.ico | Bin 25931 -> 0 bytes frontend-old/archive/src/app/layout.tsx | 31 - frontend-old/archive/src/app/login/page.tsx | 11 - frontend-old/archive/src/app/page.tsx | 9 - .../archive/src/app/register/page.tsx | 14 - .../src/app/schedule/[date]/edit/page.tsx | 12 - .../scheduled-user-dishes/history/page.tsx | 9 - .../archive/src/app/users/[id]/edit/page.tsx | 30 - .../archive/src/app/users/create/page.tsx | 60 - frontend-old/archive/src/app/users/page.tsx | 78 - .../archive/src/components/Spinner.tsx | 15 - .../components/features/OnboardingBanner.tsx | 49 - .../components/features/auth/LoginForm.tsx | 96 - .../features/auth/RegistrationForm.tsx | 104 - .../features/dishes/AddUserToDishForm.tsx | 101 - .../features/dishes/CreateDishForm.tsx | 95 - .../src/components/features/dishes/Dish.tsx | 39 - .../components/features/dishes/DishCard.tsx | 23 - .../features/dishes/EditDishForm.tsx | 87 - .../dishes/EditDishUserCardEditForm.tsx | 119 - .../features/dishes/RecurrenceLabels.tsx | 54 - .../features/dishes/SyncUsersForm.tsx | 32 - .../features/dishes/UserDishCard.tsx | 83 - .../features/navbar/MobileDropdownMenu.tsx | 69 - .../features/schedule/HistoricalDishes.tsx | 44 - .../features/schedule/ScheduleCalendar.tsx | 75 - .../features/schedule/ScheduleEditForm.tsx | 142 - .../schedule/ScheduleRegenerateButton.tsx | 35 - .../schedule/ScheduleRegenerateForm.tsx | 78 - .../features/schedule/UpcomingDishes.tsx | 62 - .../features/schedule/UserDishEditCard.tsx | 79 - .../features/schedule/dayCard/DateBadge.tsx | 27 - .../schedule/dayCard/ScheduleDayCard.tsx | 50 - .../dayCard/ScheduleDayCardUserDish.tsx | 32 - .../features/users/EditUserForm.tsx | 63 - .../src/components/layout/AuthGuard.tsx | 48 - .../archive/src/components/layout/Card.tsx | 15 - .../archive/src/components/layout/NavBar.tsx | 83 - .../archive/src/components/ui/Alert.tsx | 34 - .../archive/src/components/ui/Button.tsx | 62 - .../components/ui/Buttons/OutlineButton.tsx | 37 - .../ui/Buttons/OutlineLinkButton.tsx | 49 - .../src/components/ui/Buttons/SolidButton.tsx | 39 - .../components/ui/Buttons/SolidLinkButton.tsx | 46 - .../archive/src/components/ui/Description.tsx | 17 - frontend-old/archive/src/components/ui/Hr.tsx | 14 - .../archive/src/components/ui/Label.tsx | 25 - .../archive/src/components/ui/Modal.tsx | 62 - .../archive/src/components/ui/PageTitle.tsx | 18 - .../src/components/ui/RecurrenceInput.tsx | 65 - .../src/components/ui/SectionTitle.tsx | 16 - .../archive/src/components/ui/Toggle.tsx | 42 - .../archive/src/context/AuthContext.tsx | 46 - frontend-old/archive/src/helpers/Date.ts | 18 - .../archive/src/hooks/useFetchDishes.ts | 22 - .../archive/src/hooks/useFetchUsers.ts | 22 - frontend-old/archive/src/hooks/useRoutes.ts | 32 - .../archive/src/styles/base/globals.css | 19 - .../archive/src/styles/components/buttons.css | 42 - .../archive/src/styles/components/select.css | 0 frontend-old/archive/src/styles/main.css | 10 - .../archive/src/styles/theme/borders.css | 14 - .../archive/src/styles/theme/colors.css | 10 - .../src/styles/theme/colors/background.css | 226 - .../src/styles/theme/colors/border.css | 286 - .../archive/src/styles/theme/colors/root.css | 193 - .../archive/src/styles/theme/colors/text.css | 216 - .../archive/src/styles/theme/fonts.css | 95 - frontend-old/archive/src/types/DishType.ts | 20 - .../archive/src/types/ScheduleType.ts | 27 - .../src/types/ScheduledUserDishType.ts | 21 - .../archive/src/types/UserDishType.ts | 8 - frontend-old/archive/src/types/UserType.ts | 7 - .../archive/src/utils/api/apiRequest.ts | 107 - frontend-old/archive/src/utils/api/auth.ts | 28 - frontend-old/archive/src/utils/api/dishApi.ts | 149 - .../archive/src/utils/api/scheduleApi.ts | 112 - .../src/utils/api/scheduledUserDishesApi.ts | 77 - .../archive/src/utils/api/userDishApi.ts | 18 - .../archive/src/utils/api/usersApi.ts | 139 - frontend-old/archive/src/utils/dateBuilder.ts | 24 - .../archive/src/utils/scheduleBuilder.ts | 19 - frontend-old/archive/tailwind.config.ts | 18 - frontend-old/archive/tsconfig.json | 27 - frontend-old/package-lock.json | 5099 ----------------- frontend-old/package.json | 35 - frontend-old/public/favicon.ico | Bin 15086 -> 0 bytes frontend-old/react-router.config.ts | 7 - frontend-old/tsconfig.json | 31 - frontend-old/vite.config.ts | 20 - resources/views/billing/index.blade.php | 106 - .../views/components/layouts/app.blade.php | 48 +- resources/views/dashboard.blade.php | 7 - resources/views/subscription/index.blade.php | 51 - resources/views/welcome.blade.php | 176 - routes/console.php | 5 - routes/web.php | 31 +- routes/web/subscription.php | 18 - shell.nix | 32 +- .../Planner/Actions/SeedDemoPlannerAction.php | 104 - 241 files changed, 203 insertions(+), 16625 deletions(-) delete mode 100644 app/Console/Commands/PurgeDemoAccountsCommand.php delete mode 100644 app/Enums/AppModeEnum.php delete mode 100644 app/Http/Controllers/SubscriptionController.php delete mode 100644 app/Http/Middleware/DemoMiddleware.php delete mode 100644 app/Http/Middleware/RequireSaasMode.php delete mode 100644 app/Http/Middleware/RequireSubscription.php delete mode 100644 app/helpers.php delete mode 100755 bin/start-dev delete mode 100644 database/migrations/2026_01_06_000525_create_customer_columns.php delete mode 100644 database/migrations/2026_01_06_000526_create_subscriptions_table.php delete mode 100644 database/migrations/2026_01_06_000527_create_subscription_items_table.php delete mode 100644 database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php delete mode 100644 database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php delete mode 100644 frontend-old/.dockerignore delete mode 100644 frontend-old/.gitignore delete mode 100644 frontend-old/Dockerfile delete mode 100644 frontend-old/README.md delete mode 100644 frontend-old/app/app.css delete mode 100644 frontend-old/app/components/Spinner.tsx delete mode 100644 frontend-old/app/components/features/OnboardingBanner.tsx delete mode 100644 frontend-old/app/components/features/auth/LoginForm.tsx delete mode 100644 frontend-old/app/components/features/auth/RegisterForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/AddUserToDishForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/CreateDishForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/Dish.tsx delete mode 100644 frontend-old/app/components/features/dishes/DishCard.tsx delete mode 100644 frontend-old/app/components/features/dishes/EditDishForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/RecurrenceLabels.tsx delete mode 100644 frontend-old/app/components/features/dishes/SyncUsersForm.tsx delete mode 100644 frontend-old/app/components/features/dishes/UserDishCard.tsx delete mode 100644 frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx delete mode 100644 frontend-old/app/components/features/schedule/HistoricalDishes.tsx delete mode 100644 frontend-old/app/components/features/schedule/ScheduleCalendar.tsx delete mode 100644 frontend-old/app/components/features/schedule/ScheduleEditForm.tsx delete mode 100644 frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx delete mode 100644 frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx delete mode 100644 frontend-old/app/components/features/schedule/UpcomingDishes.tsx delete mode 100644 frontend-old/app/components/features/schedule/UserDishEditCard.tsx delete mode 100644 frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx delete mode 100644 frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx delete mode 100644 frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx delete mode 100644 frontend-old/app/components/features/users/EditUserForm.tsx delete mode 100644 frontend-old/app/components/layout/AuthGuard.tsx delete mode 100644 frontend-old/app/components/layout/Card.tsx delete mode 100644 frontend-old/app/components/layout/NavBar.tsx delete mode 100644 frontend-old/app/components/pages/PrivatePage.tsx delete mode 100644 frontend-old/app/components/pages/PublicPage.tsx delete mode 100644 frontend-old/app/components/ui/Alert.tsx delete mode 100644 frontend-old/app/components/ui/Button.tsx delete mode 100644 frontend-old/app/components/ui/Buttons/OutlineButton.tsx delete mode 100644 frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx delete mode 100644 frontend-old/app/components/ui/Buttons/SolidButton.tsx delete mode 100644 frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx delete mode 100644 frontend-old/app/components/ui/Description.tsx delete mode 100644 frontend-old/app/components/ui/Hr.tsx delete mode 100644 frontend-old/app/components/ui/Label.tsx delete mode 100644 frontend-old/app/components/ui/Modal.tsx delete mode 100644 frontend-old/app/components/ui/PageTitle.tsx delete mode 100644 frontend-old/app/components/ui/RecurrenceInput.tsx delete mode 100644 frontend-old/app/components/ui/SectionTitle.tsx delete mode 100644 frontend-old/app/components/ui/Toggle.tsx delete mode 100644 frontend-old/app/context/AuthContext.tsx delete mode 100644 frontend-old/app/helpers/Date.ts delete mode 100644 frontend-old/app/hooks/useFetchDishes.ts delete mode 100644 frontend-old/app/hooks/useFetchUsers.ts delete mode 100644 frontend-old/app/hooks/useRoutes.ts delete mode 100644 frontend-old/app/root.tsx delete mode 100644 frontend-old/app/routes.ts delete mode 100644 frontend-old/app/routes/dishes.$id.edit.tsx delete mode 100644 frontend-old/app/routes/dishes.create.tsx delete mode 100644 frontend-old/app/routes/dishes.tsx delete mode 100644 frontend-old/app/routes/home.tsx delete mode 100644 frontend-old/app/routes/schedule.$date.edit.tsx delete mode 100644 frontend-old/app/routes/scheduled-user-dishes.history.tsx delete mode 100644 frontend-old/app/routes/users.$id.edit.tsx delete mode 100644 frontend-old/app/routes/users.create.tsx delete mode 100644 frontend-old/app/routes/users.tsx delete mode 100644 frontend-old/app/styles/base/globals.css delete mode 100644 frontend-old/app/styles/components/buttons.css delete mode 100644 frontend-old/app/styles/components/select.css delete mode 100644 frontend-old/app/styles/main.css delete mode 100644 frontend-old/app/styles/theme/borders.css delete mode 100644 frontend-old/app/styles/theme/colors.css delete mode 100644 frontend-old/app/styles/theme/colors/background.css delete mode 100644 frontend-old/app/styles/theme/colors/border.css delete mode 100644 frontend-old/app/styles/theme/colors/root.css delete mode 100644 frontend-old/app/styles/theme/colors/text.css delete mode 100644 frontend-old/app/styles/theme/fonts.css delete mode 100644 frontend-old/app/types/DishType.ts delete mode 100644 frontend-old/app/types/RecurrenceType.ts delete mode 100644 frontend-old/app/types/ScheduleType.ts delete mode 100644 frontend-old/app/types/ScheduledUserDishType.ts delete mode 100644 frontend-old/app/types/UserDishType.ts delete mode 100644 frontend-old/app/types/UserDishWithoutUserType.ts delete mode 100644 frontend-old/app/types/UserType.ts delete mode 100644 frontend-old/app/utils/api/apiRequest.ts delete mode 100644 frontend-old/app/utils/api/auth.ts delete mode 100644 frontend-old/app/utils/api/dishApi.ts delete mode 100644 frontend-old/app/utils/api/scheduleApi.ts delete mode 100644 frontend-old/app/utils/api/scheduledUserDishesApi.ts delete mode 100644 frontend-old/app/utils/api/userDishApi.ts delete mode 100644 frontend-old/app/utils/api/usersApi.ts delete mode 100644 frontend-old/app/utils/dateBuilder.ts delete mode 100644 frontend-old/app/utils/scheduleBuilder.ts delete mode 100644 frontend-old/app/welcome/logo-dark.svg delete mode 100644 frontend-old/app/welcome/logo-light.svg delete mode 100644 frontend-old/app/welcome/welcome.tsx delete mode 100644 frontend-old/archive/.dockerignore delete mode 100644 frontend-old/archive/.env.local.example delete mode 100644 frontend-old/archive/.gitignore delete mode 100644 frontend-old/archive/README.md delete mode 100755 frontend-old/archive/bin/update.sh delete mode 100755 frontend-old/archive/build_and_push.sh delete mode 100644 frontend-old/archive/eslint.config.mjs delete mode 100644 frontend-old/archive/next.config.ts delete mode 100644 frontend-old/archive/package.json delete mode 100644 frontend-old/archive/postcss.config.mjs delete mode 100644 frontend-old/archive/public/dish-planner.webp delete mode 100644 frontend-old/archive/public/file.svg delete mode 100644 frontend-old/archive/public/globe.svg delete mode 100644 frontend-old/archive/public/next.svg delete mode 100644 frontend-old/archive/public/vercel.svg delete mode 100644 frontend-old/archive/public/window.svg delete mode 100644 frontend-old/archive/src/app/dishes/[id]/delete/page.tsx delete mode 100644 frontend-old/archive/src/app/dishes/[id]/edit/page.tsx delete mode 100644 frontend-old/archive/src/app/dishes/create/page.tsx delete mode 100644 frontend-old/archive/src/app/dishes/page.tsx delete mode 100644 frontend-old/archive/src/app/favicon.ico delete mode 100644 frontend-old/archive/src/app/layout.tsx delete mode 100644 frontend-old/archive/src/app/login/page.tsx delete mode 100644 frontend-old/archive/src/app/page.tsx delete mode 100644 frontend-old/archive/src/app/register/page.tsx delete mode 100644 frontend-old/archive/src/app/schedule/[date]/edit/page.tsx delete mode 100644 frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx delete mode 100644 frontend-old/archive/src/app/users/[id]/edit/page.tsx delete mode 100644 frontend-old/archive/src/app/users/create/page.tsx delete mode 100644 frontend-old/archive/src/app/users/page.tsx delete mode 100644 frontend-old/archive/src/components/Spinner.tsx delete mode 100644 frontend-old/archive/src/components/features/OnboardingBanner.tsx delete mode 100644 frontend-old/archive/src/components/features/auth/LoginForm.tsx delete mode 100644 frontend-old/archive/src/components/features/auth/RegistrationForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/Dish.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/DishCard.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/EditDishForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx delete mode 100644 frontend-old/archive/src/components/features/dishes/UserDishCard.tsx delete mode 100644 frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx delete mode 100644 frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx delete mode 100644 frontend-old/archive/src/components/features/users/EditUserForm.tsx delete mode 100644 frontend-old/archive/src/components/layout/AuthGuard.tsx delete mode 100644 frontend-old/archive/src/components/layout/Card.tsx delete mode 100644 frontend-old/archive/src/components/layout/NavBar.tsx delete mode 100644 frontend-old/archive/src/components/ui/Alert.tsx delete mode 100644 frontend-old/archive/src/components/ui/Button.tsx delete mode 100644 frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx delete mode 100644 frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx delete mode 100644 frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx delete mode 100644 frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx delete mode 100644 frontend-old/archive/src/components/ui/Description.tsx delete mode 100644 frontend-old/archive/src/components/ui/Hr.tsx delete mode 100644 frontend-old/archive/src/components/ui/Label.tsx delete mode 100644 frontend-old/archive/src/components/ui/Modal.tsx delete mode 100644 frontend-old/archive/src/components/ui/PageTitle.tsx delete mode 100644 frontend-old/archive/src/components/ui/RecurrenceInput.tsx delete mode 100644 frontend-old/archive/src/components/ui/SectionTitle.tsx delete mode 100644 frontend-old/archive/src/components/ui/Toggle.tsx delete mode 100644 frontend-old/archive/src/context/AuthContext.tsx delete mode 100644 frontend-old/archive/src/helpers/Date.ts delete mode 100644 frontend-old/archive/src/hooks/useFetchDishes.ts delete mode 100644 frontend-old/archive/src/hooks/useFetchUsers.ts delete mode 100644 frontend-old/archive/src/hooks/useRoutes.ts delete mode 100644 frontend-old/archive/src/styles/base/globals.css delete mode 100644 frontend-old/archive/src/styles/components/buttons.css delete mode 100644 frontend-old/archive/src/styles/components/select.css delete mode 100644 frontend-old/archive/src/styles/main.css delete mode 100644 frontend-old/archive/src/styles/theme/borders.css delete mode 100644 frontend-old/archive/src/styles/theme/colors.css delete mode 100644 frontend-old/archive/src/styles/theme/colors/background.css delete mode 100644 frontend-old/archive/src/styles/theme/colors/border.css delete mode 100644 frontend-old/archive/src/styles/theme/colors/root.css delete mode 100644 frontend-old/archive/src/styles/theme/colors/text.css delete mode 100644 frontend-old/archive/src/styles/theme/fonts.css delete mode 100644 frontend-old/archive/src/types/DishType.ts delete mode 100644 frontend-old/archive/src/types/ScheduleType.ts delete mode 100644 frontend-old/archive/src/types/ScheduledUserDishType.ts delete mode 100644 frontend-old/archive/src/types/UserDishType.ts delete mode 100644 frontend-old/archive/src/types/UserType.ts delete mode 100644 frontend-old/archive/src/utils/api/apiRequest.ts delete mode 100644 frontend-old/archive/src/utils/api/auth.ts delete mode 100644 frontend-old/archive/src/utils/api/dishApi.ts delete mode 100644 frontend-old/archive/src/utils/api/scheduleApi.ts delete mode 100644 frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts delete mode 100644 frontend-old/archive/src/utils/api/userDishApi.ts delete mode 100644 frontend-old/archive/src/utils/api/usersApi.ts delete mode 100644 frontend-old/archive/src/utils/dateBuilder.ts delete mode 100644 frontend-old/archive/src/utils/scheduleBuilder.ts delete mode 100644 frontend-old/archive/tailwind.config.ts delete mode 100644 frontend-old/archive/tsconfig.json delete mode 100644 frontend-old/package-lock.json delete mode 100644 frontend-old/package.json delete mode 100644 frontend-old/public/favicon.ico delete mode 100644 frontend-old/react-router.config.ts delete mode 100644 frontend-old/tsconfig.json delete mode 100644 frontend-old/vite.config.ts delete mode 100644 resources/views/billing/index.blade.php delete mode 100644 resources/views/subscription/index.blade.php delete mode 100644 resources/views/welcome.blade.php delete mode 100644 routes/web/subscription.php delete mode 100644 src/DishPlanner/Planner/Actions/SeedDemoPlannerAction.php diff --git a/.dockerignore b/.dockerignore index c8ef8b6..f40e224 100644 --- a/.dockerignore +++ b/.dockerignore @@ -47,9 +47,6 @@ Dockerfile* docker-compose*.yml .dockerignore -# Frontend old -frontend-old/ - # Build artifacts public/build/ public/hot diff --git a/LICENSE.md b/LICENSE.md index 6b111d1..be3f7b2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,106 +1,94 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 -Copyright (C) 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. Preamble -The GNU General Public License is a free, copyleft license for -software and other kinds of works. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. -The licenses for most software and other practical works are designed + The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to +our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. +software for all its users. -When we speak of free software, we are referring to freedom, not + When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. -Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. -For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. -Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - -Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and + The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS -0. Definitions. + 0. Definitions. -"This License" refers to version 3 of the GNU General Public License. + "This License" refers to version 3 of the GNU Affero General Public License. -"Copyright" also means copyright-like laws that apply to other kinds of + "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. -"The Program" refers to any copyrightable work licensed under this + "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. -To "modify" a work means to copy from or adapt all or part of the work + To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. -A "covered work" means either the unmodified Program or a work based + A "covered work" means either the unmodified Program or a work based on the Program. -To "propagate" a work means to do anything with it that, without + To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. -To "convey" a work means any kind of propagation that enables other + To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. -An interactive user interface displays "Appropriate Legal Notices" + An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the @@ -109,18 +97,18 @@ the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. -1. Source Code. + 1. Source Code. -The "source code" for a work means the preferred form of the work + The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. -A "Standard Interface" means an interface that either is an official + A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. -The "System Libraries" of an executable work include anything, other + The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that @@ -131,7 +119,7 @@ (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. -The "Corresponding Source" for a work in object code form means all + The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's @@ -144,16 +132,16 @@ such as by intimate data communication or control flow between those subprograms and other parts of the work. -The Corresponding Source need not include anything that users + The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. -The Corresponding Source for a work in source code form is that + The Corresponding Source for a work in source code form is that same work. -2. Basic Permissions. + 2. Basic Permissions. -All rights granted under this License are granted for the term of + All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a @@ -161,7 +149,7 @@ content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. -You may make, run and propagate covered works that you do not + You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you @@ -172,19 +160,19 @@ and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. -Conveying under any other circumstances is permitted solely under + Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. -3. Protecting Users' Legal Rights From Anti-Circumvention Law. + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. -No covered work shall be deemed part of an effective technological + No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. -When you convey a covered work, you waive any legal power to forbid + When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or @@ -192,9 +180,9 @@ users, your or third parties' legal rights to forbid circumvention of technological measures. -4. Conveying Verbatim Copies. + 4. Conveying Verbatim Copies. -You may convey verbatim copies of the Program's source code as you + You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any @@ -202,12 +190,12 @@ keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. -You may charge any price or no price for each copy that you convey, + You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. -5. Conveying Modified Source Versions. + 5. Conveying Modified Source Versions. -You may convey a work based on the Program, or the modifications to + You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: @@ -232,7 +220,7 @@ interfaces that do not display Appropriate Legal Notices, your work need not make them do so. -A compilation of a covered work with other separate and independent + A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an @@ -242,9 +230,9 @@ in an aggregate does not cause this License to apply to the other parts of the aggregate. -6. Conveying Non-Source Forms. + 6. Conveying Non-Source Forms. -You may convey a covered work in object code form under the terms + You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: @@ -290,11 +278,11 @@ Source of the work are being offered to the general public at no charge under subsection 6d. -A separable portion of the object code, whose source code is excluded + A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. -A "User Product" is either (1) a "consumer product", which means any + A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, @@ -307,7 +295,7 @@ commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. -"Installation Information" for a User Product means any methods, + "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must @@ -315,7 +303,7 @@ code is in no case prevented or interfered with solely because modification has been made. -If you convey an object code work under this section in, or with, or + If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a @@ -326,7 +314,7 @@ modified object code on the User Product (for example, the work has been installed in ROM). -The requirement to provide Installation Information does not include a + The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a @@ -334,15 +322,15 @@ adversely affects the operation of the network or violates the rules and protocols for communication across the network. -Corresponding Source conveyed, and Installation Information provided, + Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. -7. Additional Terms. + 7. Additional Terms. -"Additional permissions" are terms that supplement the terms of this + "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent @@ -351,14 +339,14 @@ under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. -When you convey a copy of a covered work, you may at your option + When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. -Notwithstanding any other provision of this License, for material you + Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: @@ -385,7 +373,7 @@ any liability that these contractual assumptions directly impose on those licensors and authors. -All other non-permissive additional terms are considered "further + All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further @@ -395,46 +383,46 @@ of that license document, provided that the further restriction does not survive such relicensing or conveying. -If you add terms to a covered work in accord with this section, you + If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. -Additional terms, permissive or non-permissive, may be stated in the + Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. -8. Termination. + 8. Termination. -You may not propagate or modify a covered work except as expressly + You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). -However, if you cease all violation of this License, then your + However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. -Moreover, your license from a particular copyright holder is + Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. -Termination of your rights under this section does not terminate the + Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. -9. Acceptance Not Required for Having Copies. + 9. Acceptance Not Required for Having Copies. -You are not required to accept this License in order to receive or + You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, @@ -443,14 +431,14 @@ not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. -10. Automatic Licensing of Downstream Recipients. + 10. Automatic Licensing of Downstream Recipients. -Each time you convey a covered work, the recipient automatically + Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. -An "entity transaction" is a transaction transferring control of an + An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that @@ -460,7 +448,7 @@ Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. -You may not impose any further restrictions on the exercise of the + You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation @@ -468,13 +456,13 @@ any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. -11. Patents. + 11. Patents. -A "contributor" is a copyright holder who authorizes use under this + A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". -A contributor's "essential patent claims" are all patent claims + A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, @@ -484,19 +472,19 @@ patent sublicenses in a manner consistent with the requirements of this License. -Each contributor grants you a non-exclusive, worldwide, royalty-free + Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. -In the following three paragraphs, a "patent license" is any express + In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. -If you convey a covered work, knowingly relying on a patent license, + If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, @@ -510,7 +498,7 @@ in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. -If, pursuant to or in connection with a single transaction or + If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify @@ -518,7 +506,7 @@ you grant is automatically extended to all recipients of the covered work and works based on it. -A patent license is "discriminatory" if it does not include within + A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered @@ -533,13 +521,13 @@ contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. -Nothing in this License shall be construed as excluding or limiting + Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. -12. No Surrender of Others' Freedom. + 12. No Surrender of Others' Freedom. -If conditions are imposed on you (whether by court order, agreement or + If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this @@ -549,46 +537,56 @@ the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. -13. Use with the GNU Affero General Public License. + 13. Remote Network Interaction; Use with the GNU General Public License. -Notwithstanding any other provision of this License, you have + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single +under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. -14. Revised Versions of this License. + 14. Revised Versions of this License. -The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published +GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. -If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. -Later license versions may give you additional or different + Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. -15. Disclaimer of Warranty. + 15. Disclaimer of Warranty. -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, @@ -597,9 +595,9 @@ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -16. Limitation of Liability. + 16. Limitation of Liability. -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE @@ -609,9 +607,9 @@ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -17. Interpretation of Sections 15 and 16. + 17. Interpretation of Sections 15 and 16. -If the disclaimer of warranty and limitation of liability provided + If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the @@ -622,11 +620,11 @@ How to Apply These Terms to Your New Programs -If you develop a new program, and you want it to be of the greatest + If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. -To do so, attach the following notices to the program. It is safest + To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. @@ -635,40 +633,29 @@ Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by + it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. -If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - -You should also get your employer (if you work as a programmer) or school, + You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see +For more information on this, and how to apply and follow the GNU AGPL, see . - -The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README.md b/README.md index da0dd61..a233b72 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ ## ✨ Features ## 🚀 Self-hosting -The production image is available at `codeberg.org/dish-planner/app:latest`. +The production image is available at `forge.lvl0.xyz/lvl0/dishplanner:latest`. ### docker-compose.yml ```yaml services: app: - image: codeberg.org/dish-planner/app:latest + image: forge.lvl0.xyz/lvl0/dishplanner:latest container_name: dishplanner_app restart: always ports: @@ -85,8 +85,8 @@ ## 🔧 Development ### NixOS / Nix ```bash -git clone https://codeberg.org/dish-planner/app.git -cd dish-planner +git clone https://forge.lvl0.xyz/lvl0/dishplanner.git +cd dishplanner nix-shell ``` @@ -121,8 +121,8 @@ ### Other Platforms ## 📄 License -This project is open-source software licensed under the [MIT license](LICENSE.md). +This project is open-source software licensed under the [AGPL-3.0 license](LICENSE.md). ## 📞 Support -For issues and questions, please use [Codeberg Issues](https://codeberg.org/dish-planner/app/issues). \ No newline at end of file +For issues and questions, please use [Forgejo Issues](https://forge.lvl0.xyz/lvl0/dishplanner/issues). \ No newline at end of file diff --git a/app/Console/Commands/PurgeDemoAccountsCommand.php b/app/Console/Commands/PurgeDemoAccountsCommand.php deleted file mode 100644 index f131076..0000000 --- a/app/Console/Commands/PurgeDemoAccountsCommand.php +++ /dev/null @@ -1,28 +0,0 @@ -error('This command can only run in demo mode.'); - - return self::FAILURE; - } - - $count = Planner::where('created_at', '<', now()->subHours(24))->delete(); - - $this->info("Purged {$count} demo accounts."); - - return self::SUCCESS; - } -} diff --git a/app/Enums/AppModeEnum.php b/app/Enums/AppModeEnum.php deleted file mode 100644 index f92857f..0000000 --- a/app/Enums/AppModeEnum.php +++ /dev/null @@ -1,40 +0,0 @@ -route('dashboard'); - } - Auth::logout(); $request->session()->invalidate(); diff --git a/app/Http/Controllers/SubscriptionController.php b/app/Http/Controllers/SubscriptionController.php deleted file mode 100644 index 24d0dc9..0000000 --- a/app/Http/Controllers/SubscriptionController.php +++ /dev/null @@ -1,112 +0,0 @@ -user(); - - if ($planner->subscribed()) { - return redirect()->route('dashboard'); - } - - $plan = $request->input('plan', 'monthly'); - $priceId = $plan === 'yearly' - ? config('services.stripe.price_yearly') - : config('services.stripe.price_monthly'); - - return $planner->newSubscription('default', $priceId) - ->checkout([ - 'success_url' => route('subscription.success') . '?session_id={CHECKOUT_SESSION_ID}', - 'cancel_url' => route('subscription.index'), - ]); - } - - public function success(Request $request): RedirectResponse - { - $sessionId = $request->query('session_id'); - - if ($sessionId) { - $planner = $request->user(); - $session = Cashier::stripe()->checkout->sessions->retrieve($sessionId, [ - 'expand' => ['subscription'], - ]); - - if ($session->subscription && ! $planner->subscribed()) { - $subscription = $session->subscription; - - $planner->subscriptions()->create([ - 'type' => 'default', - 'stripe_id' => $subscription->id, - 'stripe_status' => $subscription->status, - 'stripe_price' => $subscription->items->data[0]->price->id ?? null, - 'quantity' => $subscription->items->data[0]->quantity ?? 1, - 'trial_ends_at' => $subscription->trial_end ? now()->setTimestamp($subscription->trial_end) : null, - 'ends_at' => null, - ]); - } - } - - return redirect()->route('dashboard')->with('success', 'Subscription activated!'); - } - - public function billing(Request $request) - { - $planner = $request->user(); - $subscription = $planner->subscription(); - - if (! $subscription) { - return redirect()->route('subscription.index'); - } - - $planType = match ($subscription->stripe_price) { - config('services.stripe.price_yearly') => 'Yearly', - config('services.stripe.price_monthly') => 'Monthly', - default => 'Unknown', - }; - - $nextBillingDate = null; - if ($subscription->stripe_status === 'active') { - try { - $stripeSubscription = Cashier::stripe()->subscriptions->retrieve($subscription->stripe_id); - $nextBillingDate = $stripeSubscription->current_period_end - ? now()->setTimestamp($stripeSubscription->current_period_end) - : null; - } catch (\Exception $e) { - // Stripe API error - continue without next billing date - } - } - - return view('billing.index', [ - 'subscription' => $subscription, - 'planner' => $planner, - 'planType' => $planType, - 'nextBillingDate' => $nextBillingDate, - ]); - } - - public function cancel(Request $request): RedirectResponse - { - $planner = $request->user(); - - if (! $planner->subscribed()) { - return back()->with('error', 'No active subscription found.'); - } - - $planner->subscription()->cancel(); - - return back()->with('success', 'Subscription canceled. Access will continue until the end of your billing period.'); - } - - public function billingPortal(Request $request) - { - return $request->user()->redirectToBillingPortal(route('billing')); - } -} diff --git a/app/Http/Middleware/DemoMiddleware.php b/app/Http/Middleware/DemoMiddleware.php deleted file mode 100644 index 62bea02..0000000 --- a/app/Http/Middleware/DemoMiddleware.php +++ /dev/null @@ -1,43 +0,0 @@ - 'Demo User', - 'email' => 'demo-' . Str::uuid() . '@demo.local', - 'password' => Hash::make(Str::random(32)), - ]); - - resolve(SeedDemoPlannerAction::class)->execute($planner); - - return $planner; - }); - - Auth::login($planner); - - return $next($request); - } -} diff --git a/app/Http/Middleware/RequireSaasMode.php b/app/Http/Middleware/RequireSaasMode.php deleted file mode 100644 index 0950eb6..0000000 --- a/app/Http/Middleware/RequireSaasMode.php +++ /dev/null @@ -1,19 +0,0 @@ -requiresSubscription()) { - return $next($request); - } - - $planner = $request->user(); - - if (! $planner?->subscribed()) { - return redirect()->route('subscription.index'); - } - - return $next($request); - } -} diff --git a/app/Models/Planner.php b/app/Models/Planner.php index 947a5d9..c53777e 100644 --- a/app/Models/Planner.php +++ b/app/Models/Planner.php @@ -7,7 +7,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -use Laravel\Cashier\Billable; use Laravel\Sanctum\HasApiTokens; /** @@ -19,7 +18,7 @@ */ class Planner extends Authenticatable { - use Billable, HasApiTokens, HasFactory, Notifiable; + use HasApiTokens, HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3067e8c..0bbbb1e 100755 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,12 +4,10 @@ use App\Exceptions\CustomException; use App\Models\Dish; -use App\Models\Planner; use App\Models\Schedule; use App\Models\ScheduledUserDish; use App\Models\User; use App\Models\UserDish; -use Laravel\Cashier\Cashier; use DishPlanner\Dish\Policies\DishPolicy; use DishPlanner\Schedule\Policies\SchedulePolicy; use DishPlanner\ScheduledUserDish\Policies\ScheduledUserDishPolicy; @@ -47,8 +45,6 @@ public function render($request, Throwable $e) public function boot(): void { - Cashier::useCustomerModel(Planner::class); - Gate::policy(Dish::class, DishPolicy::class); Gate::policy(Schedule::class, SchedulePolicy::class); Gate::policy(ScheduledUserDish::class, ScheduledUserDishPolicy::class); diff --git a/app/helpers.php b/app/helpers.php deleted file mode 100644 index 55f6f40..0000000 --- a/app/helpers.php +++ /dev/null @@ -1,31 +0,0 @@ -isApp(); - } -} - -if (! function_exists('is_mode_saas')) { - function is_mode_saas(): bool - { - return AppModeEnum::current()->isSaas(); - } -} - -if (! function_exists('is_mode_demo')) { - function is_mode_demo(): bool - { - return AppModeEnum::current()->isDemo(); - } -} - -if (! function_exists('allows_logout')) { - function allows_logout(): bool - { - return AppModeEnum::current()->allowsLogout(); - } -} \ No newline at end of file diff --git a/bin/build_and_push.sh b/bin/build_and_push.sh index b3ce7d4..2c36dc5 100755 --- a/bin/build_and_push.sh +++ b/bin/build_and_push.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker build -t 192.168.178.152:50114/dishplanner-backend . -docker push 192.168.178.152:50114/dishplanner-backend +docker build -t forge.lvl0.xyz/lvl0/dishplanner:latest . +docker push forge.lvl0.xyz/lvl0/dishplanner:latest diff --git a/bin/start-dev b/bin/start-dev deleted file mode 100755 index 4d31519..0000000 --- a/bin/start-dev +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Get script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -# Detect Docker or Podman -CONTAINER_CLI="" -# Check if docker is actually podman in disguise -if command -v docker &> /dev/null && docker --version 2>&1 | grep -q "podman"; then - CONTAINER_CLI="podman" - echo -e "${GREEN}Using Podman (via docker alias)${NC}" -elif command -v docker &> /dev/null && docker info &> /dev/null; then - CONTAINER_CLI="docker" - echo -e "${GREEN}Using Docker${NC}" -elif command -v podman &> /dev/null; then - CONTAINER_CLI="podman" - echo -e "${GREEN}Using Podman${NC}" -else - echo -e "${RED}✗ Neither Docker nor Podman found. Please install one of them first.${NC}" - echo -e "${YELLOW}Install Docker: https://docs.docker.com/get-docker/${NC}" - echo -e "${YELLOW}Install Podman: https://podman.io/getting-started/installation${NC}" - exit 1 -fi - -echo -e "${GREEN}=== Dish Planner Development Setup ===${NC}\n" - -# Check if .env exists in backend -if [ ! -f "$PROJECT_ROOT/backend/.env" ]; then - echo -e "${YELLOW}Creating backend/.env from .env.example...${NC}" - cp "$PROJECT_ROOT/backend/.env.example" "$PROJECT_ROOT/backend/.env" - echo -e "${GREEN}✓ Created backend/.env${NC}\n" -fi - -# Ensure APP_PORT is set for Podman (can't use privileged port 80) -if [ "$CONTAINER_CLI" = "podman" ] && ! grep -q "^APP_PORT=" "$PROJECT_ROOT/backend/.env"; then - echo -e "${YELLOW}Setting APP_PORT=8000 for Podman (non-privileged port)...${NC}" - echo "APP_PORT=8000" >> "$PROJECT_ROOT/backend/.env" -fi - -# Backend setup -echo -e "${GREEN}=== Backend Setup ===${NC}" -cd "$PROJECT_ROOT/backend" - -# Install dependencies if vendor doesn't exist -if [ ! -d "vendor" ]; then - echo -e "${YELLOW}No vendor directory found. Installing dependencies with Docker...${NC}" - # Use a standalone PHP/Composer Docker image to install dependencies - # This is the recommended Laravel approach for first-time setup - # Configure for Docker or Podman - VOLUME_OPTS="$PROJECT_ROOT/backend:/var/www/html" - USER_OPTS="-u $(id -u):$(id -g)" - EXTRA_OPTS="" - - if [ "$CONTAINER_CLI" = "podman" ]; then - # Podman on SELinux systems needs :Z and --userns=keep-id to maintain user ID - VOLUME_OPTS="$VOLUME_OPTS:Z" - USER_OPTS="" - EXTRA_OPTS="--userns=keep-id" - fi - - $CONTAINER_CLI run --rm \ - $USER_OPTS \ - $EXTRA_OPTS \ - -v "$VOLUME_OPTS" \ - -w /var/www/html \ - docker.io/laravelsail/php84-composer:latest \ - composer install --ignore-platform-reqs - - echo -e "${GREEN}✓ Backend dependencies installed${NC}\n" -else - echo -e "${GREEN}✓ Backend dependencies already installed${NC}\n" -fi - -# Check if database volume exists - if not, we're doing fresh initialization -FRESH_DB=false -if ! $CONTAINER_CLI volume inspect sail-mysql &>/dev/null && ! $CONTAINER_CLI volume inspect backend_sail-mysql &>/dev/null; then - FRESH_DB=true - echo -e "${YELLOW}Fresh database initialization detected${NC}" -fi - -# Start containers using compose directly (docker-compose.override.yml is automatically used) -echo -e "${YELLOW}Starting backend containers...${NC}" -if $CONTAINER_CLI compose up -d 2>&1 | tee /tmp/compose-up.log; then - echo -e "${GREEN}✓ Containers started${NC}\n" -else - echo -e "${RED}✗ Failed to start containers. Check /tmp/compose-up.log for details${NC}" - exit 1 -fi - -# Wait for database to be ready -echo -e "${YELLOW}Waiting for database to be ready...${NC}" -MAX_ATTEMPTS=30 -ATTEMPT=0 -while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if $CONTAINER_CLI compose exec mysql mysqladmin ping -h localhost --silent 2>/dev/null; then - echo -e "${GREEN}✓ Database is ready${NC}" - break - fi - ATTEMPT=$((ATTEMPT + 1)) - echo -e "${YELLOW}Waiting for database... (attempt $ATTEMPT/$MAX_ATTEMPTS)${NC}" - sleep 2 -done - -if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo -e "${RED}✗ Database failed to become ready${NC}" - exit 1 -fi - -# Give MySQL extra time on fresh initialization to create users -if [ "$FRESH_DB" = true ]; then - echo -e "${YELLOW}Waiting for fresh database to complete initialization...${NC}" - sleep 10 -else - sleep 3 -fi - -# Run migrations -echo -e "${YELLOW}Running database migrations...${NC}" -$CONTAINER_CLI compose exec backend php artisan migrate --force - -# Check if database has data -echo -e "${YELLOW}Checking if database needs seeding...${NC}" -TABLE_COUNT=$($CONTAINER_CLI compose exec backend php artisan tinker --execute="echo \DB::table('users')->count();" 2>/dev/null | tail -1 | tr -d '[:space:]' || echo "0") -# Default to 0 if not a number -if ! [[ "$TABLE_COUNT" =~ ^[0-9]+$ ]]; then - TABLE_COUNT=0 -fi -if [ "$TABLE_COUNT" -eq "0" ]; then - echo -e "${YELLOW}Database is empty. Run seeders? (y/n)${NC}" - read -r response - if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then - $CONTAINER_CLI compose exec backend php artisan db:seed - echo -e "${GREEN}✓ Database seeded${NC}\n" - fi -fi - -echo -e "${GREEN}✓ Backend setup complete${NC}" -echo -e "${GREEN}Backend API running at: http://localhost:8000${NC}\n" - -# Display summary -echo -e "${GREEN}=== Development Environment Ready ===${NC}" -echo -e "${GREEN}Backend API:${NC} http://localhost:8000" -echo -e "${GREEN}Frontend:${NC} http://localhost:5173" -echo -e "${GREEN}Database:${NC} MySQL on localhost:3306" -echo -e "" -echo -e "${YELLOW}Note:${NC} Frontend container will install dependencies and start automatically." -echo -e "${YELLOW}To view frontend logs:${NC}" -echo -e " cd backend && $CONTAINER_CLI compose logs -f frontend" -echo -e "" -echo -e "${YELLOW}To stop all services:${NC}" -echo -e " cd backend && $CONTAINER_CLI compose down" diff --git a/bin/update.sh b/bin/update.sh index 7320639..2c864a3 100755 --- a/bin/update.sh +++ b/bin/update.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -echo "🔄 Pulling latest backend changes..." +echo "🔄 Pulling latest changes..." git pull origin main echo "📦 Installing PHP dependencies..." @@ -15,4 +15,4 @@ php artisan config:cache php artisan route:cache php artisan view:cache -echo "✅ Backend update complete!" +echo "✅ Update complete!" diff --git a/bootstrap/app.php b/bootstrap/app.php index 578208e..5e69532 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,15 +1,11 @@ group(base_path('routes/web/subscription.php')); - }, ) ->withMiddleware(function (Middleware $middleware) { // Apply ForceJsonResponse only to API routes $middleware->api(ForceJsonResponse::class); - $middleware->web(DemoMiddleware::class); - $middleware->alias([ - 'subscription' => RequireSubscription::class, - 'saas' => RequireSaasMode::class, - ]); - - // Exclude Stripe webhook from CSRF verification - $middleware->validateCsrfTokens(except: [ - 'stripe/webhook', - ]); }) ->withExceptions(function (Exceptions $exceptions) { $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) { diff --git a/composer.json b/composer.json index 2ecb17b..8f7b9ef 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,9 @@ "laravel", "framework" ], - "license": "MIT", + "license": "AGPL-3.0-only", "require": { "php": "^8.2", - "laravel/cashier": "^16.1", "laravel/framework": "^12.9.2", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", @@ -27,9 +26,6 @@ "phpunit/phpunit": "^11.0.1" }, "autoload": { - "files": [ - "app/helpers.php" - ], "psr-4": { "App\\": "app/", "DishPlanner\\": "src/DishPlanner/", diff --git a/config/app.php b/config/app.php index 58954c9..df3f5f0 100644 --- a/config/app.php +++ b/config/app.php @@ -28,19 +28,6 @@ 'env' => env('APP_ENV', 'production'), - /* - |-------------------------------------------------------------------------- - | Application Mode - |-------------------------------------------------------------------------- - | - | Determines the application deployment mode: 'app' for self-hosted, - | 'saas' for multi-tenant SaaS, 'demo' for demonstration instances. - | - */ - - 'mode' => env('APP_MODE', 'app'), - 'demo_subscribe_url' => env('APP_DEMO_SUBSCRIBE_URL', 'https://dishplanner.app'), - /* |-------------------------------------------------------------------------- | Application Debug Mode diff --git a/config/services.php b/config/services.php index cf3ce61..27a3617 100644 --- a/config/services.php +++ b/config/services.php @@ -35,14 +35,4 @@ ], ], - 'stripe' => [ - 'key' => env('STRIPE_KEY'), - 'secret' => env('STRIPE_SECRET'), - 'webhook' => [ - 'secret' => env('STRIPE_WEBHOOK_SECRET'), - ], - 'price_monthly' => env('STRIPE_PRICE_MONTHLY'), - 'price_yearly' => env('STRIPE_PRICE_YEARLY'), - ], - ]; diff --git a/database/migrations/2026_01_06_000525_create_customer_columns.php b/database/migrations/2026_01_06_000525_create_customer_columns.php deleted file mode 100644 index 131d232..0000000 --- a/database/migrations/2026_01_06_000525_create_customer_columns.php +++ /dev/null @@ -1,34 +0,0 @@ -string('stripe_id')->nullable()->index(); - $table->string('pm_type')->nullable(); - $table->string('pm_last_four', 4)->nullable(); - $table->timestamp('trial_ends_at')->nullable(); - }); - } - - public function down(): void - { - Schema::table('planners', function (Blueprint $table) { - $table->dropIndex([ - 'stripe_id', - ]); - - $table->dropColumn([ - 'stripe_id', - 'pm_type', - 'pm_last_four', - 'trial_ends_at', - ]); - }); - } -}; diff --git a/database/migrations/2026_01_06_000526_create_subscriptions_table.php b/database/migrations/2026_01_06_000526_create_subscriptions_table.php deleted file mode 100644 index 9043296..0000000 --- a/database/migrations/2026_01_06_000526_create_subscriptions_table.php +++ /dev/null @@ -1,37 +0,0 @@ -id(); - $table->foreignId('planner_id'); - $table->string('type'); - $table->string('stripe_id')->unique(); - $table->string('stripe_status'); - $table->string('stripe_price')->nullable(); - $table->integer('quantity')->nullable(); - $table->timestamp('trial_ends_at')->nullable(); - $table->timestamp('ends_at')->nullable(); - $table->timestamps(); - - $table->index(['planner_id', 'stripe_status']); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('subscriptions'); - } -}; diff --git a/database/migrations/2026_01_06_000527_create_subscription_items_table.php b/database/migrations/2026_01_06_000527_create_subscription_items_table.php deleted file mode 100644 index 420e23f..0000000 --- a/database/migrations/2026_01_06_000527_create_subscription_items_table.php +++ /dev/null @@ -1,34 +0,0 @@ -id(); - $table->foreignId('subscription_id'); - $table->string('stripe_id')->unique(); - $table->string('stripe_product'); - $table->string('stripe_price'); - $table->integer('quantity')->nullable(); - $table->timestamps(); - - $table->index(['subscription_id', 'stripe_price']); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('subscription_items'); - } -}; diff --git a/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php b/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php deleted file mode 100644 index 033bb82..0000000 --- a/database/migrations/2026_01_06_000528_add_meter_id_to_subscription_items_table.php +++ /dev/null @@ -1,28 +0,0 @@ -string('meter_id')->nullable()->after('stripe_price'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('subscription_items', function (Blueprint $table) { - $table->dropColumn('meter_id'); - }); - } -}; diff --git a/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php b/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php deleted file mode 100644 index b157b3a..0000000 --- a/database/migrations/2026_01_06_000529_add_meter_event_name_to_subscription_items_table.php +++ /dev/null @@ -1,28 +0,0 @@ -string('meter_event_name')->nullable()->after('quantity'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('subscription_items', function (Blueprint $table) { - $table->dropColumn('meter_event_name'); - }); - } -}; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index aa612c8..088e6c7 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,7 +1,7 @@ # Production Docker Compose services: app: - image: codeberg.org/dish-planner/app:latest + image: forge.lvl0.xyz/lvl0/dishplanner:latest container_name: dishplanner_app restart: always ports: diff --git a/frontend-old/.dockerignore b/frontend-old/.dockerignore deleted file mode 100644 index 9b8d514..0000000 --- a/frontend-old/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -.react-router -build -node_modules -README.md \ No newline at end of file diff --git a/frontend-old/.gitignore b/frontend-old/.gitignore deleted file mode 100644 index 9b7c041..0000000 --- a/frontend-old/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.DS_Store -/node_modules/ - -# React Router -/.react-router/ -/build/ diff --git a/frontend-old/Dockerfile b/frontend-old/Dockerfile deleted file mode 100644 index 207bf93..0000000 --- a/frontend-old/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM node:20-alpine AS development-dependencies-env -COPY . /app -WORKDIR /app -RUN npm ci - -FROM node:20-alpine AS production-dependencies-env -COPY ./package.json package-lock.json /app/ -WORKDIR /app -RUN npm ci --omit=dev - -FROM node:20-alpine AS build-env -COPY . /app/ -COPY --from=development-dependencies-env /app/node_modules /app/node_modules -WORKDIR /app -RUN npm run build - -FROM node:20-alpine -COPY ./package.json package-lock.json /app/ -COPY --from=production-dependencies-env /app/node_modules /app/node_modules -COPY --from=build-env /app/build /app/build -WORKDIR /app -CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/frontend-old/README.md b/frontend-old/README.md deleted file mode 100644 index 5c4780a..0000000 --- a/frontend-old/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Welcome to React Router! - -A modern, production-ready template for building full-stack React applications using React Router. - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default) - -## Features - -- 🚀 Server-side rendering -- ⚡️ Hot Module Replacement (HMR) -- 📦 Asset bundling and optimization -- 🔄 Data loading and mutations -- 🔒 TypeScript by default -- 🎉 TailwindCSS for styling -- 📖 [React Router docs](https://reactrouter.com/) - -## Getting Started - -### Installation - -Install the dependencies: - -```bash -npm install -``` - -### Development - -Start the development server with HMR: - -```bash -npm run dev -``` - -Your application will be available at `http://localhost:5173`. - -## Building for Production - -Create a production build: - -```bash -npm run build -``` - -## Deployment - -### Docker Deployment - -To build and run using Docker: - -```bash -docker build -t my-app . - -# Run the container -docker run -p 3000:3000 my-app -``` - -The containerized application can be deployed to any platform that supports Docker, including: - -- AWS ECS -- Google Cloud Run -- Azure Container Apps -- Digital Ocean App Platform -- Fly.io -- Railway - -### DIY Deployment - -If you're familiar with deploying Node applications, the built-in app server is production-ready. - -Make sure to deploy the output of `npm run build` - -``` -├── package.json -├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) -├── build/ -│ ├── client/ # Static assets -│ └── server/ # Server-side code -``` - -## Styling - -This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. - ---- - -Built with ❤️ using React Router. diff --git a/frontend-old/app/app.css b/frontend-old/app/app.css deleted file mode 100644 index 2187b39..0000000 --- a/frontend-old/app/app.css +++ /dev/null @@ -1,18 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&family=Syncopate:wght@400;700&display=swap'); -@import "tailwindcss"; - -@import "./styles/main.css"; - -@theme { - --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} - -html, -body { - @apply bg-white dark:bg-gray-950; - - @media (prefers-color-scheme: dark) { - color-scheme: dark; - } -} diff --git a/frontend-old/app/components/Spinner.tsx b/frontend-old/app/components/Spinner.tsx deleted file mode 100644 index f170616..0000000 --- a/frontend-old/app/components/Spinner.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const Spinner = () => { - - return ( -
- - - - -
- ) - -} - -export default Spinner \ No newline at end of file diff --git a/frontend-old/app/components/features/OnboardingBanner.tsx b/frontend-old/app/components/features/OnboardingBanner.tsx deleted file mode 100644 index ee97858..0000000 --- a/frontend-old/app/components/features/OnboardingBanner.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Link } from "react-router" -import useRoutes from "@/hooks/useRoutes" -import { UserType } from "@/types/UserType" -import { DishType } from "@/types/DishType" - -interface Props { - dishes: DishType[], - users: UserType[] -} - -const OnboardingBanner = ({ dishes, users }: Props) => { - const routes = useRoutes(); - - const steps = [ - { - label: "Create a user", - href: routes.user.create(), - count: users.length - }, { - label: "Create a dish", - href: routes.dish.create(), - count: dishes.length - } - ] - - return ( -
-
Welcome to DishPlanner
-
To get you started, please follow these steps to set up your account. This will ensure a better - experience. -
- - { - steps.map((step, index) => ( -
- { - step.count === 0 - ? { step.label } - :
{ step.label }
- } -
- )) - } -
- ) -} - -export default OnboardingBanner; \ No newline at end of file diff --git a/frontend-old/app/components/features/auth/LoginForm.tsx b/frontend-old/app/components/features/auth/LoginForm.tsx deleted file mode 100644 index 3e693d8..0000000 --- a/frontend-old/app/components/features/auth/LoginForm.tsx +++ /dev/null @@ -1,98 +0,0 @@ - -import React, { useEffect, useState } from 'react'; -import { Link, useLocation, useNavigate } from 'react-router'; -import { useAuth } from '@/context/AuthContext'; -import { login } from "@/utils/api/auth"; -import useRoutes from "@/hooks/useRoutes"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import Alert from "@/components/ui/Alert"; - -const LoginForm = () => { - const { login: authLogin } = useAuth(); - const routes = useRoutes(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [alertSuccess, setAlertSuccess] = useState([]) - const navigate = useNavigate(); - const location = useLocation(); - const searchParams = new URLSearchParams(location.search); - const isRegistered = searchParams.get('registered') === 'true'; - - useEffect(() => { - if (isRegistered) { - setAlertSuccess(['Registration successful!',' You can now log in.']); - - const timer = setTimeout(() => { - const params = new URLSearchParams(searchParams.toString()); - params.delete('registered'); - const newUrl = `${window.location.pathname}?${params.toString()}`; - - navigate(newUrl, { replace: true }); - }, 3000); - - return () => clearTimeout(timer) - } - }, [isRegistered, navigate, searchParams]) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - try { - await login(email, password); - authLogin(); - navigate('/', { replace: true }); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Login failed'; - setError(errorMessage); - } - }; - - return ( -
-
- DISH PLANNER -
-
- { alertSuccess.length > 0 && - - {alertSuccess.map((msg, index) => ( - - {msg} -
-
- ))} -
- } -
- {error &&

{error}

} - setEmail(e.target.value)} - required - className="w-full p-2 mb-4 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value)} - required - className="w-full p-2 mb-4 border rounded text-secondary border-secondary bg-gray-600" - /> - Login - - Create an account - -
-
-
- ) -} - -export default LoginForm \ No newline at end of file diff --git a/frontend-old/app/components/features/auth/RegisterForm.tsx b/frontend-old/app/components/features/auth/RegisterForm.tsx deleted file mode 100644 index 908d624..0000000 --- a/frontend-old/app/components/features/auth/RegisterForm.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { useState } from 'react'; -import { register } from "@/utils/api/auth"; -import useRoutes from "@/hooks/useRoutes"; -import SectionTitle from "@/components/ui/SectionTitle"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import { Link, useNavigate } from "react-router" - -const RegisterForm = () => { - const routes = useRoutes(); - const [name, setName] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [passwordAgain, setPasswordAgain] = useState(''); - const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [isRegistered, setIsRegistered] = useState(false); - const navigate = useNavigate(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (password !== passwordAgain) { - setError("Passwords do not match."); - return; - } - - try { - setIsLoading(true); - - await register(name, email, password, passwordAgain); - // navigate('/login?registered=true', { replace: true }); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Registration\n failed'; - setError(errorMessage); - } finally { - setIsRegistered(true); - setIsLoading(false); - } - }; - - if (isRegistered) { - return
- Registration successful! - Please continue to the login page. -
- } - - return ( -
-
- DISH PLANNER -
-
-
-

Register

- { error &&

{ error }

} - setName(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setEmail(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - setPasswordAgain(e.target.value) } - required - className="w-full p-2 border rounded border-secondary bg-gray-600 text-secondary" - /> - - Create Account - - - Back to Login - -
-
-
- ) -} - -export default RegisterForm \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx b/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx deleted file mode 100644 index 974cd11..0000000 --- a/frontend-old/app/components/features/dishes/AddUserToDishForm.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import {addUserToDish} from "@/utils/api/dishApi"; -import OutlineButton from "@/components/ui/Buttons/OutlineButton"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const AddUserToDishForm = ({ dish, reloadDish }: Props) => { - const [showAdd, setShowAdd] = useState(false); - const [selectedUser, setSelectedUser] = useState("-1"); - const { users, isLoading: isUsersLoading } = useFetchUsers(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (selectedUser === "-1") { - alert("Please select a valid user."); - return; - } - - const userToAdd = users.find((user: UserType) => user.id === parseInt(selectedUser)); - - if (!userToAdd) { - alert("User not found."); - return; - } - - addUserToDish(dish.id, userToAdd.id) - .then(() => { - setShowAdd(false); - setSelectedUser("-1"); - reloadDish(); - }) - .catch(() => { - alert("Failed to add user, please try again."); - }); - }; - - if (isUsersLoading) { - return ; - } - - const remainingUsers = users.filter( - (user: UserType) => - !dish.users.find((dishUser: UserType) => dishUser.id === user.id) - ); - - return ( - <> - setShowAdd(!showAdd)} - disabled={remainingUsers.length === 0} - type="button" - > - Add User - - - { showAdd && ( -
-
-
-
- -
- -
- - Add User - -
-
-
-
- )} - - ); -}; - -export default AddUserToDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/CreateDishForm.tsx b/frontend-old/app/components/features/dishes/CreateDishForm.tsx deleted file mode 100644 index 69a139f..0000000 --- a/frontend-old/app/components/features/dishes/CreateDishForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState } from "react"; -import { useNavigate } from "react-router"; -import { createDish } from "~/utils/api/dishApi"; -import PageTitle from "~/components/ui/PageTitle"; -import Alert from "~/components/ui/Alert"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "~/components/ui/Hr" - -const CreateDishForm = () => { - const navigate = useNavigate() - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - // Validate client-side input - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await createDish(name); - if (result) { - navigate('/dishes') - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred."); - } finally { - setLoading(false); - } - } - - return ( -
-
- Create Dish -
- -
- { error && ( - { error } - ) } - -
- - setName(e.target.value) } // Update the name state on change - className="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-secondary focus:bg-gray-900" - placeholder="Enter dish name" - /> -
- - - { loading ? "Saving..." : "Save Changes" } - -
- -
- - } - > - Back to dishes - -
- ); - - -}; - -export default CreateDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/Dish.tsx b/frontend-old/app/components/features/dishes/Dish.tsx deleted file mode 100644 index 44128c7..0000000 --- a/frontend-old/app/components/features/dishes/Dish.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {DishType} from "~/types/DishType"; - -import {PencilIcon, TrashIcon} from '@heroicons/react/24/solid' -import { Link } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import {UserType} from "~/types/UserType"; -import Card from "~/components/layout/Card"; - -const Dish = ({ dish }: { dish: DishType}) => { - const routes = useRoutes(); - - return ( - -
-

{ dish.name }

- - { - dish.users.map((user: UserType) => ( -
{user.name.slice(0, 1)}
- )) - } -
-
- -
- -
- - -
- -
- -
-
- ) -} - -export default Dish \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/DishCard.tsx b/frontend-old/app/components/features/dishes/DishCard.tsx deleted file mode 100644 index d36a656..0000000 --- a/frontend-old/app/components/features/dishes/DishCard.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {DishType} from "@/types/DishType"; - -interface Props { - user: UserType, - dish: DishType, -} - -const DishCard = ({ user, dish }: Props) => { - return ( -
-
- { user.name.slice(0, 1) } -
-
- { dish ? dish.name : '-' } -
-
- ) -} - -export default DishCard \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/EditDishForm.tsx b/frontend-old/app/components/features/dishes/EditDishForm.tsx deleted file mode 100644 index a35002e..0000000 --- a/frontend-old/app/components/features/dishes/EditDishForm.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, {useState} from "react"; -import { useNavigate } from "react-router"; -import Alert from "~/components/ui/Alert"; -import {updateDish} from "~/utils/api/dishApi"; -import {DishType} from "~/types/DishType"; -import useRoutes from "~/hooks/useRoutes"; -import Spinner from "~/components/Spinner"; -import Button from "~/components/ui/Button" - -interface Props { - dish: DishType -} - -const EditDishForm = ({ dish }: Props) => { - const [name, setName] = useState(dish.name); - const [error, setError] = useState(""); - const navigate = useNavigate() - const [loading, setLoading] = useState(false); - const routes = useRoutes(); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await updateDish(dish.id, name); - if (result) { - navigate(routes.dish.index()) - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred"); - } finally { - setLoading(false); // Reset loading state - } - } - - if (loading) { - return ; - } - - return ( -
- { - error != '' && { error } - } - - {/* Dish name input */} -
- - setName(e.target.value)} // Update the name state on change - className="p-2 border rounded w-full bg-gray-500 border-secondary background-secondary" - /> -
- - {/* Save button */} - -
- ); -} - -export default EditDishForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx b/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx deleted file mode 100644 index 1ac08cd..0000000 --- a/frontend-old/app/components/features/dishes/EditDishUserCardEditForm.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from "react"; -import SectionTitle from "@/components/ui/SectionTitle"; -import {syncUserDishRecurrences} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import {UserDishType} from "@/types/ScheduledUserDishType"; -import {RecurrenceType} from "@/types/RecurrenceType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - userDish: UserDishType - onSubmit: () => void -} - -const EditDishUserCardEditForm = ({ userDish, onSubmit}: Props) => { - const weeklyRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\WeeklyRecurrence') - const minimumRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\MinimumRecurrence') - - const wv = weeklyRecurrence ? weeklyRecurrence.value : undefined - const mv = minimumRecurrence ? minimumRecurrence.value : undefined - - const [isWeeklyOn, setIsWeeklyOn] = React.useState(weeklyRecurrence !== undefined); - const [isMinimumOn, setIsMinimumOn] = React.useState(minimumRecurrence !== undefined); - const [weekday, setWeekday] = React.useState(wv ?? 0); - const [minimumValue, setMinimumValue] = React.useState(mv ?? 7); - const [loading, setLoading] = React.useState(false); - - const handleSubmit = () => { - const recurrences = [] - - if (isWeeklyOn) { - recurrences.push({ - type: 'App\\Models\\WeeklyRecurrence', - value: weekday, - }); - } - - if (isMinimumOn) { - recurrences.push({ - type: 'App\\Models\\MinimumRecurrence', - value: minimumValue, - }); - } - - setLoading(true) - syncUserDishRecurrences(userDish.dish.id, userDish.user.id, recurrences as RecurrenceType[]) - .then((data) => console.log('request data', data)) - .finally(() => { - setLoading(false) - onSubmit() - }) - } - - if (loading) { - return ; - } - - return ( -
- Recurrences - -
-
- setIsWeeklyOn(!isWeeklyOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- { - isWeeklyOn && ( -
- - -
- ) - } -
- -
-
- setIsMinimumOn(!isMinimumOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-500 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- - { - isMinimumOn && ( -
- setMinimumValue(parseInt(e.currentTarget.value))} min="0" max="365" className="background-secondary border-secondary border-2 w-12 px-2" /> - -
- ) - } -
- - Save -
- ); -} - -export default EditDishUserCardEditForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx b/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx deleted file mode 100644 index deff7c3..0000000 --- a/frontend-old/app/components/features/dishes/RecurrenceLabels.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import {RecurrenceType} from "@/types/RecurrenceType"; - -interface Props { - recurrences: RecurrenceType[]; -} - -const RecurrenceLabels = ({recurrences}: Props) => { - const weeklyRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\WeeklyRecurrence'); - const minimumRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\MinimumRecurrence'); - - const renderWeeklyRecurrence = () => { - if (weeklyRecurrences == undefined || weeklyRecurrences.length == 0) { - return ''; - } - - const weekdayString = (() => { - switch (weeklyRecurrences[0].value) { - case 0: return "Sunday" - case 1: return "Monday" - case 2: return "Tuesday"; - case 3: return "Wednesday"; - case 4: return "Thursday"; - case 5: return "Friday"; - case 6: return "Saturday"; - default: return "Invalid day"; - } - }) - - return ( -
- { weekdayString() } -
- ) - } - const renderMinimumRecurrence = () => { - if (minimumRecurrences == undefined || minimumRecurrences.length == 0) { - return ''; - } - - return ( -
- min: { minimumRecurrences[0].value } -
- ) - } - - return <> - { renderWeeklyRecurrence() } - { renderMinimumRecurrence() } - ; -}; - -export default RecurrenceLabels; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/SyncUsersForm.tsx b/frontend-old/app/components/features/dishes/SyncUsersForm.tsx deleted file mode 100644 index d1f7264..0000000 --- a/frontend-old/app/components/features/dishes/SyncUsersForm.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import UserDishCard from "@/components/features/dishes/UserDishCard"; -import SectionTitle from "@/components/ui/SectionTitle"; -import AddUserToDishForm from "@/components/features/dishes/AddUserToDishForm"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const SyncUsersForm = ({ dish, reloadDish }: Props) => { - return ( -
- Users - - - - {dish.users.map((user: UserType) => ( - - ))} -
- ); -}; - -export default SyncUsersForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/dishes/UserDishCard.tsx b/frontend-old/app/components/features/dishes/UserDishCard.tsx deleted file mode 100644 index 5681434..0000000 --- a/frontend-old/app/components/features/dishes/UserDishCard.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, {useEffect} from "react"; -import {DishType} from "~/types/DishType"; -import {UserType} from "~/types/UserType"; -import { Link } from "react-router"; -import {PencilIcon, TrashIcon} from "@heroicons/react/24/solid"; -import {removeUserFromDish} from "~/utils/api/dishApi"; -import EditDishUserCardEditForm from "~/components/features/dishes/EditDishUserCardEditForm"; -import {getUserDishForUserAndDish} from "~/utils/api/usersApi"; -import Spinner from "~/components/Spinner"; -import RecurrenceLabels from "~/components/features/dishes/RecurrenceLabels"; -import {UserDishType} from "~/types/ScheduledUserDishType"; - -interface Props { - dish: DishType - user: UserType - reloadDish: () => void -} - -const UserDishCard = ({dish, user, reloadDish}: Props) => { - const [userDish, setUserDish] = React.useState(null); - const [userDishLoading, setUserDishLoading] = React.useState(true); - const [isEditMode, setIsEditMode] = React.useState(false); - - useEffect(() => { - getUserDishForUserAndDish(user.id, dish.id) - .then((userDish) => setUserDish(userDish)) - .finally(() => setUserDishLoading(false)) - }, [dish, user]); - - const handleRemove = () => { - removeUserFromDish(dish.id, user.id) - .then(() => reloadDish()) - .catch(() => { - alert("Failed to remove user, please try again."); - }); - }; - - if (userDishLoading || !userDish) { - return - } - - const onUserCardSubmit = () => { - setIsEditMode(false); - reloadDish() - } - - return ( -
-
-
- {user.name} -
- -
- -
- -
- setIsEditMode(!isEditMode)} to="#"> -
- -
- -
-
- -
- -
- -
-
- - {isEditMode && ( -
- -
- )} -
- ); -} - -export default UserDishCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx b/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx deleted file mode 100644 index 46ccdf0..0000000 --- a/frontend-old/app/components/features/navbar/MobileDropdownMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Link } from "react-router"; -import React from "react"; -import useRoutes from "~/hooks/useRoutes"; -import classNames from "classnames"; - -interface Props { - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; - handleLogout: (e: React.MouseEvent) => void; -} - -const divStyles = classNames( - 'absolute', 'text-xxl', 'rounded-b', 'top-full mt-1', 'left-0', 'right-0', '', 'py-2', - 'bg-gray-600', 'border-secondary', 'shadow-md', 'flex', 'flex-col', 'space-y-3', - 'md:hidden' -) - -const linkStyles = classNames( - 'border-b-2', 'border-secondary', 'uppercase', - 'text-primary', 'hover:background-secondary', 'pb-2', 'pl-5', - 'space-grotesk', 'text-xl' -) - -const MobileDropdownMenu = ({ isOpen, setIsOpen, handleLogout }: Props) => { - const routes = useRoutes(); - - if (!isOpen) return null; - - return ( -
- setIsOpen(false)} - > - Home - - setIsOpen(false)} - > - Dishes - - setIsOpen(false)} - > - Users - - setIsOpen(false)} - > - History - - - Logout - -
- ) -} - -export default MobileDropdownMenu \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/HistoricalDishes.tsx b/frontend-old/app/components/features/schedule/HistoricalDishes.tsx deleted file mode 100644 index ad3718f..0000000 --- a/frontend-old/app/components/features/schedule/HistoricalDishes.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client" - -import {useEffect, useState} from "react"; -import {DateTime} from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import {ScheduleType} from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import {listSchedule} from "@/utils/api/scheduleApi"; - -const HistoricalDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const yesterday = DateTime.now().minus({ days: 1 }).toFormat('yyyy-LL-dd'); - - useEffect(() => { - listSchedule(undefined, yesterday) - .then((dishes: ScheduleType[]) => dishes - .sort((a: ScheduleType, b: ScheduleType) => new Date(b.date).getTime() - new Date(a.date).getTime()) - ) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)) - }, [yesterday]); - - if (isLoading) { - return ; - } - - if (!schedule || Object.keys(schedule).length === 0) { - return ( -
- No dishes scheduled -
- ); - } - - return
- History - -
-} - -export default HistoricalDishes \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx b/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx deleted file mode 100644 index fce02c2..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleCalendar.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import ScheduleDayCard from "@/components/features/schedule/dayCard/ScheduleDayCard"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; - -const generateDates = (startDate: string, days: number): string[] => { - const dates = []; - const start = new Date(startDate); - - for (let i = 0; i < days; i++) { - const currentDate = new Date(start); - currentDate.setDate(start.getDate() + i); - dates.push(currentDate.toISOString().split('T')[0]); - } - - return dates; -}; - - -const fillCalendar = (schedules: ScheduleType[]): FilledScheduleType[] => { - /* -Array(14) - 0: - date: "2025-05-05" - id: 2 - is_skipped: false - scheduled_user_dishes: [] - */ - - const dates = generateDates((new Date()).toISOString().split('T')[0], 31) - - return dates.map((date): FilledScheduleType => { - console.log(date) - - const schedule = schedules.find((schedule: ScheduleType) => schedule.date == date) - - if (schedule) { - return schedule - } - - return { - date, - scheduled_user_dishes: [] - } - }) -} - -interface Props { - schedule: ScheduleType[]; -} - -const ScheduleCalendar = ({ schedule }: Props) => { - const {users, isLoading: areUsersLoading} = useFetchUsers(); - - if (areUsersLoading) return - - const fullCalendar = fillCalendar(schedule) - - return ( -
- { fullCalendar.map((schedule, index) => ( - - ))} -
- ) -} - -export default ScheduleCalendar \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx b/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx deleted file mode 100644 index df5db7b..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleEditForm.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { ScheduleType } from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import PageTitle from "@/components/ui/PageTitle"; -import { getScheduleForDate, scheduleUserDish, updateScheduleForDate } from "@/utils/api/scheduleApi"; -import { UserDishType } from "@/types/ScheduledUserDishType"; -import Label from "@/components/ui/Label"; -import SectionTitle from "@/components/ui/SectionTitle"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import { listUserDishes } from "@/utils/api/userDishApi"; -import scheduleBuilder from "@/utils/scheduleBuilder"; -import transformDate from "@/utils/dateBuilder"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" -import Button from "@/components/ui/Button" - -interface Props { - date: string; -} - -const ScheduleEditForm = ({ date }: Props) => { - const [schedule, setSchedule] = useState() - const [userDishes, setUserDishes] = useState([]) - const [isScheduleLoading, setIsScheduleLoading] = useState(true); - const [areUserDishesLoading, setAreUserDishesLoading] = useState(true); - const { users } = useFetchUsers(); - - useEffect(() => { - getScheduleForDate(date) - .then((sched: ScheduleType) => setSchedule(sched)) - .finally(() => setIsScheduleLoading(false)) - }, [date]); - - - useEffect(() => { - listUserDishes() - .then((user_dishes: UserDishType[]) => setUserDishes(user_dishes)) - .finally(() => setAreUserDishesLoading(false)) - }, []); - - const handleSkipDay = () => { - updateScheduleForDate(date, true) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleUnskipDay = () => { - updateScheduleForDate(date, false) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleChange = (e: React.ChangeEvent, userId: number) => { - const userDishId = parseInt(e.currentTarget.value); - - if (userDishId === 0) { - scheduleUserDish(date, userId, null, true).then(() => window.location.reload()); - return; - } - - scheduleUserDish(date, userId, userDishId).then(() => window.location.reload()); - } - - if (isScheduleLoading || areUserDishesLoading || !schedule) { - return - } - - const scheduleData = scheduleBuilder(schedule, users, userDishes) - - return
-
-
- Edit Day -
-
- { transformDate(schedule.date) } -
-
- -
- - { - userDishes.length === 0 &&
-
No dishes found assigned to this user.
-
Go ahead and add some first, or choose to skip the day.
-
(dishes ={`>`} edit ={`>`} add user)
-
- } - - { schedule.is_skipped - ? - : ( - <> - { - scheduleData - .map((scheduleData) =>
-
{ scheduleData.user.name }
-
- -
-
) - } - - ) - } - -
Changes are saved automatically
- -
- -
- -
{ - schedule.is_skipped - ? - : - }
-
-
-} - -export default ScheduleEditForm \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx b/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx deleted file mode 100644 index 3a27044..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleRegenerateButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState} from "react"; -import Modal from "@/components/ui/Modal"; -import ScheduleRegenerateForm from "@/components/features/schedule/ScheduleRegenerateForm"; -import {ArrowPathIcon} from "@heroicons/react/16/solid"; - -interface ScheduleRegenerateButtonProps { - onModalClose?: () => void; -} - -const ScheduleRegenerateButton = ({ onModalClose }: ScheduleRegenerateButtonProps) => { - const [open, setOpen] = useState(false); - - const handleCloseModal = () => { - setOpen(false) - if (onModalClose) { - onModalClose() - } - } - - const modalChildren = handleCloseModal()}/> - const buttonChild =
-
- - return -}; - -export default ScheduleRegenerateButton; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx b/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx deleted file mode 100644 index 3a9a4bf..0000000 --- a/frontend-old/app/components/features/schedule/ScheduleRegenerateForm.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {DialogTitle} from "@headlessui/react"; -import Toggle from "@/components/ui/Toggle"; -import {useEffect, useState} from "react"; -import {generateSchedule} from "@/utils/api/scheduleApi"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface ScheduleRegenerateFormProps { - closeModal: () => void; -} - -const ScheduleRegenerateForm = ({closeModal}: ScheduleRegenerateFormProps) => { - const [overwrite, setOverwrite] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - }, [overwrite]); - - const close = () => { - closeModal(); - } - - const handleToggle = () => { - setOverwrite(!overwrite) - } - - const handleSubmit = () => { - generateSchedule(overwrite) - .then(() => close()) - .catch((err) => setError(err)) - } - - return <> -
-
-
- - Regenerate Schedule - -
-
- { - error && { error } - } -
- -
-
- -
-
-
- - -
-
-
-
- handleSubmit()} - className="inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold shadow-xs sm:ml-3 sm:w-auto" - > - Regenerate - - close()} - className="mt-3 inline-flex w-full justify-center rounded-md bg-gray-500 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs border-secondary ring-inset sm:mt-0 sm:w-auto" - > - Cancel - -
- ; -}; - -export default ScheduleRegenerateForm; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/UpcomingDishes.tsx b/frontend-old/app/components/features/schedule/UpcomingDishes.tsx deleted file mode 100644 index 99f69d4..0000000 --- a/frontend-old/app/components/features/schedule/UpcomingDishes.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import { DateTime } from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import Spinner from "@/components/Spinner"; -import { ScheduleType } from "@/types/ScheduleType"; -import { listSchedule } from "@/utils/api/scheduleApi"; -import OnboardingBanner from "@/components/features/OnboardingBanner" -import { useFetchUsers } from "@/hooks/useFetchUsers" -import { useFetchDishes } from "@/hooks/useFetchDishes" -import ScheduleRegenerateButton from "@/components/features/schedule/ScheduleRegenerateButton"; - -const UpcomingDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const today = DateTime.now().toFormat("yyyy-LL-dd"); - - const fetchSchedule = useCallback(() => { - setIsLoading(true); - listSchedule(today) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)); - }, [today]); - - useEffect(() => { - fetchSchedule(); - }, [fetchSchedule]); - - const { users, isLoading: areUsersLoading } = useFetchUsers(); - const { dishes, isLoading: areDishesLoading } = useFetchDishes(); - - if (isLoading || areUsersLoading || areDishesLoading) { - return ; - } - - if (users.length === 0 || dishes.length === 0) { - return - } - - return ( -
-
-
- Schedule -
-
- -
-
- { - !schedule || Object.keys(schedule).length === 0 - ?
No dishes scheduled
- : - } -
- ); -}; - -export default UpcomingDishes; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/UserDishEditCard.tsx b/frontend-old/app/components/features/schedule/UserDishEditCard.tsx deleted file mode 100644 index 653efbd..0000000 --- a/frontend-old/app/components/features/schedule/UserDishEditCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {FormEvent, useMemo, useState} from "react"; -import {DishType} from "@/types/DishType"; -import {ScheduledUserDishType} from "@/types/ScheduledUserDishType"; -import {updateScheduledUserDish} from "@/utils/api/scheduledUserDishesApi"; -import Alert from "@/components/ui/Alert"; -import classNames from "classnames"; - -interface Props { - scheduledUserDish: ScheduledUserDishType - allDishes: DishType[] -} - -const UserDishEditCard = ({ scheduledUserDish, allDishes }: Props) => { - const [selectedUserDishId, setSelectedUserDishId] = useState(scheduledUserDish.user_dish ? scheduledUserDish.user_dish.id : 0) - const [errorMessage, setErrorMessage] = useState("") - const [isSuccess, setIsSuccess] = useState(false); - - const selectStyle = classNames( - 'p-2', 'rounded', 'w-full', 'background-secondary', - 'focus:outline-none', - 'transition-[border-color] ease-out duration-1000', 'border-2', // Keep consistent base styles - { - 'border-green-500': isSuccess, // Green border when successful - 'border-red-500': !isSuccess && errorMessage !== "", // Red border when there's an error - 'border-secondary': !isSuccess && errorMessage === "", // Default border for neutral state - } - ) - - const handleOnChange = (e: FormEvent) => { - const userDishId = parseInt(e.currentTarget.value); - setSelectedUserDishId(userDishId); - - updateScheduledUserDish(scheduledUserDish.id, userDishId) - .then(() => { - setIsSuccess(false); - setTimeout(() => { - setIsSuccess(true); - setTimeout(() => setIsSuccess(false), 1000); - }, 0); - }) - .catch((error) => { - setErrorMessage(error); // Log API errors - }); - }; - - const filteredDishes = useMemo(() => - allDishes.filter((dish: DishType) => - dish.users.some((user) => user.id === scheduledUserDish.user_dish.user.id) - ), - [allDishes, scheduledUserDish.user_dish.user.id] - ) - - return ( -
-
{scheduledUserDish.user_dish.user.name}
- - { errorMessage !== "" && { errorMessage } } - - - -
- ); -}; - -export default UserDishEditCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx b/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx deleted file mode 100644 index 1338f51..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/DateBadge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {DateTime} from "luxon"; -import React from "react"; -import classNames from "classnames"; - -interface Props { - date: string - className?: string; -} - -const DateBadge = ({ className, date }: Props) => { - const isToday = DateTime.fromISO(date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const textStyle = classNames("inline font-bold", { - 'text-accent-blue': isToday, - 'text-secondary': !isToday, - }, className) - - return ( -
-
{DateTime.fromISO(date).toFormat("dd")}
-
-
{DateTime.fromISO(date).toFormat("LLL")}
-
- ) -} - -export default DateBadge \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx b/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx deleted file mode 100644 index 8c6b874..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCard.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import {UserType} from "~/types/UserType"; -import ScheduleDayCardUserDish from "~/components/features/schedule/dayCard/ScheduleDayCardUserDish"; -import { FilledScheduleType, ScheduleType } from "~/types/ScheduleType"; -import { Link } from "react-router"; -import {PencilSquareIcon} from "@heroicons/react/24/outline"; -import useRoutes from "~/hooks/useRoutes"; -import DateBadge from "~/components/features/schedule/dayCard/DateBadge"; -import { DateTime } from "luxon" -import classNames from "classnames" - -interface Props { - schedule: ScheduleType|FilledScheduleType; - users: UserType[]; -} - -const ScheduleDayCard = ({schedule, users}: Props) => { - const routes = useRoutes() - const isToday = DateTime.fromISO(schedule.date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const containerStyles = classNames( - 'w-full bg-gray-500 pt-5 pb-2 rounded-2xl text-xl', { - 'border-2 text-accent-blue border-accent-blue': isToday, - } - ) - - return ( -
- - -
- { - users.map((user) => ) - } - -
- - Edit - -
-
-
- ); -}; - -export default ScheduleDayCard; \ No newline at end of file diff --git a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx b/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx deleted file mode 100644 index 35cf58d..0000000 --- a/frontend-old/app/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import { ScheduledUserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; - -interface Props { - schedule: ScheduleType|FilledScheduleType; - user: UserType; -} - -const ScheduleDayCardUserDish = ({ schedule, user }: Props) => { - const getDish = (user: UserType) => { - const scheduled_dishes = schedule.scheduled_user_dishes.filter((scheduled_user_dish: ScheduledUserDishType) => ( - scheduled_user_dish.user_dish?.user.id == user.id - )) - - if (scheduled_dishes.length > 0) { - return scheduled_dishes[0].user_dish.dish.name - } - - return '/' - } - - return ( -
-
{ user.name } :
-
{ getDish(user) }
-
- ); -}; - -export default ScheduleDayCardUserDish; \ No newline at end of file diff --git a/frontend-old/app/components/features/users/EditUserForm.tsx b/frontend-old/app/components/features/users/EditUserForm.tsx deleted file mode 100644 index e26cf58..0000000 --- a/frontend-old/app/components/features/users/EditUserForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {useState} from "react"; -import { useNavigate } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import {updateUser} from "~/utils/api/usersApi"; -import PageTitle from "~/components/ui/PageTitle"; -import { Link } from "react-router"; -import Alert from "~/components/ui/Alert"; -import {UserType} from "~/types/UserType"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; - -interface Props { - user: UserType; -} - -const EditUserForm = ({ user }: Props) => { - - const [name, setName] = useState(user.name); - const [error, setError] = useState(''); - const navigate = useNavigate(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - // validateName - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - updateUser(user, name) - .then(() => { - navigate(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Update -
-
- ); -} - -export default EditUserForm; \ No newline at end of file diff --git a/frontend-old/app/components/layout/AuthGuard.tsx b/frontend-old/app/components/layout/AuthGuard.tsx deleted file mode 100644 index 9f6cf29..0000000 --- a/frontend-old/app/components/layout/AuthGuard.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useAuth } from '@/context/AuthContext'; -import React, { useEffect } from 'react'; -import { useLocation, useNavigate } from "react-router" - -export default function AuthGuard({ children }: { children: React.ReactNode }) { - const { isAuthenticated } = useAuth(); - const navigate = useNavigate(); - const location = useLocation(); - - const publicRoutes = ['/login', '/register']; - const isPublic = publicRoutes.includes(location.pathname); - - useEffect(() => { - // Handle redirects based on auth state - if (isAuthenticated && isPublic) { - // Redirect authenticated users away from public pages - navigate('/', { replace: true }); - } else if (!isAuthenticated && !isPublic) { - // Redirect unauthenticated users trying to access protected pages - navigate('/login', { replace: true }); - } - }, [isAuthenticated, location.pathname, isPublic, navigate]); - - // Render children for all routes - redirects will happen via useEffect - return <>{children}; -} \ No newline at end of file diff --git a/frontend-old/app/components/layout/Card.tsx b/frontend-old/app/components/layout/Card.tsx deleted file mode 100644 index 4491cab..0000000 --- a/frontend-old/app/components/layout/Card.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; - -interface Props { - children: React.ReactNode; -} - -const Card = ({ children }: Props) => { - return ( -
- { children } -
- ) -} - -export default Card \ No newline at end of file diff --git a/frontend-old/app/components/layout/NavBar.tsx b/frontend-old/app/components/layout/NavBar.tsx deleted file mode 100644 index 05d71a9..0000000 --- a/frontend-old/app/components/layout/NavBar.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useState } from "react"; -import { Link, useNavigate } from "react-router"; -import useRoutes from "~/hooks/useRoutes"; -import MobileDropdownMenu from "~/components/features/navbar/MobileDropdownMenu"; -import { useAuth } from "~/context/AuthContext"; - -const NavBar = () => { - const [isOpen, setIsOpen] = useState(false); - const routes = useRoutes(); - const navigate = useNavigate(); - const {isAuthenticated, logout} = useAuth(); - - const handleLogout = (e: React.MouseEvent) => { - e.preventDefault(); - logout(); - navigate('/login', { replace: true }); - }; - - return ( - - ); -}; - -export default NavBar; \ No newline at end of file diff --git a/frontend-old/app/components/pages/PrivatePage.tsx b/frontend-old/app/components/pages/PrivatePage.tsx deleted file mode 100644 index 3b08c86..0000000 --- a/frontend-old/app/components/pages/PrivatePage.tsx +++ /dev/null @@ -1,9 +0,0 @@ -const PrivatePage = () => { - return ( -
- private -
- ) -} - -export default PrivatePage \ No newline at end of file diff --git a/frontend-old/app/components/pages/PublicPage.tsx b/frontend-old/app/components/pages/PublicPage.tsx deleted file mode 100644 index 8e0f5cb..0000000 --- a/frontend-old/app/components/pages/PublicPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Outlet } from "react-router" - -const PublicPage = () => { - return ( -
- -
- ) -} - -export default PublicPage \ No newline at end of file diff --git a/frontend-old/app/components/ui/Alert.tsx b/frontend-old/app/components/ui/Alert.tsx deleted file mode 100644 index 9f08022..0000000 --- a/frontend-old/app/components/ui/Alert.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react" -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - type: 'error' | 'warning' | 'info' | 'success'; -} - -const Alert = ({ children, className, type }: Props) => { - let bgColor = 'bg-blue-200' - let fgColor = 'bg-blue-800' - - if (type == 'error') { - bgColor = 'bg-red-200' - fgColor = 'bg-red-800' - } else if (type == 'warning') { - bgColor = 'bg-orange-200' - fgColor = 'bg-orange-800' - } else if (type == 'success') { - bgColor = 'border-2 border-green-500' - fgColor = 'text-green-500' - } - - const styles = classNames(fgColor, bgColor, className, 'rounded') - - return ( -
- { children} -
- ) -} - -export default Alert \ No newline at end of file diff --git a/frontend-old/app/components/ui/Button.tsx b/frontend-old/app/components/ui/Button.tsx deleted file mode 100644 index 756e6b5..0000000 --- a/frontend-old/app/components/ui/Button.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Link } from "react-router"; -import React from "react"; -import classNames from "classnames"; - -interface ButtonProps { - appearance?: 'solid' | 'outline' | 'text'; - children: React.ReactNode; - className?: string; - href?: string; - icon?: React.ReactNode; - onClick?: () => void; - disabled?: boolean; - size?: 'small' | 'medium' | 'large'; - type?: 'button' | 'submit' | 'reset'; - variant?: 'primary' | 'secondary' | 'accent'; -} - -const Button = ({ appearance, children, className, disabled, href, icon, onClick, - size = 'medium', type, - variant = 'primary' -}: ButtonProps) => { - const styles = classNames( - "flex items-center space-x-1", - "justify-center font-size-18 py-2 px-4 rounded flex", - { - 'border-2 border-primary background-red text-white': variant === 'primary' && appearance === 'solid', - 'border-2 border-primary text-primary': variant === 'primary' && appearance === 'outline', - 'text-primary': variant === 'primary' && appearance === 'text', - 'border-2 border-secondary text-secondary': variant === 'secondary' && appearance === 'outline', - 'border-2 border-accent-blue text-accent-blue': variant === 'accent' && appearance === 'outline', - }, - className - ) - - const iconClassNames = classNames({ - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - if (href !== undefined) { - return ( - - { icon && iconElement} - { children} - - ) - } - - return -} - -export default Button \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/OutlineButton.tsx b/frontend-old/app/components/ui/Buttons/OutlineButton.tsx deleted file mode 100644 index 5530482..0000000 --- a/frontend-old/app/components/ui/Buttons/OutlineButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const OutlineButton = ({ children, className, disabled = false, onClick, size, type }: Props) => { - const style = classNames( - "justify-center border-2 border-accent font-size-18 text-accent-blue py-2 px-4 rounded flex", - { 'text-xs': size === "small" }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default OutlineButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx b/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx deleted file mode 100644 index 6032bc3..0000000 --- a/frontend-old/app/components/ui/Buttons/OutlineLinkButton.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import { Link } from "react-router" - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const OutlineLinkButton = ({ children, className, href, icon, size = "medium", variant }: Props) => { - const linkClassNames = classNames( - "underline font-default pt-3 pb-3 px-4 rounded mb-0 flex", - { - 'text-primary border-primary': variant === "primary", - 'text-secondary border-secondary': variant === "secondary", - 'text-accent-blue border-accent': !variant || !["primary", "secondary"].includes(variant), - }, { - 'text-size-14': size === "small", - 'font-size-18': !size || size === "medium", - 'text-2xl': size === "large", - }, - className, - ) - - const iconClassNames = classNames("mt-0.5", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - - {iconElement} - {children} - - ) -} - -export default OutlineLinkButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/SolidButton.tsx b/frontend-old/app/components/ui/Buttons/SolidButton.tsx deleted file mode 100644 index dffc021..0000000 --- a/frontend-old/app/components/ui/Buttons/SolidButton.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const SolidButton = ({ children, className, disabled = false, onClick, size, type }: Props) => { - const style = classNames( - "py-2 px-4 bg-primary text-white text-xl p-2 rounded hover:bg-secondary mb-0", - { - 'text-xs': size === "small", - 'font-size-18': !size || size === "medium", - }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default SolidButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx b/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx deleted file mode 100644 index ce4929c..0000000 --- a/frontend-old/app/components/ui/Buttons/SolidLinkButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import { Link } from "react-router" - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const SolidLinkButton = ({ children, className, href, icon, size = "medium", variant }: Props) => { - const style = classNames( - "py-2 px-4 text-xl p-2 rounded hover:bg-secondary mb-0 text-center flex", - { - 'background-red text-white': variant === "primary", - 'background-secondary border-2 border-secondary': variant === "secondary", - }, - className - ) - - const iconClassNames = classNames("mt-1", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as React.ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - -
- {iconElement} - {children} -
- - ) -} - -export default SolidLinkButton \ No newline at end of file diff --git a/frontend-old/app/components/ui/Description.tsx b/frontend-old/app/components/ui/Description.tsx deleted file mode 100644 index 8b429c0..0000000 --- a/frontend-old/app/components/ui/Description.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -interface Props { - children: React.ReactNode; - className?: string; -} - -const Description = ({ children, className }: Props) => { - const style = classNames("italic font-size-16", - className - ) - - return

{ children }

-} - -export default Description \ No newline at end of file diff --git a/frontend-old/app/components/ui/Hr.tsx b/frontend-old/app/components/ui/Hr.tsx deleted file mode 100644 index f1eee60..0000000 --- a/frontend-old/app/components/ui/Hr.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import classNames from "classnames" - -interface HrProps { - className?: string; -} - -const Hr = ({ className }: HrProps) => { - const styles = classNames("my-4 border-secondary", className) - - return
-} - -export default Hr \ No newline at end of file diff --git a/frontend-old/app/components/ui/Label.tsx b/frontend-old/app/components/ui/Label.tsx deleted file mode 100644 index a62d4e7..0000000 --- a/frontend-old/app/components/ui/Label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; - -interface LabelProps { - href?: string; - children: React.ReactNode; - onClick?: () => void; -} - -const Label = ({ href, children, onClick }: LabelProps) => { - const styles = "items-center space-x-1 background-accent p-2 rounded" - - if (href !== undefined) { - return ( -
- { children} -
- ) - } - - return -} - -export default Label \ No newline at end of file diff --git a/frontend-old/app/components/ui/Modal.tsx b/frontend-old/app/components/ui/Modal.tsx deleted file mode 100644 index e60fdb5..0000000 --- a/frontend-old/app/components/ui/Modal.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { type JSX } from "react"; -import classNames from "classnames"; -import Button from "@/components/ui/Button" - -interface ModalProps { - buttonChildren?: JSX.Element; - buttonClassName?: string; - buttonLabel?: string; - modalChildren: JSX.Element; - modalOpen?: boolean; - setModalOpen: (open: boolean) => void; -} - -const Modal = ({ - buttonLabel, - buttonClassName, - modalChildren, - modalOpen, - buttonChildren, - setModalOpen, -}: ModalProps) => { - const buttonStyles = classNames(buttonClassName, 'anta-regular'); - - const closeModal = () => { - setModalOpen(false) - } - - return ( - <> - - - {/**/} - {/* */} - {/*
*/} - {/* */} - {/* */} - {/* closeModal()}/>*/} - {/* {modalChildren}*/} - {/* */} - {/*
*/} - {/* */} - {/*
*/} - - ) -} - -export default Modal; \ No newline at end of file diff --git a/frontend-old/app/components/ui/PageTitle.tsx b/frontend-old/app/components/ui/PageTitle.tsx deleted file mode 100644 index 569b2f3..0000000 --- a/frontend-old/app/components/ui/PageTitle.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string, - className?: string, -} - -const PageTitle = ({ children, className }: Props) => { - const styles = classNames( - 'ml-4 text-2xl font-default uppercase w-full text-accent-blue font-bold', - className, - ) - - return

{ children }

-} - -export default PageTitle \ No newline at end of file diff --git a/frontend-old/app/components/ui/RecurrenceInput.tsx b/frontend-old/app/components/ui/RecurrenceInput.tsx deleted file mode 100644 index 0d82560..0000000 --- a/frontend-old/app/components/ui/RecurrenceInput.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, {useState} from "react"; - -interface Props { - value: number; - setValue: (value: number) => void; -} - -const RecurrenceInput = ({ value, setValue}: Props) => { - const [openInput, setOpenInput] = useState<'category' | 'number'>([7, 365].includes(value) ? 'category' : 'number') - - const toggleInput = (e: React.MouseEvent) => { - e.preventDefault() - setOpenInput(openInput == 'category' ? 'number' : 'category') - } - - const toggleButton = () => { - return ( - - ) - } - - const prepareValue = (v: string) => { - setValue(parseInt(v)) - } - - return ( -
-
- - - { toggleButton() } -
- -
- - prepareValue(e.target.value)} - className="p-2 border rounded w-full bg-gray-500 border-secondary" - /> - { toggleButton() } -
-
- ) -} - -export default RecurrenceInput \ No newline at end of file diff --git a/frontend-old/app/components/ui/SectionTitle.tsx b/frontend-old/app/components/ui/SectionTitle.tsx deleted file mode 100644 index 93d13c4..0000000 --- a/frontend-old/app/components/ui/SectionTitle.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string; - className?: string; -} - -const SectionTitle = ({ children, className }: Props) => { - const style = classNames("block font-size-18 uppercase w-full pl-2 text-accent-blue", - className - ) - - return

{ children }

-} - -export default SectionTitle \ No newline at end of file diff --git a/frontend-old/app/components/ui/Toggle.tsx b/frontend-old/app/components/ui/Toggle.tsx deleted file mode 100644 index 0d6b9e9..0000000 --- a/frontend-old/app/components/ui/Toggle.tsx +++ /dev/null @@ -1,41 +0,0 @@ - -interface ToggleProps { - checked: boolean; - onChange: (checked: boolean) => void; -} - -const Toggle = ({ checked, onChange }: ToggleProps) => { - const handleChange = () => { - onChange(checked); - } - - return ( - - ); -}; - -export default Toggle; \ No newline at end of file diff --git a/frontend-old/app/context/AuthContext.tsx b/frontend-old/app/context/AuthContext.tsx deleted file mode 100644 index 1cd3db1..0000000 --- a/frontend-old/app/context/AuthContext.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { createContext, useContext, useEffect, useState } from 'react'; - -interface AuthContextProps { - isAuthenticated: boolean; - login: () => void; - logout: () => void; -} - -const AuthContext = createContext({ - isAuthenticated: false, - login: () => {}, - logout: () => {}, -}); - -export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - // Start with false during SSR, will be updated on client - const [isAuthenticated, setIsAuthenticated] = useState(false); - - useEffect(() => { - // Check token on client after mount - const token = localStorage.getItem('token'); - setIsAuthenticated(!!token); - }, []); - - const login = () => { - setIsAuthenticated(true); - }; - - const logout = () => { - setIsAuthenticated(false); - localStorage.removeItem('token'); - }; - - return ( - - {children} - - ); -}; - -export const useAuth = () => useContext(AuthContext); \ No newline at end of file diff --git a/frontend-old/app/helpers/Date.ts b/frontend-old/app/helpers/Date.ts deleted file mode 100644 index f183c86..0000000 --- a/frontend-old/app/helpers/Date.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DateTime } from 'luxon'; - -// Validate if a given string matches the "yyyy-MM-dd" format and is a valid date -export const isValidDate = (date: string): boolean => { - const parsedDate = DateTime.fromFormat(date, 'yyyy-MM-dd'); - return parsedDate.isValid && parsedDate.toFormat('yyyy-MM-dd') === date; -}; - -// Format a date to a specific string format -export const formatDate = (date: Date | string, format: string = 'yyyy-MM-dd'): string => { - const parsedDate = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date); - return parsedDate.toFormat(format); -}; - -// Compare two dates to see if one is before the other -export const isBefore = (date1: string, date2: string): boolean => { - return DateTime.fromISO(date1) < DateTime.fromISO(date2); -}; diff --git a/frontend-old/app/hooks/useFetchDishes.ts b/frontend-old/app/hooks/useFetchDishes.ts deleted file mode 100644 index a7ab747..0000000 --- a/frontend-old/app/hooks/useFetchDishes.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import { listDishes } from "@/utils/api/dishApi" -import { DishType } from "@/types/DishType" - -export const useFetchDishes = () => { - const [dishes, setDishes] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchDishes = async () => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchDishes(); - }, []); - - return { dishes, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/app/hooks/useFetchUsers.ts b/frontend-old/app/hooks/useFetchUsers.ts deleted file mode 100644 index f6df05b..0000000 --- a/frontend-old/app/hooks/useFetchUsers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import {UserType} from "@/types/UserType"; -import {listUsers} from "@/utils/api/usersApi"; - -export const useFetchUsers = () => { - const [users, setUsers] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchUsers = async () => { - listUsers() - .then((users: UserType[]) => setUsers(users)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchUsers(); - }, []); - - return { users, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/app/hooks/useRoutes.ts b/frontend-old/app/hooks/useRoutes.ts deleted file mode 100644 index 255a545..0000000 --- a/frontend-old/app/hooks/useRoutes.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { DishType } from "@/types/DishType"; -import type { UserType } from "@/types/UserType"; - -const useRoutes = () => { - return { - home: () => "/", - auth: { - login: () => "/login", - register: () => "/register", - }, - dish: { - index: () => "/dishes", - create: () => "/dishes/create", - edit: (dish: DishType) => `/dishes/${ dish.id }/edit`, - delete: (dish: DishType) => `/dishes/${ dish.id }/delete`, - }, - schedule: { - date: { - edit: (date: string) => `/schedule/${ date }/edit` - }, - history: () => "/scheduled-user-dishes/history", - }, - user: { - index: () => "/users", - create: () => `/users/create`, - edit: (user: UserType) => `/users/${ user.id }/edit`, - delete: (user: UserType) => `/users/${ user.id }/delete`, - } - }; -}; - -export default useRoutes; \ No newline at end of file diff --git a/frontend-old/app/root.tsx b/frontend-old/app/root.tsx deleted file mode 100644 index baa343e..0000000 --- a/frontend-old/app/root.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { - isRouteErrorResponse, - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, -} from "react-router"; - -import type { Route } from "./+types/root"; -import "./app.css"; -import React from "react" -import { AuthProvider } from "~/context/AuthContext" -import AuthGuard from "~/components/layout/AuthGuard" -import NavBar from "~/components/layout/NavBar" - -export const links: Route.LinksFunction = () => [ - { rel: "preconnect", href: "https://fonts.googleapis.com" }, - { - rel: "preconnect", - href: "https://fonts.gstatic.com", - crossOrigin: "anonymous", - }, - { - rel: "stylesheet", - href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", - }, -]; - -export function Layout({ children }: { children: React.ReactNode }) { - return ( - - - - - - - - - - - -
{ children }
-
-
- - - - - ); -} - -export default function App() { - return ; -} - -export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { - let message = "Oops!"; - let details = "An unexpected error occurred."; - let stack: string | undefined; - - if (isRouteErrorResponse(error)) { - message = error.status === 404 ? "404" : "Error"; - details = - error.status === 404 - ? "The requested page could not be found." - : error.statusText || details; - } else if (import.meta.env.DEV && error && error instanceof Error) { - details = error.message; - stack = error.stack; - } - - return ( -
-

{ message }

-

{ details }

- { stack && ( -
-          { stack }
-        
- ) } -
- ); -} diff --git a/frontend-old/app/routes.ts b/frontend-old/app/routes.ts deleted file mode 100644 index 8a4afe8..0000000 --- a/frontend-old/app/routes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { type RouteConfig, index, route } from "@react-router/dev/routes"; - -export default [ - index("routes/home.tsx"), - route("login", "./components/features/auth/LoginForm.tsx"), - route("register", "./components/features/auth/RegisterForm.tsx"), - - // Dishes routes - route("dishes", "routes/dishes.tsx"), - route("dishes/create", "routes/dishes.create.tsx"), - route("dishes/:id/edit", "routes/dishes.$id.edit.tsx"), - - // Users routes - route("users", "routes/users.tsx"), - route("users/create", "routes/users.create.tsx"), - route("users/:id/edit", "routes/users.$id.edit.tsx"), - - // Schedule routes - route("schedule/:date/edit", "routes/schedule.$date.edit.tsx"), - - // History route - route("scheduled-user-dishes/history", "routes/scheduled-user-dishes.history.tsx"), -] satisfies RouteConfig; diff --git a/frontend-old/app/routes/dishes.$id.edit.tsx b/frontend-old/app/routes/dishes.$id.edit.tsx deleted file mode 100644 index a8a3fa0..0000000 --- a/frontend-old/app/routes/dishes.$id.edit.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import type { Route } from "./+types/dishes.$id.edit"; -import { useCallback, useEffect, useState } from "react"; -import PageTitle from "~/components/ui/PageTitle"; -import EditDishForm from "~/components/features/dishes/EditDishForm"; -import { DishType } from "~/types/DishType"; -import Spinner from "~/components/Spinner"; -import { fetchDish } from "~/utils/api/dishApi"; -import SyncUsersForm from "~/components/features/dishes/SyncUsersForm"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import useRoutes from "~/hooks/useRoutes"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; -import Hr from "~/components/ui/Hr"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit Dish" }, - { name: "description", content: "Edit dish details" }, - ]; -} - -export default function EditDishPage() { - const params = useParams(); - const id = Number(params.id); - const [dish, setDish] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const routes = useRoutes(); - - const loadDish = useCallback(async () => { - try { - const fetchedDish = await fetchDish(id); - setDish(fetchedDish); - } catch (error) { - console.error("Error fetching dish:", error); - throw new Error("No token found in localStorage."); - } finally { - setIsLoading(false); - } - }, [id]); - - useEffect(() => { - loadDish(); - }, [loadDish]); - - if (isLoading || dish === null) { - return ; - } - - return ( -
-
- Edit Dish - - -

BACK

-
-
- - - -
- - -
- ); -} diff --git a/frontend-old/app/routes/dishes.create.tsx b/frontend-old/app/routes/dishes.create.tsx deleted file mode 100644 index a43e88d..0000000 --- a/frontend-old/app/routes/dishes.create.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/dishes.create"; -import CreateDishForm from "~/components/features/dishes/CreateDishForm"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Create Dish" }, - { name: "description", content: "Create a new dish" }, - ]; -} - -export default function CreateDishPage() { - return ; -} diff --git a/frontend-old/app/routes/dishes.tsx b/frontend-old/app/routes/dishes.tsx deleted file mode 100644 index 7bed7de..0000000 --- a/frontend-old/app/routes/dishes.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import type { Route } from "./+types/dishes"; -import PageTitle from "~/components/ui/PageTitle"; -import { DishType } from "~/types/DishType"; -import Dish from "~/components/features/dishes/Dish"; -import { PlusIcon } from "@heroicons/react/24/solid"; -import { useEffect, useState } from "react"; -import useRoutes from "~/hooks/useRoutes"; -import { listDishes } from "~/utils/api/dishApi"; -import Button from "~/components/ui/Button"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Dishes" }, - { name: "description", content: "Manage your dishes" }, - ]; -} - -export default function DishesIndexPage() { - const routes = useRoutes(); - - const [dishes, setDishes] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .finally(() => setLoading(false)); - }, []); - - if (loading) return

Loading...

; - - if (!dishes) { - return

Loading...

; - } - - return ( - <> -
-
- Dishes -
-
- -
-
- - {dishes.length === 0 ? ( -

No dishes found :(

- ) : ( - dishes.map((dish: DishType, index: number) => ( - - )) - )} - - ); -} diff --git a/frontend-old/app/routes/home.tsx b/frontend-old/app/routes/home.tsx deleted file mode 100644 index a1c6ee6..0000000 --- a/frontend-old/app/routes/home.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/home"; -import UpcomingDishes from "~/components/features/schedule/UpcomingDishes"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Schedule" }, - { name: "description", content: "View and manage your upcoming dish schedule" }, - ]; -} - -export default function Home() { - return ; -} diff --git a/frontend-old/app/routes/schedule.$date.edit.tsx b/frontend-old/app/routes/schedule.$date.edit.tsx deleted file mode 100644 index 4122f13..0000000 --- a/frontend-old/app/routes/schedule.$date.edit.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Route } from "./+types/schedule.$date.edit"; -import ScheduleEditForm from "~/components/features/schedule/ScheduleEditForm"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit Schedule" }, - { name: "description", content: "Edit schedule for a specific date" }, - ]; -} - -const ScheduleEditPage = () => { - const params = useParams(); - const date = params.date as string; - - return ; -}; - -export default ScheduleEditPage; diff --git a/frontend-old/app/routes/scheduled-user-dishes.history.tsx b/frontend-old/app/routes/scheduled-user-dishes.history.tsx deleted file mode 100644 index 591f015..0000000 --- a/frontend-old/app/routes/scheduled-user-dishes.history.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/scheduled-user-dishes.history"; -import HistoricalDishes from "~/components/features/schedule/HistoricalDishes"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - History" }, - { name: "description", content: "View historical dishes" }, - ]; -} - -export default function HistoryPage() { - return ; -} diff --git a/frontend-old/app/routes/users.$id.edit.tsx b/frontend-old/app/routes/users.$id.edit.tsx deleted file mode 100644 index 2b784ed..0000000 --- a/frontend-old/app/routes/users.$id.edit.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { Route } from "./+types/users.$id.edit"; -import { useEffect, useState } from "react"; -import { UserType } from "~/types/UserType"; -import { showUser } from "~/utils/api/usersApi"; -import Spinner from "~/components/Spinner"; -import EditUserForm from "~/components/features/users/EditUserForm"; -import { useParams } from "react-router"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Edit User" }, - { name: "description", content: "Edit user details" }, - ]; -} - -const UpdateUsersPage = () => { - const params = useParams(); - const id = Number(params.id); - const [user, setUser] = useState(null); - - useEffect(() => { - showUser(id).then((user: UserType) => setUser(user)); - }, [id]); - - if (!user) { - return ; - } - - return ; -}; - -export default UpdateUsersPage; diff --git a/frontend-old/app/routes/users.create.tsx b/frontend-old/app/routes/users.create.tsx deleted file mode 100644 index 34feb54..0000000 --- a/frontend-old/app/routes/users.create.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import type { Route } from "./+types/users.create"; -import PageTitle from "~/components/ui/PageTitle"; -import useRoutes from "~/hooks/useRoutes"; -import { useNavigate } from "react-router"; -import { useState } from "react"; -import Alert from "~/components/ui/Alert"; -import { createUser } from "~/utils/api/usersApi"; -import { Link } from "react-router"; -import SolidButton from "~/components/ui/Buttons/SolidButton"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Create User" }, - { name: "description", content: "Create a new user" }, - ]; -} - -const CreateUsersPage = () => { - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const navigate = useNavigate(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!name.trim()) { - setError("Name cannot be empty."); - return; - } - - createUser(name).then(() => { - navigate(routes.user.index()); - }); - }; - - return ( -
- Create User - - Back to users - - -
- {error != "" && ( - - {error} - - )} - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - - Create - -
-
- ); -}; - -export default CreateUsersPage; diff --git a/frontend-old/app/routes/users.tsx b/frontend-old/app/routes/users.tsx deleted file mode 100644 index a8a6039..0000000 --- a/frontend-old/app/routes/users.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { Route } from "./+types/users"; -import PageTitle from "~/components/ui/PageTitle"; -import { useFetchUsers } from "~/hooks/useFetchUsers"; -import Spinner from "~/components/Spinner"; -import useRoutes from "~/hooks/useRoutes"; -import { Link } from "react-router"; -import { PencilIcon, PlusIcon, TrashIcon } from "@heroicons/react/24/solid"; -import React from "react"; -import { deleteUser } from "~/utils/api/usersApi"; -import { UserType } from "~/types/UserType"; -import Card from "~/components/layout/Card"; -import OutlineLinkButton from "~/components/ui/Buttons/OutlineLinkButton"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "Dish Planner - Users" }, - { name: "description", content: "Manage your users" }, - ]; -} - -const UsersPage = () => { - const { users, isLoading } = useFetchUsers(); - const routes = useRoutes(); - - const handleDelete = (user: UserType) => { - deleteUser(user).then(() => window.location.reload()); - }; - - if (isLoading) { - return ; - } - - const usersList = () => { - return users.map((user) => ( - -
{user.name}
-
-
- -
- -
- -
-
- handleDelete(user)}> -
- -
- -
-
-
- )); - }; - - return ( -
-
-
- Users -
- -
- - -

Add User

-
-
-
- - {users && users.length > 0 ? usersList() :
No users
} -
- ); -}; - -export default UsersPage; diff --git a/frontend-old/app/styles/base/globals.css b/frontend-old/app/styles/base/globals.css deleted file mode 100644 index 918cbfb..0000000 --- a/frontend-old/app/styles/base/globals.css +++ /dev/null @@ -1,19 +0,0 @@ -html, body { - margin: 0; - padding: 0; - width: 100%; - overflow-x: hidden; -} - -body { - font-family: Arial, Helvetica, sans-serif; -} - - -.toggle-input:checked { - background-color: #22c55e; /* bg-green-500 */ -} - -.toggle-input:checked ~ span:last-child { - --tw-translate-x: 1.75rem; /* translate-x-7 */ -} \ No newline at end of file diff --git a/frontend-old/app/styles/components/buttons.css b/frontend-old/app/styles/components/buttons.css deleted file mode 100644 index 07fc4de..0000000 --- a/frontend-old/app/styles/components/buttons.css +++ /dev/null @@ -1,42 +0,0 @@ -.button-primary-solid { - background-color: var(--color-primary); - color: var(--color-secondary-200); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} -.button-primary-outline { - background-color: var(--color-background); - color: var(--color-primary); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} - -.button-secondary-solid { - background-color: var(--color-secondary); - color: var(--color-primary); - border: 1px solid var(--color-secondary); -} - -.button-accent-solid { - background-color: var(--color-accent-blue); - color: var(--color-secondary-900); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline { - background-color: var(--color-background); - color: var(--color-accent-blue); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline:hover { - background-color: var(--color-background-400); -} \ No newline at end of file diff --git a/frontend-old/app/styles/components/select.css b/frontend-old/app/styles/components/select.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend-old/app/styles/main.css b/frontend-old/app/styles/main.css deleted file mode 100644 index 83d03cc..0000000 --- a/frontend-old/app/styles/main.css +++ /dev/null @@ -1,6 +0,0 @@ -@import "./theme/borders.css"; -@import "./theme/fonts.css"; -@import "./components/buttons.css"; - -@import "./base/globals.css"; -@import "./theme/colors.css"; diff --git a/frontend-old/app/styles/theme/borders.css b/frontend-old/app/styles/theme/borders.css deleted file mode 100644 index b4ed5db..0000000 --- a/frontend-old/app/styles/theme/borders.css +++ /dev/null @@ -1,14 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} -.border-accent-800 { - border-color: var(--color-accent-blue-800); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors.css b/frontend-old/app/styles/theme/colors.css deleted file mode 100644 index f283897..0000000 --- a/frontend-old/app/styles/theme/colors.css +++ /dev/null @@ -1,10 +0,0 @@ -@import './colors/root.css'; -@import './colors/background.css'; -@import './colors/border.css'; -@import './colors/text.css'; - -body { - color: var(--color-secondary) !important; - background: var(--color-gray-600) !important; -} - diff --git a/frontend-old/app/styles/theme/colors/background.css b/frontend-old/app/styles/theme/colors/background.css deleted file mode 100644 index e8548f5..0000000 --- a/frontend-old/app/styles/theme/colors/background.css +++ /dev/null @@ -1,226 +0,0 @@ -.bg-gray-100 { - background-color: var(--color-gray-100) !important; -} -.bg-gray-200 { - background-color: var(--color-gray-200) !important; -} -.bg-gray-300 { - background-color: var(--color-gray-300) !important; -} -.bg-gray-400 { - background-color: var(--color-gray-400) !important; -} -.bg-gray-500 { - background-color: var(--color-gray-500) !important; -} -.bg-gray-600 { - background-color: var(--color-gray-600) !important; -} -.bg-gray-700 { - background-color: var(--color-gray-700) !important; -} -.bg-gray-800 { - background-color: var(--color-gray-800) !important; -} -.bg-gray-900 { - background-color: var(--color-gray-900) !important; -} - - -.bg-primary { - background-color: var(--color-primary) !important; -} - - -.bg-accent-blue { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-100 { - background-color: var(--color-accent-blue-100) !important; -} -.bg-accent-blue-200 { - background-color: var(--color-accent-blue-200) !important; -} -.bg-accent-blue-300 { - background-color: var(--color-accent-blue-300) !important; -} -.bg-accent-blue-400 { - background-color: var(--color-accent-blue-400) !important; -} -.bg-accent-blue-500 { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-600 { - background-color: var(--color-accent-blue-600) !important; -} -.bg-accent-blue-700 { - background-color: var(--color-accent-blue-700) !important; -} -.bg-accent-blue-800 { - background-color: var(--color-accent-blue-800) !important; -} -.bg-accent-blue-900 { - background-color: var(--color-accent-blue-900) !important; -} - - -.bg-accent-yellow { - background-color: var(--color-accent-yellow) !important; -} - -.bg-accent-yellow-100 { - background-color: var(--color-accent-yellow-100) !important; -} - -.bg-accent-yellow-200 { - background-color: var(--color-accent-yellow-200) !important; -} - -.bg-accent-yellow-300 { - background-color: var(--color-accent-yellow-300) !important; -} - -.bg-accent-yellow-400 { - background-color: var(--color-accent-yellow-400) !important; -} - -.bg-accent-yellow-500 { - background-color: var(--color-accent-yellow-500) !important; -} - -.bg-accent-yellow-600 { - background-color: var(--color-accent-yellow-600) !important; -} - -.bg-accent-yellow-700 { - background-color: var(--color-accent-yellow-700) !important; -} - -.bg-accent-yellow-800 { - background-color: var(--color-accent-yellow-800) !important; -} - -.bg-accent-yellow-900 { - background-color: var(--color-accent-yellow-900) !important; -} - - -.bg-success { - background-color: var(--color-success) !important; -} - -.bg-success-100 { - background-color: var(--color-success-100) !important; -} - -.bg-success-200 { - background-color: var(--color-success-200) !important; -} - -.bg-success-300 { - background-color: var(--color-success-300) !important; -} - -.bg-success-400 { - background-color: var(--color-success-400) !important; -} - -.bg-success-500 { - background-color: var(--color-success-500) !important; -} - -.bg-success-600 { - background-color: var(--color-success-600) !important; -} - -.bg-success-700 { - background-color: var(--color-success-700) !important; -} - -.bg-success-800 { - background-color: var(--color-success-800) !important; -} - -.bg-success-900 { - background-color: var(--color-success-900) !important; -} - -.bg-warning { - background-color: var(--color-warning) !important; -} - -.bg-warning-100 { - background-color: var(--color-warning-100) !important; -} - -.bg-warning-200 { - background-color: var(--color-warning-200) !important; -} - -.bg-warning-300 { - background-color: var(--color-warning-300) !important; -} - -.bg-warning-400 { - background-color: var(--color-warning-400) !important; -} - -.bg-warning-500 { - background-color: var(--color-warning-500) !important; -} - -.bg-warning-600 { - background-color: var(--color-warning-600) !important; -} - -.bg-warning-700 { - background-color: var(--color-warning-700) !important; -} - -.bg-warning-800 { - background-color: var(--color-warning-800) !important; -} - -.bg-warning-900 { - background-color: var(--color-warning-900) !important; -} - -.bg-danger { - background-color: var(--color-danger) !important; -} - -.bg-danger-100 { - background-color: var(--color-danger-100) !important; -} - -.bg-danger-200 { - background-color: var(--color-danger-200) !important; -} - -.bg-danger-300 { - background-color: var(--color-danger-300) !important; -} - -.bg-danger-400 { - background-color: var(--color-danger-400) !important; -} - -.bg-danger-500 { - background-color: var(--color-danger-500) !important; -} - -.bg-danger-600 { - background-color: var(--color-danger-600) !important; -} - -.bg-danger-700 { - background-color: var(--color-danger-700) !important; -} - -.bg-danger-800 { - background-color: var(--color-danger-800) !important; -} - -.bg-danger-900 { - background-color: var(--color-danger-900) !important; -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors/border.css b/frontend-old/app/styles/theme/colors/border.css deleted file mode 100644 index 8975296..0000000 --- a/frontend-old/app/styles/theme/colors/border.css +++ /dev/null @@ -1,286 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-primary-100 { - border-color: var(--color-primary-100); -} - -.border-primary-200 { - border-color: var(--color-primary-200); -} - -.border-primary-300 { - border-color: var(--color-primary-300); -} - -.border-primary-400 { - border-color: var(--color-primary-400); -} - -.border-primary-500 { - border-color: var(--color-primary-500); -} - -.border-primary-600 { - border-color: var(--color-primary-600); -} - -.border-primary-700 { - border-color: var(--color-primary-700); -} - -.border-primary-800 { - border-color: var(--color-primary-800); -} - -.border-primary-900 { - border-color: var(--color-primary-900); -} - - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-secondary-100 { - border-color: var(--color-secondary-100); -} - -.border-secondary-200 { - border-color: var(--color-secondary-200); -} - -.border-secondary-300 { - border-color: var(--color-secondary-300); -} - -.border-secondary-400 { - border-color: var(--color-secondary-400); -} - -.border-secondary-500 { - border-color: var(--color-secondary-500); -} - -.border-secondary-600 { - border-color: var(--color-secondary-600); -} - -.border-secondary-700 { - border-color: var(--color-secondary-700); -} - -.border-secondary-800 { - border-color: var(--color-secondary-800); -} - -.border-secondary-900 { - border-color: var(--color-secondary-900); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} - -.border-accent-blue-100 { - border-color: var(--color-accent-blue-100); -} - -.border-accent-blue-200 { - border-color: var(--color-accent-blue-200); -} - -.border-accent-blue-300 { - border-color: var(--color-accent-blue-300); -} - -.border-accent-blue-400 { - border-color: var(--color-accent-blue-400); -} - -.border-accent-blue-500 { - border-color: var(--color-accent-blue-500); -} - -.border-accent-blue-600 { - border-color: var(--color-accent-blue-600); -} - -.border-accent-blue-700 { - border-color: var(--color-accent-blue-700); -} - -.border-accent-blue-800 { - border-color: var(--color-accent-blue-800); -} - -.border-accent-blue-900 { - border-color: var(--color-accent-blue-900); -} - - -.border-accent-yellow { - border-color: var(--color-accent-yellow); -} - -.border-accent-yellow-100 { - border-color: var(--color-accent-yellow-100); -} - -.border-accent-yellow-200 { - border-color: var(--color-accent-yellow-200); -} - -.border-accent-yellow-300 { - border-color: var(--color-accent-yellow-300); -} - -.border-accent-yellow-400 { - border-color: var(--color-accent-yellow-400); -} - -.border-accent-yellow-500 { - border-color: var(--color-accent-yellow-500); -} - -.border-accent-yellow-600 { - border-color: var(--color-accent-yellow-600); -} - -.border-accent-yellow-700 { - border-color: var(--color-accent-yellow-700); -} - -.border-accent-yellow-800 { - border-color: var(--color-accent-yellow-800); -} - -.border-accent-yellow-900 { - border-color: var(--color-accent-yellow-900); -} - - -.border-background { - border-color: var(--color-background) !important; -} - -.border-danger { - border-color: var(--color-danger); -} - -.border-danger-100 { - border-color: var(--color-danger-100); -} - -.border-danger-200 { - border-color: var(--color-danger-200); -} - -.border-danger-300 { - border-color: var(--color-danger-300); -} - -.border-danger-400 { - border-color: var(--color-danger-400); -} - -.border-danger-500 { - border-color: var(--color-danger-500); -} - -.border-danger-600 { - border-color: var(--color-danger-600); -} - -.border-danger-700 { - border-color: var(--color-danger-700); -} - -.border-danger-800 { - border-color: var(--color-danger-800); -} - -.border-danger-900 { - border-color: var(--color-danger-900); -} - -.border-success { - border-color: var(--color-success); -} - -.border-success-100 { - border-color: var(--color-success-100); -} - -.border-success-200 { - border-color: var(--color-success-200); -} - -.border-success-300 { - border-color: var(--color-success-300); -} - -.border-success-400 { - border-color: var(--color-success-400); -} - -.border-success-500 { - border-color: var(--color-success-500); -} - -.border-success-600 { - border-color: var(--color-success-600); -} - -.border-success-700 { - border-color: var(--color-success-700); -} - -.border-success-800 { - border-color: var(--color-success-800); -} - -.border-success-900 { - border-color: var(--color-success-900); -} - -.border-warning { - border-color: var(--color-warning); -} - -.border-warning-100 { - border-color: var(--color-warning-100); -} - -.border-warning-200 { - border-color: var(--color-warning-200); -} - -.border-warning-300 { - border-color: var(--color-warning-300); -} - -.border-warning-400 { - border-color: var(--color-warning-400); -} - -.border-warning-500 { - border-color: var(--color-warning-500); -} - -.border-warning-600 { - border-color: var(--color-warning-600); -} - -.border-warning-700 { - border-color: var(--color-warning-700); -} - -.border-warning-800 { - border-color: var(--color-warning-800); -} - -.border-warning-900 { - border-color: var(--color-warning-900); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/colors/root.css b/frontend-old/app/styles/theme/colors/root.css deleted file mode 100644 index acffac0..0000000 --- a/frontend-old/app/styles/theme/colors/root.css +++ /dev/null @@ -1,193 +0,0 @@ -:root { - --color-rose-50: #FFF5FC; - --color-rose-100: #FCE6F5; - --color-rose-200: #FAC3E7; - --color-rose-300: #F7A1D5; - --color-rose-400: #F25EAB; - --color-rose-500: #ED1F79; - --color-rose-600: #D61A68; - --color-rose-700: #B3124F; - --color-rose-800: #8F0B39; - --color-rose-900: #6B0626; - --color-rose-950: #450315; - - --color-deluge-50: #FAF7FC; - --color-deluge-100: #F2EDF7; - --color-deluge-200: #E2DAF0; - --color-deluge-300: #CEC3E6; - --color-deluge-400: #A49BD1; - --color-deluge-500: #7776BC; - --color-deluge-600: #6361AB; - --color-deluge-700: #43428C; - --color-deluge-800: #2C2B70; - --color-deluge-900: #191854; - --color-deluge-950: #0A0A36; - - --color-malibu-50: #FAFEFF; - --color-malibu-100: #F5FDFF; - --color-malibu-200: #E1F6FC; - --color-malibu-300: #CDEDFA; - --color-malibu-400: #ABDEF7; - --color-malibu-500: #85C7F2; - --color-malibu-600: #6EACDB; - --color-malibu-700: #4A81B5; - --color-malibu-800: #305F91; - --color-malibu-900: #1B3F6E; - --color-malibu-950: #0B2247; - - --color-gamboge-50: #FFFDF2; - --color-gamboge-100: #FCF7E3; - --color-gamboge-200: #FAECBB; - --color-gamboge-300: #F5DC93; - --color-gamboge-400: #EDBB47; - --color-gamboge-500: #E59500; - --color-gamboge-600: #CF7F00; - --color-gamboge-700: #AB6100; - --color-gamboge-800: #8A4700; - --color-gamboge-900: #663000; - --color-gamboge-950: #421C00; - - --color-ebony-clay-100: #9AA2B3; /* Soft slate */ - --color-ebony-clay-200: #7A8093; /* Balanced midtone */ - --color-ebony-clay-300: #5D637A; /* Former 400 */ - --color-ebony-clay-400: #444760; /* New shadowed steel */ - --color-ebony-clay-500: #2B2C41; - --color-ebony-clay-600: #24263C; /* Adjusted — less jumpy */ - --color-ebony-clay-700: #1D1E36; /* Interpolated midpoint */ - --color-ebony-clay-800: #131427; /* Slightly lifted from old 800 */ - --color-ebony-clay-900: #0A0B1C; - --color-ebony-clay-950: #030412; - - --color-alizarin-crimson-50: #FFF5FA; - --color-alizarin-crimson-100: #FCE6F1; - --color-alizarin-crimson-200: #FAC3DC; - --color-alizarin-crimson-300: #F59FC0; - --color-alizarin-crimson-400: #F05D82; - --color-alizarin-crimson-500: #E71D36; - --color-alizarin-crimson-600: #D1192F; - --color-alizarin-crimson-700: #AD1121; - --color-alizarin-crimson-800: #8C0B18; - --color-alizarin-crimson-900: #69060E; - --color-alizarin-crimson-950: #420308; - - --color-spring-green-50: #F5FFFC; - --color-spring-green-100: #E8FFF9; - --color-spring-green-200: #C7FFEE; - --color-spring-green-300: #A4FCDF; - --color-spring-green-400: #62FCBC; - --color-spring-green-500: #21FA90; - --color-spring-green-600: #1BE07A; - --color-spring-green-700: #13BA5E; - --color-spring-green-800: #0C9646; - --color-spring-green-900: #07702D; - --color-spring-green-950: #03471A; - - --color-burning-orange-50: #FFFBF5; - --color-burning-orange-100: #FFF7EB; - --color-burning-orange-200: #FFE8CC; - --color-burning-orange-300: #FFD5AD; - --color-burning-orange-400: #FFA973; - --color-burning-orange-500: #FF6B35; - --color-burning-orange-600: #E65A2C; - --color-burning-orange-700: #BF441F; - --color-burning-orange-800: #993114; - --color-burning-orange-900: #731F0A; - --color-burning-orange-950: #4A1004; - - /* Standard naming */ - - --color-primary: var(--color-rose-500); - --color-primary-100: var(--color-rose-100); - --color-primary-200: var(--color-rose-200); - --color-primary-300: var(--color-rose-300); - --color-primary-400: var(--color-rose-400); - --color-primary-500: var(--color-rose-500); - --color-primary-600: var(--color-rose-600); - --color-primary-700: var(--color-rose-700); - --color-primary-800: var(--color-rose-800); - --color-primary-900: var(--color-rose-900); - - --color-secondary: var(--color-deluge-500); - --color-secondary-100: var(--color-deluge-100); - --color-secondary-200: var(--color-deluge-200); - --color-secondary-300: var(--color-deluge-300); - --color-secondary-400: var(--color-deluge-400); - --color-secondary-500: var(--color-deluge-500); - --color-secondary-600: var(--color-deluge-600); - --color-secondary-700: var(--color-deluge-700); - --color-secondary-800: var(--color-deluge-800); - --color-secondary-900: var(--color-deluge-900); - - --color-accent-blue: var(--color-malibu-500); - --color-accent-blue-100: var(--color-malibu-100); - --color-accent-blue-200: var(--color-malibu-200); - --color-accent-blue-300: var(--color-malibu-300); - --color-accent-blue-400: var(--color-malibu-400); - --color-accent-blue-500: var(--color-malibu-500); - --color-accent-blue-600: var(--color-malibu-600); - --color-accent-blue-700: var(--color-malibu-700); - --color-accent-blue-800: var(--color-malibu-800); - --color-accent-blue-900: var(--color-malibu-900); - - --color-accent-yellow: var(--color-gamboge-500); - --color-accent-yellow-50: var(--color-gamboge-50); - --color-accent-yellow-100: var(--color-gamboge-100); - --color-accent-yellow-200: var(--color-gamboge-200); - --color-accent-yellow-300: var(--color-gamboge-300); - --color-accent-yellow-400: var(--color-gamboge-400); - --color-accent-yellow-500: var(--color-gamboge-500); - --color-accent-yellow-600: var(--color-gamboge-600); - --color-accent-yellow-700: var(--color-gamboge-700); - --color-accent-yellow-800: var(--color-gamboge-800); - --color-accent-yellow-900: var(--color-gamboge-900); - --color-accent-yellow-950: var(--color-gamboge-950); - - --color-gray-100: var(--color-ebony-clay-100); - --color-gray-200: var(--color-ebony-clay-200); - --color-gray-300: var(--color-ebony-clay-300); - --color-gray-400: var(--color-ebony-clay-400); - --color-gray-500: var(--color-ebony-clay-500); - --color-gray-600: var(--color-ebony-clay-600); - --color-gray-700: var(--color-ebony-clay-700); - --color-gray-800: var(--color-ebony-clay-800); - --color-gray-900: var(--color-ebony-clay-900); - - --color-danger: var(--color-alizarin-crimson-500); - --color-danger-50: var(--color-alizarin-crimson-50); - --color-danger-100: var(--color-alizarin-crimson-100); - --color-danger-200: var(--color-alizarin-crimson-200); - --color-danger-300: var(--color-alizarin-crimson-300); - --color-danger-400: var(--color-alizarin-crimson-400); - --color-danger-500: var(--color-alizarin-crimson-500); - --color-danger-600: var(--color-alizarin-crimson-600); - --color-danger-700: var(--color-alizarin-crimson-700); - --color-danger-800: var(--color-alizarin-crimson-800); - --color-danger-900: var(--color-alizarin-crimson-900); - --color-danger-950: var(--color-alizarin-crimson-950); - - --color-success: var(--color-spring-green-500); - --color-success-50: var(--color-spring-green-50); - --color-success-100: var(--color-spring-green-100); - --color-success-200: var(--color-spring-green-200); - --color-success-300: var(--color-spring-green-300); - --color-success-400: var(--color-spring-green-400); - --color-success-500: var(--color-spring-green-500); - --color-success-600: var(--color-spring-green-600); - --color-success-700: var(--color-spring-green-700); - --color-success-800: var(--color-spring-green-800); - --color-success-900: var(--color-spring-green-900); - --color-success-950: var(--color-spring-green-950); - - --color-warning: var(--color-burning-orange-500); - --color-warning-50: var(--color-burning-orange-50); - --color-warning-100: var(--color-burning-orange-100); - --color-warning-200: var(--color-burning-orange-200); - --color-warning-300: var(--color-burning-orange-300); - --color-warning-400: var(--color-burning-orange-400); - --color-warning-500: var(--color-burning-orange-500); - --color-warning-600: var(--color-burning-orange-600); - --color-warning-700: var(--color-burning-orange-700); - --color-warning-800: var(--color-burning-orange-800); - --color-warning-900: var(--color-burning-orange-900); - --color-warning-950: var(--color-burning-orange-950); -} diff --git a/frontend-old/app/styles/theme/colors/text.css b/frontend-old/app/styles/theme/colors/text.css deleted file mode 100644 index ca38334..0000000 --- a/frontend-old/app/styles/theme/colors/text.css +++ /dev/null @@ -1,216 +0,0 @@ -.text-primary { - color: var(--color-primary); -} -.text-primary-100 { - color: var(--color-primary-100); -} -.text-primary-200 { - color: var(--color-primary-200); -} -.text-primary-300 { - color: var(--color-primary-300); -} -.text-primary-400 { - color: var(--color-primary-400); -} -.text-primary-500 { - color: var(--color-primary-500); -} -.text-primary-600 { - color: var(--color-primary-600); -} -.text-primary-700 { - color: var(--color-primary-700); -} -.text-primary-800 { - color: var(--color-primary-800); -} -.text-primary-900 { - color: var(--color-primary-900); -} - -.text-secondary { - color: var(--color-secondary) !important; -} -.text-secondary-100 { - color: var(--color-secondary-100); -} -.text-secondary-200 { - color: var(--color-secondary-200); -} -.text-secondary-300 { - color: var(--color-secondary-300); -} -.text-secondary-400 { - color: var(--color-secondary-400); -} -.text-secondary-500 { - color: var(--color-secondary-500); -} -.text-secondary-600 { - color: var(--color-secondary-600); -} -.text-secondary-700 { - color: var(--color-secondary-700); -} -.text-secondary-800 { - color: var(--color-secondary-800); -} -.text-secondary-900 { - color: var(--color-secondary-900); -} - -.text-accent-blue { - color: var(--color-accent-blue); -} -.text-accent-blue-100 { - color: var(--color-accent-blue-100); -} -.text-accent-blue-200 { - color: var(--color-accent-blue-200); -} -.text-accent-blue-300 { - color: var(--color-accent-blue-300); -} -.text-accent-blue-400 { - color: var(--color-accent-blue-400); -} -.text-accent-blue-500 { - color: var(--color-accent-blue-500); -} -.text-accent-blue-600 { - color: var(--color-accent-blue-600); -} -.text-accent-blue-700 { - color: var(--color-accent-blue-700); -} -.text-accent-blue-800 { - color: var(--color-accent-blue-800); -} -.text-accent-blue-900 { - color: var(--color-accent-blue-900); -} - -.text-accent-yellow { - color: var(--color-accent-yellow); -} -.text-accent-yellow-100 { - color: var(--color-accent-yellow-100); -} -.text-accent-yellow-200 { - color: var(--color-accent-yellow-200); -} -.text-accent-yellow-300 { - color: var(--color-accent-yellow-300); -} -.text-accent-yellow-400 { - color: var(--color-accent-yellow-400); -} -.text-accent-yellow-500 { - color: var(--color-accent-yellow-500); -} -.text-accent-yellow-600 { - color: var(--color-accent-yellow-600); -} -.text-accent-yellow-700 { - color: var(--color-accent-yellow-700); -} -.text-accent-yellow-800 { - color: var(--color-accent-yellow-800); -} -.text-accent-yellow-900 { - color: var(--color-accent-yellow-900); -} - -.text-danger { - color: var(--color-danger); -} -.text-danger-100 { - color: var(--color-danger-100); -} -.text-danger-200 { - color: var(--color-danger-200); -} -.text-danger-300 { - color: var(--color-danger-300); -} -.text-danger-400 { - color: var(--color-danger-400); -} -.text-danger-500 { - color: var(--color-danger-500); -} -.text-danger-600 { - color: var(--color-danger-600); -} -.text-danger-700 { - color: var(--color-danger-700); -} -.text-danger-800 { - color: var(--color-danger-800); -} -.text-danger-900 { - color: var(--color-danger-900); -} - -.text-warning { - color: var(--color-warning); -} -.text-warning-100 { - color: var(--color-warning-100); -} -.text-warning-200 { - color: var(--color-warning-200); -} -.text-warning-300 { - color: var(--color-warning-300); -} -.text-warning-400 { - color: var(--color-warning-400); -} -.text-warning-500 { - color: var(--color-warning-500); -} -.text-warning-600 { - color: var(--color-warning-600); -} -.text-warning-700 { - color: var(--color-warning-700); -} -.text-warning-800 { - color: var(--color-warning-800); -} -.text-warning-900 { - color: var(--color-warning-900); -} - -.text-success { - color: var(--color-success); -} -.text-success-100 { - color: var(--color-success-100); -} -.text-success-200 { - color: var(--color-success-200); -} -.text-success-300 { - color: var(--color-success-300); -} -.text-success-400 { - color: var(--color-success-400); -} -.text-success-500 { - color: var(--color-success-500); -} -.text-success-600 { - color: var(--color-success-600); -} -.text-success-700 { - color: var(--color-success-700); -} -.text-success-800 { - color: var(--color-success-800); -} -.text-success-900 { - color: var(--color-success-900); -} \ No newline at end of file diff --git a/frontend-old/app/styles/theme/fonts.css b/frontend-old/app/styles/theme/fonts.css deleted file mode 100644 index eed2248..0000000 --- a/frontend-old/app/styles/theme/fonts.css +++ /dev/null @@ -1,93 +0,0 @@ - -/* Global font settings */ - -/* Set Space Grotesk as the default font */ -body { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; - font-size: 14px; - line-height: 1.6; - color: #333; -} - -/* Use Anta for headings */ -h1, h2, h3 { - font-family: 'Syncopate', sans-serif; - color: #111; -} - -/* Use Space Grotesk for smaller text like paragraphs */ -p { - font-family: 'Space Grotesk', sans-serif; -} - - -.font-default { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; -} - -.font-syncopate { - font-family: "Syncopate", serif !important; -} - -.font-space-grotesk { - font-family: 'Space Grotesk', sans-serif; -} - -.font-weight-100 { - font-weight: 100; -} -.font-weight-200 { - font-weight: 200; -} -.font-weight-300 { - font-weight: 300; -} -.font-weight-400 { - font-weight: 400; -} -.font-weight-500 { - font-weight: 500; -} -.font-weight-600 { - font-weight: 600; -} -.font-weight-700 { - font-weight: 700; -} -.font-weight-800 { - font-weight: 800; -} -.font-weight-900 { - font-weight: 900; -} - -.font-size-12 { - font-size: 12px !important; -} - -.font-size-14 { - font-size: 14px !important; -} - -.font-size-16 { - font-size: 16px !important; -} - -.font-size-18 { - font-size: 18px !important; -} - -.font-size-20 { - font-size: 20px !important; -} - -.font-size-24 { - font-size: 24px !important; -} - -.font-size-32 { - font-size: 32px !important; -} -.font-size-48 { - font-size: 48px !important; -} \ No newline at end of file diff --git a/frontend-old/app/types/DishType.ts b/frontend-old/app/types/DishType.ts deleted file mode 100644 index 99bd7f2..0000000 --- a/frontend-old/app/types/DishType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { UserType } from "@/types/UserType"; - -export type DishType = { - id: number - name: string, - recurrence: number, - users: UserType[], -} - -export type DishDateType = { - id: number; - date: string; - dish: DishType; - user: UserType; -} - -export type ScheduledDishesType = { - date: string; - dishes: { dish: DishType, user: UserType }[]; -} diff --git a/frontend-old/app/types/RecurrenceType.ts b/frontend-old/app/types/RecurrenceType.ts deleted file mode 100644 index 554c777..0000000 --- a/frontend-old/app/types/RecurrenceType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type RecurrenceType = { - type: "App\\Models\\WeeklyRecurrence" | "App\\Models\\MinimumRecurrence"; - value: number; -} diff --git a/frontend-old/app/types/ScheduleType.ts b/frontend-old/app/types/ScheduleType.ts deleted file mode 100644 index e9494af..0000000 --- a/frontend-old/app/types/ScheduleType.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import type { UserType } from "@/types/UserType"; - -export type ScheduleType = { - id: number; - date: string; - scheduled_user_dishes: ScheduledUserDishType[]; - is_skipped: boolean; -} - -export type FilledScheduleType = { - id?: number; - date: string; - is_skipped?: boolean; - scheduled_user_dishes: ScheduledUserDishType[]; -} - -export type ScheduleDataType = { - user: UserType; - scheduled_user_dish: UserDishType | null; - user_dishes: UserDishType[]; -} \ No newline at end of file diff --git a/frontend-old/app/types/ScheduledUserDishType.ts b/frontend-old/app/types/ScheduledUserDishType.ts deleted file mode 100644 index a853f3c..0000000 --- a/frontend-old/app/types/ScheduledUserDishType.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { UserType } from "@/types/UserType"; -import type { DishType } from "@/types/DishType"; -import type { RecurrenceType } from "@/types/RecurrenceType"; - -export type UserDishType = { - id: number; - dish: DishType; - user: UserType; - recurrences: RecurrenceType[]; -} - -export type ScheduledUserDishType = { - id: number; - user_dish: UserDishType; -} \ No newline at end of file diff --git a/frontend-old/app/types/UserDishType.ts b/frontend-old/app/types/UserDishType.ts deleted file mode 100644 index c4bd896..0000000 --- a/frontend-old/app/types/UserDishType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {RecurrenceType} from "@/types/RecurrenceType"; - -export type DishType = { - user: UserType; - dish: DishType; - recurrences: RecurrenceType[]; -} \ No newline at end of file diff --git a/frontend-old/app/types/UserDishWithoutUserType.ts b/frontend-old/app/types/UserDishWithoutUserType.ts deleted file mode 100644 index d41a7d9..0000000 --- a/frontend-old/app/types/UserDishWithoutUserType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { DishType } from "@/types/DishType"; -import type { RecurrenceType } from "@/types/RecurrenceType"; - -export type UserDishWithoutUserType = { - id: number; - dish: DishType; - recurrences: RecurrenceType[]; -} diff --git a/frontend-old/app/types/UserType.ts b/frontend-old/app/types/UserType.ts deleted file mode 100644 index 334111a..0000000 --- a/frontend-old/app/types/UserType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { UserDishWithoutUserType } from "@/types/UserDishWithoutUserType"; - -export type UserType = { - id: number; - name: string; - user_dishes: UserDishWithoutUserType[]; -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/apiRequest.ts b/frontend-old/app/utils/api/apiRequest.ts deleted file mode 100644 index 01acc70..0000000 --- a/frontend-old/app/utils/api/apiRequest.ts +++ /dev/null @@ -1,107 +0,0 @@ -export const apiRequest = async (url: string, options: RequestInit = {}) => { - const token = localStorage.getItem('token'); - - const allowedRequests = [ - '/api/auth/login', - '/api/auth/register', - ] - - if (allowedRequests.includes(url)) { - return publicRequest(url, options) - } - - if (!token) { - throw new Error('No authentication token found.' + url); - } - - return privateRequest(url, token, options); -}; - -export const publicRequest = async (url: string, options: RequestInit = {}) => { - console.log('→ Sending request', url, options.method); - - url = 'http://localhost' + url; - - const response = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - ...options, - }); - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - -export const privateRequest = async (fullUrl: string, token: string, options: RequestInit = {}) => { - const headers = { - ...(options.headers || {}), - Authorization: `Bearer ${token}`, - }; - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { headers, ...options }); - - // Authentication failure - token invalid - redirect to login - if (response.status === 401) { - localStorage.removeItem('token'); - localStorage.removeItem('refreshToken'); - - window.location.href = '/login'; - - throw new Error('Unauthorized. Redirecting to login.'); - } - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - - -// Add shorthand HTTP methods -apiRequest.get = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'GET' }); -}; - -apiRequest.post = | undefined>( - url: string, - body: TBody, - options: RequestInit = {}, -) => { - return apiRequest(url, { - ...options, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.put = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.delete = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'DELETE' }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/auth.ts b/frontend-old/app/utils/api/auth.ts deleted file mode 100644 index f4511aa..0000000 --- a/frontend-old/app/utils/api/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { apiRequest } from '@/utils/api/apiRequest'; - -export const login = async (email: string, password: string) => { - const data = await apiRequest.post('/api/auth/login', { email, password }); - - if (!data.access_token) { - throw new Error('No access token returned from login.'); - } - - localStorage.setItem('token', data.access_token); - - return data; -}; - - -export const register = async (name: string, email: string, password: string, passwordConfirmation: string) => { - const data = await apiRequest.post('/api/auth/register', { - name, - email, - password, - password_confirmation: passwordConfirmation, // Match the backend's expected parameter - }); - - // Store the token (if returned by the backend) similarly to login - localStorage.setItem('token', data.access_token); - - return data; -}; diff --git a/frontend-old/app/utils/api/dishApi.ts b/frontend-old/app/utils/api/dishApi.ts deleted file mode 100644 index 8d38dba..0000000 --- a/frontend-old/app/utils/api/dishApi.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {apiRequest} from "@/utils/api/apiRequest"; - -export const listDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dishes) { - return data.payload.dishes as DishType[]; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const fetchDish = async (id: number): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dish) { - return data.payload.dish as DishType; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const createDish = async ( - name: string, - // recurrence: number, - // userIds: number[] -) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes`, { - name, - // recurrence, - // users: userIds, - }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch(() => { - throw new Error("Failed to create dish. Please try again later."); - }); -}; - -export const updateDish = async (dish_id: number, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.put(`/api/dishes/${dish_id}`, {name}, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .catch((error) => { - throw error; - }); -}; - -export const deleteDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.delete(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const addUserToDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/add`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const removeUserFromDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/remove`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/app/utils/api/scheduleApi.ts b/frontend-old/app/utils/api/scheduleApi.ts deleted file mode 100644 index d7fcbf3..0000000 --- a/frontend-old/app/utils/api/scheduleApi.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { isValidDate } from "@/helpers/Date"; - -export const listSchedule = async (startDate?: string, endDate?: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (startDate && !isValidDate(startDate)) { - throw new Error('Invalid start date'); - } - if (endDate && !isValidDate(endDate)) { - throw new Error('Invalid end date'); - } - - const params = new URLSearchParams(); - if (startDate) params.append('start', startDate); - if (endDate) params.append('end', endDate); - - const endpoint = `/api/schedule${params.toString() ? `?${params.toString()}` : ''}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule); -}; - -export const getScheduleForDate = async (date: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Update the schedule for a specific date (e.g., mark as skipped) -export const updateScheduleForDate = async (date: string, isSkipped: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.put(endpoint, { is_skipped: isSkipped }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Generate a new schedule (optional: overwrite the existing one) -export const generateSchedule = async (overwrite: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const endpoint = `/api/schedule/generate`; - - return apiRequest.post(endpoint, { overwrite }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const scheduleUserDish = async (date: string, user_id: number, user_dish_id: number|null, skipped: boolean = false) => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - const endpoint = `/api/schedule/${date}/user-dishes`; - - return apiRequest.post(endpoint, { user_dish_id, user_id, skipped }, { headers: { Authorization: `Bearer ${token}`} }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { throw err }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/scheduledUserDishesApi.ts b/frontend-old/app/utils/api/scheduledUserDishesApi.ts deleted file mode 100644 index 8a9b66c..0000000 --- a/frontend-old/app/utils/api/scheduledUserDishesApi.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; - -export const listScheduledUserDishesStartingFromDate = async (startDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?start=${startDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const listScheduledUserDishesEndingAtDate = async (endDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?end=${endDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const getScheduledUserDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; - -export const updateScheduledUserDish = async (id: number, userDishId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const payload = userDishId > 0 - ? { user_dish_id: userDishId } - : { user_dish_id: null, is_skipped: true }; - - return apiRequest.put(`/api/scheduled-user-dishes/${id}`, payload, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; \ No newline at end of file diff --git a/frontend-old/app/utils/api/userDishApi.ts b/frontend-old/app/utils/api/userDishApi.ts deleted file mode 100644 index 39ba27f..0000000 --- a/frontend-old/app/utils/api/userDishApi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { UserDishType } from "@/types/ScheduledUserDishType"; - -export const listUserDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - return apiRequest.get(`/api/user-dishes`, { headers: { Authorization: `Bearer ${ token }` } }) - .then((data) => { - if (data?.payload?.user_dishes) return data.payload.user_dishes as UserDishType[]; - - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/app/utils/api/usersApi.ts b/frontend-old/app/utils/api/usersApi.ts deleted file mode 100644 index d07a9c6..0000000 --- a/frontend-old/app/utils/api/usersApi.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {RecurrenceType} from "@/types/RecurrenceType"; -import {apiRequest} from "@/utils/api/apiRequest"; -import {UserType} from "@/types/UserType"; - -export const listUsers = async () => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.users) { - return data.payload.users; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const showUser = async (userId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users/${userId}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - if (data?.payload?.user) { - return data.payload.user; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const createUser = async (name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.post('/api/users', {name}, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const updateUser = async (user: UserType, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.put(`/api/users/${user.id}`, { name }, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const deleteUser = async (user: UserType) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.delete(`/api/users/${user.id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - - -export const getUserDishForUserAndDish = async (userId: number, dishId: number) => { - const endpoint = `/api/users/${userId}/dishes/${dishId}`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.user_dish) { - return data.payload.user_dish; - } - throw new Error('Failed to fetch user dish'); - }) - .catch((err) => { - throw err; - }); -}; - -export const syncUserDishRecurrences = async ( - dish_id: number, - user_id: number, - recurrenceData: RecurrenceType[] -) => { - const url = `/api/users/${user_id}/dishes/${dish_id}/recurrences`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(url, { recurrences: recurrenceData }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch((err) => { - throw err; - }); -}; - diff --git a/frontend-old/app/utils/dateBuilder.ts b/frontend-old/app/utils/dateBuilder.ts deleted file mode 100644 index e41ce15..0000000 --- a/frontend-old/app/utils/dateBuilder.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DateTime } from "luxon"; - -const transformDate = (inputDate: string, addSuffix = false): string => { - const date = DateTime.fromISO(inputDate) - const day = date.day - const suffix = addSuffix ? getDaySuffix(day) : '' - return date.toFormat("MMMM") + ` ${ day }${ suffix }, ` + date.toFormat("yyyy"); -} - -const getDaySuffix = (day: number): string => { - if (day >= 11 && day <= 13) return "th"; - switch (day % 10) { - case 1: - return "st"; - case 2: - return "nd"; - case 3: - return "rd"; - default: - return "th"; - } -}; - -export default transformDate; \ No newline at end of file diff --git a/frontend-old/app/utils/scheduleBuilder.ts b/frontend-old/app/utils/scheduleBuilder.ts deleted file mode 100644 index 8bbf15f..0000000 --- a/frontend-old/app/utils/scheduleBuilder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ScheduleDataType, ScheduleType } from "@/types/ScheduleType"; -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -const ScheduleBuilder = ( - schedule: ScheduleType, - users: UserType[], - userDishes: UserDishType[] -): ScheduleDataType[] => users.map(user => { - return { - user, - scheduled_user_dish: schedule.scheduled_user_dishes - .filter((scheduledUserDish: ScheduledUserDishType) => scheduledUserDish.user_dish?.user.id === user.id) - .shift()?.user_dish ?? null, - user_dishes: userDishes.filter((userDish: UserDishType) => userDish.user.id === user.id) - } -}) - -export default ScheduleBuilder \ No newline at end of file diff --git a/frontend-old/app/welcome/logo-dark.svg b/frontend-old/app/welcome/logo-dark.svg deleted file mode 100644 index dd82028..0000000 --- a/frontend-old/app/welcome/logo-dark.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend-old/app/welcome/logo-light.svg b/frontend-old/app/welcome/logo-light.svg deleted file mode 100644 index 7328492..0000000 --- a/frontend-old/app/welcome/logo-light.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend-old/app/welcome/welcome.tsx b/frontend-old/app/welcome/welcome.tsx deleted file mode 100644 index 8ac6e1d..0000000 --- a/frontend-old/app/welcome/welcome.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import logoDark from "./logo-dark.svg"; -import logoLight from "./logo-light.svg"; - -export function Welcome() { - return ( -
-
-
-
- React Router - React Router -
-
-
- -
-
-
- ); -} - -const resources = [ - { - href: "https://reactrouter.com/docs", - text: "React Router Docs", - icon: ( - - - - ), - }, - { - href: "https://rmx.as/discord", - text: "Join Discord", - icon: ( - - - - ), - }, -]; diff --git a/frontend-old/archive/.dockerignore b/frontend-old/archive/.dockerignore deleted file mode 100644 index 11ee758..0000000 --- a/frontend-old/archive/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -.env.local diff --git a/frontend-old/archive/.env.local.example b/frontend-old/archive/.env.local.example deleted file mode 100644 index 87516ef..0000000 --- a/frontend-old/archive/.env.local.example +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost -#NODE_ENV=production \ No newline at end of file diff --git a/frontend-old/archive/.gitignore b/frontend-old/archive/.gitignore deleted file mode 100644 index b601ed9..0000000 --- a/frontend-old/archive/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -/.idea -/.env.local -/package-lock.json - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/frontend-old/archive/README.md b/frontend-old/archive/README.md deleted file mode 100644 index da15af8..0000000 --- a/frontend-old/archive/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# DishPlanner Front End - diff --git a/frontend-old/archive/bin/update.sh b/frontend-old/archive/bin/update.sh deleted file mode 100755 index 7eec906..0000000 --- a/frontend-old/archive/bin/update.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -echo "🔄 Pulling latest changes..." -git pull origin main - -echo "🔨 Installing dependencies..." -npm install - -echo "🏗️ Building frontend..." -npm run build - -echo "🔁 Restarting frontend service..." -sudo systemctl restart dishplanner-frontend - -echo "✅ Update complete!" diff --git a/frontend-old/archive/build_and_push.sh b/frontend-old/archive/build_and_push.sh deleted file mode 100755 index ba9f2c6..0000000 --- a/frontend-old/archive/build_and_push.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -docker build -t 192.168.178.152:50114/dishplanner-frontend:latest . -docker push 192.168.178.152:50114/dishplanner-frontend:latest \ No newline at end of file diff --git a/frontend-old/archive/eslint.config.mjs b/frontend-old/archive/eslint.config.mjs deleted file mode 100644 index c85fb67..0000000 --- a/frontend-old/archive/eslint.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; - -export default eslintConfig; diff --git a/frontend-old/archive/next.config.ts b/frontend-old/archive/next.config.ts deleted file mode 100644 index 536bef3..0000000 --- a/frontend-old/archive/next.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - // Rewrite /api/* to backend container (from client-side) - async rewrites() { - return [ - { - source: "/api/:path*", - destination: "http://backend:80/api/:path*", // internal Docker DNS - }, - ]; - }, -}; - -export default nextConfig; diff --git a/frontend-old/archive/package.json b/frontend-old/archive/package.json deleted file mode 100644 index 62e44db..0000000 --- a/frontend-old/archive/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "dish-planner", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "next lint", - "export": "next export" - }, - "dependencies": { - "@headlessui/react": "^2.2.0", - "@heroicons/react": "^2.2.0", - "classnames": "^2.5.1", - "luxon": "^3.5.0", - "next": "15.2.4", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@types/luxon": "^3.4.2", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "15.1.5", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" - } -} diff --git a/frontend-old/archive/postcss.config.mjs b/frontend-old/archive/postcss.config.mjs deleted file mode 100644 index 1a69fd2..0000000 --- a/frontend-old/archive/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/frontend-old/archive/public/dish-planner.webp b/frontend-old/archive/public/dish-planner.webp deleted file mode 100644 index 3eaa04fc1c5bb37607ea9c50c582ca87c943f421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237142 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≥<;xK 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 z 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

%uU}gebY+{+v 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 vYRrF$m=j-Qkdu9eW` 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 diff --git a/frontend-old/archive/public/file.svg b/frontend-old/archive/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/frontend-old/archive/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/globe.svg b/frontend-old/archive/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/frontend-old/archive/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/next.svg b/frontend-old/archive/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/frontend-old/archive/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/vercel.svg b/frontend-old/archive/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/frontend-old/archive/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/public/window.svg b/frontend-old/archive/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/frontend-old/archive/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx b/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx deleted file mode 100644 index 95627e2..0000000 --- a/frontend-old/archive/src/app/dishes/[id]/delete/page.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import {useEffect, useState} from "react"; -import {use} from "react"; -import PageTitle from "@/components/ui/PageTitle"; -import {DishType} from "@/types/DishType"; -import {useRouter} from "next/navigation"; -import Link from "next/link"; -import Alert from "@/components/ui/Alert"; -import useRoutes from "@/hooks/useRoutes"; -import {deleteDish, fetchDish} from "@/utils/api/dishApi"; -import {UserType} from "@/types/UserType"; - -export default function EditDishPage({params}: { params: Promise<{ id: number }> }) { - const [name, setName] = useState(""); - const [recurrence, setRecurrence] = useState(0); - const [users, setUsers] = useState([]); - const [error, setError] = useState(""); - const [isLoading, setIsLoading] = useState(true); // To handle loading state - const {id} = use(params) - const router = useRouter() - const routes = useRoutes(); - - useEffect(() => { - fetchDish(id) - .then((dish: DishType) => { - setName(dish.name); - setRecurrence(dish.recurrence); - setUsers(dish.users); - }) - .catch((err) => setError(err)) - .finally(() => setIsLoading(false)); - }, [id]); // Only run when `params.id` changes - - const submitForm = (e: React.MouseEvent) => { - e.preventDefault() - - deleteDish(id) - .then(() => router.push(routes.dish.index())) - .catch((err) => setError(err)) - } - - if (isLoading) { - return

Loading...

; - } - - return ( -
-
- Delete Dish -
- - { - error != '' && { error } - } - -
-
- Are you sure you want to delete this dish? -
-
- name: {name}
- recurrence: {recurrence} - users: {users.map((user) => user.name).join(', ')} -
- - -
- No, take me back -
- - -
-
- ); -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx b/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx deleted file mode 100644 index 364f9b1..0000000 --- a/frontend-old/archive/src/app/dishes/[id]/edit/page.tsx +++ /dev/null @@ -1,59 +0,0 @@ -"use client"; - -import {use, useCallback, useEffect, useState} from "react"; -import PageTitle from "@/components/ui/PageTitle"; -import EditDishForm from "@/components/features/dishes/EditDishForm"; -import {DishType} from "@/types/DishType"; -import Spinner from "@/components/Spinner"; -import {fetchDish} from "@/utils/api/dishApi"; -import SyncUsersForm from "@/components/features/dishes/SyncUsersForm"; -import {ChevronLeftIcon} from "@heroicons/react/16/solid"; -import useRoutes from "@/hooks/useRoutes"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; -import Hr from "@/components/ui/Hr" - -export default function EditDishPage({ params }: { params: Promise<{ id: number }> }) { - const { id } = use(params) - const [dish, setDish] = useState(null) - const [isLoading, setIsLoading] = useState(true); - const routes = useRoutes(); - - const loadDish = useCallback(async () => { - try { - const fetchedDish = await fetchDish(id); - setDish(fetchedDish); - } catch (error) { - console.error("Error fetching dish:", error); - throw new Error('No token found in localStorage.'); - } finally { - setIsLoading(false); - } - }, [id]); - - useEffect(() => { - loadDish(); - }, [loadDish]); - - - if (isLoading || dish === null) { - return - } - - return ( -
-
- Edit Dish - - -

BACK

-
-
- - - -
- - -
- ); -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/create/page.tsx b/frontend-old/archive/src/app/dishes/create/page.tsx deleted file mode 100644 index 156c038..0000000 --- a/frontend-old/archive/src/app/dishes/create/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -"use client"; - -import CreateDishForm from "@/components/features/dishes/CreateDishForm"; - -export default function CreateDishPage() { - return -} \ No newline at end of file diff --git a/frontend-old/archive/src/app/dishes/page.tsx b/frontend-old/archive/src/app/dishes/page.tsx deleted file mode 100644 index 274a848..0000000 --- a/frontend-old/archive/src/app/dishes/page.tsx +++ /dev/null @@ -1,57 +0,0 @@ -"use client"; - -import PageTitle from "@/components/ui/PageTitle"; -import {DishType} from "@/types/DishType"; -import Dish from "@/components/features/dishes/Dish"; -import {PlusIcon} from "@heroicons/react/24/solid"; -import {useEffect, useState} from "react"; -import useRoutes from "@/hooks/useRoutes"; -import {listDishes} from "@/utils/api/dishApi"; -import Button from "@/components/ui/Button" -export const dynamic = 'force-dynamic'; - -export default function DishesIndexPage() { - const routes = useRoutes(); - - const [dishes, setDishes] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .finally(() => setLoading(false)); - }, []); - - if (loading) return

Loading...

; - - if (! dishes) { - return

Loading...

- } - - return ( - <> -
-
- Dishes -
-
- -
-
- - { - dishes.length === 0 - ?

No dishes found :(

- : dishes.map((dish: DishType, index: number) => ) - } - - ); -} diff --git a/frontend-old/archive/src/app/favicon.ico b/frontend-old/archive/src/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/frontend-old/archive/src/app/layout.tsx b/frontend-old/archive/src/app/layout.tsx deleted file mode 100644 index f6d242b..0000000 --- a/frontend-old/archive/src/app/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react"; -import type { Metadata } from 'next'; -import NavBar from '@/components/layout/NavBar'; -import { AuthProvider } from '@/context/AuthContext'; -import AuthGuard from "@/components/layout/AuthGuard"; -import '@/styles/main.css'; - -export const metadata: Metadata = { - title: 'DishPlanner', - description: 'Schedule your dishes', -}; - -export default function RootLayout({children,}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - Dish Planner - - - - - -
{children}
-
-
- - - ); -} diff --git a/frontend-old/archive/src/app/login/page.tsx b/frontend-old/archive/src/app/login/page.tsx deleted file mode 100644 index cf92a57..0000000 --- a/frontend-old/archive/src/app/login/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client'; - -import LoginForm from "@/components/features/auth/LoginForm"; - -export default function LoginPage() { - return ( -
- -
- ); -} diff --git a/frontend-old/archive/src/app/page.tsx b/frontend-old/archive/src/app/page.tsx deleted file mode 100644 index 8c98217..0000000 --- a/frontend-old/archive/src/app/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import UpcomingDishes from "@/components/features/schedule/UpcomingDishes"; -export const dynamic = 'force-dynamic'; - -export default async function FrontPage() { - - return ( - - ); -} diff --git a/frontend-old/archive/src/app/register/page.tsx b/frontend-old/archive/src/app/register/page.tsx deleted file mode 100644 index 1cad575..0000000 --- a/frontend-old/archive/src/app/register/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -'use client'; - -import RegistrationForm from "@/components/features/auth/RegistrationForm"; - -const RegistrationPage = () => { - return ( -
- -
- ); - -} - -export default RegistrationPage \ No newline at end of file diff --git a/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx b/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx deleted file mode 100644 index a1d3097..0000000 --- a/frontend-old/archive/src/app/schedule/[date]/edit/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use client"; - -import { use } from "react"; -import ScheduleEditForm from "@/components/features/schedule/ScheduleEditForm"; - -const ScheduleEditPage = ({ params }: { params: Promise<{ date: string }> }) => { - const { date } = use(params) - - return -} - -export default ScheduleEditPage \ No newline at end of file diff --git a/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx b/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx deleted file mode 100644 index aa6f772..0000000 --- a/frontend-old/archive/src/app/scheduled-user-dishes/history/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import HistoricalDishes from "@/components/features/schedule/HistoricalDishes"; -export const dynamic = 'force-dynamic'; - -export default async function HistoryPage() { - - return ( - - ); -} diff --git a/frontend-old/archive/src/app/users/[id]/edit/page.tsx b/frontend-old/archive/src/app/users/[id]/edit/page.tsx deleted file mode 100644 index 3545ae0..0000000 --- a/frontend-old/archive/src/app/users/[id]/edit/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client' - -import {FC, use, useEffect, useState} from "react"; -import {UserType} from "@/types/UserType"; -import {showUser} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import EditUserForm from "@/components/features/users/EditUserForm"; -export const dynamic = 'force-dynamic'; - -interface Props { - params: Promise<{ id: number }>; -} - -const UpdateUsersPage: FC = ({ params }) => { - const { id } = use(params) - const [user, setUser] = useState(null) - - useEffect(() => { - showUser(id) - .then((user: UserType) => setUser(user)) - }, [id]); - - if (!user) { - return - } - - return -} - -export default UpdateUsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/app/users/create/page.tsx b/frontend-old/archive/src/app/users/create/page.tsx deleted file mode 100644 index 80480f9..0000000 --- a/frontend-old/archive/src/app/users/create/page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -'use client' - -import PageTitle from "@/components/ui/PageTitle"; -import useRoutes from "@/hooks/useRoutes"; -import {useRouter} from "next/navigation"; -import {useState} from "react"; -import Alert from "@/components/ui/Alert"; -import {createUser} from "@/utils/api/usersApi"; -import Link from "next/link"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -export const dynamic = 'force-dynamic'; - -const CreateUsersPage = () => { - const [name, setName] = useState(''); - const [error, setError] = useState(''); - const router = useRouter(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - createUser(name) - .then(() => { - router.push(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Create -
-
- ); -} - -export default CreateUsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/app/users/page.tsx b/frontend-old/archive/src/app/users/page.tsx deleted file mode 100644 index 0943dd8..0000000 --- a/frontend-old/archive/src/app/users/page.tsx +++ /dev/null @@ -1,78 +0,0 @@ -'use client' - -import PageTitle from "@/components/ui/PageTitle"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import useRoutes from "@/hooks/useRoutes"; -import Link from "next/link"; -import {PencilIcon, PlusIcon, TrashIcon} from "@heroicons/react/24/solid"; -import React from "react"; -import {deleteUser} from "@/utils/api/usersApi"; -import {UserType} from "@/types/UserType"; -import Card from "@/components/layout/Card"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; - -const UsersPage = () => { - const { users, isLoading } = useFetchUsers(); - const routes = useRoutes(); - - const handleDelete = (user: UserType) => { - deleteUser(user) - .then(() => window.location.reload()) - } - - if (isLoading) { - return ; - } - - const usersList = () => { - return users.map((user) => ( - -
- {user.name} -
-
-
- -
- -
- -
-
- handleDelete(user)}> -
- -
- -
-
-
- )) - }; - - return ( -
-
-
- Users -
- -
- - -

Add User

-
-
-
- - { - users && users.length > 0 - ? usersList() - :
No users
- } -
- ); -} - -export default UsersPage; \ No newline at end of file diff --git a/frontend-old/archive/src/components/Spinner.tsx b/frontend-old/archive/src/components/Spinner.tsx deleted file mode 100644 index f170616..0000000 --- a/frontend-old/archive/src/components/Spinner.tsx +++ /dev/null @@ -1,15 +0,0 @@ -const Spinner = () => { - - return ( -
- - - - -
- ) - -} - -export default Spinner \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/OnboardingBanner.tsx b/frontend-old/archive/src/components/features/OnboardingBanner.tsx deleted file mode 100644 index 9bbc003..0000000 --- a/frontend-old/archive/src/components/features/OnboardingBanner.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { FC } from "react" -import Link from "next/link" -import useRoutes from "@/hooks/useRoutes" -import { UserType } from "@/types/UserType" -import { DishType } from "@/types/DishType" - -interface Props { - dishes: DishType[], - users: UserType[] -} - -const OnboardingBanner: FC = ({ dishes, users }) => { - const routes = useRoutes(); - - const steps = [ - { - label: "Create a user", - href: routes.user.create(), - count: users.length - }, { - label: "Create a dish", - href: routes.dish.create(), - count: dishes.length - } - ] - - return ( -
-
Welcome to DishPlanner
-
To get you started, please follow these steps to set up your account. This will ensure a better - experience. -
- - { - steps.map((step, index) => ( -
- { - step.count === 0 - ? { step.label } - :
{ step.label }
- } -
- )) - } -
- ) -} - -export default OnboardingBanner; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/auth/LoginForm.tsx b/frontend-old/archive/src/components/features/auth/LoginForm.tsx deleted file mode 100644 index 2189c36..0000000 --- a/frontend-old/archive/src/components/features/auth/LoginForm.tsx +++ /dev/null @@ -1,96 +0,0 @@ -'use client'; - -import React, { useEffect, useState } from 'react'; -import { useAuth } from '@/context/AuthContext'; -import { login } from "@/utils/api/auth"; -import { useRouter } from 'next/navigation'; -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import { useSearchParams } from 'next/navigation'; -import Alert from "@/components/ui/Alert"; - -export default function LoginForm() { - const { login: authLogin } = useAuth(); - const router = useRouter(); - const routes = useRoutes(); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const searchParams = useSearchParams(); - const [alertSuccess, setAlertSuccess] = useState([]) - - // handle registration success message - const isRegistered = searchParams.get('registered') === 'true'; - - useEffect(() => { - if (isRegistered) { - setAlertSuccess(['Registration successful!',' You can now log in.']); - - const timer = setTimeout(() => { - const params = new URLSearchParams(searchParams.toString()); - params.delete('registered'); - const newUrl = `${window.location.pathname}?${params.toString()}`; - - router.replace(newUrl); - }, 3000); - - return () => clearTimeout(timer) - } - }, [isRegistered, router, searchParams]) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - - try { - await login(email, password); - authLogin(); - router.replace('/'); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Login failed'; - setError(errorMessage); - } - }; - - return ( -
- { alertSuccess.length > 0 && - - {alertSuccess.map((msg, index) => ( - - {msg} -
-
- ))} -
- } -
- {error &&

{error}

} - setEmail(e.target.value)} - required - className="w-full p-2 mb-4 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value)} - required - className="w-full p-2 mb-4 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - Login - - Create an account - -
-
- ); -} diff --git a/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx b/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx deleted file mode 100644 index 0738381..0000000 --- a/frontend-old/archive/src/components/features/auth/RegistrationForm.tsx +++ /dev/null @@ -1,104 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { register } from "@/utils/api/auth"; -import { useRouter } from 'next/navigation'; -import useRoutes from "@/hooks/useRoutes"; -import Link from "next/link"; -import SectionTitle from "@/components/ui/SectionTitle"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -export default function LoginForm() { - const router = useRouter(); - const routes = useRoutes(); - const [name, setName] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [passwordAgain, setPasswordAgain] = useState(''); - const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [isRegistered, setIsRegistered] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (password !== passwordAgain) { - setError("Passwords do not match."); - return; - } - - try { - setIsLoading(true); - - await register(name, email, password, passwordAgain); - - router.replace('/login?registered=true'); - } catch (err) { - const errorMessage = - err instanceof Error - ? err.message - : 'Registration\n failed'; - setError(errorMessage); - } finally { - setIsRegistered(true); - setIsLoading(false); - } - }; - - if (isRegistered) { - return
- Registration successful! - Please continue to the login page. -
- } - - return ( -
-
-

Register

- { error &&

{ error }

} - setName(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setEmail(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPassword(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - setPasswordAgain(e.target.value) } - required - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Create Account - - - Back to Login - -
-
- ); -} diff --git a/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx b/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx deleted file mode 100644 index 2e2d2c8..0000000 --- a/frontend-old/archive/src/components/features/dishes/AddUserToDishForm.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { FC, useState } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; -import {addUserToDish} from "@/utils/api/dishApi"; -import OutlineButton from "@/components/ui/Buttons/OutlineButton"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const AddUserToDishForm: FC = ({ dish, reloadDish }) => { - const [showAdd, setShowAdd] = useState(false); - const [selectedUser, setSelectedUser] = useState("-1"); - const { users, isLoading: isUsersLoading } = useFetchUsers(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - if (selectedUser === "-1") { - alert("Please select a valid user."); - return; - } - - const userToAdd = users.find((user: UserType) => user.id === parseInt(selectedUser)); - - if (!userToAdd) { - alert("User not found."); - return; - } - - addUserToDish(dish.id, userToAdd.id) - .then(() => { - setShowAdd(false); - setSelectedUser("-1"); - reloadDish(); - }) - .catch(() => { - alert("Failed to add user, please try again."); - }); - }; - - if (isUsersLoading) { - return ; - } - - const remainingUsers = users.filter( - (user: UserType) => - !dish.users.find((dishUser: UserType) => dishUser.id === user.id) - ); - - return ( - <> - setShowAdd(!showAdd)} - disabled={remainingUsers.length === 0} - type="button" - > - Add User - - - { showAdd && ( -
-
-
-
- -
- -
- - Add User - -
-
-
-
- )} - - ); -}; - -export default AddUserToDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx b/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx deleted file mode 100644 index 242c1f2..0000000 --- a/frontend-old/archive/src/components/features/dishes/CreateDishForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState } from "react"; -import { useRouter } from "next/navigation"; -import { createDish } from "@/utils/api/dishApi"; -import PageTitle from "@/components/ui/PageTitle"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; -import OutlineLinkButton from "@/components/ui/Buttons/OutlineLinkButton"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" - -const CreateDishForm = () => { - const router = useRouter() - const [name, setName] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - // Validate client-side input - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await createDish(name); - if (result) { - router.push('/dishes') - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred."); - } finally { - setLoading(false); - } - } - - return ( -
-
- Create Dish -
- -
- { error && ( - { error } - ) } - -
- - setName(e.target.value) } // Update the name state on change - className="w-full p-2 mb-4 border rounded bg-gray-600 border-secondary text-secondary focus:bg-gray-900" - placeholder="Enter dish name" - /> -
- - - { loading ? "Saving..." : "Save Changes" } - -
- -
- - } - > - Back to dishes - -
- ); - - -}; - -export default CreateDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/Dish.tsx b/frontend-old/archive/src/components/features/dishes/Dish.tsx deleted file mode 100644 index 7933a41..0000000 --- a/frontend-old/archive/src/components/features/dishes/Dish.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import {DishType} from "@/types/DishType"; - -import {PencilIcon, TrashIcon} from '@heroicons/react/24/solid' -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import {UserType} from "@/types/UserType"; -import Card from "@/components/layout/Card"; - -const Dish = ({ dish }: { dish: DishType}) => { - const routes = useRoutes(); - - return ( - -
-

{ dish.name }

- - { - dish.users.map((user: UserType) => ( -
{user.name.slice(0, 1)}
- )) - } -
-
- -
- -
- - -
- -
- -
-
- ) -} - -export default Dish \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/DishCard.tsx b/frontend-old/archive/src/components/features/dishes/DishCard.tsx deleted file mode 100644 index 66b2c52..0000000 --- a/frontend-old/archive/src/components/features/dishes/DishCard.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {FC} from "react"; -import {DishType} from "@/types/DishType"; - -interface Props { - user: UserType, - dish: DishType, -} - -const DishCard: FC = ({ user, dish }: Props) => { - return ( -
-
- { user.name.slice(0, 1) } -
-
- { dish ? dish.name : '-' } -
-
- ) -} - -export default DishCard \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx b/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx deleted file mode 100644 index cfffb2a..0000000 --- a/frontend-old/archive/src/components/features/dishes/EditDishForm.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, {FC, useState} from "react"; -import {useRouter} from "next/navigation"; -import Alert from "@/components/ui/Alert"; -import {updateDish} from "@/utils/api/dishApi"; -import {DishType} from "@/types/DishType"; -import useRoutes from "@/hooks/useRoutes"; -import Spinner from "@/components/Spinner"; -import Button from "@/components/ui/Button" - -interface Props { - dish: DishType -} - -const EditDishForm: FC = ({ dish }) => { - const [name, setName] = useState(dish.name); - const [error, setError] = useState(""); - const router = useRouter() - const [loading, setLoading] = useState(false); - const routes = useRoutes(); - - const validateForm = () => { - if (!name.trim()) { - setError("Dish name cannot be empty."); - return false; - } - - return true; - }; - - const submitForm = async (e: React.FormEvent) => { - e.preventDefault() - - if (!validateForm()) return; - - setError(""); - setLoading(true); - - try { - const result = await updateDish(dish.id, name); - if (result) { - router.push(routes.dish.index()) - } - } catch (error: unknown) { - setError(error instanceof Error ? error.message : "An unexpected error occurred"); - } finally { - setLoading(false); // Reset loading state - } - } - - if (loading) { - return ; - } - - return ( -
- { - error != '' && { error } - } - - {/* Dish name input */} -
- - setName(e.target.value)} // Update the name state on change - className="p-2 border rounded w-full bg-gray-500 border-secondary background-secondary" - /> -
- - {/* Save button */} - -
- ); -} - -export default EditDishForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx b/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx deleted file mode 100644 index eb4bc06..0000000 --- a/frontend-old/archive/src/components/features/dishes/EditDishUserCardEditForm.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, {FC} from "react"; -import SectionTitle from "@/components/ui/SectionTitle"; -import {syncUserDishRecurrences} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import {UserDishType} from "@/types/ScheduledUserDishType"; -import {RecurrenceType} from "@/types/ScheduleType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - userDish: UserDishType - onSubmit: () => void -} - -const EditDishUserCardEditForm: FC = ({ userDish, onSubmit}) => { - const weeklyRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\WeeklyRecurrence') - const minimumRecurrence = userDish.recurrences.find((recurrence) => recurrence.type === 'App\\Models\\MinimumRecurrence') - - const wv = weeklyRecurrence ? weeklyRecurrence.value : undefined - const mv = minimumRecurrence ? minimumRecurrence.value : undefined - - const [isWeeklyOn, setIsWeeklyOn] = React.useState(weeklyRecurrence !== undefined); - const [isMinimumOn, setIsMinimumOn] = React.useState(minimumRecurrence !== undefined); - const [weekday, setWeekday] = React.useState(wv ?? 0); - const [minimumValue, setMinimumValue] = React.useState(mv ?? 7); - const [loading, setLoading] = React.useState(false); - - const handleSubmit = () => { - const recurrences = [] - - if (isWeeklyOn) { - recurrences.push({ - type: 'App\\Models\\WeeklyRecurrence', - value: weekday, - }); - } - - if (isMinimumOn) { - recurrences.push({ - type: 'App\\Models\\MinimumRecurrence', - value: minimumValue, - }); - } - - setLoading(true) - syncUserDishRecurrences(userDish.dish.id, userDish.user.id, recurrences as RecurrenceType[]) - .then((data) => console.log('request data', data)) - .finally(() => { - setLoading(false) - onSubmit() - }) - } - - if (loading) { - return ; - } - - return ( -
- Recurrences - -
-
- setIsWeeklyOn(!isWeeklyOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- { - isWeeklyOn && ( -
- - -
- ) - } -
- -
-
- setIsMinimumOn(!isMinimumOn)} - className="w-4 h-4 border border-gray-300 rounded-sm bg-gray-500 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" - /> - -
- - { - isMinimumOn && ( -
- setMinimumValue(parseInt(e.currentTarget.value))} min="0" max="365" className="background-secondary border-secondary border-2 w-12 px-2" /> - -
- ) - } -
- - Save -
- ); -} - -export default EditDishUserCardEditForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx b/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx deleted file mode 100644 index bba81ba..0000000 --- a/frontend-old/archive/src/components/features/dishes/RecurrenceLabels.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import {FC} from "react"; -import {RecurrenceType} from "@/types/ScheduleType"; - -interface Props { - recurrences: RecurrenceType[]; -} - -const RecurrenceLabels: FC = ({recurrences}) => { - const weeklyRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\WeeklyRecurrence'); - const minimumRecurrences = recurrences.filter(recurrence => recurrence.type === 'App\\Models\\MinimumRecurrence'); - - const renderWeeklyRecurrence = () => { - if (weeklyRecurrences == undefined || weeklyRecurrences.length == 0) { - return ''; - } - - const weekdayString = (() => { - switch (weeklyRecurrences[0].value) { - case 0: return "Sunday" - case 1: return "Monday" - case 2: return "Tuesday"; - case 3: return "Wednesday"; - case 4: return "Thursday"; - case 5: return "Friday"; - case 6: return "Saturday"; - default: return "Invalid day"; - } - }) - - return ( -
- { weekdayString() } -
- ) - } - const renderMinimumRecurrence = () => { - if (minimumRecurrences == undefined || minimumRecurrences.length == 0) { - return ''; - } - - return ( -
- min: { minimumRecurrences[0].value } -
- ) - } - - return <> - { renderWeeklyRecurrence() } - { renderMinimumRecurrence() } - ; -}; - -export default RecurrenceLabels; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx b/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx deleted file mode 100644 index 296167d..0000000 --- a/frontend-old/archive/src/components/features/dishes/SyncUsersForm.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC } from "react"; -import { DishType } from "@/types/DishType"; -import { UserType } from "@/types/UserType"; -import UserDishCard from "@/components/features/dishes/UserDishCard"; -import SectionTitle from "@/components/ui/SectionTitle"; -import AddUserToDishForm from "@/components/features/dishes/AddUserToDishForm"; - -interface Props { - dish: DishType; - reloadDish: () => void; -} - -const SyncUsersForm: FC = ({ dish, reloadDish }) => { - return ( -
- Users - - - - {dish.users.map((user: UserType) => ( - - ))} -
- ); -}; - -export default SyncUsersForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx b/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx deleted file mode 100644 index 4e06e8c..0000000 --- a/frontend-old/archive/src/components/features/dishes/UserDishCard.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, {FC, useEffect} from "react"; -import {DishType} from "@/types/DishType"; -import {UserType} from "@/types/UserType"; -import Link from "next/link"; -import {PencilIcon, TrashIcon} from "@heroicons/react/24/solid"; -import {removeUserFromDish} from "@/utils/api/dishApi"; -import EditDishUserCardEditForm from "@/components/features/dishes/EditDishUserCardEditForm"; -import {getUserDishForUserAndDish} from "@/utils/api/usersApi"; -import Spinner from "@/components/Spinner"; -import RecurrenceLabels from "@/components/features/dishes/RecurrenceLabels"; -import {UserDishType} from "@/types/ScheduledUserDishType"; - -interface Props { - dish: DishType - user: UserType - reloadDish: () => void -} - -const UserDishCard: FC = ({dish, user, reloadDish}) => { - const [userDish, setUserDish] = React.useState(null); - const [userDishLoading, setUserDishLoading] = React.useState(true); - const [isEditMode, setIsEditMode] = React.useState(false); - - useEffect(() => { - getUserDishForUserAndDish(user.id, dish.id) - .then((userDish) => setUserDish(userDish)) - .finally(() => setUserDishLoading(false)) - }, [dish, user]); - - const handleRemove = () => { - removeUserFromDish(dish.id, user.id) - .then(() => reloadDish()) - .catch(() => { - alert("Failed to remove user, please try again."); - }); - }; - - if (userDishLoading || !userDish) { - return - } - - const onUserCardSubmit = () => { - setIsEditMode(false); - reloadDish() - } - - return ( -
-
-
- {user.name} -
- -
- -
- -
- setIsEditMode(!isEditMode)} href="#"> -
- -
- -
-
- -
- -
- -
-
- - {isEditMode && ( -
- -
- )} -
- ); -} - -export default UserDishCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx b/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx deleted file mode 100644 index 3e8639a..0000000 --- a/frontend-old/archive/src/components/features/navbar/MobileDropdownMenu.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import Link from "next/link"; -import React, {FC} from "react"; -import useRoutes from "@/hooks/useRoutes"; -import classNames from "classnames"; - -interface Props { - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; - handleLogout: (e: React.MouseEvent) => void; -} - -const divStyles = classNames( - 'absolute', 'text-xxl', 'rounded-b', 'top-full mt-1', 'left-0', 'right-0', '', 'py-2', - 'bg-gray-600', 'border-secondary', 'shadow-md', 'flex', 'flex-col', 'space-y-3', - 'md:hidden' -) - -const linkStyles = classNames( - 'border-b-2', 'border-secondary', 'uppercase', - 'text-primary', 'hover:background-secondary', 'pb-2', 'pl-5', - 'space-grotesk', 'text-xl' -) - -const MobileDropdownMenu: FC = ({ isOpen, setIsOpen, handleLogout }) => { - const routes = useRoutes(); - - if (!isOpen) return null; - - return ( -
- setIsOpen(false)} - > - Home - - setIsOpen(false)} - > - Dishes - - setIsOpen(false)} - > - Users - - setIsOpen(false)} - > - History - - - Logout - -
- ) -} - -export default MobileDropdownMenu \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx b/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx deleted file mode 100644 index ad3718f..0000000 --- a/frontend-old/archive/src/components/features/schedule/HistoricalDishes.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client" - -import {useEffect, useState} from "react"; -import {DateTime} from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import {ScheduleType} from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import {listSchedule} from "@/utils/api/scheduleApi"; - -const HistoricalDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const yesterday = DateTime.now().minus({ days: 1 }).toFormat('yyyy-LL-dd'); - - useEffect(() => { - listSchedule(undefined, yesterday) - .then((dishes: ScheduleType[]) => dishes - .sort((a: ScheduleType, b: ScheduleType) => new Date(b.date).getTime() - new Date(a.date).getTime()) - ) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)) - }, [yesterday]); - - if (isLoading) { - return ; - } - - if (!schedule || Object.keys(schedule).length === 0) { - return ( -
- No dishes scheduled -
- ); - } - - return
- History - -
-} - -export default HistoricalDishes \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx deleted file mode 100644 index b146f6a..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleCalendar.tsx +++ /dev/null @@ -1,75 +0,0 @@ -"use client" - -import {FC} from "react"; -import ScheduleDayCard from "@/components/features/schedule/dayCard/ScheduleDayCard"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import {useFetchUsers} from "@/hooks/useFetchUsers"; -import Spinner from "@/components/Spinner"; - -const generateDates = (startDate: string, days: number): string[] => { - const dates = []; - const start = new Date(startDate); - - for (let i = 0; i < days; i++) { - const currentDate = new Date(start); - currentDate.setDate(start.getDate() + i); - dates.push(currentDate.toISOString().split('T')[0]); - } - - return dates; -}; - - -const fillCalendar = (schedules: ScheduleType[]): FilledScheduleType[] => { - /* -Array(14) - 0: - date: "2025-05-05" - id: 2 - is_skipped: false - scheduled_user_dishes: [] - */ - - const dates = generateDates((new Date()).toISOString().split('T')[0], 31) - - return dates.map((date): FilledScheduleType => { - console.log(date) - - const schedule = schedules.find((schedule: ScheduleType) => schedule.date == date) - - if (schedule) { - return schedule - } - - return { - date, - scheduled_user_dishes: [] - } - }) -} - -interface Props { - schedule: ScheduleType[]; -} - -const ScheduleCalendar: FC = ({ schedule }: Props) => { - const {users, isLoading: areUsersLoading} = useFetchUsers(); - - if (areUsersLoading) return - - const fullCalendar = fillCalendar(schedule) - - return ( -
- { fullCalendar.map((schedule, index) => ( - - ))} -
- ) -} - -export default ScheduleCalendar \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx deleted file mode 100644 index 0f436a9..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleEditForm.tsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; - -import React, { FC, useEffect, useState } from "react"; -import { ScheduleType } from "@/types/ScheduleType"; -import Spinner from "@/components/Spinner"; -import PageTitle from "@/components/ui/PageTitle"; -import { getScheduleForDate, scheduleUserDish, updateScheduleForDate } from "@/utils/api/scheduleApi"; -import { UserDishType } from "@/types/ScheduledUserDishType"; -import Label from "@/components/ui/Label"; -import SectionTitle from "@/components/ui/SectionTitle"; -import { useFetchUsers } from "@/hooks/useFetchUsers"; -import { listUserDishes } from "@/utils/api/userDishApi"; -import scheduleBuilder from "@/utils/scheduleBuilder"; -import transformDate from "@/utils/dateBuilder"; -import { ChevronLeftIcon } from "@heroicons/react/16/solid"; -import Hr from "@/components/ui/Hr" -import Button from "@/components/ui/Button" - -interface Props { - date: string; -} - -const ScheduleEditForm: FC = ({ date }) => { - const [schedule, setSchedule] = useState() - const [userDishes, setUserDishes] = useState([]) - const [isScheduleLoading, setIsScheduleLoading] = useState(true); - const [areUserDishesLoading, setAreUserDishesLoading] = useState(true); - const { users } = useFetchUsers(); - - useEffect(() => { - getScheduleForDate(date) - .then((sched: ScheduleType) => setSchedule(sched)) - .finally(() => setIsScheduleLoading(false)) - }, [date]); - - - useEffect(() => { - listUserDishes() - .then((user_dishes: UserDishType[]) => setUserDishes(user_dishes)) - .finally(() => setAreUserDishesLoading(false)) - }, []); - - const handleSkipDay = () => { - updateScheduleForDate(date, true) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleUnskipDay = () => { - updateScheduleForDate(date, false) - .then((schedule: ScheduleType) => { - setSchedule(schedule) - }) - } - - const handleChange = (e: React.ChangeEvent, userId: number) => { - const userDishId = parseInt(e.currentTarget.value); - - if (userDishId === 0) { - scheduleUserDish(date, userId, null, true).then(() => window.location.reload()); - return; - } - - scheduleUserDish(date, userId, userDishId).then(() => window.location.reload()); - } - - if (isScheduleLoading || areUserDishesLoading || !schedule) { - return - } - - const scheduleData = scheduleBuilder(schedule, users, userDishes) - - return
-
-
- Edit Day -
-
- { transformDate(schedule.date) } -
-
- -
- - { - userDishes.length === 0 &&
-
No dishes found assigned to this user.
-
Go ahead and add some first, or choose to skip the day.
-
(dishes ={`>`} edit ={`>`} add user)
-
- } - - { schedule.is_skipped - ? - : ( - <> - { - scheduleData - .map((scheduleData) =>
-
{ scheduleData.user.name }
-
- -
-
) - } - - ) - } - -
Changes are saved automatically
- -
- -
- -
{ - schedule.is_skipped - ? - : - }
-
-
-} - -export default ScheduleEditForm \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx deleted file mode 100644 index 5b60c2b..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import {FC, useState} from "react"; -import Modal from "@/components/ui/Modal"; -import ScheduleRegenerateForm from "@/components/features/schedule/ScheduleRegenerateForm"; -import {ArrowPathIcon} from "@heroicons/react/16/solid"; - -interface ScheduleRegenerateButtonProps { - onModalClose?: () => void; -} - -const ScheduleRegenerateButton: FC = ({ onModalClose }) => { - const [open, setOpen] = useState(false); - - const handleCloseModal = () => { - setOpen(false) - if (onModalClose) { - onModalClose() - } - } - - const modalChildren = handleCloseModal()}/> - const buttonChild =
-
- - return -}; - -export default ScheduleRegenerateButton; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx b/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx deleted file mode 100644 index 017b8c2..0000000 --- a/frontend-old/archive/src/components/features/schedule/ScheduleRegenerateForm.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {DialogTitle} from "@headlessui/react"; -import Toggle from "@/components/ui/Toggle"; -import {FC, useEffect, useState} from "react"; -import {generateSchedule} from "@/utils/api/scheduleApi"; -import Alert from "@/components/ui/Alert"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface ScheduleRegenerateFormProps { - closeModal: () => void; -} - -const ScheduleRegenerateForm: FC = ({closeModal}) => { - const [overwrite, setOverwrite] = useState(false); - const [error, setError] = useState(""); - - useEffect(() => { - }, [overwrite]); - - const close = () => { - closeModal(); - } - - const handleToggle = () => { - setOverwrite(!overwrite) - } - - const handleSubmit = () => { - generateSchedule(overwrite) - .then(() => close()) - .catch((err) => setError(err)) - } - - return <> -
-
-
- - Regenerate Schedule - -
-
- { - error && { error } - } -
- -
-
- -
-
-
- - -
-
-
-
- handleSubmit()} - className="inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold shadow-xs sm:ml-3 sm:w-auto" - > - Regenerate - - close()} - className="mt-3 inline-flex w-full justify-center rounded-md bg-gray-500 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs border-secondary ring-inset sm:mt-0 sm:w-auto" - > - Cancel - -
- ; -}; - -export default ScheduleRegenerateForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx b/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx deleted file mode 100644 index c7731d0..0000000 --- a/frontend-old/archive/src/components/features/schedule/UpcomingDishes.tsx +++ /dev/null @@ -1,62 +0,0 @@ -"use client" - -import { useCallback, useEffect, useState } from "react"; -import { DateTime } from "luxon"; -import ScheduleCalendar from "@/components/features/schedule/ScheduleCalendar"; -import PageTitle from "@/components/ui/PageTitle"; -import Spinner from "@/components/Spinner"; -import { ScheduleType } from "@/types/ScheduleType"; -import { listSchedule } from "@/utils/api/scheduleApi"; -import OnboardingBanner from "@/components/features/OnboardingBanner" -import { useFetchUsers } from "@/hooks/useFetchUsers" -import { useFetchDishes } from "@/hooks/useFetchDishes" -import ScheduleRegenerateButton from "@/components/features/schedule/ScheduleRegenerateButton"; - -const UpcomingDishes = () => { - const [schedule, setSchedule] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - const today = DateTime.now().toFormat("yyyy-LL-dd"); - - const fetchSchedule = useCallback(() => { - setIsLoading(true); - listSchedule(today) - .then((dishes) => setSchedule(dishes)) - .finally(() => setIsLoading(false)); - }, [today]); - - useEffect(() => { - fetchSchedule(); - }, [fetchSchedule]); - - const { users, isLoading: areUsersLoading } = useFetchUsers(); - const { dishes, isLoading: areDishesLoading } = useFetchDishes(); - - if (isLoading || areUsersLoading || areDishesLoading) { - return ; - } - - if (users.length === 0 || dishes.length === 0) { - return - } - - return ( -
-
-
- Schedule -
-
- -
-
- { - !schedule || Object.keys(schedule).length === 0 - ?
No dishes scheduled
- : - } -
- ); -}; - -export default UpcomingDishes; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx b/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx deleted file mode 100644 index b82fc1b..0000000 --- a/frontend-old/archive/src/components/features/schedule/UserDishEditCard.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import {FC, FormEvent, useMemo, useState} from "react"; -import {DishType} from "@/types/DishType"; -import {ScheduledUserDishType} from "@/types/ScheduledUserDishType"; -import {updateScheduledUserDish} from "@/utils/api/scheduledUserDishesApi"; -import Alert from "@/components/ui/Alert"; -import classNames from "classnames"; - -interface Props { - scheduledUserDish: ScheduledUserDishType - allDishes: DishType[] -} - -const UserDishEditCard: FC = ({ scheduledUserDish, allDishes }) => { - const [selectedUserDishId, setSelectedUserDishId] = useState(scheduledUserDish.user_dish ? scheduledUserDish.user_dish.id : 0) - const [errorMessage, setErrorMessage] = useState("") - const [isSuccess, setIsSuccess] = useState(false); - - const selectStyle = classNames( - 'p-2', 'rounded', 'w-full', 'background-secondary', - 'focus:outline-none', - 'transition-[border-color] ease-out duration-1000', 'border-2', // Keep consistent base styles - { - 'border-green-500': isSuccess, // Green border when successful - 'border-red-500': !isSuccess && errorMessage !== "", // Red border when there's an error - 'border-secondary': !isSuccess && errorMessage === "", // Default border for neutral state - } - ) - - const handleOnChange = (e: FormEvent) => { - const userDishId = parseInt(e.currentTarget.value); - setSelectedUserDishId(userDishId); - - updateScheduledUserDish(scheduledUserDish.id, userDishId) - .then(() => { - setIsSuccess(false); - setTimeout(() => { - setIsSuccess(true); - setTimeout(() => setIsSuccess(false), 1000); - }, 0); - }) - .catch((error) => { - setErrorMessage(error); // Log API errors - }); - }; - - const filteredDishes = useMemo(() => - allDishes.filter((dish: DishType) => - dish.users.some((user) => user.id === scheduledUserDish.user_dish.user.id) - ), - [allDishes, scheduledUserDish.user_dish.user.id] - ) - - return ( -
-
{scheduledUserDish.user_dish.user.name}
- - { errorMessage !== "" && { errorMessage } } - - - -
- ); -}; - -export default UserDishEditCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx deleted file mode 100644 index 9a61d87..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/DateBadge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {DateTime} from "luxon"; -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - date: string - className?: string; -} - -const DateBadge: FC = ({ className, date }) => { - const isToday = DateTime.fromISO(date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const textStyle = classNames("inline font-bold", { - 'text-accent-blue': isToday, - 'text-secondary': !isToday, - }, className) - - return ( -
-
{DateTime.fromISO(date).toFormat("dd")}
-
-
{DateTime.fromISO(date).toFormat("LLL")}
-
- ) -} - -export default DateBadge \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx deleted file mode 100644 index 77c68e0..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCard.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, {FC} from "react"; -import {UserType} from "@/types/UserType"; -import ScheduleDayCardUserDish from "@/components/features/schedule/dayCard/ScheduleDayCardUserDish"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; -import Link from "next/link"; -import {PencilSquareIcon} from "@heroicons/react/24/outline"; -import useRoutes from "@/hooks/useRoutes"; -import DateBadge from "@/components/features/schedule/dayCard/DateBadge"; -import { DateTime } from "luxon" -import classNames from "classnames" - -interface Props { - schedule: ScheduleType|FilledScheduleType; - users: UserType[]; -} - -const ScheduleDayCard: FC = ({schedule, users}) => { - const routes = useRoutes() - const isToday = DateTime.fromISO(schedule.date).toFormat("yyyy-LL-dd") == DateTime.now().toFormat("yyyy-LL-dd") - - const containerStyles = classNames( - 'w-full bg-gray-500 pt-5 pb-2 rounded-2xl text-xl', { - 'border-2 text-accent-blue border-accent-blue': isToday, - } - ) - - return ( -
- - -
- { - users.map((user) => ) - } - -
- - Edit - -
-
-
- ); -}; - -export default ScheduleDayCard; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx b/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx deleted file mode 100644 index 64f0f0e..0000000 --- a/frontend-old/archive/src/components/features/schedule/dayCard/ScheduleDayCardUserDish.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC } from "react"; -import { ScheduledUserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; -import { FilledScheduleType, ScheduleType } from "@/types/ScheduleType"; - -interface Props { - schedule: ScheduleType|FilledScheduleType; - user: UserType; -} - -const ScheduleDayCardUserDish: FC = ({ schedule, user }) => { - const getDish = (user: UserType) => { - const scheduled_dishes = schedule.scheduled_user_dishes.filter((scheduled_user_dish: ScheduledUserDishType) => ( - scheduled_user_dish.user_dish?.user.id == user.id - )) - - if (scheduled_dishes.length > 0) { - return scheduled_dishes[0].user_dish.dish.name - } - - return '/' - } - - return ( -
-
{ user.name } :
-
{ getDish(user) }
-
- ); -}; - -export default ScheduleDayCardUserDish; \ No newline at end of file diff --git a/frontend-old/archive/src/components/features/users/EditUserForm.tsx b/frontend-old/archive/src/components/features/users/EditUserForm.tsx deleted file mode 100644 index 1593dd9..0000000 --- a/frontend-old/archive/src/components/features/users/EditUserForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, {FC, useState} from "react"; -import {useRouter} from "next/navigation"; -import useRoutes from "@/hooks/useRoutes"; -import {updateUser} from "@/utils/api/usersApi"; -import PageTitle from "@/components/ui/PageTitle"; -import Link from "next/link"; -import Alert from "@/components/ui/Alert"; -import {UserType} from "@/types/UserType"; -import SolidButton from "@/components/ui/Buttons/SolidButton"; - -interface Props { - user: UserType; -} - -const EditUserForm: FC = ({ user }) => { - - const [name, setName] = useState(user.name); - const [error, setError] = useState(''); - const router = useRouter(); - const routes = useRoutes(); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - // validateName - if (!name.trim()) { - setError('Name cannot be empty.'); - return; - } - - updateUser(user, name) - .then(() => { - router.push(routes.user.index()) - }) - } - - return ( -
- Create User - Back to users - -
- { - error != '' && { error } - } - - - setName(e.target.value)} - className="w-full p-2 border rounded bg-primary border-secondary bg-gray-600 text-secondary" - /> - - Update -
-
- ); -} - -export default EditUserForm; \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/AuthGuard.tsx b/frontend-old/archive/src/components/layout/AuthGuard.tsx deleted file mode 100644 index c4e0482..0000000 --- a/frontend-old/archive/src/components/layout/AuthGuard.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import { useAuth } from '@/context/AuthContext'; -import { useRouter, usePathname } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; - -// Optional Loading spinner component to display while loading -const LoadingSpinner = () => ( -
-
-
-); - -export default function AuthGuard({ children }: { children: React.ReactNode }) { - const { isAuthenticated } = useAuth(); // Access the authentication state from AuthContext - const router = useRouter(); - const pathname = usePathname(); - const [loading, setLoading] = useState(true); - - // Define public routes that can be accessed without authentication - const publicRoutes = ['/login', '/register']; - const isPublic = publicRoutes.includes(pathname); - - useEffect(() => { - // Determine behavior based on auth state and route type - if (isAuthenticated === null) { - // Await authentication resolution (e.g., token check) - setLoading(true); - } else if (isAuthenticated && isPublic) { - // Redirect authenticated users away from public pages - router.replace('/'); - } else if (!isAuthenticated && !isPublic) { - // Redirect unauthenticated users trying to access protected pages - router.replace('/login'); - } else { - // Otherwise, stop loading since the state is resolved - setLoading(false); - } - }, [isAuthenticated, pathname, isPublic, router]); - - // Show a spinner while authentication state is loading - if (loading) { - return ; - } - - // Render children only when the authentication state and path are valid - return <>{children}; -} \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/Card.tsx b/frontend-old/archive/src/components/layout/Card.tsx deleted file mode 100644 index 6784526..0000000 --- a/frontend-old/archive/src/components/layout/Card.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, {FC} from "react"; - -interface Props { - children: React.ReactNode; -} - -const Card: FC = ({ children }) => { - return ( -
- { children } -
- ) -} - -export default Card \ No newline at end of file diff --git a/frontend-old/archive/src/components/layout/NavBar.tsx b/frontend-old/archive/src/components/layout/NavBar.tsx deleted file mode 100644 index 67cadb2..0000000 --- a/frontend-old/archive/src/components/layout/NavBar.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import React, { useState } from "react"; -import Link from "next/link"; -import useRoutes from "@/hooks/useRoutes"; -import MobileDropdownMenu from "@/components/features/navbar/MobileDropdownMenu"; -import {useRouter} from "next/navigation"; -import {useAuth} from "@/context/AuthContext"; - -const NavBar = () => { - const [isOpen, setIsOpen] = useState(false); - const routes = useRoutes(); - const router = useRouter(); - const {isAuthenticated, logout} = useAuth(); - - const handleLogout = (e: React.MouseEvent) => { - e.preventDefault(); - logout(); - router.replace('/login'); - }; - - return ( - - ); -}; - -export default NavBar; \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Alert.tsx b/frontend-old/archive/src/components/ui/Alert.tsx deleted file mode 100644 index c32070c..0000000 --- a/frontend-old/archive/src/components/ui/Alert.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - type: 'error' | 'warning' | 'info' | 'success'; -} - -const Alert: FC = ({ children, className, type } ) => { - let bgColor = 'bg-blue-200' - let fgColor = 'bg-blue-800' - - if (type == 'error') { - bgColor = 'bg-red-200' - fgColor = 'bg-red-800' - } else if (type == 'warning') { - bgColor = 'bg-orange-200' - fgColor = 'bg-orange-800' - } else if (type == 'success') { - bgColor = 'border-2 border-green-500' - fgColor = 'text-green-500' - } - - const styles = classNames(fgColor, bgColor, className, 'rounded') - - return ( -
- { children} -
- ) -} - -export default Alert \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Button.tsx b/frontend-old/archive/src/components/ui/Button.tsx deleted file mode 100644 index 4ad006c..0000000 --- a/frontend-old/archive/src/components/ui/Button.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Link from "next/link"; -import React, { FC, ReactElement, ReactNode } from "react"; -import classNames from "classnames"; - -interface ButtonProps { - appearance?: 'solid' | 'outline' | 'text'; - children: ReactNode; - className?: string; - href?: string; - icon?: ReactNode; - onClick?: () => void; - disabled?: boolean; - size?: 'small' | 'medium' | 'large'; - type?: 'button' | 'submit' | 'reset'; - variant?: 'primary' | 'secondary' | 'accent'; -} - -const Button: FC = ({ appearance, children, className, disabled, href, icon, onClick, - size = 'medium', type, - variant = 'primary' -}) => { - const styles = classNames( - "flex items-center space-x-1", - "justify-center font-size-18 py-2 px-4 rounded flex", - { - 'border-2 border-primary background-red text-white': variant === 'primary' && appearance === 'solid', - 'border-2 border-primary text-primary': variant === 'primary' && appearance === 'outline', - 'text-primary': variant === 'primary' && appearance === 'text', - 'border-2 border-secondary text-secondary': variant === 'secondary' && appearance === 'outline', - 'border-2 border-accent-blue text-accent-blue': variant === 'accent' && appearance === 'outline', - }, - className - ) - - const iconClassNames = classNames({ - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - if (href !== undefined) { - return ( - - { icon && iconElement} - { children} - - ) - } - - return -} - -export default Button \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx b/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx deleted file mode 100644 index b668b6b..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/OutlineButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { FC } from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const OutlineButton: FC = ({ children, className, disabled = false, onClick, size, type }) => { - const style = classNames( - "justify-center border-2 border-accent font-size-18 text-accent-blue py-2 px-4 rounded flex", - { 'text-xs': size === "small" }, - className - ) - - if (onClick === undefined) { - onClick = () => { - } - } - - return ( - - ) -} - -export default OutlineButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx b/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx deleted file mode 100644 index ab0e9b1..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/OutlineLinkButton.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { FC, ReactElement } from "react"; -import classNames from "classnames"; -import Link from "next/link"; - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const OutlineLinkButton: FC = ({ children, className, href, icon, size = "medium", variant }) => { - const linkClassNames = classNames( - "underline font-default pt-3 pb-3 px-4 rounded mb-0 flex", - { - 'text-primary border-primary': variant === "primary", - 'text-secondary border-secondary': variant === "secondary", - 'text-accent-blue border-accent': !variant || !["primary", "secondary"].includes(variant), - }, { - 'text-size-14': size === "small", - 'font-size-18': !size || size === "medium", - 'text-2xl': size === "large", - }, - className, - ) - - const iconClassNames = classNames("mt-0.5", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - - {iconElement} - {children} - - ) -} - -export default OutlineLinkButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx b/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx deleted file mode 100644 index 62ac01d..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/SolidButton.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, {FC} from "react"; -import classNames from "classnames"; - -interface Props { - children: React.ReactNode; - className?: string; - disabled?: boolean; - onClick?: () => void; - size?: "small" | "medium" | "large"; - type: 'submit' | 'button'; -} - -const SolidButton: FC = ({ children, className, disabled = false, onClick, size, type }) => { - const style = classNames( - "py-2 px-4 bg-primary text-white text-xl p-2 rounded hover:bg-secondary mb-0", - { - 'text-xs' : size === "small", - 'font-size-18' : !size || size === "medium", - }, - className - ) - - if (onClick === undefined) { - onClick = () => {} - } - - return ( - - ) -} - -export default SolidButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx b/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx deleted file mode 100644 index 77a78dd..0000000 --- a/frontend-old/archive/src/components/ui/Buttons/SolidLinkButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { FC, ReactElement } from "react"; -import classNames from "classnames"; -import Link from "next/link"; - -interface Props { - children: React.ReactNode; - className?: string; - href: string; - icon?: React.ReactNode; - size?: "small" | "medium" | "large"; - variant?: "primary" | "secondary"; -} - -const SolidLinkButton: FC = ({ children, className, href, icon, size = "medium", variant }) => { - const style = classNames( - "py-2 px-4 text-xl p-2 rounded hover:bg-secondary mb-0 text-center flex", - { - 'background-red text-white': variant === "primary", - 'background-secondary border-2 border-secondary': variant === "secondary", - }, - className - ) - - const iconClassNames = classNames("mt-1", { - "h-4 w-4 mr-1": size === "small", - "h-5 w-5 mr-1": size === "medium", // Default size - "h-7 w-7 mr-2": size === "large", - }); - - const iconElement = - React.isValidElement(icon) && - React.cloneElement(icon as ReactElement<{ className?: string }>, { - className: iconClassNames, - }); - - return ( - -
- {iconElement} - {children} -
- - ) -} - -export default SolidLinkButton \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Description.tsx b/frontend-old/archive/src/components/ui/Description.tsx deleted file mode 100644 index 8b429c0..0000000 --- a/frontend-old/archive/src/components/ui/Description.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import classNames from "classnames"; -import React from "react"; - -interface Props { - children: React.ReactNode; - className?: string; -} - -const Description = ({ children, className }: Props) => { - const style = classNames("italic font-size-16", - className - ) - - return

{ children }

-} - -export default Description \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Hr.tsx b/frontend-old/archive/src/components/ui/Hr.tsx deleted file mode 100644 index 59f0d2d..0000000 --- a/frontend-old/archive/src/components/ui/Hr.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { FC } from "react" -import classNames from "classnames" - -interface HrProps { - className?: string; -} - -const Hr: FC = ({ className }) => { - const styles = classNames("my-4 border-secondary", className) - - return
-} - -export default Hr \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Label.tsx b/frontend-old/archive/src/components/ui/Label.tsx deleted file mode 100644 index 80bea0b..0000000 --- a/frontend-old/archive/src/components/ui/Label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, {FC, ReactNode} from "react"; - -interface LabelProps { - href?: string; - children: ReactNode; - onClick?: () => void; -} - -const Label: FC = ({ href, children, onClick }) => { - const styles = "items-center space-x-1 background-accent p-2 rounded" - - if (href !== undefined) { - return ( -
- { children} -
- ) - } - - return -} - -export default Label \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Modal.tsx b/frontend-old/archive/src/components/ui/Modal.tsx deleted file mode 100644 index e6e5c9c..0000000 --- a/frontend-old/archive/src/components/ui/Modal.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import {FC, JSX} from "react"; -import {Dialog, DialogBackdrop, DialogPanel} from "@headlessui/react"; -import classNames from "classnames"; -import {XMarkIcon} from "@heroicons/react/24/outline"; -import Button from "@/components/ui/Button" - -interface ModalProps { - buttonChildren?: JSX.Element; - buttonClassName?: string; - buttonLabel?: string; - modalChildren: JSX.Element; - modalOpen?: boolean; - setModalOpen: (open: boolean) => void; -} - -const Modal: FC = ({ - buttonLabel, - buttonClassName, - modalChildren, - modalOpen, - buttonChildren, - setModalOpen, -}) => { - const buttonStyles = classNames(buttonClassName, 'anta-regular'); - - const closeModal = () => { - setModalOpen(false) - } - - return ( - <> - - - - -
-
- - closeModal()}/> - {modalChildren} - -
-
-
- - ) -} - -export default Modal; \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/PageTitle.tsx b/frontend-old/archive/src/components/ui/PageTitle.tsx deleted file mode 100644 index ca65f18..0000000 --- a/frontend-old/archive/src/components/ui/PageTitle.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import classNames from "classnames"; -import {FC} from "react"; - -interface Props { - children: string, - className?: string, -} - -const PageTitle: FC = ({ children, className }) => { - const styles = classNames( - 'ml-4 text-2xl font-default uppercase w-full text-accent-blue font-bold', - className, - ) - - return

{ children }

-} - -export default PageTitle \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/RecurrenceInput.tsx b/frontend-old/archive/src/components/ui/RecurrenceInput.tsx deleted file mode 100644 index 756715d..0000000 --- a/frontend-old/archive/src/components/ui/RecurrenceInput.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, {FC, useState} from "react"; - -interface Props { - value: number; - setValue: (value: number) => void; -} - -const RecurrenceInput: FC = ({ value, setValue}) => { - const [openInput, setOpenInput] = useState<'category' | 'number'>([7, 365].includes(value) ? 'category' : 'number') - - const toggleInput = (e: React.MouseEvent) => { - e.preventDefault() - setOpenInput(openInput == 'category' ? 'number' : 'category') - } - - const toggleButton = () => { - return ( - - ) - } - - const prepareValue = (v: string) => { - setValue(parseInt(v)) - } - - return ( -
-
- - - { toggleButton() } -
- -
- - prepareValue(e.target.value)} - className="p-2 border rounded w-full bg-gray-500 border-secondary" - /> - { toggleButton() } -
-
- ) -} - -export default RecurrenceInput \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/SectionTitle.tsx b/frontend-old/archive/src/components/ui/SectionTitle.tsx deleted file mode 100644 index 93d13c4..0000000 --- a/frontend-old/archive/src/components/ui/SectionTitle.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import classNames from "classnames"; - -interface Props { - children: string; - className?: string; -} - -const SectionTitle = ({ children, className }: Props) => { - const style = classNames("block font-size-18 uppercase w-full pl-2 text-accent-blue", - className - ) - - return

{ children }

-} - -export default SectionTitle \ No newline at end of file diff --git a/frontend-old/archive/src/components/ui/Toggle.tsx b/frontend-old/archive/src/components/ui/Toggle.tsx deleted file mode 100644 index 786b093..0000000 --- a/frontend-old/archive/src/components/ui/Toggle.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import {FC} from "react"; - -interface ToggleProps { - checked: boolean; - onChange: (checked: boolean) => void; -} - -const Toggle: FC = ({ checked, onChange }) => { - const handleChange = () => { - onChange(checked); - } - - return ( - - ); -}; - -export default Toggle; \ No newline at end of file diff --git a/frontend-old/archive/src/context/AuthContext.tsx b/frontend-old/archive/src/context/AuthContext.tsx deleted file mode 100644 index 8d77eb3..0000000 --- a/frontend-old/archive/src/context/AuthContext.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client" - -import React, { createContext, useContext, useEffect, useState } from 'react'; - -interface AuthContextProps { - isAuthenticated: boolean | null; - login: () => void; - logout: () => void; -} - -const AuthContext = createContext({ - isAuthenticated: null, - login: () => {}, - logout: () => {}, -}); - -export const AuthProvider = ({ children }: { children: React.ReactNode }) => { - const [isAuthenticated, setIsAuthenticated] = useState(null); - - useEffect(() => { - const token = localStorage.getItem('token'); - if (token) { - // You could add any token validation logic here - setIsAuthenticated(true); - } else { - setIsAuthenticated(false); - } - }, []); - - const login = () => { - setIsAuthenticated(true); - }; - - const logout = () => { - setIsAuthenticated(false); - localStorage.removeItem('token'); - }; - - return ( - - {children} - - ); -}; - -export const useAuth = () => useContext(AuthContext); \ No newline at end of file diff --git a/frontend-old/archive/src/helpers/Date.ts b/frontend-old/archive/src/helpers/Date.ts deleted file mode 100644 index f183c86..0000000 --- a/frontend-old/archive/src/helpers/Date.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DateTime } from 'luxon'; - -// Validate if a given string matches the "yyyy-MM-dd" format and is a valid date -export const isValidDate = (date: string): boolean => { - const parsedDate = DateTime.fromFormat(date, 'yyyy-MM-dd'); - return parsedDate.isValid && parsedDate.toFormat('yyyy-MM-dd') === date; -}; - -// Format a date to a specific string format -export const formatDate = (date: Date | string, format: string = 'yyyy-MM-dd'): string => { - const parsedDate = typeof date === 'string' ? DateTime.fromISO(date) : DateTime.fromJSDate(date); - return parsedDate.toFormat(format); -}; - -// Compare two dates to see if one is before the other -export const isBefore = (date1: string, date2: string): boolean => { - return DateTime.fromISO(date1) < DateTime.fromISO(date2); -}; diff --git a/frontend-old/archive/src/hooks/useFetchDishes.ts b/frontend-old/archive/src/hooks/useFetchDishes.ts deleted file mode 100644 index a7ab747..0000000 --- a/frontend-old/archive/src/hooks/useFetchDishes.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import { listDishes } from "@/utils/api/dishApi" -import { DishType } from "@/types/DishType" - -export const useFetchDishes = () => { - const [dishes, setDishes] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchDishes = async () => { - listDishes() - .then((dishes: DishType[]) => setDishes(dishes)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchDishes(); - }, []); - - return { dishes, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/hooks/useFetchUsers.ts b/frontend-old/archive/src/hooks/useFetchUsers.ts deleted file mode 100644 index f6df05b..0000000 --- a/frontend-old/archive/src/hooks/useFetchUsers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useState, useEffect } from "react"; -import {UserType} from "@/types/UserType"; -import {listUsers} from "@/utils/api/usersApi"; - -export const useFetchUsers = () => { - const [users, setUsers] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchUsers = async () => { - listUsers() - .then((users: UserType[]) => setUsers(users)) - .catch((err) => setError((err as Error).message || "An error occurred.")) - .finally(() => setIsLoading(false)); - }; - - fetchUsers(); - }, []); - - return { users, isLoading, error }; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/hooks/useRoutes.ts b/frontend-old/archive/src/hooks/useRoutes.ts deleted file mode 100644 index 7120da4..0000000 --- a/frontend-old/archive/src/hooks/useRoutes.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {UserType} from "@/types/UserType"; - -const useRoutes = () => { - return { - home: () => "/", - auth: { - login: () => "/login", - register: () => "/register", - }, - dish: { - index: () => "/dishes", - create: () => "/dishes/create", - edit: (dish: DishType) => `/dishes/${dish.id}/edit`, - delete: (dish: DishType) => `/dishes/${dish.id}/delete`, - }, - schedule: { - date: { - edit: (date: string) => `/schedule/${date}/edit` - }, - history: () => "/scheduled-user-dishes/history", - }, - user: { - index: () => "/users", - create: () => `/users/create`, - edit: (user: UserType) => `/users/${user.id}/edit`, - delete: (user: UserType) => `/users/${user.id}/delete`, - } - }; -}; - -export default useRoutes; \ No newline at end of file diff --git a/frontend-old/archive/src/styles/base/globals.css b/frontend-old/archive/src/styles/base/globals.css deleted file mode 100644 index 918cbfb..0000000 --- a/frontend-old/archive/src/styles/base/globals.css +++ /dev/null @@ -1,19 +0,0 @@ -html, body { - margin: 0; - padding: 0; - width: 100%; - overflow-x: hidden; -} - -body { - font-family: Arial, Helvetica, sans-serif; -} - - -.toggle-input:checked { - background-color: #22c55e; /* bg-green-500 */ -} - -.toggle-input:checked ~ span:last-child { - --tw-translate-x: 1.75rem; /* translate-x-7 */ -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/components/buttons.css b/frontend-old/archive/src/styles/components/buttons.css deleted file mode 100644 index 07fc4de..0000000 --- a/frontend-old/archive/src/styles/components/buttons.css +++ /dev/null @@ -1,42 +0,0 @@ -.button-primary-solid { - background-color: var(--color-primary); - color: var(--color-secondary-200); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} -.button-primary-outline { - background-color: var(--color-background); - color: var(--color-primary); - border: 1px solid var(--color-primary); - text-transform: uppercase; - font-family: "Anta", serif; - font-style: normal; - font-size: 1.1rem; - font-weight: 600; - padding: 4px 16px 2px 16px; -} - -.button-secondary-solid { - background-color: var(--color-secondary); - color: var(--color-primary); - border: 1px solid var(--color-secondary); -} - -.button-accent-solid { - background-color: var(--color-accent-blue); - color: var(--color-secondary-900); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline { - background-color: var(--color-background); - color: var(--color-accent-blue); - border: 1px solid var(--color-accent-blue); -} -.button-accent-outline:hover { - background-color: var(--color-background-400); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/components/select.css b/frontend-old/archive/src/styles/components/select.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend-old/archive/src/styles/main.css b/frontend-old/archive/src/styles/main.css deleted file mode 100644 index 5ca69ea..0000000 --- a/frontend-old/archive/src/styles/main.css +++ /dev/null @@ -1,10 +0,0 @@ -@import "./theme/borders.css"; -@import "./theme/fonts.css"; -@import "./components/buttons.css"; - -@import "./base/globals.css"; -@import "./theme/colors.css"; - -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/frontend-old/archive/src/styles/theme/borders.css b/frontend-old/archive/src/styles/theme/borders.css deleted file mode 100644 index b4ed5db..0000000 --- a/frontend-old/archive/src/styles/theme/borders.css +++ /dev/null @@ -1,14 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} -.border-accent-800 { - border-color: var(--color-accent-blue-800); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors.css b/frontend-old/archive/src/styles/theme/colors.css deleted file mode 100644 index 338bcd7..0000000 --- a/frontend-old/archive/src/styles/theme/colors.css +++ /dev/null @@ -1,10 +0,0 @@ -@import './colors/root.css'; -@import 'colors/background.css'; -@import 'colors/border.css'; -@import 'colors/text.css'; - -body { - color: var(--color-secondary) !important; - background: var(--color-gray-600) !important; -} - diff --git a/frontend-old/archive/src/styles/theme/colors/background.css b/frontend-old/archive/src/styles/theme/colors/background.css deleted file mode 100644 index e8548f5..0000000 --- a/frontend-old/archive/src/styles/theme/colors/background.css +++ /dev/null @@ -1,226 +0,0 @@ -.bg-gray-100 { - background-color: var(--color-gray-100) !important; -} -.bg-gray-200 { - background-color: var(--color-gray-200) !important; -} -.bg-gray-300 { - background-color: var(--color-gray-300) !important; -} -.bg-gray-400 { - background-color: var(--color-gray-400) !important; -} -.bg-gray-500 { - background-color: var(--color-gray-500) !important; -} -.bg-gray-600 { - background-color: var(--color-gray-600) !important; -} -.bg-gray-700 { - background-color: var(--color-gray-700) !important; -} -.bg-gray-800 { - background-color: var(--color-gray-800) !important; -} -.bg-gray-900 { - background-color: var(--color-gray-900) !important; -} - - -.bg-primary { - background-color: var(--color-primary) !important; -} - - -.bg-accent-blue { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-100 { - background-color: var(--color-accent-blue-100) !important; -} -.bg-accent-blue-200 { - background-color: var(--color-accent-blue-200) !important; -} -.bg-accent-blue-300 { - background-color: var(--color-accent-blue-300) !important; -} -.bg-accent-blue-400 { - background-color: var(--color-accent-blue-400) !important; -} -.bg-accent-blue-500 { - background-color: var(--color-accent-blue-500) !important; -} -.bg-accent-blue-600 { - background-color: var(--color-accent-blue-600) !important; -} -.bg-accent-blue-700 { - background-color: var(--color-accent-blue-700) !important; -} -.bg-accent-blue-800 { - background-color: var(--color-accent-blue-800) !important; -} -.bg-accent-blue-900 { - background-color: var(--color-accent-blue-900) !important; -} - - -.bg-accent-yellow { - background-color: var(--color-accent-yellow) !important; -} - -.bg-accent-yellow-100 { - background-color: var(--color-accent-yellow-100) !important; -} - -.bg-accent-yellow-200 { - background-color: var(--color-accent-yellow-200) !important; -} - -.bg-accent-yellow-300 { - background-color: var(--color-accent-yellow-300) !important; -} - -.bg-accent-yellow-400 { - background-color: var(--color-accent-yellow-400) !important; -} - -.bg-accent-yellow-500 { - background-color: var(--color-accent-yellow-500) !important; -} - -.bg-accent-yellow-600 { - background-color: var(--color-accent-yellow-600) !important; -} - -.bg-accent-yellow-700 { - background-color: var(--color-accent-yellow-700) !important; -} - -.bg-accent-yellow-800 { - background-color: var(--color-accent-yellow-800) !important; -} - -.bg-accent-yellow-900 { - background-color: var(--color-accent-yellow-900) !important; -} - - -.bg-success { - background-color: var(--color-success) !important; -} - -.bg-success-100 { - background-color: var(--color-success-100) !important; -} - -.bg-success-200 { - background-color: var(--color-success-200) !important; -} - -.bg-success-300 { - background-color: var(--color-success-300) !important; -} - -.bg-success-400 { - background-color: var(--color-success-400) !important; -} - -.bg-success-500 { - background-color: var(--color-success-500) !important; -} - -.bg-success-600 { - background-color: var(--color-success-600) !important; -} - -.bg-success-700 { - background-color: var(--color-success-700) !important; -} - -.bg-success-800 { - background-color: var(--color-success-800) !important; -} - -.bg-success-900 { - background-color: var(--color-success-900) !important; -} - -.bg-warning { - background-color: var(--color-warning) !important; -} - -.bg-warning-100 { - background-color: var(--color-warning-100) !important; -} - -.bg-warning-200 { - background-color: var(--color-warning-200) !important; -} - -.bg-warning-300 { - background-color: var(--color-warning-300) !important; -} - -.bg-warning-400 { - background-color: var(--color-warning-400) !important; -} - -.bg-warning-500 { - background-color: var(--color-warning-500) !important; -} - -.bg-warning-600 { - background-color: var(--color-warning-600) !important; -} - -.bg-warning-700 { - background-color: var(--color-warning-700) !important; -} - -.bg-warning-800 { - background-color: var(--color-warning-800) !important; -} - -.bg-warning-900 { - background-color: var(--color-warning-900) !important; -} - -.bg-danger { - background-color: var(--color-danger) !important; -} - -.bg-danger-100 { - background-color: var(--color-danger-100) !important; -} - -.bg-danger-200 { - background-color: var(--color-danger-200) !important; -} - -.bg-danger-300 { - background-color: var(--color-danger-300) !important; -} - -.bg-danger-400 { - background-color: var(--color-danger-400) !important; -} - -.bg-danger-500 { - background-color: var(--color-danger-500) !important; -} - -.bg-danger-600 { - background-color: var(--color-danger-600) !important; -} - -.bg-danger-700 { - background-color: var(--color-danger-700) !important; -} - -.bg-danger-800 { - background-color: var(--color-danger-800) !important; -} - -.bg-danger-900 { - background-color: var(--color-danger-900) !important; -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors/border.css b/frontend-old/archive/src/styles/theme/colors/border.css deleted file mode 100644 index 8975296..0000000 --- a/frontend-old/archive/src/styles/theme/colors/border.css +++ /dev/null @@ -1,286 +0,0 @@ -.border-primary { - border-color: var(--color-primary); -} - -.border-primary-100 { - border-color: var(--color-primary-100); -} - -.border-primary-200 { - border-color: var(--color-primary-200); -} - -.border-primary-300 { - border-color: var(--color-primary-300); -} - -.border-primary-400 { - border-color: var(--color-primary-400); -} - -.border-primary-500 { - border-color: var(--color-primary-500); -} - -.border-primary-600 { - border-color: var(--color-primary-600); -} - -.border-primary-700 { - border-color: var(--color-primary-700); -} - -.border-primary-800 { - border-color: var(--color-primary-800); -} - -.border-primary-900 { - border-color: var(--color-primary-900); -} - - -.border-secondary { - border-color: var(--color-secondary); -} - -.border-secondary-100 { - border-color: var(--color-secondary-100); -} - -.border-secondary-200 { - border-color: var(--color-secondary-200); -} - -.border-secondary-300 { - border-color: var(--color-secondary-300); -} - -.border-secondary-400 { - border-color: var(--color-secondary-400); -} - -.border-secondary-500 { - border-color: var(--color-secondary-500); -} - -.border-secondary-600 { - border-color: var(--color-secondary-600); -} - -.border-secondary-700 { - border-color: var(--color-secondary-700); -} - -.border-secondary-800 { - border-color: var(--color-secondary-800); -} - -.border-secondary-900 { - border-color: var(--color-secondary-900); -} - -.border-accent-blue { - border-color: var(--color-accent-blue); -} - -.border-accent-blue-100 { - border-color: var(--color-accent-blue-100); -} - -.border-accent-blue-200 { - border-color: var(--color-accent-blue-200); -} - -.border-accent-blue-300 { - border-color: var(--color-accent-blue-300); -} - -.border-accent-blue-400 { - border-color: var(--color-accent-blue-400); -} - -.border-accent-blue-500 { - border-color: var(--color-accent-blue-500); -} - -.border-accent-blue-600 { - border-color: var(--color-accent-blue-600); -} - -.border-accent-blue-700 { - border-color: var(--color-accent-blue-700); -} - -.border-accent-blue-800 { - border-color: var(--color-accent-blue-800); -} - -.border-accent-blue-900 { - border-color: var(--color-accent-blue-900); -} - - -.border-accent-yellow { - border-color: var(--color-accent-yellow); -} - -.border-accent-yellow-100 { - border-color: var(--color-accent-yellow-100); -} - -.border-accent-yellow-200 { - border-color: var(--color-accent-yellow-200); -} - -.border-accent-yellow-300 { - border-color: var(--color-accent-yellow-300); -} - -.border-accent-yellow-400 { - border-color: var(--color-accent-yellow-400); -} - -.border-accent-yellow-500 { - border-color: var(--color-accent-yellow-500); -} - -.border-accent-yellow-600 { - border-color: var(--color-accent-yellow-600); -} - -.border-accent-yellow-700 { - border-color: var(--color-accent-yellow-700); -} - -.border-accent-yellow-800 { - border-color: var(--color-accent-yellow-800); -} - -.border-accent-yellow-900 { - border-color: var(--color-accent-yellow-900); -} - - -.border-background { - border-color: var(--color-background) !important; -} - -.border-danger { - border-color: var(--color-danger); -} - -.border-danger-100 { - border-color: var(--color-danger-100); -} - -.border-danger-200 { - border-color: var(--color-danger-200); -} - -.border-danger-300 { - border-color: var(--color-danger-300); -} - -.border-danger-400 { - border-color: var(--color-danger-400); -} - -.border-danger-500 { - border-color: var(--color-danger-500); -} - -.border-danger-600 { - border-color: var(--color-danger-600); -} - -.border-danger-700 { - border-color: var(--color-danger-700); -} - -.border-danger-800 { - border-color: var(--color-danger-800); -} - -.border-danger-900 { - border-color: var(--color-danger-900); -} - -.border-success { - border-color: var(--color-success); -} - -.border-success-100 { - border-color: var(--color-success-100); -} - -.border-success-200 { - border-color: var(--color-success-200); -} - -.border-success-300 { - border-color: var(--color-success-300); -} - -.border-success-400 { - border-color: var(--color-success-400); -} - -.border-success-500 { - border-color: var(--color-success-500); -} - -.border-success-600 { - border-color: var(--color-success-600); -} - -.border-success-700 { - border-color: var(--color-success-700); -} - -.border-success-800 { - border-color: var(--color-success-800); -} - -.border-success-900 { - border-color: var(--color-success-900); -} - -.border-warning { - border-color: var(--color-warning); -} - -.border-warning-100 { - border-color: var(--color-warning-100); -} - -.border-warning-200 { - border-color: var(--color-warning-200); -} - -.border-warning-300 { - border-color: var(--color-warning-300); -} - -.border-warning-400 { - border-color: var(--color-warning-400); -} - -.border-warning-500 { - border-color: var(--color-warning-500); -} - -.border-warning-600 { - border-color: var(--color-warning-600); -} - -.border-warning-700 { - border-color: var(--color-warning-700); -} - -.border-warning-800 { - border-color: var(--color-warning-800); -} - -.border-warning-900 { - border-color: var(--color-warning-900); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/colors/root.css b/frontend-old/archive/src/styles/theme/colors/root.css deleted file mode 100644 index acffac0..0000000 --- a/frontend-old/archive/src/styles/theme/colors/root.css +++ /dev/null @@ -1,193 +0,0 @@ -:root { - --color-rose-50: #FFF5FC; - --color-rose-100: #FCE6F5; - --color-rose-200: #FAC3E7; - --color-rose-300: #F7A1D5; - --color-rose-400: #F25EAB; - --color-rose-500: #ED1F79; - --color-rose-600: #D61A68; - --color-rose-700: #B3124F; - --color-rose-800: #8F0B39; - --color-rose-900: #6B0626; - --color-rose-950: #450315; - - --color-deluge-50: #FAF7FC; - --color-deluge-100: #F2EDF7; - --color-deluge-200: #E2DAF0; - --color-deluge-300: #CEC3E6; - --color-deluge-400: #A49BD1; - --color-deluge-500: #7776BC; - --color-deluge-600: #6361AB; - --color-deluge-700: #43428C; - --color-deluge-800: #2C2B70; - --color-deluge-900: #191854; - --color-deluge-950: #0A0A36; - - --color-malibu-50: #FAFEFF; - --color-malibu-100: #F5FDFF; - --color-malibu-200: #E1F6FC; - --color-malibu-300: #CDEDFA; - --color-malibu-400: #ABDEF7; - --color-malibu-500: #85C7F2; - --color-malibu-600: #6EACDB; - --color-malibu-700: #4A81B5; - --color-malibu-800: #305F91; - --color-malibu-900: #1B3F6E; - --color-malibu-950: #0B2247; - - --color-gamboge-50: #FFFDF2; - --color-gamboge-100: #FCF7E3; - --color-gamboge-200: #FAECBB; - --color-gamboge-300: #F5DC93; - --color-gamboge-400: #EDBB47; - --color-gamboge-500: #E59500; - --color-gamboge-600: #CF7F00; - --color-gamboge-700: #AB6100; - --color-gamboge-800: #8A4700; - --color-gamboge-900: #663000; - --color-gamboge-950: #421C00; - - --color-ebony-clay-100: #9AA2B3; /* Soft slate */ - --color-ebony-clay-200: #7A8093; /* Balanced midtone */ - --color-ebony-clay-300: #5D637A; /* Former 400 */ - --color-ebony-clay-400: #444760; /* New shadowed steel */ - --color-ebony-clay-500: #2B2C41; - --color-ebony-clay-600: #24263C; /* Adjusted — less jumpy */ - --color-ebony-clay-700: #1D1E36; /* Interpolated midpoint */ - --color-ebony-clay-800: #131427; /* Slightly lifted from old 800 */ - --color-ebony-clay-900: #0A0B1C; - --color-ebony-clay-950: #030412; - - --color-alizarin-crimson-50: #FFF5FA; - --color-alizarin-crimson-100: #FCE6F1; - --color-alizarin-crimson-200: #FAC3DC; - --color-alizarin-crimson-300: #F59FC0; - --color-alizarin-crimson-400: #F05D82; - --color-alizarin-crimson-500: #E71D36; - --color-alizarin-crimson-600: #D1192F; - --color-alizarin-crimson-700: #AD1121; - --color-alizarin-crimson-800: #8C0B18; - --color-alizarin-crimson-900: #69060E; - --color-alizarin-crimson-950: #420308; - - --color-spring-green-50: #F5FFFC; - --color-spring-green-100: #E8FFF9; - --color-spring-green-200: #C7FFEE; - --color-spring-green-300: #A4FCDF; - --color-spring-green-400: #62FCBC; - --color-spring-green-500: #21FA90; - --color-spring-green-600: #1BE07A; - --color-spring-green-700: #13BA5E; - --color-spring-green-800: #0C9646; - --color-spring-green-900: #07702D; - --color-spring-green-950: #03471A; - - --color-burning-orange-50: #FFFBF5; - --color-burning-orange-100: #FFF7EB; - --color-burning-orange-200: #FFE8CC; - --color-burning-orange-300: #FFD5AD; - --color-burning-orange-400: #FFA973; - --color-burning-orange-500: #FF6B35; - --color-burning-orange-600: #E65A2C; - --color-burning-orange-700: #BF441F; - --color-burning-orange-800: #993114; - --color-burning-orange-900: #731F0A; - --color-burning-orange-950: #4A1004; - - /* Standard naming */ - - --color-primary: var(--color-rose-500); - --color-primary-100: var(--color-rose-100); - --color-primary-200: var(--color-rose-200); - --color-primary-300: var(--color-rose-300); - --color-primary-400: var(--color-rose-400); - --color-primary-500: var(--color-rose-500); - --color-primary-600: var(--color-rose-600); - --color-primary-700: var(--color-rose-700); - --color-primary-800: var(--color-rose-800); - --color-primary-900: var(--color-rose-900); - - --color-secondary: var(--color-deluge-500); - --color-secondary-100: var(--color-deluge-100); - --color-secondary-200: var(--color-deluge-200); - --color-secondary-300: var(--color-deluge-300); - --color-secondary-400: var(--color-deluge-400); - --color-secondary-500: var(--color-deluge-500); - --color-secondary-600: var(--color-deluge-600); - --color-secondary-700: var(--color-deluge-700); - --color-secondary-800: var(--color-deluge-800); - --color-secondary-900: var(--color-deluge-900); - - --color-accent-blue: var(--color-malibu-500); - --color-accent-blue-100: var(--color-malibu-100); - --color-accent-blue-200: var(--color-malibu-200); - --color-accent-blue-300: var(--color-malibu-300); - --color-accent-blue-400: var(--color-malibu-400); - --color-accent-blue-500: var(--color-malibu-500); - --color-accent-blue-600: var(--color-malibu-600); - --color-accent-blue-700: var(--color-malibu-700); - --color-accent-blue-800: var(--color-malibu-800); - --color-accent-blue-900: var(--color-malibu-900); - - --color-accent-yellow: var(--color-gamboge-500); - --color-accent-yellow-50: var(--color-gamboge-50); - --color-accent-yellow-100: var(--color-gamboge-100); - --color-accent-yellow-200: var(--color-gamboge-200); - --color-accent-yellow-300: var(--color-gamboge-300); - --color-accent-yellow-400: var(--color-gamboge-400); - --color-accent-yellow-500: var(--color-gamboge-500); - --color-accent-yellow-600: var(--color-gamboge-600); - --color-accent-yellow-700: var(--color-gamboge-700); - --color-accent-yellow-800: var(--color-gamboge-800); - --color-accent-yellow-900: var(--color-gamboge-900); - --color-accent-yellow-950: var(--color-gamboge-950); - - --color-gray-100: var(--color-ebony-clay-100); - --color-gray-200: var(--color-ebony-clay-200); - --color-gray-300: var(--color-ebony-clay-300); - --color-gray-400: var(--color-ebony-clay-400); - --color-gray-500: var(--color-ebony-clay-500); - --color-gray-600: var(--color-ebony-clay-600); - --color-gray-700: var(--color-ebony-clay-700); - --color-gray-800: var(--color-ebony-clay-800); - --color-gray-900: var(--color-ebony-clay-900); - - --color-danger: var(--color-alizarin-crimson-500); - --color-danger-50: var(--color-alizarin-crimson-50); - --color-danger-100: var(--color-alizarin-crimson-100); - --color-danger-200: var(--color-alizarin-crimson-200); - --color-danger-300: var(--color-alizarin-crimson-300); - --color-danger-400: var(--color-alizarin-crimson-400); - --color-danger-500: var(--color-alizarin-crimson-500); - --color-danger-600: var(--color-alizarin-crimson-600); - --color-danger-700: var(--color-alizarin-crimson-700); - --color-danger-800: var(--color-alizarin-crimson-800); - --color-danger-900: var(--color-alizarin-crimson-900); - --color-danger-950: var(--color-alizarin-crimson-950); - - --color-success: var(--color-spring-green-500); - --color-success-50: var(--color-spring-green-50); - --color-success-100: var(--color-spring-green-100); - --color-success-200: var(--color-spring-green-200); - --color-success-300: var(--color-spring-green-300); - --color-success-400: var(--color-spring-green-400); - --color-success-500: var(--color-spring-green-500); - --color-success-600: var(--color-spring-green-600); - --color-success-700: var(--color-spring-green-700); - --color-success-800: var(--color-spring-green-800); - --color-success-900: var(--color-spring-green-900); - --color-success-950: var(--color-spring-green-950); - - --color-warning: var(--color-burning-orange-500); - --color-warning-50: var(--color-burning-orange-50); - --color-warning-100: var(--color-burning-orange-100); - --color-warning-200: var(--color-burning-orange-200); - --color-warning-300: var(--color-burning-orange-300); - --color-warning-400: var(--color-burning-orange-400); - --color-warning-500: var(--color-burning-orange-500); - --color-warning-600: var(--color-burning-orange-600); - --color-warning-700: var(--color-burning-orange-700); - --color-warning-800: var(--color-burning-orange-800); - --color-warning-900: var(--color-burning-orange-900); - --color-warning-950: var(--color-burning-orange-950); -} diff --git a/frontend-old/archive/src/styles/theme/colors/text.css b/frontend-old/archive/src/styles/theme/colors/text.css deleted file mode 100644 index 1683846..0000000 --- a/frontend-old/archive/src/styles/theme/colors/text.css +++ /dev/null @@ -1,216 +0,0 @@ -.text-primary { - color: var(--color-primary); -} -.text-primary-100 { - color: var(--color-primary-100); -} -.text-primary-200 { - color: var(--color-primary-200); -} -.text-primary-300 { - color: var(--color-primary-300); -} -.text-primary-400 { - color: var(--color-primary-400); -} -.text-primary-500 { - color: var(--color-primary-500); -} -.text-primary-600 { - color: var(--color-primary-600); -} -.text-primary-700 { - color: var(--color-primary-700); -} -.text-primary-800 { - color: var(--color-primary-800); -} -.text-primary-900 { - color: var(--color-primary-900); -} - -.text-secondary { - color: var(--color-secondary); -} -.text-secondary-100 { - color: var(--color-secondary-100); -} -.text-secondary-200 { - color: var(--color-secondary-200); -} -.text-secondary-300 { - color: var(--color-secondary-300); -} -.text-secondary-400 { - color: var(--color-secondary-400); -} -.text-secondary-500 { - color: var(--color-secondary-500); -} -.text-secondary-600 { - color: var(--color-secondary-600); -} -.text-secondary-700 { - color: var(--color-secondary-700); -} -.text-secondary-800 { - color: var(--color-secondary-800); -} -.text-secondary-900 { - color: var(--color-secondary-900); -} - -.text-accent-blue { - color: var(--color-accent-blue); -} -.text-accent-blue-100 { - color: var(--color-accent-blue-100); -} -.text-accent-blue-200 { - color: var(--color-accent-blue-200); -} -.text-accent-blue-300 { - color: var(--color-accent-blue-300); -} -.text-accent-blue-400 { - color: var(--color-accent-blue-400); -} -.text-accent-blue-500 { - color: var(--color-accent-blue-500); -} -.text-accent-blue-600 { - color: var(--color-accent-blue-600); -} -.text-accent-blue-700 { - color: var(--color-accent-blue-700); -} -.text-accent-blue-800 { - color: var(--color-accent-blue-800); -} -.text-accent-blue-900 { - color: var(--color-accent-blue-900); -} - -.text-accent-yellow { - color: var(--color-accent-yellow); -} -.text-accent-yellow-100 { - color: var(--color-accent-yellow-100); -} -.text-accent-yellow-200 { - color: var(--color-accent-yellow-200); -} -.text-accent-yellow-300 { - color: var(--color-accent-yellow-300); -} -.text-accent-yellow-400 { - color: var(--color-accent-yellow-400); -} -.text-accent-yellow-500 { - color: var(--color-accent-yellow-500); -} -.text-accent-yellow-600 { - color: var(--color-accent-yellow-600); -} -.text-accent-yellow-700 { - color: var(--color-accent-yellow-700); -} -.text-accent-yellow-800 { - color: var(--color-accent-yellow-800); -} -.text-accent-yellow-900 { - color: var(--color-accent-yellow-900); -} - -.text-danger { - color: var(--color-danger); -} -.text-danger-100 { - color: var(--color-danger-100); -} -.text-danger-200 { - color: var(--color-danger-200); -} -.text-danger-300 { - color: var(--color-danger-300); -} -.text-danger-400 { - color: var(--color-danger-400); -} -.text-danger-500 { - color: var(--color-danger-500); -} -.text-danger-600 { - color: var(--color-danger-600); -} -.text-danger-700 { - color: var(--color-danger-700); -} -.text-danger-800 { - color: var(--color-danger-800); -} -.text-danger-900 { - color: var(--color-danger-900); -} - -.text-warning { - color: var(--color-warning); -} -.text-warning-100 { - color: var(--color-warning-100); -} -.text-warning-200 { - color: var(--color-warning-200); -} -.text-warning-300 { - color: var(--color-warning-300); -} -.text-warning-400 { - color: var(--color-warning-400); -} -.text-warning-500 { - color: var(--color-warning-500); -} -.text-warning-600 { - color: var(--color-warning-600); -} -.text-warning-700 { - color: var(--color-warning-700); -} -.text-warning-800 { - color: var(--color-warning-800); -} -.text-warning-900 { - color: var(--color-warning-900); -} - -.text-success { - color: var(--color-success); -} -.text-success-100 { - color: var(--color-success-100); -} -.text-success-200 { - color: var(--color-success-200); -} -.text-success-300 { - color: var(--color-success-300); -} -.text-success-400 { - color: var(--color-success-400); -} -.text-success-500 { - color: var(--color-success-500); -} -.text-success-600 { - color: var(--color-success-600); -} -.text-success-700 { - color: var(--color-success-700); -} -.text-success-800 { - color: var(--color-success-800); -} -.text-success-900 { - color: var(--color-success-900); -} \ No newline at end of file diff --git a/frontend-old/archive/src/styles/theme/fonts.css b/frontend-old/archive/src/styles/theme/fonts.css deleted file mode 100644 index 2646418..0000000 --- a/frontend-old/archive/src/styles/theme/fonts.css +++ /dev/null @@ -1,95 +0,0 @@ -/* Import Space Grotesk from Google Fonts */ -@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&family=Syncopate:wght@400;700&display=swap'); - -/* Global font settings */ - -/* Set Space Grotesk as the default font */ -body { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; - font-size: 14px; - line-height: 1.6; - color: #333; -} - -/* Use Anta for headings */ -h1, h2, h3 { - font-family: 'Syncopate', sans-serif; - color: #111; -} - -/* Use Space Grotesk for smaller text like paragraphs */ -p { - font-family: 'Space Grotesk', sans-serif; -} - - -.font-default { - font-family: system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; -} - -.font-syncopate { - font-family: "Syncopate", serif !important; -} - -.font-space-grotesk { - font-family: 'Space Grotesk', sans-serif; -} - -.font-weight-100 { - font-weight: 100; -} -.font-weight-200 { - font-weight: 200; -} -.font-weight-300 { - font-weight: 300; -} -.font-weight-400 { - font-weight: 400; -} -.font-weight-500 { - font-weight: 500; -} -.font-weight-600 { - font-weight: 600; -} -.font-weight-700 { - font-weight: 700; -} -.font-weight-800 { - font-weight: 800; -} -.font-weight-900 { - font-weight: 900; -} - -.font-size-12 { - font-size: 12px !important; -} - -.font-size-14 { - font-size: 14px !important; -} - -.font-size-16 { - font-size: 16px !important; -} - -.font-size-18 { - font-size: 18px !important; -} - -.font-size-20 { - font-size: 20px !important; -} - -.font-size-24 { - font-size: 24px !important; -} - -.font-size-32 { - font-size: 32px !important; -} -.font-size-48 { - font-size: 48px !important; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/DishType.ts b/frontend-old/archive/src/types/DishType.ts deleted file mode 100644 index e9986c2..0000000 --- a/frontend-old/archive/src/types/DishType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {UserType} from "@/types/UserType"; - -export type DishType = { - id: number - name: string, - recurrence: number, - users: UserType[], -} - -export type DishDateType = { - id: number; - date: string; - dish: DishType; - user: UserType; -} - -export type ScheduledDishesType = { - date: string; - dishes: { dish: DishType, user: UserType }[]; -} diff --git a/frontend-old/archive/src/types/ScheduleType.ts b/frontend-old/archive/src/types/ScheduleType.ts deleted file mode 100644 index 9b03fcf..0000000 --- a/frontend-old/archive/src/types/ScheduleType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -export type RecurrenceType = { - type: "App\\Models\\WeeklyRecurrence" | "App\\Models\\MinimumRecurrence"; - value: number; -} - -export type ScheduleType = { - id: number; - date: string; - scheduled_user_dishes: ScheduledUserDishType[]; - is_skipped: boolean; -} - -export type FilledScheduleType = { - id?: number; - date: string; - is_skipped?: boolean; - scheduled_user_dishes: ScheduledUserDishType[]; -} - -export type ScheduleDataType = { - user: UserType; - scheduled_user_dish: UserDishType | null; - user_dishes: UserDishType[]; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/ScheduledUserDishType.ts b/frontend-old/archive/src/types/ScheduledUserDishType.ts deleted file mode 100644 index 5ff7c94..0000000 --- a/frontend-old/archive/src/types/ScheduledUserDishType.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {DishType} from "@/types/DishType"; -import {RecurrenceType} from "@/types/ScheduleType"; - -export type UserDishType = { - id: number; - dish: DishType; - user: UserType; - recurrences: RecurrenceType[]; -} - -export type UserDishWithoutUserType = { - id: number; - dish: DishType; - recurrences: RecurrenceType[]; -} - -export type ScheduledUserDishType = { - id: number; - user_dish: UserDishType; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/UserDishType.ts b/frontend-old/archive/src/types/UserDishType.ts deleted file mode 100644 index 32f9dfc..0000000 --- a/frontend-old/archive/src/types/UserDishType.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {UserType} from "@/types/UserType"; -import {RecurrenceType} from "@/types/ScheduleType"; - -export type DishType = { - user: UserType; - dish: DishType; - recurrences: RecurrenceType[]; -} \ No newline at end of file diff --git a/frontend-old/archive/src/types/UserType.ts b/frontend-old/archive/src/types/UserType.ts deleted file mode 100644 index 1129ff0..0000000 --- a/frontend-old/archive/src/types/UserType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {UserDishWithoutUserType} from "@/types/ScheduledUserDishType"; - -export type UserType = { - id: number; - name: string; - user_dishes: UserDishWithoutUserType[]; -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/apiRequest.ts b/frontend-old/archive/src/utils/api/apiRequest.ts deleted file mode 100644 index e0690fa..0000000 --- a/frontend-old/archive/src/utils/api/apiRequest.ts +++ /dev/null @@ -1,107 +0,0 @@ -export const apiRequest = async (url: string, options: RequestInit = {}) => { - const token = localStorage.getItem('token'); - - const allowedRequests = [ - '/api/auth/login', - '/api/auth/register', - ] - - if (allowedRequests.includes(url)) { - return publicRequest(url, options) - } - - if (!token) { - throw new Error('No authentication token found.' + url); - } - - return privateRequest(url, token, options); -}; - -export const publicRequest = async (fullUrl: string, options: RequestInit = {}) => { - console.log('→ Sending request', fullUrl, options.method); - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { - headers: { - ...(options.headers || {}), - }, - ...options, - }); - - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - -export const privateRequest = async (fullUrl: string, token: string, options: RequestInit = {}) => { - const headers = { - ...(options.headers || {}), - Authorization: `Bearer ${token}`, - }; - - const url = `${process.env.NEXT_PUBLIC_API_URL}${fullUrl}`; - - const response = await fetch(url, { headers, ...options }); - - // Authentication failure - token invalid - redirect to login - if (response.status === 401) { - localStorage.removeItem('token'); - localStorage.removeItem('refreshToken'); - - window.location.href = '/login'; - - throw new Error('Unauthorized. Redirecting to login.'); - } - - if (!response.ok) { - throw new Error(`HTTP Error: ${response.status} ${response.statusText}`); - } - - return response.json(); -} - - -// Add shorthand HTTP methods -apiRequest.get = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'GET' }); -}; - -apiRequest.post = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.put = | undefined>( - url: string, - body: TBody, - options: RequestInit = {} -) => { - return apiRequest(url, { - ...options, - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - ...(options.headers || {}), - }, - body: body ? JSON.stringify(body) : undefined, - }); -}; - -apiRequest.delete = (url: string, options: RequestInit = {}) => { - return apiRequest(url, { ...options, method: 'DELETE' }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/auth.ts b/frontend-old/archive/src/utils/api/auth.ts deleted file mode 100644 index f4511aa..0000000 --- a/frontend-old/archive/src/utils/api/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { apiRequest } from '@/utils/api/apiRequest'; - -export const login = async (email: string, password: string) => { - const data = await apiRequest.post('/api/auth/login', { email, password }); - - if (!data.access_token) { - throw new Error('No access token returned from login.'); - } - - localStorage.setItem('token', data.access_token); - - return data; -}; - - -export const register = async (name: string, email: string, password: string, passwordConfirmation: string) => { - const data = await apiRequest.post('/api/auth/register', { - name, - email, - password, - password_confirmation: passwordConfirmation, // Match the backend's expected parameter - }); - - // Store the token (if returned by the backend) similarly to login - localStorage.setItem('token', data.access_token); - - return data; -}; diff --git a/frontend-old/archive/src/utils/api/dishApi.ts b/frontend-old/archive/src/utils/api/dishApi.ts deleted file mode 100644 index 8d38dba..0000000 --- a/frontend-old/archive/src/utils/api/dishApi.ts +++ /dev/null @@ -1,149 +0,0 @@ -import {DishType} from "@/types/DishType"; -import {apiRequest} from "@/utils/api/apiRequest"; - -export const listDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dishes) { - return data.payload.dishes as DishType[]; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const fetchDish = async (id: number): Promise => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.dish) { - return data.payload.dish as DishType; - } - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; - -export const createDish = async ( - name: string, - // recurrence: number, - // userIds: number[] -) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes`, { - name, - // recurrence, - // users: userIds, - }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch(() => { - throw new Error("Failed to create dish. Please try again later."); - }); -}; - -export const updateDish = async (dish_id: number, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.put(`/api/dishes/${dish_id}`, {name}, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .catch((error) => { - throw error; - }); -}; - -export const deleteDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.delete(`/api/dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const addUserToDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/add`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; - -export const removeUserFromDish = async (dish_id: number, user_id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(`/api/dishes/${dish_id}/users/remove`, { - users: [user_id], - }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - return data; - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/archive/src/utils/api/scheduleApi.ts b/frontend-old/archive/src/utils/api/scheduleApi.ts deleted file mode 100644 index d7fcbf3..0000000 --- a/frontend-old/archive/src/utils/api/scheduleApi.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { isValidDate } from "@/helpers/Date"; - -export const listSchedule = async (startDate?: string, endDate?: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (startDate && !isValidDate(startDate)) { - throw new Error('Invalid start date'); - } - if (endDate && !isValidDate(endDate)) { - throw new Error('Invalid end date'); - } - - const params = new URLSearchParams(); - if (startDate) params.append('start', startDate); - if (endDate) params.append('end', endDate); - - const endpoint = `/api/schedule${params.toString() ? `?${params.toString()}` : ''}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule); -}; - -export const getScheduleForDate = async (date: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Update the schedule for a specific date (e.g., mark as skipped) -export const updateScheduleForDate = async (date: string, isSkipped: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - if (!isValidDate(date)) { - throw new Error('Invalid date'); - } - - const endpoint = `/api/schedule/${date}`; - - return apiRequest.put(endpoint, { is_skipped: isSkipped }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -// Generate a new schedule (optional: overwrite the existing one) -export const generateSchedule = async (overwrite: boolean) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const endpoint = `/api/schedule/generate`; - - return apiRequest.post(endpoint, { overwrite }, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const scheduleUserDish = async (date: string, user_id: number, user_dish_id: number|null, skipped: boolean = false) => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - const endpoint = `/api/schedule/${date}/user-dishes`; - - return apiRequest.post(endpoint, { user_dish_id, user_id, skipped }, { headers: { Authorization: `Bearer ${token}`} }) - .then((scheduleData) => scheduleData?.payload?.schedule) - .catch((err) => { throw err }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts b/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts deleted file mode 100644 index 8a9b66c..0000000 --- a/frontend-old/archive/src/utils/api/scheduledUserDishesApi.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; - -export const listScheduledUserDishesStartingFromDate = async (startDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?start=${startDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const listScheduledUserDishesEndingAtDate = async (endDate: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes?end=${endDate}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.schedule) - .catch((err) => { - throw err; - }); -}; - -export const getScheduledUserDish = async (id: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/scheduled-user-dishes/${id}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; - -export const updateScheduledUserDish = async (id: number, userDishId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - const payload = userDishId > 0 - ? { user_dish_id: userDishId } - : { user_dish_id: null, is_skipped: true }; - - return apiRequest.put(`/api/scheduled-user-dishes/${id}`, payload, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((scheduledDishesData) => scheduledDishesData?.payload?.scheduled_user_dish) - .catch((err) => { - throw err; - }); -}; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/api/userDishApi.ts b/frontend-old/archive/src/utils/api/userDishApi.ts deleted file mode 100644 index 39ba27f..0000000 --- a/frontend-old/archive/src/utils/api/userDishApi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { apiRequest } from "@/utils/api/apiRequest"; -import { UserDishType } from "@/types/ScheduledUserDishType"; - -export const listUserDishes = async (): Promise => { - const token = localStorage.getItem('token'); - - if (!token) throw new Error('No token found in localStorage.'); - - return apiRequest.get(`/api/user-dishes`, { headers: { Authorization: `Bearer ${ token }` } }) - .then((data) => { - if (data?.payload?.user_dishes) return data.payload.user_dishes as UserDishType[]; - - throw new Error('SOMETHING WENT WRONG'); - }) - .catch((error) => { - throw error; - }); -}; diff --git a/frontend-old/archive/src/utils/api/usersApi.ts b/frontend-old/archive/src/utils/api/usersApi.ts deleted file mode 100644 index 8d70ed8..0000000 --- a/frontend-old/archive/src/utils/api/usersApi.ts +++ /dev/null @@ -1,139 +0,0 @@ -import {RecurrenceType} from "@/types/ScheduleType"; -import {apiRequest} from "@/utils/api/apiRequest"; -import {UserType} from "@/types/UserType"; - -export const listUsers = async () => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users`, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.users) { - return data.payload.users; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const showUser = async (userId: number) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(`/api/users/${userId}`, { - headers: { - Authorization: `Bearer ${token}`, - } - }) - .then((data) => { - if (data?.payload?.user) { - return data.payload.user; - } - throw new Error('Failed to fetch users'); - }) - .catch((err) => { - throw err; - }); -}; - -export const createUser = async (name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.post('/api/users', {name}, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const updateUser = async (user: UserType, name: string) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.put(`/api/users/${user.id}`, { name }, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - -export const deleteUser = async (user: UserType) => { - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return await apiRequest.delete(`/api/users/${user.id}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); -}; - - -export const getUserDishForUserAndDish = async (userId: number, dishId: number) => { - const endpoint = `/api/users/${userId}/dishes/${dishId}`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }}) - .then((data) => { - if (data?.payload?.user_dish) { - return data.payload.user_dish; - } - throw new Error('Failed to fetch user dish'); - }) - .catch((err) => { - throw err; - }); -}; - -export const syncUserDishRecurrences = async ( - dish_id: number, - user_id: number, - recurrenceData: RecurrenceType[] -) => { - const url = `/api/users/${user_id}/dishes/${dish_id}/recurrences`; - const token = localStorage.getItem('token'); - - if (!token) { - throw new Error('No token found in localStorage.'); - } - - return apiRequest.post(url, { recurrences: recurrenceData }, { - headers: { - Authorization: `Bearer ${token}`, - }, - }).catch((err) => { - throw err; - }); -}; - diff --git a/frontend-old/archive/src/utils/dateBuilder.ts b/frontend-old/archive/src/utils/dateBuilder.ts deleted file mode 100644 index e41ce15..0000000 --- a/frontend-old/archive/src/utils/dateBuilder.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { DateTime } from "luxon"; - -const transformDate = (inputDate: string, addSuffix = false): string => { - const date = DateTime.fromISO(inputDate) - const day = date.day - const suffix = addSuffix ? getDaySuffix(day) : '' - return date.toFormat("MMMM") + ` ${ day }${ suffix }, ` + date.toFormat("yyyy"); -} - -const getDaySuffix = (day: number): string => { - if (day >= 11 && day <= 13) return "th"; - switch (day % 10) { - case 1: - return "st"; - case 2: - return "nd"; - case 3: - return "rd"; - default: - return "th"; - } -}; - -export default transformDate; \ No newline at end of file diff --git a/frontend-old/archive/src/utils/scheduleBuilder.ts b/frontend-old/archive/src/utils/scheduleBuilder.ts deleted file mode 100644 index 8bbf15f..0000000 --- a/frontend-old/archive/src/utils/scheduleBuilder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ScheduleDataType, ScheduleType } from "@/types/ScheduleType"; -import { ScheduledUserDishType, UserDishType } from "@/types/ScheduledUserDishType"; -import { UserType } from "@/types/UserType"; - -const ScheduleBuilder = ( - schedule: ScheduleType, - users: UserType[], - userDishes: UserDishType[] -): ScheduleDataType[] => users.map(user => { - return { - user, - scheduled_user_dish: schedule.scheduled_user_dishes - .filter((scheduledUserDish: ScheduledUserDishType) => scheduledUserDish.user_dish?.user.id === user.id) - .shift()?.user_dish ?? null, - user_dishes: userDishes.filter((userDish: UserDishType) => userDish.user.id === user.id) - } -}) - -export default ScheduleBuilder \ No newline at end of file diff --git a/frontend-old/archive/tailwind.config.ts b/frontend-old/archive/tailwind.config.ts deleted file mode 100644 index 109807b..0000000 --- a/frontend-old/archive/tailwind.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Config } from "tailwindcss"; - -export default { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - colors: { - background: "var(--background)", - foreground: "var(--foreground)", - }, - }, - }, - plugins: [], -} satisfies Config; diff --git a/frontend-old/archive/tsconfig.json b/frontend-old/archive/tsconfig.json deleted file mode 100644 index c133409..0000000 --- a/frontend-old/archive/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/frontend-old/package-lock.json b/frontend-old/package-lock.json deleted file mode 100644 index af085dc..0000000 --- a/frontend-old/package-lock.json +++ /dev/null @@ -1,5099 +0,0 @@ -{ - "name": "my-react-router-app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "my-react-router-app", - "dependencies": { - "@headlessui/react": "^2.2.9", - "@heroicons/react": "^2.2.0", - "@react-router/node": "^7.5.3", - "@react-router/serve": "^7.5.3", - "@types/luxon": "^3.7.1", - "classnames": "^2.5.1", - "isbot": "^5.1.27", - "luxon": "^3.7.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router": "^7.5.3" - }, - "devDependencies": { - "@react-router/dev": "^7.5.3", - "@tailwindcss/vite": "^4.1.6", - "@types/node": "^20", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "tailwindcss": "^4.1.6", - "typescript": "^5.8.3", - "vite": "^6.3.3", - "vite-tsconfig-paths": "^5.1.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", - "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", - "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helpers": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", - "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.1", - "@babel/types": "^7.27.1", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", - "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", - "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", - "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", - "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", - "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.1", - "@babel/parser": "^7.27.1", - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/react": { - "version": "0.26.28", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", - "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.1.2", - "@floating-ui/utils": "^0.2.8", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", - "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.4" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@headlessui/react": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz", - "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/react": "^0.26.16", - "@react-aria/focus": "^3.20.2", - "@react-aria/interactions": "^3.25.0", - "@tanstack/react-virtual": "^3.13.9", - "use-sync-external-store": "^1.5.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "react-dom": "^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/@heroicons/react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", - "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", - "license": "MIT", - "peerDependencies": { - "react": ">= 16 || ^19.0.0-rc" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mjackson/node-fetch-server": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz", - "integrity": "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==", - "license": "MIT" - }, - "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/@npmcli/package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^4.1.0", - "glob": "^10.2.2", - "hosted-git-info": "^6.1.1", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "proc-log": "^3.0.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", - "dev": true, - "license": "ISC", - "dependencies": { - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@react-aria/focus": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz", - "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/interactions": "^3.25.6", - "@react-aria/utils": "^3.31.0", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/interactions": { - "version": "3.25.6", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz", - "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/ssr": "^3.9.10", - "@react-aria/utils": "^3.31.0", - "@react-stately/flags": "^3.1.2", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/ssr": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", - "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/utils": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.31.0.tgz", - "integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/ssr": "^3.9.10", - "@react-stately/flags": "^3.1.2", - "@react-stately/utils": "^3.10.8", - "@react-types/shared": "^3.32.1", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-router/dev": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.6.0.tgz", - "integrity": "sha512-XSxEslex0ddJPxNNgdU1Eqmc9lsY/lhcLNCcRLAtlrOPyOz3Y8kIPpAf5T/U2AG3HGXFVBa9f8aQ7wXU3wTJSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.21.8", - "@babel/generator": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/plugin-syntax-decorators": "^7.22.10", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/preset-typescript": "^7.21.5", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.22.5", - "@npmcli/package-json": "^4.0.1", - "@react-router/node": "7.6.0", - "arg": "^5.0.1", - "babel-dead-code-elimination": "^1.0.6", - "chokidar": "^4.0.0", - "dedent": "^1.5.3", - "es-module-lexer": "^1.3.1", - "exit-hook": "2.2.1", - "fs-extra": "^10.0.0", - "jsesc": "3.0.2", - "lodash": "^4.17.21", - "pathe": "^1.1.2", - "picocolors": "^1.1.1", - "prettier": "^2.7.1", - "react-refresh": "^0.14.0", - "semver": "^7.3.7", - "set-cookie-parser": "^2.6.0", - "valibot": "^0.41.0", - "vite-node": "3.0.0-beta.2" - }, - "bin": { - "react-router": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@react-router/serve": "^7.6.0", - "react-router": "^7.6.0", - "typescript": "^5.1.0", - "vite": "^5.1.0 || ^6.0.0", - "wrangler": "^3.28.2 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@react-router/serve": { - "optional": true - }, - "typescript": { - "optional": true - }, - "wrangler": { - "optional": true - } - } - }, - "node_modules/@react-router/express": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.6.0.tgz", - "integrity": "sha512-nxSTCcTsVx94bXOI9JjG7Cg338myi8EdQWTOjA97v2ApX35wZm/ZDYos5MbrvZiMi0aB4KgAD62o4byNqF9Z1A==", - "license": "MIT", - "dependencies": { - "@react-router/node": "7.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "express": "^4.17.1 || ^5", - "react-router": "7.6.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/node": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.6.0.tgz", - "integrity": "sha512-agjDPUzisLdGJ7Q2lx/Z3OfdS2t1k6qv/nTvA45iahGsQJCMDvMqVoIi7iIULKQJwrn4HWjM9jqEp75+WsMOXg==", - "license": "MIT", - "dependencies": { - "@mjackson/node-fetch-server": "^0.2.0", - "source-map-support": "^0.5.21", - "stream-slice": "^0.1.2", - "undici": "^6.19.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.6.0", - "typescript": "^5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/serve": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.6.0.tgz", - "integrity": "sha512-2O8ALEYgJfimvEdNRqMpnZb2N+DQ5UK/SKo9Xo3mTkt3no0rNTcNxzmhzD2tm92Q/HI7kHmMY1nBegNB2i1abA==", - "license": "MIT", - "dependencies": { - "@react-router/express": "7.6.0", - "@react-router/node": "7.6.0", - "compression": "^1.7.4", - "express": "^4.19.2", - "get-port": "5.1.1", - "morgan": "^1.10.0", - "source-map-support": "^0.5.21" - }, - "bin": { - "react-router-serve": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.6.0" - } - }, - "node_modules/@react-stately/flags": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", - "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - } - }, - "node_modules/@react-stately/utils": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz", - "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/shared": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz", - "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", - "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", - "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", - "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", - "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", - "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", - "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", - "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", - "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", - "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", - "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", - "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", - "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", - "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", - "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", - "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", - "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", - "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", - "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", - "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", - "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz", - "integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.29.2", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.6" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz", - "integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-x64": "4.1.6", - "@tailwindcss/oxide-freebsd-x64": "4.1.6", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.6", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-x64-musl": "4.1.6", - "@tailwindcss/oxide-wasm32-wasi": "4.1.6", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.6" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz", - "integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz", - "integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz", - "integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz", - "integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz", - "integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz", - "integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz", - "integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz", - "integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz", - "integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz", - "integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.9", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz", - "integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz", - "integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.6.tgz", - "integrity": "sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.6", - "@tailwindcss/oxide": "4.1.6", - "tailwindcss": "4.1.6" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6" - } - }, - "node_modules/@tanstack/react-virtual": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", - "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", - "license": "MIT", - "dependencies": { - "@tanstack/virtual-core": "3.13.12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@tanstack/virtual-core": { - "version": "3.13.12", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", - "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.17.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.46.tgz", - "integrity": "sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/react": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", - "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.1.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.4.tgz", - "integrity": "sha512-WxYAszDYgsMV31OVyoG4jbAgJI1Gw0Xq9V19zwhy6+hUUJlJIdZ3r/cbdmTqFv++SktQkZ/X+46yGFxp5XJBEg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.0.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/babel-dead-code-elimination": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz", - "integrity": "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001717", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", - "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.0.2", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.152", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz", - "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/exit-hook": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz", - "integrity": "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isbot": { - "version": "5.1.28", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.28.tgz", - "integrity": "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw==", - "license": "Unlicense", - "engines": { - "node": ">=18" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lightningcss": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", - "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.2", - "lightningcss-darwin-x64": "1.29.2", - "lightningcss-freebsd-x64": "1.29.2", - "lightningcss-linux-arm-gnueabihf": "1.29.2", - "lightningcss-linux-arm64-gnu": "1.29.2", - "lightningcss-linux-arm64-musl": "1.29.2", - "lightningcss-linux-x64-gnu": "1.29.2", - "lightningcss-linux-x64-musl": "1.29.2", - "lightningcss-win32-arm64-msvc": "1.29.2", - "lightningcss-win32-x64-msvc": "1.29.2" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", - "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", - "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", - "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", - "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", - "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", - "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", - "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", - "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", - "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", - "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/luxon": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", - "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", - "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", - "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router/node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/rollup": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", - "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.2", - "@rollup/rollup-android-arm64": "4.40.2", - "@rollup/rollup-darwin-arm64": "4.40.2", - "@rollup/rollup-darwin-x64": "4.40.2", - "@rollup/rollup-freebsd-arm64": "4.40.2", - "@rollup/rollup-freebsd-x64": "4.40.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", - "@rollup/rollup-linux-arm-musleabihf": "4.40.2", - "@rollup/rollup-linux-arm64-gnu": "4.40.2", - "@rollup/rollup-linux-arm64-musl": "4.40.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-musl": "4.40.2", - "@rollup/rollup-linux-s390x-gnu": "4.40.2", - "@rollup/rollup-linux-x64-gnu": "4.40.2", - "@rollup/rollup-linux-x64-musl": "4.40.2", - "@rollup/rollup-win32-arm64-msvc": "4.40.2", - "@rollup/rollup-win32-ia32-msvc": "4.40.2", - "@rollup/rollup-win32-x64-msvc": "4.40.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-slice": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/stream-slice/-/stream-slice-0.1.2.tgz", - "integrity": "sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "license": "MIT" - }, - "node_modules/tailwindcss": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz", - "integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsconfck": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.5.tgz", - "integrity": "sha512-CLDfGgUp7XPswWnezWwsCRxNmgQjhYq3VXHM0/XIRxhVrKw0M1if9agzryh1QS3nxjCROvV+xWxoJO1YctzzWg==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici": { - "version": "6.21.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", - "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==", - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/valibot": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.41.0.tgz", - "integrity": "sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "typescript": ">=5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.0.0-beta.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.0-beta.2.tgz", - "integrity": "sha512-ofTf6cfRdL30Wbl9n/BX81EyIR5s4PReLmSurrxQ+koLaWUNOEo8E0lCM53OJkb8vpa2URM2nSrxZsIFyvY1rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/frontend-old/package.json b/frontend-old/package.json deleted file mode 100644 index cc47acc..0000000 --- a/frontend-old/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "my-react-router-app", - "private": true, - "type": "module", - "scripts": { - "build": "react-router build", - "dev": "react-router dev", - "start": "react-router-serve ./build/server/index.js", - "typecheck": "react-router typegen && tsc" - }, - "dependencies": { - "@headlessui/react": "^2.2.9", - "@heroicons/react": "^2.2.0", - "@react-router/node": "^7.5.3", - "@react-router/serve": "^7.5.3", - "@types/luxon": "^3.7.1", - "classnames": "^2.5.1", - "isbot": "^5.1.27", - "luxon": "^3.7.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "react-router": "^7.5.3" - }, - "devDependencies": { - "@react-router/dev": "^7.5.3", - "@tailwindcss/vite": "^4.1.6", - "@types/node": "^20", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "tailwindcss": "^4.1.6", - "typescript": "^5.8.3", - "vite": "^6.3.3", - "vite-tsconfig-paths": "^5.1.4" - } -} diff --git a/frontend-old/public/favicon.ico b/frontend-old/public/favicon.ico deleted file mode 100644 index 5dbdfcddcb14182535f6d32d1c900681321b1aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI33v3ic7{|AFEmuJ-;v>ep_G*NPi6KM`qNryCe1PIJ8siIN1WZ(7qVa)RVtmC% z)Ch?tN+afMKm;5@rvorJk zcXnoOc4q51HBQnQH_jn!cAg&XI1?PlX>Kl^k8qq0;zkha`kY$Fxt#=KNJAE9CMdpW zqr4#g8`nTw191(+H4xW8Tmyru2I^3=J1G3emPxkPXA=3{vvuvse_WWSshqaqls^-m zgB7q8&Vk*aYRe?sn$n53dGH#%3y%^vxv{pL*-h0Z4bmb_(k6{FL7HWIz(V*HT#IcS z-wE{)+0x1U!RUPt3gB97%p}@oHxF4|6S*+Yw=_tLtxZ~`S=z6J?O^AfU>7qOX`JNBbV&8+bO0%@fhQitKIJ^O^ zpgIa__qD_y07t@DFlBJ)8SP_#^j{6jpaXt{U%=dx!qu=4u7^21lWEYHPPY5U3TcoQ zX_7W+lvZi>TapNk_X>k-KO%MC9iZp>1E`N34gHKd9tK&){jq2~7OsJ>!G0FzxQFw6G zm&Vb(2#-T|rM|n3>uAsG_hnbvUKFf3#ay@u4uTzia~NY%XgCHfx4^To4BDU@)HlV? z@EN=g^ymETa1sQK{kRwyE4Ax8?wT&GvaG@ASO}{&a17&^v`y z!oPdiSiia^oov(Z)QhG2&|FgE{M9_4hJROGbnj>#$~ZF$-G^|zPj*QApltKe?;u;uKHJ~-V!=VLkg7Kgct)l7u39f@%VG8e3f$N-B zAu3a4%ZGf)r+jPAYCSLt73m_J3}p>}6Tx0j(wg4vvKhP!DzgiWANiE;Ppvp}P2W@m z-VbYn+NXFF?6ngef5CfY6ZwKnWvNV4z6s^~yMXw2i5mv}jC$6$46g?G|CPAu{W5qF zDobS=zb2ILX9D827g*NtGe5w;>frjanY{f)hrBP_2ehBt1?`~ypvg_Ot4x1V+43P@Ve8>qd)9NX_jWdLo`Zfy zoeam9)@Dpym{4m@+LNxXBPjPKA7{3a&H+~xQvr>C_A;7=JrfK~$M2pCh>|xLz>W6SCs4qC|#V`)# z)0C|?$o>jzh<|-cpf

K7osU{Xp5PG4-K+L2G=)c3f&}H&M3wo7TlO_UJjQ-Oq&_ zjAc9=nNIYz{c3zxOiS5UfcE1}8#iI4@uy;$Q7>}u`j+OU0N<*Ezx$k{x_27+{s2Eg z`^=rhtIzCm!_UcJ?Db~Lh-=_))PT3{Q0{Mwdq;0>ZL%l3+;B&4!&xm#%HYAK|;b456Iv&&f$VQHf` z>$*K9w8T+paVwc7fLfMlhQ4)*zL_SG{~v4QR;IuX-(oRtYAhWOlh`NLoX0k$RUYMi z2Y!bqpdN}wz8q`-%>&Le@q|jFw92ErW-hma-le?S z-@OZt2EEUm4wLsuEMkt4zlyy29_3S50JAcQHTtgTC{P~%-mvCTzrjXOc|{}N`Cz`W zSj7CrXfa7lcsU0J(0uSX6G`54t^7}+OLM0n(|g4waOQ}bd3%!XLh?NX9|8G_|06Ie zD5F1)w5I~!et7lA{G^;uf7aqT`KE&2qx9|~O;s6t!gb`+zVLJyT2T)l*8l(j diff --git a/frontend-old/react-router.config.ts b/frontend-old/react-router.config.ts deleted file mode 100644 index 6ff16f9..0000000 --- a/frontend-old/react-router.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Config } from "@react-router/dev/config"; - -export default { - // Config options... - // Server-side render by default, to enable SPA mode set this to `false` - ssr: true, -} satisfies Config; diff --git a/frontend-old/tsconfig.json b/frontend-old/tsconfig.json deleted file mode 100644 index cfe4682..0000000 --- a/frontend-old/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "include": [ - "**/*", - "**/.server/**/*", - "**/.client/**/*", - ".react-router/types/**/*" - ], - "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], - "baseUrl": ".", - "paths": { - "~/*": ["./app/*"], - "@/*": ["./app/*"] - }, - "esModuleInterop": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true - }, - "optimizeDeps": { - "include": ["react", "react-dom"] - } -} diff --git a/frontend-old/vite.config.ts b/frontend-old/vite.config.ts deleted file mode 100644 index 93a885d..0000000 --- a/frontend-old/vite.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { reactRouter } from "@react-router/dev/vite"; -import tailwindcss from "@tailwindcss/vite"; -import { defineConfig } from "vite"; -import tsconfigPaths from "vite-tsconfig-paths"; - -export default defineConfig({ - plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], - server: { - proxy: { - "/api": { - target: "http://localhost:8000", - changeOrigin: true, - }, - "/sanctum": { - target: "http://localhost:8000", - changeOrigin: true, - }, - }, - }, -}); diff --git a/resources/views/billing/index.blade.php b/resources/views/billing/index.blade.php deleted file mode 100644 index 00c8900..0000000 --- a/resources/views/billing/index.blade.php +++ /dev/null @@ -1,106 +0,0 @@ - -

- diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index fd3cd3a..c007beb 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -13,16 +13,6 @@
- @if(is_mode_demo()) - - - @endif -
@@ -157,19 +140,12 @@ class="block text-2xl font-medium {{ request()->routeIs('schedule.*') ? 'text-ac
{{ Auth::user()->name }}
- @if(is_mode_saas()) - - Billing - - @endif - @if(allows_logout()) -
- @csrf - -
- @endif +
+ @csrf + +
@else
diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index e77b7b0..3724df0 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,13 +1,6 @@
- @if (session('success')) -
-

Welcome to Dish Planner!

-

Your subscription is now active. Start planning your dishes!

-
- @endif -

DASHBOARD

diff --git a/resources/views/subscription/index.blade.php b/resources/views/subscription/index.blade.php deleted file mode 100644 index a44e7e9..0000000 --- a/resources/views/subscription/index.blade.php +++ /dev/null @@ -1,51 +0,0 @@ - -
-
-

SUBSCRIPTION

- - @if(auth()->user()->subscribed()) -
-

Active Subscription

-

You have an active subscription.

- -
- @csrf - -
-
- @else -
-

Subscribe to Dish Planner

- -
-
- @csrf - -
-

Monthly

-

Billed monthly

- -
-
- -
- @csrf - -
-

Yearly

-

Billed annually

- -
-
-
-
- @endif -
-
-
diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php deleted file mode 100644 index b9d609c..0000000 --- a/resources/views/welcome.blade.php +++ /dev/null @@ -1,176 +0,0 @@ - - - - - - - Laravel - - - - - - - @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) - @vite(['resources/css/app.css', 'resources/js/app.js']) - @else - - @endif - - - - - diff --git a/routes/console.php b/routes/console.php index afc6101..eff2ed2 100644 --- a/routes/console.php +++ b/routes/console.php @@ -2,12 +2,7 @@ use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\Schedule; Artisan::command('inspire', function () { $this->comment(Inspiring::quote()); })->purpose('Display an inspiring quote')->hourly(); - -Schedule::command('demo:purge') - ->dailyAt('03:00') - ->when(fn () => is_mode_demo()); diff --git a/routes/web.php b/routes/web.php index f23fbc5..c1e49da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,7 +3,6 @@ use Illuminate\Support\Facades\Route; use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\Auth\RegisterController; -use App\Http\Controllers\SubscriptionController; Route::get('/', function () { return redirect()->route('dashboard'); @@ -26,25 +25,19 @@ Route::middleware('auth')->group(function () { Route::post('/logout', [LoginController::class, 'logout'])->name('logout'); - // Routes requiring active subscription in SaaS mode - Route::middleware('subscription')->group(function () { - Route::get('/dashboard', function () { - return view('dashboard'); - })->name('dashboard'); + Route::get('/dashboard', function () { + return view('dashboard'); + })->name('dashboard'); - Route::get('/dishes', function () { - return view('dishes.index'); - })->name('dishes.index'); + Route::get('/dishes', function () { + return view('dishes.index'); + })->name('dishes.index'); - Route::get('/schedule', function () { - return view('schedule.index'); - })->name('schedule.index'); + Route::get('/schedule', function () { + return view('schedule.index'); + })->name('schedule.index'); - Route::get('/users', function () { - return view('users.index'); - })->name('users.index'); - - Route::get('/billing', [SubscriptionController::class, 'billing'])->name('billing')->middleware('saas'); - Route::get('/billing/portal', [SubscriptionController::class, 'billingPortal'])->name('billing.portal')->middleware('saas'); - }); + Route::get('/users', function () { + return view('users.index'); + })->name('users.index'); }); diff --git a/routes/web/subscription.php b/routes/web/subscription.php deleted file mode 100644 index d3258b9..0000000 --- a/routes/web/subscription.php +++ /dev/null @@ -1,18 +0,0 @@ -name('cashier.webhook'); - -Route::middleware('auth')->group(function () { - Route::get('/subscription', function () { - return view('subscription.index'); - })->name('subscription.index'); - - Route::post('/subscription/checkout', [SubscriptionController::class, 'checkout'])->name('subscription.checkout'); - Route::get('/subscription/success', [SubscriptionController::class, 'success'])->name('subscription.success'); - Route::post('/subscription/cancel', [SubscriptionController::class, 'cancel'])->name('subscription.cancel'); -}); diff --git a/shell.nix b/shell.nix index d0087b5..d4b44be 100644 --- a/shell.nix +++ b/shell.nix @@ -85,24 +85,24 @@ pkgs.mkShell { prod-build() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" - local NAMESPACE="dish-planner" - local IMAGE_NAME="app" + local REGISTRY="forge.lvl0.xyz" + local NAMESPACE="lvl0" + local IMAGE_NAME="dishplanner" echo "🔨 Building production image..." podman build --format docker -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . echo "✅ Build complete: ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" - echo "Run 'prod-push' to push to Codeberg" + echo "Run 'prod-push' to push to Forgejo" } prod-push() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" - local NAMESPACE="dish-planner" - local IMAGE_NAME="app" + local REGISTRY="forge.lvl0.xyz" + local NAMESPACE="lvl0" + local IMAGE_NAME="dishplanner" - echo "📤 Pushing to Codeberg registry..." + echo "📤 Pushing to Forgejo registry..." if podman push ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}; then echo "✅ Image pushed to ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" else @@ -114,15 +114,15 @@ pkgs.mkShell { prod-build-nc() { local TAG="''${1:-latest}" - local REGISTRY="codeberg.org" - local NAMESPACE="dish-planner" - local IMAGE_NAME="app" + local REGISTRY="forge.lvl0.xyz" + local NAMESPACE="lvl0" + local IMAGE_NAME="dishplanner" echo "🔨 Building production image (no cache)..." podman build --format docker --no-cache -f Dockerfile -t ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG} . echo "✅ Build complete: ''${REGISTRY}/''${NAMESPACE}/''${IMAGE_NAME}:''${TAG}" - echo "Run 'prod-push' to push to Codeberg" + echo "Run 'prod-push' to push to Forgejo" } prod-build-push() { @@ -131,8 +131,8 @@ pkgs.mkShell { } prod-login() { - echo "📝 Logging into Codeberg registry..." - podman login codeberg.org + echo "📝 Logging into Forgejo registry..." + podman login forge.lvl0.xyz } echo "🚀 Dish Planner Development Environment" @@ -154,9 +154,9 @@ pkgs.mkShell { echo " dev-fix-permissions - Fix Docker-created file permissions" echo "" echo "Production commands:" - echo " prod-login - Login to Codeberg registry" + echo " prod-login - Login to Forgejo registry" echo " prod-build [tag] - Build production image (default: latest)" - echo " prod-push [tag] - Push image to Codeberg" + echo " prod-push [tag] - Push image to Forgejo" echo " prod-build-push - Build and push in one command" echo "" diff --git a/src/DishPlanner/Planner/Actions/SeedDemoPlannerAction.php b/src/DishPlanner/Planner/Actions/SeedDemoPlannerAction.php deleted file mode 100644 index 78b2ec4..0000000 --- a/src/DishPlanner/Planner/Actions/SeedDemoPlannerAction.php +++ /dev/null @@ -1,104 +0,0 @@ -createUsers($planner); - $this->createDishes($planner, $users); - $this->generateSchedule($planner); - } - - private function createUsers(Planner $planner): array - { - $names = ['Alice', 'Bob', 'Charlie']; - - return array_map( - fn (string $name) => User::create([ - 'planner_id' => $planner->id, - 'name' => $name, - ]), - $names - ); - } - - private function createDishes(Planner $planner, array $users): void - { - foreach ($this->dishNames as $dishName) { - $dish = Dish::create([ - 'planner_id' => $planner->id, - 'name' => $dishName, - ]); - - // Randomly assign dish to 1-3 users - $count = rand(1, count($users)); - $userIds = collect($users)->random($count)->pluck('id'); - $dish->users()->attach($userIds); - } - } - - private function generateSchedule(Planner $planner): void - { - resolve(GenerateScheduleForPeriodAction::class)->execute($planner); - } -}