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 : []"
@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 }">
<v-btn rounded large class="ml-2 mt-3" v-bind="attrs" v-on="on">
<v-icon left large color="primary">
@ -20,7 +20,7 @@
{{ $t("general.create") }}
</v-btn>
</template>
<v-list>
<v-list dense class="my-0 py-0">
<template v-for="(item, index) in createLinks">
<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>
@ -91,9 +91,7 @@ export default defineComponent({
to: "/recipe/create?tab=url",
restricted: true,
},
{
divider: true,
},
{ divider: true },
{
icon: this.$globals.icons.edit,
title: "Create",
@ -101,16 +99,30 @@ export default defineComponent({
to: "/recipe/create?tab=new",
restricted: true,
},
{
divider: true,
},
{ divider: true },
{
icon: this.$globals.icons.zip,
title: "Restore",
title: "Recipe from zip",
subtitle: "Restore from a exported recipe",
to: "/recipe/create?tab=zip",
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: [
{
@ -138,12 +150,6 @@ export default defineComponent({
to: "/meal-plan/this-week",
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>
<script lang="ts">
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
import { computed, reactive } from "@vue/reactivity";
import { defineComponent, ref, useContext, computed, reactive } from "@nuxtjs/composition-api";
export default defineComponent({
layout: "basic",

View File

@ -1,7 +1,16 @@
<template>
<v-container>
<v-card>
<v-card-title class="headline">New Recipe</v-card-title>
<!-- Create Meal Dialog -->
<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-menu
v-model="pickerMenu"
@ -25,40 +34,51 @@
</template>
<v-date-picker v-model="newMeal.date" no-title @input="pickerMenu = false"></v-date-picker>
</v-menu>
<v-autocomplete
v-if="!noteOnly"
v-model="newMeal.recipeId"
label="Meal Recipe"
:items="allRecipes"
item-text="name"
item-value="id"
:return-object="false"
></v-autocomplete>
<template v-else>
<v-text-field v-model="newMeal.title" label="Meal Title"> </v-text-field>
<v-textarea v-model="newMeal.text" label="Meal Note"> </v-textarea>
</template>
<v-card-text>
<v-autocomplete
v-if="!dialog.note"
v-model="newMeal.recipeId"
label="Meal Recipe"
:items="allRecipes"
item-text="name"
item-value="id"
:return-object="false"
></v-autocomplete>
<template v-else>
<v-text-field v-model="newMeal.title" label="Meal Title"> </v-text-field>
<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-actions>
<v-switch v-model="noteOnly" label="Note Only"></v-switch>
<v-spacer></v-spacer>
<BaseButton @click="actions.createOne(newMeal)" />
</v-card-actions>
</v-card>
</BaseDialog>
<div class="d-flex justify-center my-2 align-center" style="gap: 10px">
<v-btn icon color="info" rounded outlined @click="backOneWeek">
<v-icon>{{ $globals.icons.back }} </v-icon>
</v-btn>
<v-btn rounded outlined readonly style="pointer-events: none">
{{ $d(weekRange.start, "short") }} - {{ $d(weekRange.end, "short") }}
</v-btn>
<v-btn icon color="info" rounded outlined @click="forwardOneWeek">
<v-icon>{{ $globals.icons.forward }} </v-icon>
</v-btn>
<!-- Date Forward / Back -->
<div class="d-flex justify-center flex-column">
<h3 class="text-h6 mt-2 text-center">{{ $d(weekRange.start, "short") }} - {{ $d(weekRange.end, "short") }}</h3>
<div class="d-flex justify-center my-2 align-center" style="gap: 10px">
<v-btn icon color="info" outlined @click="backOneWeek">
<v-icon>{{ $globals.icons.back }} </v-icon>
</v-btn>
<v-btn icon color="info" outlined @click="forwardOneWeek">
<v-icon>{{ $globals.icons.forward }} </v-icon>
</v-btn>
</div>
</div>
<v-switch v-model="edit" label="Editor"></v-switch>
<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">
{{ $d(plan.date, "short") }}
</p>
@ -71,46 +91,83 @@
style="min-height: 150px"
@end="onMoveCallback"
>
<v-hover v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" open-delay="100">
<v-card class="my-2">
<v-list-item>
<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-card>
</v-hover>
<v-card v-for="mealplan in plan.meals" :key="mealplan.id" v-model="hover[mealplan.id]" class="my-1">
<v-list-item>
<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 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>
<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-row>
</v-container>
</template>
<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 { SortableEvent } from "sortablejs"; // eslint-disable-line
import draggable from "vuedraggable";
import { useMealplans } from "~/composables/use-group-mealplan";
import { useRecipes, allRecipes } from "~/composables/use-recipes";
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
export default defineComponent({
components: {
draggable,
RecipeCardImage,
},
setup() {
const { mealplans, actions } = useMealplans();
useRecipes(true, true);
const state = reactive({
edit: false,
hover: {},
pickerMenu: null,
noteOnly: false,
start: null as Date | null,
today: new Date(),
end: null as Date | null,
@ -169,9 +226,9 @@ export default defineComponent({
const weekRange = computed(() => {
// @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
const start = subDays(state.today, 2);
const start = subDays(state.today, 1);
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({
date: null,
date: "",
title: "",
text: "",
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 {
resetDialog,
randomMeal,
dialog,
domMealDialog,
openDialog,
mealplans,
actions,
newMeal,
@ -206,4 +311,10 @@ export default defineComponent({
},
});
</script>
<style lang="css">
.col-borders {
border-top: 1px solid #e0e0e0;
}
</style>

View File

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