diff --git a/.github/workflows/frontend-lint.yml b/.github/workflows/frontend-lint.yml index 3b52072a5e85..32e3370c71d6 100644 --- a/.github/workflows/frontend-lint.yml +++ b/.github/workflows/frontend-lint.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [15] + node: [16] steps: - name: Checkout 🛎 diff --git a/.vscode/settings.json b/.vscode/settings.json index 4b8f80bf053c..cc6bfa5fbab3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,6 +42,7 @@ "python.testing.pytestArgs": ["tests"], "python.testing.pytestEnabled": true, "python.testing.unittestEnabled": false, + "python.analysis.typeCheckingMode": "off", "search.mode": "reuseEditor", "vetur.validation.template": false, "python.sortImports.path": "${workspaceFolder}/.venv/bin/isort" diff --git a/Dockerfile b/Dockerfile index 99583e9041a7..c6211f4a77be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -135,7 +135,6 @@ WORKDIR / # copy frontend # COPY --from=frontend-build /app/dist $MEALIE_HOME/dist -COPY ./dev/data/templates $MEALIE_HOME/data/templates COPY ./Caddyfile $MEALIE_HOME # Grab CRF++ Model Release diff --git a/dev/data/templates/recipes.md b/dev/data/templates/recipes.md new file mode 100644 index 000000000000..6ee2d4bb6972 --- /dev/null +++ b/dev/data/templates/recipes.md @@ -0,0 +1,24 @@ + + +![Recipe Image](../../images/{{ recipe.slug }}/original.jpg) + +# {{ recipe.name }} +{{ recipe.description }} + +## Ingredients +{% for ingredient in recipe.recipeIngredient %} +- [ ] {{ ingredient }} {% endfor %} + +## Instructions +{% for step in recipe.recipeInstructions %} +- [ ] {{ step.text }} {% endfor %} + +{% for note in recipe.notes %} +**{{ note.title }}:** {{ note.text }} +{% endfor %} + +--- + +Tags: {{ recipe.tags }} +Categories: {{ recipe.categories }} +Original URL: {{ recipe.orgURL }} \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 69ea80b91faf..940b971f3d23 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts as builder +FROM node:16 as builder WORKDIR /app @@ -21,7 +21,7 @@ RUN rm -rf node_modules && \ --non-interactive \ --production=true -FROM node:15-alpine +FROM node:16-alpine WORKDIR /app diff --git a/frontend/api/class-interfaces/group-multiple-purpose-labels.ts b/frontend/api/class-interfaces/group-multiple-purpose-labels.ts new file mode 100644 index 000000000000..8d587fd9ee72 --- /dev/null +++ b/frontend/api/class-interfaces/group-multiple-purpose-labels.ts @@ -0,0 +1,22 @@ +import { BaseCRUDAPI } from "../_base"; + +const prefix = "/api"; + +const routes = { + labels: `${prefix}/groups/labels`, + labelsId: (id: string | number) => `${prefix}/groups/labels/${id}`, +}; + +export interface CreateLabel { + name: string; +} + +export interface Label extends CreateLabel { + id: string; + groupId: string; +} + +export class MultiPurposeLabelsApi extends BaseCRUDAPI { + baseRoute = routes.labels; + itemRoute = routes.labelsId; +} diff --git a/frontend/api/class-interfaces/group-shopping-lists.ts b/frontend/api/class-interfaces/group-shopping-lists.ts new file mode 100644 index 000000000000..aa3f1da4706d --- /dev/null +++ b/frontend/api/class-interfaces/group-shopping-lists.ts @@ -0,0 +1,60 @@ +import { BaseCRUDAPI } from "../_base"; +import { ApiRequestInstance } from "~/types/api"; +import { IngredientFood, IngredientUnit } from "~/types/api-types/recipe"; + +const prefix = "/api"; + +const routes = { + shoppingLists: `${prefix}/groups/shopping/lists`, + shoppingListsId: (id: string) => `${prefix}/groups/shopping/lists/${id}`, + shoppingListIdAddRecipe: (id: string, recipeId: number) => `${prefix}/groups/shopping/lists/${id}/recipe/${recipeId}`, +}; + +export interface ShoppingListItemCreate { + id: string; + shoppingListId: string; + checked: boolean; + position: number; + note: string; + quantity: number; + + isFood: boolean; + unit: IngredientUnit | null; + food: IngredientFood | null; + + labelId: string | null; + label?: { + id: string; + name: string; + }; +} + +export interface ShoppingListCreate { + name: string; +} + +export interface ShoppingListSummary extends ShoppingListCreate { + id: string; + groupId: string; +} + +export interface ShoppingList extends ShoppingListSummary { + listItems: ShoppingListItemCreate[]; +} + +export class ShoppingListsApi extends BaseCRUDAPI { + baseRoute = routes.shoppingLists; + itemRoute = routes.shoppingListsId; + + async addRecipe(itemId: string, recipeId: number) { + return await this.requests.post(routes.shoppingListIdAddRecipe(itemId, recipeId), {}); + } +} + +export class ShoppingApi { + public lists: ShoppingListsApi; + + constructor(requests: ApiRequestInstance) { + this.lists = new ShoppingListsApi(requests); + } +} diff --git a/frontend/api/index.ts b/frontend/api/index.ts index e3e4189df549..5de55dfcf5ff 100644 --- a/frontend/api/index.ts +++ b/frontend/api/index.ts @@ -21,6 +21,8 @@ import { AdminAPI } from "./admin-api"; import { ToolsApi } from "./class-interfaces/tools"; import { GroupMigrationApi } from "./class-interfaces/group-migrations"; import { GroupReportsApi } from "./class-interfaces/group-reports"; +import { ShoppingApi } from "./class-interfaces/group-shopping-lists"; +import { MultiPurposeLabelsApi } from "./class-interfaces/group-multiple-purpose-labels"; import { ApiRequestInstance } from "~/types/api"; class Api { @@ -46,6 +48,8 @@ class Api { public groupReports: GroupReportsApi; public grouperServerTasks: GroupServerTaskAPI; public tools: ToolsApi; + public shopping: ShoppingApi; + public multiPurposeLabels: MultiPurposeLabelsApi; // Utils public upload: UploadFile; @@ -74,6 +78,8 @@ class Api { // Group this.groupMigration = new GroupMigrationApi(requests); this.groupReports = new GroupReportsApi(requests); + this.shopping = new ShoppingApi(requests); + this.multiPurposeLabels = new MultiPurposeLabelsApi(requests); // Admin this.events = new EventsAPI(requests); diff --git a/frontend/assets/main.css b/frontend/assets/main.css index b3ae261c65d8..5dd067b74ef6 100644 --- a/frontend/assets/main.css +++ b/frontend/assets/main.css @@ -22,3 +22,20 @@ .theme--dark.v-card { background-color: #2b2b2b !important; } + +.left-border { + border-left: 5px solid var(--v-primary-base) !important; +} + +.handle { + cursor: grab; +} + +.hidden { + visibility: hidden !important; +} + +.v-card__text, +.v-card__title { + word-break: normal !important; +} diff --git a/frontend/components/Domain/Recipe/RecipeActionMenu.vue b/frontend/components/Domain/Recipe/RecipeActionMenu.vue index ad3b3dfe2d86..4ef8d50826aa 100644 --- a/frontend/components/Domain/Recipe/RecipeActionMenu.vue +++ b/frontend/components/Domain/Recipe/RecipeActionMenu.vue @@ -58,7 +58,6 @@ show-print :menu-top="false" :slug="slug" - :name="name" :menu-icon="$globals.icons.mdiDotsHorizontal" fab color="info" @@ -69,6 +68,7 @@ edit: false, download: true, mealplanner: true, + shoppingList: true, print: true, share: true, }" diff --git a/frontend/components/Domain/Recipe/RecipeCard.vue b/frontend/components/Domain/Recipe/RecipeCard.vue index b9819869685a..474b9fbfb68d 100644 --- a/frontend/components/Domain/Recipe/RecipeCard.vue +++ b/frontend/components/Domain/Recipe/RecipeCard.vue @@ -38,6 +38,7 @@ edit: true, download: true, mealplanner: true, + shoppingList: true, print: false, share: true, }" diff --git a/frontend/components/Domain/Recipe/RecipeCardMobile.vue b/frontend/components/Domain/Recipe/RecipeCardMobile.vue index 3b3f6492593b..597473a8d3d9 100644 --- a/frontend/components/Domain/Recipe/RecipeCardMobile.vue +++ b/frontend/components/Domain/Recipe/RecipeCardMobile.vue @@ -39,6 +39,7 @@ edit: true, download: true, mealplanner: true, + shoppingList: true, print: false, share: true, }" diff --git a/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue b/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue index f5d3057ed33e..e5b4b943fd1c 100644 --- a/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue +++ b/frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue @@ -11,7 +11,7 @@ - + {{ icon }} diff --git a/frontend/components/Domain/Recipe/RecipeContextMenu.vue b/frontend/components/Domain/Recipe/RecipeContextMenu.vue index 12f29ed7346c..db7413f820b9 100644 --- a/frontend/components/Domain/Recipe/RecipeContextMenu.vue +++ b/frontend/components/Domain/Recipe/RecipeContextMenu.vue @@ -46,6 +46,21 @@ + + + + + {{ list.name }} + + + + diff --git a/frontend/components/Layout/AppSidebar.vue b/frontend/components/Layout/AppSidebar.vue index b8b0bdfeea39..69e0f78b8c1b 100644 --- a/frontend/components/Layout/AppSidebar.vue +++ b/frontend/components/Layout/AppSidebar.vue @@ -60,7 +60,9 @@