From c24d5326085ec8977054459c2bacb08e00661252 Mon Sep 17 00:00:00 2001 From: "Hoa (Kyle) Trinh" Date: Fri, 20 Jun 2025 00:09:12 +0700 Subject: [PATCH] feat: Migrate to Nuxt 3 framework (#5184) Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com> --- .devcontainer/devcontainer.json | 5 +- .github/workflows/build-package.yml | 2 +- .github/workflows/e2e.yml | 2 +- .github/workflows/test-frontend.yml | 6 +- .gitignore | 5 + .vscode/settings.json | 1 + Taskfile.yml | 2 +- dev/code-generation/gen_ts_locales.py | 9 +- docker/Dockerfile | 2 +- frontend/.eslintrc.js | 74 - frontend/assets/css/fonts.css | 192 +- frontend/assets/css/main.css | 16 +- .../Domain/Cookbook/CookbookEditor.vue | 68 +- .../Domain/Cookbook/CookbookPage.vue | 214 +- .../Domain/Group/GroupExportData.vue | 29 +- .../Domain/Group/GroupPreferencesEditor.vue | 22 +- .../Household/GroupHouseholdSelector.vue | 26 +- .../Household/GroupMealPlanDayContextMenu.vue | 59 +- .../Household/GroupMealPlanRuleForm.vue | 49 +- .../Domain/Household/GroupWebhookEditor.vue | 47 +- .../Household/HouseholdPreferencesEditor.vue | 261 +- .../components/Domain/QueryFilterBuilder.vue | 768 +- .../Domain/Recipe/RecipeActionMenu.vue | 72 +- .../components/Domain/Recipe/RecipeAssets.vue | 116 +- .../components/Domain/Recipe/RecipeCard.vue | 67 +- .../Domain/Recipe/RecipeCardImage.vue | 25 +- .../Domain/Recipe/RecipeCardMobile.vue | 175 +- .../Domain/Recipe/RecipeCardSection.vue | 278 +- .../components/Domain/Recipe/RecipeChips.vue | 22 +- .../Domain/Recipe/RecipeContextMenu.vue | 138 +- .../Recipe/RecipeDataAliasManagerDialog.vue | 64 +- .../Domain/Recipe/RecipeDataTable.vue | 140 +- .../Recipe/RecipeDialogAddToShoppingList.vue | 143 +- .../Domain/Recipe/RecipeDialogBulkAdd.vue | 90 +- .../Recipe/RecipeDialogPrintPreferences.vue | 73 +- .../Domain/Recipe/RecipeDialogSearch.vue | 80 +- .../Domain/Recipe/RecipeDialogShare.vue | 123 +- .../Domain/Recipe/RecipeExplorerPage.vue | 308 +- .../Domain/Recipe/RecipeFavoriteBadge.vue | 42 +- .../Domain/Recipe/RecipeImageUploadBtn.vue | 41 +- .../Domain/Recipe/RecipeIngredientEditor.vue | 461 +- .../Domain/Recipe/RecipeIngredientHtml.vue | 10 +- .../Recipe/RecipeIngredientListItem.vue | 55 +- .../Domain/Recipe/RecipeIngredients.vue | 62 +- .../Domain/Recipe/RecipeLastMade.vue | 116 +- .../components/Domain/Recipe/RecipeList.vue | 174 +- .../components/Domain/Recipe/RecipeNotes.vue | 95 +- .../Domain/Recipe/RecipeNutrition.vue | 58 +- .../Domain/Recipe/RecipeOrganizerDialog.vue | 70 +- .../Domain/Recipe/RecipeOrganizerPage.vue | 113 +- .../Domain/Recipe/RecipeOrganizerSelector.vue | 77 +- .../Domain/Recipe/RecipePage/RecipePage.vue | 463 +- .../RecipePageParts/RecipePageComments.vue | 136 +- .../RecipePageEditorToolbar.vue | 115 +- .../RecipePageParts/RecipePageFooter.vue | 142 +- .../RecipePageParts/RecipePageHeader.vue | 27 +- .../RecipePageParts/RecipePageInfoCard.vue | 50 +- .../RecipePageInfoCardImage.vue | 22 +- .../RecipePageParts/RecipePageInfoEditor.vue | 144 +- .../RecipePageIngredientEditor.vue | 229 +- .../RecipePageIngredientToolsView.vue | 63 +- .../RecipePageInstructions.vue | 527 +- .../RecipePageParts/RecipePageOrganizers.vue | 80 +- .../RecipePageParts/RecipePageScale.vue | 26 +- .../Domain/Recipe/RecipePage/index.ts | 3 - .../Domain/Recipe/RecipePrintContainer.vue | 15 +- .../Domain/Recipe/RecipePrintView.vue | 175 +- .../components/Domain/Recipe/RecipeRating.vue | 50 +- .../Domain/Recipe/RecipeScaleEditButton.vue | 112 +- .../Recipe/RecipeSearchFilterSelector.vue | 21 +- .../Domain/Recipe/RecipeSettingsMenu.vue | 43 +- .../Domain/Recipe/RecipeSettingsSwitches.vue | 56 +- .../Domain/Recipe/RecipeSuggestion.vue | 47 +- .../Domain/Recipe/RecipeTimeCard.vue | 93 +- .../Domain/Recipe/RecipeTimeline.vue | 195 +- .../Domain/Recipe/RecipeTimelineBadge.vue | 53 +- .../Recipe/RecipeTimelineContextMenu.vue | 285 +- .../Domain/Recipe/RecipeTimelineItem.vue | 123 +- .../components/Domain/Recipe/RecipeYield.vue | 31 +- frontend/components/Domain/SearchFilter.vue | 157 +- .../Domain/ShoppingList/MultiPurposeLabel.vue | 14 +- .../ShoppingList/MultiPurposeLabelSection.vue | 33 +- .../Domain/ShoppingList/ShoppingListItem.vue | 169 +- .../ShoppingList/ShoppingListItemEditor.vue | 73 +- .../components/Domain/User/UserAvatar.vue | 45 +- .../Domain/User/UserInviteDialog.vue | 83 +- .../Domain/User/UserPasswordStrength.vue | 9 +- .../Domain/User/UserProfileLinkCard.vue | 72 +- .../Domain/User/UserRegistrationForm.vue | 46 +- frontend/components/Layout/DefaultLayout.vue | 230 +- .../Layout/LayoutParts/AppFooter.vue | 29 +- .../Layout/LayoutParts/AppHeader.vue | 102 +- .../Layout/LayoutParts/AppSidebar.vue | 213 +- .../Layout/LayoutParts/TheSnackbar.vue | 58 +- frontend/components/global/AdvancedOnly.vue | 10 +- frontend/components/global/AppButtonCopy.vue | 27 +- .../components/global/AppButtonUpload.vue | 45 +- frontend/components/global/AppLoader.vue | 38 +- frontend/components/global/AppToolbar.vue | 22 +- frontend/components/global/AutoForm.vue | 311 +- .../components/global/BannerExperimental.vue | 14 +- frontend/components/global/BannerWarning.vue | 20 +- frontend/components/global/BaseButton.vue | 40 +- .../components/global/BaseButtonGroup.vue | 54 +- .../global/BaseCardSectionTitle.vue | 18 +- frontend/components/global/BaseDialog.vue | 284 +- frontend/components/global/BaseDivider.vue | 10 +- .../components/global/BaseOverflowButton.vue | 150 +- frontend/components/global/BasePageTitle.vue | 29 +- frontend/components/global/BaseStatCard.vue | 38 +- frontend/components/global/BaseWizard.vue | 98 +- frontend/components/global/ButtonLink.vue | 15 +- frontend/components/global/ContextMenu.vue | 33 +- frontend/components/global/CrudTable.vue | 112 +- frontend/components/global/DevDumpJson.vue | 4 +- frontend/components/global/DocLink.vue | 18 +- frontend/components/global/DropZone.vue | 34 +- frontend/components/global/HelpIcon.vue | 28 +- frontend/components/global/ImageCropper.vue | 287 +- frontend/components/global/InputColor.vue | 45 +- frontend/components/global/InputLabelType.vue | 43 +- frontend/components/global/InputQuantity.vue | 23 +- frontend/components/global/LanguageDialog.vue | 74 +- frontend/components/global/MarkdownEditor.vue | 29 +- .../components/global/RecipeJsonEditor.vue | 56 +- frontend/components/global/ReportTable.vue | 37 +- frontend/components/global/SafeMarkdown.vue | 43 +- frontend/components/global/StatsCards.vue | 28 +- frontend/components/global/ToggleState.vue | 18 +- frontend/components/global/WakelockSwitch.vue | 80 +- frontend/composables/api/api-client.ts | 21 +- frontend/composables/api/static-routes.ts | 13 +- frontend/composables/api/use-app-info.ts | 12 +- .../composables/api/use-axios-download.ts | 22 - frontend/composables/api/use-downloader.ts | 18 + .../partials/use-actions-factory.ts | 29 +- .../composables/partials/use-store-factory.ts | 19 +- .../composables/recipe-page/shared-state.ts | 29 +- .../use-extract-ingredient-references.test.ts | 89 +- .../use-extract-ingredient-references.ts | 76 +- frontend/composables/recipes/use-fraction.ts | 12 +- .../recipes/use-recipe-ingredients.test.ts | 18 +- .../recipes/use-recipe-ingredients.ts | 14 +- .../recipes/use-recipe-nutrition.ts | 52 +- .../recipes/use-recipe-permissions.test.ts | 16 +- .../recipes/use-recipe-permissions.ts | 10 +- .../composables/recipes/use-recipe-search.ts | 9 +- .../recipes/use-recipe-timeline-events.ts | 16 +- .../composables/recipes/use-recipe-tools.ts | 15 +- frontend/composables/recipes/use-recipe.ts | 3 +- frontend/composables/recipes/use-recipes.ts | 18 +- .../composables/recipes/use-scaled-amount.ts | 4 +- .../composables/store/use-category-store.ts | 9 +- frontend/composables/store/use-food-store.ts | 9 +- .../composables/store/use-household-store.ts | 7 +- frontend/composables/store/use-label-store.ts | 7 +- frontend/composables/store/use-tag-store.ts | 9 +- frontend/composables/store/use-tool-store.ts | 9 +- frontend/composables/store/use-unit-store.ts | 7 +- frontend/composables/store/use-user-store.ts | 7 +- frontend/composables/use-backups.ts | 5 +- frontend/composables/use-context-presents.ts | 11 +- frontend/composables/use-copy.ts | 21 +- frontend/composables/use-group-cookbooks.ts | 28 +- frontend/composables/use-group-mealplan.ts | 26 +- .../composables/use-group-recipe-actions.ts | 20 +- frontend/composables/use-group-webhooks.ts | 12 +- frontend/composables/use-groups.ts | 17 +- frontend/composables/use-households.ts | 25 +- .../use-locales/available-locales.ts | 2 +- .../composables/use-locales/use-locales.ts | 44 +- frontend/composables/use-logged-in-state.ts | 15 +- .../composables/use-navigation-warning.ts | 10 +- frontend/composables/use-passwords.test.ts | 5 +- frontend/composables/use-passwords.ts | 32 +- .../composables/use-query-filter-builder.ts | 87 +- frontend/composables/use-router.ts | 11 +- .../use-setup/common-settings-form.ts | 49 +- .../use-shopping-list-item-actions.ts | 69 +- frontend/composables/use-text-color.ts | 4 +- frontend/composables/use-toast.ts | 2 - frontend/composables/use-user.ts | 43 +- frontend/composables/use-users/preferences.ts | 26 +- frontend/composables/use-users/user-form.ts | 33 +- .../composables/use-users/user-ratings.ts | 13 +- .../use-users/user-registration-form.ts | 18 +- frontend/composables/use-utils.ts | 44 +- frontend/composables/use-validators.ts | 7 +- frontend/composables/useMealieAuth.ts | 61 + frontend/eslint.config.mjs | 24 + frontend/i18n.config.ts | 57 + frontend/lang/locales/af-ZA.ts | 8 + frontend/lang/locales/ar-SA.ts | 8 + frontend/lang/locales/bg-BG.ts | 8 + frontend/lang/locales/ca-ES.ts | 8 + frontend/lang/locales/cs-CZ.ts | 8 + frontend/lang/locales/da-DK.ts | 8 + frontend/lang/locales/de-DE.ts | 8 + frontend/lang/locales/el-GR.ts | 8 + frontend/lang/locales/en-GB.ts | 8 + frontend/lang/locales/en-US.ts | 9 + frontend/lang/locales/es-ES.ts | 8 + frontend/lang/locales/et-EE.ts | 8 + frontend/lang/locales/fi-FI.ts | 8 + frontend/lang/locales/fr-BE.ts | 8 + frontend/lang/locales/fr-CA.ts | 8 + frontend/lang/locales/fr-FR.ts | 8 + frontend/lang/locales/gl-ES.ts | 8 + frontend/lang/locales/he-IL.ts | 8 + frontend/lang/locales/hr-HR.ts | 8 + frontend/lang/locales/hu-HU.ts | 8 + frontend/lang/locales/is-IS.ts | 8 + frontend/lang/locales/it-IT.ts | 8 + frontend/lang/locales/ja-JP.ts | 8 + frontend/lang/locales/ko-KR.ts | 8 + frontend/lang/locales/lt-LT.ts | 8 + frontend/lang/locales/lv-LV.ts | 8 + frontend/lang/locales/nl-NL.ts | 8 + frontend/lang/locales/no-NO.ts | 8 + frontend/lang/locales/pl-PL.ts | 8 + frontend/lang/locales/pt-BR.ts | 8 + frontend/lang/locales/pt-PT.ts | 8 + frontend/lang/locales/ro-RO.ts | 8 + frontend/lang/locales/ru-RU.ts | 8 + frontend/lang/locales/sk-SK.ts | 8 + frontend/lang/locales/sl-SI.ts | 8 + frontend/lang/locales/sr-SP.ts | 8 + frontend/lang/locales/sv-SE.ts | 8 + frontend/lang/locales/tr-TR.ts | 8 + frontend/lang/locales/uk-UA.ts | 8 + frontend/lang/locales/vi-VN.ts | 8 + frontend/lang/locales/zh-CN.ts | 8 + frontend/lang/locales/zh-TW.ts | 8 + frontend/lang/messages/hr-HR.json | 2 +- frontend/layouts/admin.vue | 184 +- frontend/layouts/basic.vue | 11 +- frontend/layouts/blank.vue | 15 +- frontend/layouts/default.vue | 4 +- frontend/layouts/error.vue | 72 +- frontend/lib/api/admin/admin-about.ts | 2 +- frontend/lib/api/admin/admin-analytics.ts | 2 +- frontend/lib/api/admin/admin-backups.ts | 4 +- frontend/lib/api/admin/admin-debug.ts | 2 +- frontend/lib/api/admin/admin-groups.ts | 5 +- frontend/lib/api/admin/admin-households.ts | 3 +- frontend/lib/api/admin/admin-maintenance.ts | 4 +- frontend/lib/api/admin/admin-users.ts | 2 +- frontend/lib/api/base/base-clients.ts | 28 +- frontend/lib/api/base/route.ts | 3 +- frontend/lib/api/client-admin.ts | 2 +- frontend/lib/api/client-public.ts | 2 +- frontend/lib/api/client-user.ts | 2 +- frontend/lib/api/public/explore/cookbooks.ts | 11 +- frontend/lib/api/public/explore/foods.ts | 11 +- frontend/lib/api/public/explore/households.ts | 11 +- frontend/lib/api/public/explore/organizers.ts | 33 +- frontend/lib/api/public/explore/recipes.ts | 11 +- frontend/lib/api/types/admin.ts | 2 +- frontend/lib/api/types/analytics.ts | 2 +- frontend/lib/api/types/cookbook.ts | 2 +- frontend/lib/api/types/events.ts | 2 +- frontend/lib/api/types/group.ts | 2 +- frontend/lib/api/types/household.ts | 2 +- frontend/lib/api/types/labels.ts | 2 +- frontend/lib/api/types/meal-plan.ts | 7 +- frontend/lib/api/types/non-generated.ts | 2 +- frontend/lib/api/types/recipe.ts | 4 +- frontend/lib/api/types/reports.ts | 2 +- frontend/lib/api/types/response.ts | 2 +- frontend/lib/api/types/user.ts | 3 +- frontend/lib/api/user/backups.ts | 2 +- frontend/lib/api/user/email.ts | 6 +- frontend/lib/api/user/group-cookbooks.ts | 2 +- frontend/lib/api/user/group-event-notifier.ts | 2 +- frontend/lib/api/user/group-mealplan-rules.ts | 2 +- frontend/lib/api/user/group-mealplan.ts | 2 +- frontend/lib/api/user/group-migrations.ts | 4 +- .../api/user/group-multiple-purpose-labels.ts | 2 +- frontend/lib/api/user/group-recipe-actions.ts | 22 +- frontend/lib/api/user/group-reports.ts | 2 +- frontend/lib/api/user/group-seeder.ts | 4 +- frontend/lib/api/user/group-shopping-lists.ts | 4 +- frontend/lib/api/user/group-webhooks.ts | 2 +- frontend/lib/api/user/groups.ts | 8 +- frontend/lib/api/user/households.ts | 10 +- frontend/lib/api/user/organizer-categories.ts | 2 +- frontend/lib/api/user/organizer-tags.ts | 2 +- frontend/lib/api/user/organizer-tools.ts | 2 +- frontend/lib/api/user/recipe-bulk-actions.ts | 8 +- frontend/lib/api/user/recipe-foods.ts | 3 +- frontend/lib/api/user/recipe-units.ts | 3 +- .../lib/api/user/recipes/recipe-comments.ts | 2 +- frontend/lib/api/user/recipes/recipe-share.ts | 2 +- frontend/lib/api/user/recipes/recipe.ts | 18 +- frontend/lib/api/user/upload.ts | 4 +- frontend/lib/api/user/user-registration.ts | 2 +- frontend/lib/api/user/users.ts | 2 +- frontend/lib/api/user/utils.ts | 2 +- frontend/lib/icons/icon-type.ts | 2 +- frontend/lib/icons/icons.ts | 6 +- frontend/lib/validators/inputs.test.ts | 1 + frontend/lib/validators/inputs.ts | 4 +- frontend/middleware/admin-only.ts | 17 +- frontend/middleware/advanced-only.ts | 19 +- .../middleware/can-manage-household-only.ts | 14 +- frontend/middleware/can-manage-only.ts | 14 +- frontend/middleware/can-organize-only.ts | 19 +- frontend/middleware/group-only.ts | 15 +- frontend/nuxt.config.js | 538 - frontend/nuxt.config.ts | 479 + frontend/package.json | 86 +- frontend/pages/admin.vue | 9 + frontend/pages/admin/backups.vue | 151 +- frontend/pages/admin/debug/openai.vue | 68 +- frontend/pages/admin/debug/parser.vue | 118 +- frontend/pages/admin/maintenance/index.vue | 91 +- .../admin/manage/groups/{_id.vue => [id].vue} | 81 +- frontend/pages/admin/manage/groups/index.vue | 88 +- .../manage/households/{_id.vue => [id].vue} | 90 +- .../pages/admin/manage/households/index.vue | 128 +- .../admin/manage/users/{_id.vue => [id].vue} | 129 +- frontend/pages/admin/manage/users/create.vue | 56 +- frontend/pages/admin/manage/users/index.vue | 101 +- frontend/pages/admin/setup.vue | 712 +- frontend/pages/admin/site-settings.vue | 725 +- frontend/pages/forgot-password.vue | 72 +- .../cookbooks/[slug].vue} | 5 +- .../pages/g/[groupSlug]/cookbooks/index.vue | 260 + .../g/{_groupSlug => [groupSlug]}/index.vue | 3 +- .../pages/g/[groupSlug]/r/[slug]/index.vue | 58 + .../r/[slug]}/ingredient-parser.vue | 169 +- .../{_groupSlug => [groupSlug]}/r/create.vue | 70 +- .../r/create/bulk.vue | 114 +- .../r/create/debug.vue | 53 +- .../r/create/html.vue | 88 +- .../r/create/image.vue | 67 +- .../r/create/index.vue | 6 +- .../r/create/new.vue | 32 +- .../r/create/url.vue | 105 +- .../r/create/zip.vue | 27 +- .../recipes/categories/index.vue | 19 +- .../recipes/finder/index.vue | 313 +- .../recipes/tags/index.vue | 19 +- .../pages/g/[groupSlug]/recipes/timeline.vue | 67 + .../recipes/tools/index.vue | 37 +- .../pages/g/[groupSlug]/shared/r/[id].vue | 47 + .../pages/g/_groupSlug/cookbooks/index.vue | 232 - frontend/pages/g/_groupSlug/r/_slug/index.vue | 60 - .../pages/g/_groupSlug/recipes/timeline.vue | 50 - frontend/pages/g/_groupSlug/shared/r/_id.vue | 49 - frontend/pages/group/data.vue | 85 +- frontend/pages/group/data/categories.vue | 75 +- frontend/pages/group/data/foods.vue | 234 +- frontend/pages/group/data/index.vue | 12 +- frontend/pages/group/data/labels.vue | 119 +- frontend/pages/group/data/recipe-actions.vue | 78 +- frontend/pages/group/data/recipes.vue | 190 +- frontend/pages/group/data/tags.vue | 75 +- frontend/pages/group/data/tools.vue | 100 +- frontend/pages/group/data/units.vue | 227 +- frontend/pages/group/index.vue | 39 +- frontend/pages/group/migrations.vue | 163 +- frontend/pages/group/reports/[id].vue | 88 + frontend/pages/group/reports/_id.vue | 70 - frontend/pages/household/index.vue | 240 +- frontend/pages/household/mealplan/planner.vue | 91 +- .../pages/household/mealplan/planner/edit.vue | 197 +- .../pages/household/mealplan/planner/types.ts | 6 +- .../pages/household/mealplan/planner/view.vue | 62 +- .../pages/household/mealplan/settings.vue | 119 +- frontend/pages/household/members.vue | 137 +- frontend/pages/household/notifiers.vue | 177 +- frontend/pages/household/webhooks.vue | 65 +- frontend/pages/index.vue | 34 +- frontend/pages/login.vue | 312 +- frontend/pages/register/index.vue | 288 +- frontend/pages/register/states.ts | 2 - frontend/pages/reset-password.vue | 70 +- .../shopping-lists/{_id.vue => [id].vue} | 574 +- frontend/pages/shopping-lists/index.vue | 177 +- .../pages/user/{_id => [id]}/favorites.vue | 27 +- frontend/pages/user/profile/api-tokens.vue | 119 +- frontend/pages/user/profile/edit.vue | 168 +- frontend/pages/user/profile/index.vue | 247 +- frontend/plugins/axios.ts | 44 + frontend/plugins/dark-mode.client.ts | 25 +- frontend/plugins/globals.ts | 44 +- frontend/plugins/theme.ts | 80 +- frontend/plugins/toast.client.ts | 18 - frontend/server/api/[...].ts | 12 + frontend/server/routes/docs.ts | 9 + frontend/server/routes/openapi.json.ts | 9 + frontend/tests/utils.ts | 13 +- frontend/tsconfig.json | 36 +- frontend/types/auth.d.ts | 11 + frontend/types/auto-forms.ts | 4 + frontend/types/components.d.ts | 74 +- frontend/types/ts-shim.d.ts | 1 + frontend/types/vuetify.ts | 11 - frontend/yarn.lock | 18247 ++++++++-------- tests/e2e/login.spec.ts | 58 +- tests/e2e/playwright.config.ts | 2 +- tests/e2e/yarn.lock | 24 +- 403 files changed, 23959 insertions(+), 19557 deletions(-) delete mode 100644 frontend/.eslintrc.js delete mode 100644 frontend/components/Domain/Recipe/RecipePage/index.ts delete mode 100644 frontend/composables/api/use-axios-download.ts create mode 100644 frontend/composables/api/use-downloader.ts create mode 100644 frontend/composables/useMealieAuth.ts create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/i18n.config.ts create mode 100644 frontend/lang/locales/af-ZA.ts create mode 100644 frontend/lang/locales/ar-SA.ts create mode 100644 frontend/lang/locales/bg-BG.ts create mode 100644 frontend/lang/locales/ca-ES.ts create mode 100644 frontend/lang/locales/cs-CZ.ts create mode 100644 frontend/lang/locales/da-DK.ts create mode 100644 frontend/lang/locales/de-DE.ts create mode 100644 frontend/lang/locales/el-GR.ts create mode 100644 frontend/lang/locales/en-GB.ts create mode 100644 frontend/lang/locales/en-US.ts create mode 100644 frontend/lang/locales/es-ES.ts create mode 100644 frontend/lang/locales/et-EE.ts create mode 100644 frontend/lang/locales/fi-FI.ts create mode 100644 frontend/lang/locales/fr-BE.ts create mode 100644 frontend/lang/locales/fr-CA.ts create mode 100644 frontend/lang/locales/fr-FR.ts create mode 100644 frontend/lang/locales/gl-ES.ts create mode 100644 frontend/lang/locales/he-IL.ts create mode 100644 frontend/lang/locales/hr-HR.ts create mode 100644 frontend/lang/locales/hu-HU.ts create mode 100644 frontend/lang/locales/is-IS.ts create mode 100644 frontend/lang/locales/it-IT.ts create mode 100644 frontend/lang/locales/ja-JP.ts create mode 100644 frontend/lang/locales/ko-KR.ts create mode 100644 frontend/lang/locales/lt-LT.ts create mode 100644 frontend/lang/locales/lv-LV.ts create mode 100644 frontend/lang/locales/nl-NL.ts create mode 100644 frontend/lang/locales/no-NO.ts create mode 100644 frontend/lang/locales/pl-PL.ts create mode 100644 frontend/lang/locales/pt-BR.ts create mode 100644 frontend/lang/locales/pt-PT.ts create mode 100644 frontend/lang/locales/ro-RO.ts create mode 100644 frontend/lang/locales/ru-RU.ts create mode 100644 frontend/lang/locales/sk-SK.ts create mode 100644 frontend/lang/locales/sl-SI.ts create mode 100644 frontend/lang/locales/sr-SP.ts create mode 100644 frontend/lang/locales/sv-SE.ts create mode 100644 frontend/lang/locales/tr-TR.ts create mode 100644 frontend/lang/locales/uk-UA.ts create mode 100644 frontend/lang/locales/vi-VN.ts create mode 100644 frontend/lang/locales/zh-CN.ts create mode 100644 frontend/lang/locales/zh-TW.ts delete mode 100644 frontend/nuxt.config.js create mode 100644 frontend/nuxt.config.ts create mode 100644 frontend/pages/admin.vue rename frontend/pages/admin/manage/groups/{_id.vue => [id].vue} (53%) rename frontend/pages/admin/manage/households/{_id.vue => [id].vue} (56%) rename frontend/pages/admin/manage/users/{_id.vue => [id].vue} (62%) rename frontend/pages/g/{_groupSlug/cookbooks/_slug.vue => [groupSlug]/cookbooks/[slug].vue} (65%) create mode 100644 frontend/pages/g/[groupSlug]/cookbooks/index.vue rename frontend/pages/g/{_groupSlug => [groupSlug]}/index.vue (70%) create mode 100644 frontend/pages/g/[groupSlug]/r/[slug]/index.vue rename frontend/pages/g/{_groupSlug/r/_slug => [groupSlug]/r/[slug]}/ingredient-parser.vue (71%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create.vue (50%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/bulk.vue (63%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/debug.vue (68%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/html.vue (63%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/image.vue (75%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/index.vue (64%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/new.vue (72%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/url.vue (66%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/r/create/zip.vue (75%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/recipes/categories/index.vue (69%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/recipes/finder/index.vue (68%) rename frontend/pages/g/{_groupSlug => [groupSlug]}/recipes/tags/index.vue (70%) create mode 100644 frontend/pages/g/[groupSlug]/recipes/timeline.vue rename frontend/pages/g/{_groupSlug => [groupSlug]}/recipes/tools/index.vue (67%) create mode 100644 frontend/pages/g/[groupSlug]/shared/r/[id].vue delete mode 100644 frontend/pages/g/_groupSlug/cookbooks/index.vue delete mode 100644 frontend/pages/g/_groupSlug/r/_slug/index.vue delete mode 100644 frontend/pages/g/_groupSlug/recipes/timeline.vue delete mode 100644 frontend/pages/g/_groupSlug/shared/r/_id.vue create mode 100644 frontend/pages/group/reports/[id].vue delete mode 100644 frontend/pages/group/reports/_id.vue rename frontend/pages/shopping-lists/{_id.vue => [id].vue} (65%) rename frontend/pages/user/{_id => [id]}/favorites.vue (69%) create mode 100644 frontend/plugins/axios.ts delete mode 100644 frontend/plugins/toast.client.ts create mode 100644 frontend/server/api/[...].ts create mode 100644 frontend/server/routes/docs.ts create mode 100644 frontend/server/routes/openapi.json.ts create mode 100644 frontend/types/auth.d.ts delete mode 100644 frontend/types/vuetify.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b83fbf19c..2ceae625a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,7 +11,7 @@ // Use -bullseye variants on local on arm64/Apple Silicon. "VARIANT": "3.12-bullseye", // Options - "NODE_VERSION": "16" + "NODE_VERSION": "20" } }, "mounts": [ @@ -55,5 +55,6 @@ "ghcr.io/devcontainers/features/docker-in-docker:2": { "dockerDashComposeVersion": "v2" } - } + }, + "appPort": 3000 } diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index bae0de02c..dddc7a2f4 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -19,7 +19,7 @@ jobs: - name: Setup node env πŸ— uses: actions/setup-node@v4.0.0 with: - node-version: 16 + node-version: 20 check-latest: true - name: Get yarn cache directory path πŸ›  diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a13cc2d23..07fd7adfc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 cache: 'yarn' cache-dependency-path: ./tests/e2e/yarn.lock - name: Set up Docker Buildx diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index 00f8a2673..20d9c98f6 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -14,7 +14,7 @@ jobs: - name: Setup node env πŸ— uses: actions/setup-node@v4.0.0 with: - node-version: 16 + node-version: 20 check-latest: true - name: Get yarn cache directory path πŸ›  @@ -34,6 +34,10 @@ jobs: run: yarn working-directory: "frontend" + - name: Prepare nuxt πŸš€ + run: yarn nuxt prepare + working-directory: "frontend" + - name: Run linter πŸ‘€ run: yarn lint working-directory: "frontend" diff --git a/.gitignore b/.gitignore index cd0725b3a..b24f03a93 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ docs/site/ *temp/* .secret frontend/dist/ +frontend/.output/* +frontend/.yarn/* +frontend/.yarnrc.yml dev/code-generation/generated/* dev/data/mealie.db-journal @@ -164,3 +167,5 @@ dev/code-generation/openapi.json .run/ .task/* +.dev.env +frontend/eslint.config.deprecated.js diff --git a/.vscode/settings.json b/.vscode/settings.json index b842cd45e..61ab0e239 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,6 +18,7 @@ "source.organizeImports": "never" }, "editor.formatOnSave": true, + "eslint.useFlatConfig": true, "eslint.workingDirectories": [ "./frontend" ], diff --git a/Taskfile.yml b/Taskfile.yml index c42cb76c7..ccff66fd6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -243,7 +243,7 @@ tasks: desc: runs the frontend server dir: frontend cmds: - - yarn run dev + - yarn run dev --no-fork docker:build-from-package: desc: Builds the Docker image from the existing Python package in dist/ diff --git a/dev/code-generation/gen_ts_locales.py b/dev/code-generation/gen_ts_locales.py index e1b73242a..6ff441d22 100644 --- a/dev/code-generation/gen_ts_locales.py +++ b/dev/code-generation/gen_ts_locales.py @@ -156,12 +156,13 @@ PROJECT_DIR = Path(__file__).parent.parent.parent datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats" locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages" -nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js" +nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.ts" +i18n_config = PROJECT_DIR / "frontend" / "i18n.config.ts" reg_valid = PROJECT_DIR / "mealie" / "schema" / "_mealie" / "validators.py" """ This snippet walks the message and dat locales directories and generates the import information -for the nuxt.config.js file and automatically injects it into the nuxt.config.js file. Note that +for the nuxt.config.ts file and automatically injects it into the nuxt.config.ts file. Note that the code generation ID is hardcoded into the script and required in the nuxt config. """ @@ -173,12 +174,12 @@ def inject_nuxt_values(): all_langs = [] for match in locales_dir.glob("*.json"): - lang_string = f'{{ code: "{match.stem}", file: "{match.name}" }},' + lang_string = f'{{ code: "{match.stem}", file: "{match.name.replace(".json", ".ts")}" }},' all_langs.append(lang_string) log.debug(f"injecting locales into nuxt config -> {nuxt_config}") inject_inline(nuxt_config, CodeKeys.nuxt_local_messages, all_langs) - inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales) + inject_inline(i18n_config, CodeKeys.nuxt_local_dates, all_date_locales) def inject_registration_validation_values(): diff --git a/docker/Dockerfile b/docker/Dockerfile index ada531826..f199602d6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Frontend Build ############################################### -FROM node:16 AS frontend-builder +FROM node:20 AS frontend-builder WORKDIR /frontend diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js deleted file mode 100644 index f6ab3103b..000000000 --- a/frontend/.eslintrc.js +++ /dev/null @@ -1,74 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - node: true, - }, - parser: "vue-eslint-parser", - parserOptions: { - parser: "@typescript-eslint/parser", - requireConfigFile: false, - tsConfigRootDir: __dirname, - project: ["./tsconfig.json"], - extraFileExtensions: [".vue"], - }, - extends: [ - "@nuxtjs/eslint-config-typescript", - "plugin:nuxt/recommended", - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - // "plugin:prettier/recommended", - "prettier", - ], - // Re-add once we use nuxt bridge - // See https://v3.nuxtjs.org/getting-started/bridge#update-nuxtconfig - ignorePatterns: ["nuxt.config.js", "lib/api/types/**/*.ts"], - plugins: ["prettier"], - // add your custom rules here - rules: { - "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", - "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", - quotes: ["error", "double"], - "vue/component-name-in-template-casing": ["error", "PascalCase"], - camelcase: 0, - "vue/singleline-html-element-content-newline": "off", - "vue/multiline-html-element-content-newline": "off", - "vue/no-mutating-props": "off", - "vue/no-v-text-v-html-on-component": "warn", - "vue/no-v-for-template-key-on-child": "off", - "vue/valid-v-slot": [ - "error", - { - allowModifiers: true, - }, - ], - "@typescript-eslint/ban-ts-comment": [ - "error", - { - "ts-ignore": "allow-with-description", - }, - ], - "no-restricted-imports": [ - "error", - { paths: ["@vue/reactivity", "@vue/runtime-dom", "@vue/composition-api", "vue-demi"] }, - ], - - // TODO Gradually activate all rules - // Allow Promise in onMounted - "@typescript-eslint/no-misused-promises": [ - "error", - { - checksVoidReturn: { - arguments: false, - }, - }, - ], - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-explicit-any": "off", - }, -}; diff --git a/frontend/assets/css/fonts.css b/frontend/assets/css/fonts.css index 15d2f1305..b8c8be4c7 100644 --- a/frontend/assets/css/fonts.css +++ b/frontend/assets/css/fonts.css @@ -1,378 +1,390 @@ /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-cyrillic-ext1.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-cyrillic-ext1.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-cyrillic2.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-cyrillic2.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-greek-ext3.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-greek-ext3.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-greek4.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-greek4.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-vietnamese5.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-vietnamese5.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-latin-ext6.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-100-latin-ext6.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 100; font-display: swap; - src: url('~assets/fonts/Roboto-100-latin7.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-100-latin7.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-cyrillic-ext8.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-cyrillic-ext8.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-cyrillic9.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-cyrillic9.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-greek-ext10.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-greek-ext10.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-greek11.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-greek11.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-vietnamese12.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-vietnamese12.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-latin-ext13.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-300-latin-ext13.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 300; font-display: swap; - src: url('~assets/fonts/Roboto-300-latin14.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-300-latin14.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-cyrillic-ext15.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-cyrillic-ext15.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-cyrillic16.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-cyrillic16.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-greek-ext17.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-greek-ext17.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-greek18.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-greek18.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-vietnamese19.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-vietnamese19.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-latin-ext20.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-400-latin-ext20.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap; - src: url('~assets/fonts/Roboto-400-latin21.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-400-latin21.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-cyrillic-ext22.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-cyrillic-ext22.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-cyrillic23.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-cyrillic23.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-greek-ext24.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-greek-ext24.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-greek25.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-greek25.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-vietnamese26.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-vietnamese26.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-latin-ext27.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-500-latin-ext27.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap; - src: url('~assets/fonts/Roboto-500-latin28.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-500-latin28.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-cyrillic-ext29.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-cyrillic-ext29.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-cyrillic30.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-cyrillic30.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-greek-ext31.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-greek-ext31.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-greek32.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-greek32.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-vietnamese33.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-vietnamese33.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-latin-ext34.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-700-latin-ext34.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap; - src: url('~assets/fonts/Roboto-700-latin35.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-700-latin35.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } /* cyrillic-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-cyrillic-ext36.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-cyrillic-ext36.woff2") format("woff2"); unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; } /* cyrillic */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-cyrillic37.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-cyrillic37.woff2") format("woff2"); unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; } /* greek-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-greek-ext38.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-greek-ext38.woff2") format("woff2"); unicode-range: U+1F00-1FFF; } /* greek */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-greek39.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-greek39.woff2") format("woff2"); unicode-range: U+0370-03FF; } /* vietnamese */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-vietnamese40.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-vietnamese40.woff2") format("woff2"); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-latin-ext41.woff2') format('woff2'); + src: url("~assets/fonts/Roboto-900-latin-ext41.woff2") format("woff2"); unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { - font-family: 'Roboto'; + font-family: "Roboto"; font-style: normal; font-weight: 900; font-display: swap; - src: url('~assets/fonts/Roboto-900-latin42.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + src: url("~assets/fonts/Roboto-900-latin42.woff2") format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } diff --git a/frontend/assets/css/main.css b/frontend/assets/css/main.css index 74257bd4a..b421c15eb 100644 --- a/frontend/assets/css/main.css +++ b/frontend/assets/css/main.css @@ -17,11 +17,11 @@ } .theme--dark.v-application { - background-color: var(--v-background-base, #1e1e1e) !important; + background-color: rgb(var(--v-theme-background, 30, 30, 30)) !important; } .theme--dark.v-navigation-drawer { - background-color: var(--v-background-base, #1e1e1e) !important; + background-color: rgb(var(--v-theme-background, 30, 30, 30)) !important; } .theme--dark.v-card { @@ -29,11 +29,11 @@ } .left-border { - border-left: 5px solid var(--v-primary-base) !important; + border-left: 5px solid rgb(var(--v-theme-primary)) !important; } .left-warning-border { - border-left: 5px solid var(--v-warning-base) !important; + border-left: 5px solid rgb(var(--v-theme-warning)) !important; } .handle { @@ -56,3 +56,11 @@ text-overflow: ellipsis; max-width: 100%; } + +a { + color: rgb(var(--v-theme-primary)); +} + +.fill-height { + min-height: 100vh; +} diff --git a/frontend/components/Domain/Cookbook/CookbookEditor.vue b/frontend/components/Domain/Cookbook/CookbookEditor.vue index 8b42656f7..1ee47067d 100644 --- a/frontend/components/Domain/Cookbook/CookbookEditor.vue +++ b/frontend/components/Domain/Cookbook/CookbookEditor.vue @@ -1,17 +1,41 @@ + useSeoMeta({ + title: book?.value?.name || "Cookbook", + }); + + return { + book, + slug, + tab, + appendRecipes, + assignSorted, + recipes, + removeRecipe, + replaceRecipes, + canEdit, + dialogStates, + editTarget, + handleEditCookbook, + editCookbook, + actions, + }; + }, +}); + diff --git a/frontend/components/Domain/Group/GroupExportData.vue b/frontend/components/Domain/Group/GroupExportData.vue index 64cf4efe0..fc2f0c05f 100644 --- a/frontend/components/Domain/Group/GroupExportData.vue +++ b/frontend/components/Domain/Group/GroupExportData.vue @@ -7,21 +7,24 @@ class="elevation-0" @click:row="downloadData" > -