style(frontend): 🎨 refactor/rewrite UI for meal-planner (#717)

* add new creation links

* style(frontend): 🎨 refactor/rewrite UI for meal-planner

* add random meal

Co-authored-by: Hayden <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-10-02 16:11:50 -08:00 committed by GitHub
parent bd8882ec44
commit f9829141c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 185 additions and 69 deletions

View File

@ -11,7 +11,7 @@
:bottom-links="isAdmin ? bottomLink : []" :bottom-links="isAdmin ? bottomLink : []"
@input="sidebar = !sidebar" @input="sidebar = !sidebar"
> >
<v-menu offset-y nudge-bottom="5" open-on-hover close-delay="30" nudge-right="15"> <v-menu offset-y nudge-bottom="5" open-on-hover close-delay="50" nudge-right="15">
<template #activator="{ on, attrs }"> <template #activator="{ on, attrs }">
<v-btn rounded large class="ml-2 mt-3" v-bind="attrs" v-on="on"> <v-btn rounded large class="ml-2 mt-3" v-bind="attrs" v-on="on">
<v-icon left large color="primary"> <v-icon left large color="primary">
@ -20,7 +20,7 @@
{{ $t("general.create") }} {{ $t("general.create") }}
</v-btn> </v-btn>
</template> </template>
<v-list> <v-list dense class="my-0 py-0">
<template v-for="(item, index) in createLinks"> <template v-for="(item, index) in createLinks">
<v-divider v-if="item.divider" :key="index" class="mx-2"></v-divider> <v-divider v-if="item.divider" :key="index" class="mx-2"></v-divider>
<v-list-item v-else :key="item.title" :to="item.to" exact> <v-list-item v-else :key="item.title" :to="item.to" exact>
@ -91,9 +91,7 @@ export default defineComponent({
to: "/recipe/create?tab=url", to: "/recipe/create?tab=url",
restricted: true, restricted: true,
}, },
{ { divider: true },
divider: true,
},
{ {
icon: this.$globals.icons.edit, icon: this.$globals.icons.edit,
title: "Create", title: "Create",
@ -101,16 +99,30 @@ export default defineComponent({
to: "/recipe/create?tab=new", to: "/recipe/create?tab=new",
restricted: true, restricted: true,
}, },
{ { divider: true },
divider: true,
},
{ {
icon: this.$globals.icons.zip, icon: this.$globals.icons.zip,
title: "Restore", title: "Recipe from zip",
subtitle: "Restore from a exported recipe", subtitle: "Restore from a exported recipe",
to: "/recipe/create?tab=zip", to: "/recipe/create?tab=zip",
restricted: true, restricted: true,
}, },
{ divider: true },
{
icon: this.$globals.icons.pages,
title: "Cookbook",
subtitle: "Create a new cookbook",
to: "/user/group/cookbooks",
restricted: true,
},
{ divider: true },
{
icon: this.$globals.icons.cartCheck,
title: "Shopping List",
subtitle: "Create a new shopping list",
to: "/user/group/shopping-list/create",
restricted: true,
},
], ],
bottomLink: [ bottomLink: [
{ {
@ -138,12 +150,6 @@ export default defineComponent({
to: "/meal-plan/this-week", to: "/meal-plan/this-week",
restricted: true, restricted: true,
}, },
{
icon: this.$globals.icons.calendarToday,
title: this.$t("meal-plan.dinner-today"),
to: "/meal-plan/today",
restricted: true,
},
], ],
}, },
{ {

View File

@ -189,8 +189,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api"; import { defineComponent, ref, useContext, computed, reactive } from "@nuxtjs/composition-api";
import { computed, reactive } from "@vue/reactivity";
export default defineComponent({ export default defineComponent({
layout: "basic", layout: "basic",

View File

@ -1,7 +1,16 @@
<template> <template>
<v-container> <v-container>
<v-card> <!-- Create Meal Dialog -->
<v-card-title class="headline">New Recipe</v-card-title> <BaseDialog
ref="domMealDialog"
:title="$t('meal-plan.create-a-new-meal-plan')"
color="primary"
:icon="$globals.icons.foods"
@submit="
actions.createOne(newMeal);
resetDialog();
"
>
<v-card-text> <v-card-text>
<v-menu <v-menu
v-model="pickerMenu" v-model="pickerMenu"
@ -25,40 +34,51 @@
</template> </template>
<v-date-picker v-model="newMeal.date" no-title @input="pickerMenu = false"></v-date-picker> <v-date-picker v-model="newMeal.date" no-title @input="pickerMenu = false"></v-date-picker>
</v-menu> </v-menu>
<v-autocomplete <v-card-text>
v-if="!noteOnly" <v-autocomplete
v-model="newMeal.recipeId" v-if="!dialog.note"
label="Meal Recipe" v-model="newMeal.recipeId"
:items="allRecipes" label="Meal Recipe"
item-text="name" :items="allRecipes"
item-value="id" item-text="name"
:return-object="false" item-value="id"
></v-autocomplete> :return-object="false"
<template v-else> ></v-autocomplete>
<v-text-field v-model="newMeal.title" label="Meal Title"> </v-text-field> <template v-else>
<v-textarea v-model="newMeal.text" label="Meal Note"> </v-textarea> <v-text-field v-model="newMeal.title" label="Meal Title"> </v-text-field>
</template> <v-textarea v-model="newMeal.text" rows="2" label="Meal Note"> </v-textarea>
</template>
</v-card-text>
<v-card-actions class="my-0 py-0">
<v-switch v-model="dialog.note" class="mt-n3" label="Note Only"></v-switch>
</v-card-actions>
</v-card-text> </v-card-text>
<v-card-actions> </BaseDialog>
<v-switch v-model="noteOnly" label="Note Only"></v-switch>
<v-spacer></v-spacer>
<BaseButton @click="actions.createOne(newMeal)" />
</v-card-actions>
</v-card>
<div class="d-flex justify-center my-2 align-center" style="gap: 10px"> <!-- Date Forward / Back -->
<v-btn icon color="info" rounded outlined @click="backOneWeek"> <div class="d-flex justify-center flex-column">
<v-icon>{{ $globals.icons.back }} </v-icon> <h3 class="text-h6 mt-2 text-center">{{ $d(weekRange.start, "short") }} - {{ $d(weekRange.end, "short") }}</h3>
</v-btn> <div class="d-flex justify-center my-2 align-center" style="gap: 10px">
<v-btn rounded outlined readonly style="pointer-events: none"> <v-btn icon color="info" outlined @click="backOneWeek">
{{ $d(weekRange.start, "short") }} - {{ $d(weekRange.end, "short") }} <v-icon>{{ $globals.icons.back }} </v-icon>
</v-btn> </v-btn>
<v-btn icon color="info" rounded outlined @click="forwardOneWeek"> <v-btn icon color="info" outlined @click="forwardOneWeek">
<v-icon>{{ $globals.icons.forward }} </v-icon> <v-icon>{{ $globals.icons.forward }} </v-icon>
</v-btn> </v-btn>
</div>
</div> </div>
<v-switch v-model="edit" label="Editor"></v-switch>
<v-row class="mt-2"> <v-row class="mt-2">
<v-col v-for="(plan, index) in mealsByDate" :key="index" cols="12" sm="12" md="4" lg="3" xl="2"> <v-col
v-for="(plan, index) in mealsByDate"
:key="index"
cols="12"
sm="12"
md="4"
lg="3"
xl="2"
class="col-borders my-1 d-flex flex-column"
>
<p class="h5 text-center"> <p class="h5 text-center">
{{ $d(plan.date, "short") }} {{ $d(plan.date, "short") }}
</p> </p>
@ -71,46 +91,83 @@
style="min-height: 150px" style="min-height: 150px"
@end="onMoveCallback" @end="onMoveCallback"
> >
<v-hover v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" open-delay="100"> <v-card v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" class="my-1">
<v-card class="my-2"> <v-list-item>
<v-list-item> <v-list-item-avatar :rounded="false">
<v-list-item-content> <RecipeCardImage v-if="mealplan.recipe" tiny icon-size="25" :slug="mealplan.recipe.slug" />
<v-list-item-title class="mb-1"> <v-icon v-else>
{{ mealplan.recipe ? mealplan.recipe.name : mealplan.title }} {{ $globals.icons.primary }}
</v-list-item-title> </v-icon>
<v-list-item-subtitle> </v-list-item-avatar>
{{ mealplan.recipe ? mealplan.recipe.description : mealplan.text }} <v-list-item-content>
</v-list-item-subtitle> <v-list-item-title class="mb-1">
</v-list-item-content> {{ mealplan.recipe ? mealplan.recipe.name : mealplan.title }}
</v-list-item> </v-list-item-title>
</v-card> <v-list-item-subtitle>
</v-hover> {{ 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>
<v-card-actions>
<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-card-actions>
</v-card>
</draggable> </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>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, reactive, toRefs } from "@nuxtjs/composition-api"; import { computed, defineComponent, reactive, ref, toRefs, watch } from "@nuxtjs/composition-api";
import { isSameDay, addDays, subDays, parseISO, format } from "date-fns"; import { isSameDay, addDays, subDays, parseISO, format } from "date-fns";
import { SortableEvent } from "sortablejs"; // eslint-disable-line import { SortableEvent } from "sortablejs"; // eslint-disable-line
import draggable from "vuedraggable"; import draggable from "vuedraggable";
import { useMealplans } from "~/composables/use-group-mealplan"; import { useMealplans } from "~/composables/use-group-mealplan";
import { useRecipes, allRecipes } from "~/composables/use-recipes"; import { useRecipes, allRecipes } from "~/composables/use-recipes";
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
export default defineComponent({ export default defineComponent({
components: { components: {
draggable, draggable,
RecipeCardImage,
}, },
setup() { setup() {
const { mealplans, actions } = useMealplans(); const { mealplans, actions } = useMealplans();
useRecipes(true, true); useRecipes(true, true);
const state = reactive({ const state = reactive({
edit: false,
hover: {}, hover: {},
pickerMenu: null, pickerMenu: null,
noteOnly: false,
start: null as Date | null, start: null as Date | null,
today: new Date(), today: new Date(),
end: null as Date | null, end: null as Date | null,
@ -169,9 +226,9 @@ export default defineComponent({
const weekRange = computed(() => { const weekRange = computed(() => {
// @ts-ignore - Not Sure Why This is not working // @ts-ignore - Not Sure Why This is not working
const end = addDays(state.today, 2); const end = addDays(state.today, 6);
// @ts-ignore - Not sure why the type is invalid // @ts-ignore - Not sure why the type is invalid
const start = subDays(state.today, 2); const start = subDays(state.today, 1);
return { start, end, today: state.today }; return { start, end, today: state.today };
}); });
@ -183,14 +240,62 @@ export default defineComponent({
); );
}); });
// =====================================================
// New Meal Dialog
const domMealDialog = ref(null);
const dialog = reactive({
loading: false,
error: false,
note: false,
});
watch(dialog, () => {
if (dialog.note) {
newMeal.recipeId = null;
}
newMeal.title = "";
newMeal.text = "";
});
const newMeal = reactive({ const newMeal = reactive({
date: null, date: "",
title: "", title: "",
text: "", text: "",
recipeId: null, recipeId: null,
}); });
function openDialog(date: Date) {
newMeal.date = format(date, "yyyy-MM-dd");
// @ts-ignore
domMealDialog.value.open();
}
function resetDialog() {
newMeal.date = "";
newMeal.title = "";
newMeal.text = "";
newMeal.recipeId = null;
}
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;
// @ts-ignore
actions.createOne(newMeal);
resetDialog();
}
return { return {
resetDialog,
randomMeal,
dialog,
domMealDialog,
openDialog,
mealplans, mealplans,
actions, actions,
newMeal, newMeal,
@ -207,3 +312,9 @@ export default defineComponent({
}); });
</script> </script>
<style lang="css">
.col-borders {
border-top: 1px solid #e0e0e0;
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div></div> <div>This Week</div>
</template> </template>
<script lang="ts"> <script lang="ts">