1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-02 20:15:24 +02:00

feat(frontend): Add Meal Tags + UI Improvements (#807)

* feat: 

* fix colors

* add additional support for settings meal tag

* add defaults to recipe

* use group reciep settings

* fix login infinite loading

* disable owner on initial load

* add skeleton loader

* add v-model support

* formatting

* fix overwriting existing values

* feat(frontend):  add markdown preview for steps

* update black plus formatting

* update help text

* fix overwrite error

* remove print

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-11-20 14:30:38 -09:00 committed by GitHub
parent d4bf81dee6
commit 912cc6d956
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 456 additions and 246 deletions

View file

@ -35,6 +35,9 @@
<v-date-picker v-model="newMeal.date" no-title @input="pickerMenu = false"></v-date-picker>
</v-menu>
<v-card-text>
<v-select v-model="newMeal.entryType" :return-object="false" :items="planTypeOptions" label="Entry Type">
</v-select>
<v-autocomplete
v-if="!dialog.note"
v-model="newMeal.recipeId"
@ -68,7 +71,7 @@
</div>
</div>
<v-switch v-model="edit" label="Editor"></v-switch>
<v-row class="mt-2">
<v-row class="">
<v-col
v-for="(plan, index) in mealsByDate"
:key="index"
@ -79,68 +82,120 @@
xl="2"
class="col-borders my-1 d-flex flex-column"
>
<p class="h5 text-center">
{{ $d(plan.date, "short") }}
</p>
<draggable
tag="div"
:value="plan.meals"
group="meals"
:data-index="index"
:data-box="plan.date"
style="min-height: 150px"
@end="onMoveCallback"
>
<v-card v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" class="my-1">
<v-list-item :to="edit ? null : `/recipe/${mealplan.recipe.slug}`">
<v-list-item-avatar :rounded="false">
<RecipeCardImage v-if="mealplan.recipe" tiny icon-size="25" :slug="mealplan.recipe.slug" />
<v-icon v-else>
{{ $globals.icons.primary }}
</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="mb-1">
{{ mealplan.recipe ? mealplan.recipe.name : mealplan.title }}
</v-list-item-title>
<v-list-item-subtitle>
{{ mealplan.recipe ? mealplan.recipe.description : mealplan.text }}
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider v-if="edit" class="mx-2"></v-divider>
<v-card-actions v-if="edit">
<v-btn color="error" icon @click="actions.deleteOne(mealplan.id)">
<v-icon>{{ $globals.icons.delete }}</v-icon>
</v-btn>
<v-spacer></v-spacer>
<v-btn
v-if="mealplan.recipe"
color="info"
icon
nuxt
target="_blank"
:to="`/recipe/${mealplan.recipe.slug}`"
>
<v-icon>{{ $globals.icons.openInNew }}</v-icon>
</v-btn>
<v-sheet class="mb-2 bottom-color-border">
<p class="headline text-center mb-1">
{{ $d(plan.date, "short") }}
</p>
</v-sheet>
<!-- Day Column Recipes -->
<template v-if="edit">
<draggable
tag="div"
:value="plan.meals"
group="meals"
:data-index="index"
:data-box="plan.date"
style="min-height: 150px"
@end="onMoveCallback"
>
<v-card v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" class="my-1">
<v-list-item :to="edit || !mealplan.recipe ? null : `/recipe/${mealplan.recipe.slug}`">
<v-list-item-avatar :rounded="false">
<RecipeCardImage
v-if="mealplan.recipe"
tiny
icon-size="25"
:slug="mealplan.recipe ? mealplan.recipe.slug : ''"
/>
<v-icon v-else>
{{ $globals.icons.primary }}
</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="mb-1">
{{ mealplan.recipe ? mealplan.recipe.name : mealplan.title }}
</v-list-item-title>
<v-list-item-subtitle style="min-height: 16px">
{{ mealplan.recipe ? mealplan.recipe.description + " " : mealplan.text }}
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider class="mx-2"></v-divider>
<div class="py-2 px-2 d-flex">
<v-menu offset-y>
<template #activator="{ on, attrs }">
<v-chip v-bind="attrs" label small color="accent" v-on="on" @click.prevent>
<v-icon left>
{{ $globals.icons.tags }}
</v-icon>
{{ mealplan.entryType }}
</v-chip>
</template>
<v-list>
<v-list-item
v-for="mealType in planTypeOptions"
:key="mealType.value"
@click="actions.setType(mealplan, mealType.value)"
>
<v-list-item-title> {{ mealType.text }} </v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-spacer></v-spacer>
<v-btn color="info" class="mr-2" small icon>
<v-icon>{{ $globals.icons.cartCheck }}</v-icon>
</v-btn>
<v-btn color="error" small icon @click="actions.deleteOne(mealplan.id)">
<v-icon>{{ $globals.icons.delete }}</v-icon>
</v-btn>
</div>
</v-card>
</draggable>
<!-- Day Column Actions -->
<v-card outlined class="mt-auto">
<v-card-actions class="d-flex">
<div style="width: 50%">
<v-btn block text @click="randomMeal(plan.date)">
<v-icon large>{{ $globals.icons.diceMultiple }}</v-icon>
</v-btn>
</div>
<div style="width: 50%">
<v-btn block text @click="openDialog(plan.date)">
<v-icon large>{{ $globals.icons.createAlt }}</v-icon>
</v-btn>
</div>
</v-card-actions>
</v-card>
</draggable>
<v-card v-if="edit" outlined class="mt-auto">
<v-card-actions class="d-flex">
<div style="width: 50%">
<v-btn block text @click="randomMeal(plan.date)">
<v-icon large>{{ $globals.icons.diceMultiple }}</v-icon>
</v-btn>
</div>
<div style="width: 50%">
<v-btn block text @click="openDialog(plan.date)">
<v-icon large>{{ $globals.icons.createAlt }}</v-icon>
</v-btn>
</div>
</v-card-actions>
</v-card>
</template>
<template v-else-if="plan.meals">
<RecipeCard
v-for="mealplan in plan.meals"
:key="mealplan.id"
:recipe-id="0"
:image-height="125"
class="mb-2"
:route="mealplan.recipe ? true : false"
:slug="mealplan.recipe ? mealplan.recipe.slug : mealplan.title"
:description="mealplan.recipe ? mealplan.recipe.description : mealplan.text"
:name="mealplan.recipe ? mealplan.recipe.name : mealplan.title"
>
<template #actions>
<v-divider class="mb-0 mt-2 mx-2"></v-divider>
<v-card-actions class="justify-end mt-1">
<v-chip label small color="accent">
<v-icon left>
{{ $globals.icons.tags }}
</v-icon>
{{ mealplan.entryType }}
</v-chip>
</v-card-actions>
</template>
</RecipeCard>
</template>
<v-skeleton-loader v-else elevation="2" type="image, list-item-two-line"></v-skeleton-loader>
</v-col>
</v-row>
</v-container>
@ -151,17 +206,19 @@ import { computed, defineComponent, reactive, ref, toRefs, watch } from "@nuxtjs
import { isSameDay, addDays, subDays, parseISO, format } from "date-fns";
import { SortableEvent } from "sortablejs"; // eslint-disable-line
import draggable from "vuedraggable";
import { useMealplans } from "~/composables/use-group-mealplan";
import { useMealplans, planTypeOptions } from "~/composables/use-group-mealplan";
import { useRecipes, allRecipes } from "~/composables/recipes";
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
import RecipeCard from "~/components/Domain/Recipe/RecipeCard.vue";
export default defineComponent({
components: {
draggable,
RecipeCardImage,
RecipeCard,
},
setup() {
const { mealplans, actions } = useMealplans();
const { mealplans, actions, loading } = useMealplans();
useRecipes(true, true);
const state = reactive({
@ -242,6 +299,7 @@ export default defineComponent({
// =====================================================
// New Meal Dialog
const domMealDialog = ref(null);
const dialog = reactive({
loading: false,
@ -256,11 +314,13 @@ export default defineComponent({
newMeal.title = "";
newMeal.text = "";
});
const newMeal = reactive({
date: "",
title: "",
text: "",
recipeId: null,
recipeId: null as Number | null,
entryType: "dinner",
});
function openDialog(date: Date) {
@ -273,40 +333,45 @@ export default defineComponent({
newMeal.date = "";
newMeal.title = "";
newMeal.text = "";
newMeal.entryType = "dinner";
newMeal.recipeId = null;
}
function randomMeal(date: Date) {
async function randomMeal(date: Date) {
// TODO: Refactor to use API call to get random recipe
// @ts-ignore
const randomRecipe = allRecipes.value[Math.floor(Math.random() * allRecipes.value.length)];
newMeal.date = format(date, "yyyy-MM-dd");
// @ts-ignore
newMeal.recipeId = randomRecipe.id;
newMeal.recipeId = randomRecipe.id || null;
console.log(newMeal.recipeId, randomRecipe.id);
// @ts-ignore
actions.createOne(newMeal);
await actions.createOne({ ...newMeal });
resetDialog();
}
return {
resetDialog,
randomMeal,
...toRefs(state),
actions,
allRecipes,
backOneWeek,
days,
dialog,
domMealDialog,
openDialog,
mealplans,
actions,
newMeal,
allRecipes,
...toRefs(state),
mealsByDate,
onMoveCallback,
backOneWeek,
forwardOneWeek,
loading,
mealplans,
mealsByDate,
newMeal,
onMoveCallback,
openDialog,
planTypeOptions,
randomMeal,
resetDialog,
weekRange,
days,
};
},
head() {
@ -318,8 +383,16 @@ export default defineComponent({
</script>
<style lang="css">
.col-borders {
/* .col-borders {
border-top: 1px solid #e0e0e0;
} */
.left-color-border {
border-left: 5px solid var(--v-primary-base) !important;
}
.bottom-color-border {
border-bottom: 2px solid var(--v-primary-base) !important;
}
</style>