refactor(frontend): 🚧 rename recipe and meal-plan components

This commit is contained in:
hay-kot
2021-07-31 15:07:19 -08:00
parent 5047b3f5af
commit f6b3285ae3
33 changed files with 1111 additions and 98 deletions

View File

@@ -0,0 +1,173 @@
<template>
<v-row>
<SearchDialog ref="mealselect" @selected="setSlug" />
<BaseDialog
title="Custom Meal"
:title-icon="$globals.icons.primary"
:submit-text="$t('general.save')"
:top="true"
ref="customMealDialog"
@submit="pushCustomMeal"
>
<v-card-text>
<v-text-field autofocus v-model="customMeal.name" :label="$t('general.name')"> </v-text-field>
<v-textarea v-model="customMeal.description" :label="$t('recipe.description')"> </v-textarea>
</v-card-text>
</BaseDialog>
<v-col cols="12" sm="12" md="6" lg="4" xl="3" v-for="(planDay, index) in value" :key="index">
<v-hover v-slot="{ hover }" :open-delay="50">
<v-card :class="{ 'on-hover': hover }" :elevation="hover ? 12 : 2">
<CardImage large :slug="planDay.meals[0].slug" icon-size="200" @click="openSearch(index, modes.primary)">
<div>
<v-fade-transition>
<v-btn v-if="hover" small color="info" class="ma-1" @click.stop="addCustomItem(index, modes.primary)">
<v-icon left>
{{ $globals.icons.edit }}
</v-icon>
{{ $t("reicpe.no-recipe") }}
</v-btn>
</v-fade-transition>
</div>
</CardImage>
<v-card-title class="my-n3 mb-n6">
{{ $d(new Date(planDay.date.replaceAll("-", "/")), "short") }}
</v-card-title>
<v-card-subtitle class="mb-0 pb-0"> {{ planDay.meals[0].name }}</v-card-subtitle>
<v-hover v-slot="{ hover }">
<v-card-actions>
<v-spacer></v-spacer>
<v-fade-transition>
<v-btn v-if="hover" small color="info" text @click.stop="addCustomItem(index, modes.sides)">
<v-icon left>
{{ $globals.icons.edit }}
</v-icon>
{{ $t("reicpe.no-recipe") }}
</v-btn>
</v-fade-transition>
<v-btn color="info" outlined small @click="openSearch(index, modes.sides)">
<v-icon small class="mr-1">
{{ $globals.icons.create }}
</v-icon>
{{ $t("meal-plan.side") }}
</v-btn>
</v-card-actions>
</v-hover>
<v-divider class="mx-2"></v-divider>
<v-list dense>
<v-list-item v-for="(recipe, i) in planDay.meals.slice(1)" :key="i">
<v-list-item-avatar color="accent">
<v-img :alt="recipe.slug" :src="getImage(recipe.slug)"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="recipe.name"></v-list-item-title>
</v-list-item-content>
<v-list-item-icon>
<v-btn icon @click="removeSide(index, i + 1)">
<v-icon color="error">
{{ $globals.icons.delete }}
</v-icon>
</v-btn>
</v-list-item-icon>
</v-list-item>
</v-list>
</v-card>
</v-hover>
</v-col>
</v-row>
</template>
<script>
import SearchDialog from "../UI/Dialogs/SearchDialog";
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
import { api } from "@/api";
import CardImage from "../Recipe/CardImage.vue";
export default {
components: {
SearchDialog,
CardImage,
BaseDialog,
},
props: {
value: Array,
},
data() {
return {
activeIndex: 0,
mode: "PRIMARY",
modes: {
primary: "PRIMARY",
sides: "SIDES",
},
customMeal: {
slug: null,
name: "",
description: "",
},
};
},
methods: {
getImage(slug) {
if (slug) {
return api.recipes.recipeSmallImage(slug);
}
},
setSide(name, slug = null, description = "") {
const meal = { name: name, slug: slug, description: description };
this.value[this.activeIndex]["meals"].push(meal);
},
setPrimary(name, slug, description = "") {
this.value[this.activeIndex]["meals"][0]["slug"] = slug;
this.value[this.activeIndex]["meals"][0]["name"] = name;
this.value[this.activeIndex]["meals"][0]["description"] = description;
},
setSlug(recipe) {
switch (this.mode) {
case this.modes.primary:
this.setPrimary(recipe.name, recipe.slug);
break;
default:
this.setSide(recipe.name, recipe.slug);
break;
}
},
openSearch(index, mode) {
this.mode = mode;
this.activeIndex = index;
this.$refs.mealselect.open();
},
removeSide(dayIndex, sideIndex) {
this.value[dayIndex]["meals"].splice(sideIndex, 1);
},
addCustomItem(index, mode) {
this.mode = mode;
this.activeIndex = index;
this.$refs.customMealDialog.open();
},
pushCustomMeal() {
switch (this.mode) {
case this.modes.primary:
this.setPrimary(this.customMeal.name, this.customMeal.slug, this.customMeal.description);
break;
default:
this.setSide(this.customMeal.name, this.customMeal.slug, this.customMeal.description);
break;
}
this.customMeal = { name: "", slug: null, description: "" };
},
},
};
</script>
<style>
.relative-card {
position: relative;
}
.custom-button {
z-index: -1;
}
</style>

View File

@@ -0,0 +1,46 @@
<template>
<v-card>
<v-card-title class="headline">
{{ $t("meal-plan.edit-meal-plan") }}
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<MealPlanCard v-model="mealPlan.planDays" />
<v-row align="center" justify="end">
<v-card-actions>
<TheButton update @click="update" />
<v-spacer></v-spacer>
</v-card-actions>
</v-row>
</v-card-text>
</v-card>
</template>
<script>
import { api } from "@/api";
import { utils } from "@/utils";
import MealPlanCard from "./MealPlanCard";
export default {
components: {
MealPlanCard,
},
props: {
mealPlan: Object,
},
methods: {
formatDate(timestamp) {
let dateObject = new Date(timestamp);
return utils.getDateAsPythonDate(dateObject);
},
async update() {
if (await api.mealPlans.update(this.mealPlan.uid, this.mealPlan)) {
this.$emit("updated");
}
},
},
};
</script>
<style></style>

View File

@@ -0,0 +1,227 @@
<template>
<v-card>
<v-card-title class=" headline">
{{ $t("meal-plan.create-a-new-meal-plan") }}
<v-btn color="info" class="ml-auto" @click="setQuickWeek()">
<v-icon left> {{ $globals.icons.calendarMinus }} </v-icon>
{{ $t("meal-plan.quick-week") }}
</v-btn>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-row dense>
<v-col cols="12" lg="6" md="6" sm="12">
<v-menu
ref="menu1"
v-model="menu1"
:close-on-content-click="true"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="startComputedDateFormatted"
:label="$t('meal-plan.start-date')"
persistent-hint
:prepend-icon="$globals.icons.calendarMinus"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<DatePicker v-model="startDate" no-title @input="menu2 = false" />
</v-menu>
</v-col>
<v-col cols="12" lg="6" md="6" sm="12">
<v-menu
ref="menu2"
v-model="menu2"
:close-on-content-click="true"
transition="scale-transition"
offset-y
max-width="290px"
min-width="290px"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="endComputedDateFormatted"
:label="$t('meal-plan.end-date')"
persistent-hint
:prepend-icon="$globals.icons.calendarMinus"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<DatePicker v-model="endDate" no-title @input="menu2 = false" />
</v-menu>
</v-col>
</v-row>
</v-card-text>
<v-card-text v-if="startDate">
<MealPlanCard v-model="planDays" />
</v-card-text>
<v-row align="center" justify="end">
<v-card-actions class="mr-5">
<TheButton edit @click="random" v-if="planDays.length > 0" text>
<template v-slot:icon>
{{ $globals.icons.diceMultiple }}
</template>
{{ $t("general.random") }}
</TheButton>
<TheButton create @click="save" :disabled="planDays.length == 0" />
</v-card-actions>
</v-row>
</v-card>
</template>
<script>
const CREATE_EVENT = "created";
import DatePicker from "@/components/FormHelpers/DatePicker";
import { api } from "@/api";
import { utils } from "@/utils";
import MealPlanCard from "./MealPlanCard";
export default {
components: {
MealPlanCard,
DatePicker,
},
data() {
return {
isLoading: false,
planDays: [],
items: [],
// Dates
startDate: null,
endDate: null,
menu1: false,
menu2: false,
usedRecipes: [1],
};
},
watch: {
dateDif() {
this.planDays = [];
for (let i = 0; i < this.dateDif; i++) {
this.planDays.push({
date: this.getDate(i),
meals: [
{
name: "",
slug: "empty",
description: "empty",
},
],
});
}
},
},
async created() {
await this.$store.dispatch("requestCurrentGroup");
await this.$store.dispatch("requestAllRecipes");
await this.buildMealStore();
},
computed: {
groupSettings() {
return this.$store.getters.getCurrentGroup;
},
actualStartDate() {
if (!this.startDate) return null;
return Date.parse(this.startDate.replaceAll("-", "/"));
},
actualEndDate() {
if (!this.endDate) return null;
return Date.parse(this.endDate.replaceAll("-", "/"));
},
dateDif() {
if (!this.actualEndDate || !this.actualStartDate) return null;
let dateDif = (this.actualEndDate - this.actualStartDate) / (1000 * 3600 * 24) + 1;
if (dateDif < 1) {
return null;
}
return dateDif;
},
startComputedDateFormatted() {
return this.formatDate(this.actualStartDate);
},
endComputedDateFormatted() {
return this.formatDate(this.actualEndDate);
},
filteredRecipes() {
const recipes = this.items.filter(x => !this.usedRecipes.includes(x));
return recipes.length > 0 ? recipes : this.items;
},
allRecipes() {
return this.$store.getters.getAllRecipes;
},
},
methods: {
async buildMealStore() {
const categories = Array.from(this.groupSettings.categories, x => x.name);
this.items = await api.recipes.getAllByCategory(categories);
if (this.items.length === 0) {
this.items = this.allRecipes;
}
},
getRandom(list) {
return list[Math.floor(Math.random() * list.length)];
},
random() {
this.usedRecipes = [1];
this.planDays.forEach((_, index) => {
let recipe = this.getRandom(this.filteredRecipes);
this.planDays[index]["meals"][0]["slug"] = recipe.slug;
this.planDays[index]["meals"][0]["name"] = recipe.name;
this.usedRecipes.push(recipe);
});
},
getDate(index) {
const dateObj = new Date(this.actualStartDate.valueOf() + 1000 * 3600 * 24 * index);
return utils.getDateAsPythonDate(dateObj);
},
async save() {
const mealBody = {
group: this.groupSettings.name,
startDate: this.startDate,
endDate: this.endDate,
planDays: this.planDays,
};
if (await api.mealPlans.create(mealBody)) {
this.$emit(CREATE_EVENT);
this.planDays = [];
this.startDate = null;
this.endDate = null;
}
},
formatDate(date) {
if (!date) return null;
return this.$d(date);
},
getNextDayOfTheWeek(dayName, excludeToday = true, refDate = new Date()) {
const dayOfWeek = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"].indexOf(dayName.slice(0, 3).toLowerCase());
if (dayOfWeek < 0) return;
refDate.setUTCHours(0, 0, 0, 0);
refDate.setDate(refDate.getDate() + +!!excludeToday + ((dayOfWeek + 7 - refDate.getDay() - +!!excludeToday) % 7));
return refDate;
},
setQuickWeek() {
const nextMonday = this.getNextDayOfTheWeek("Monday", false);
const nextEndDate = new Date(nextMonday);
nextEndDate.setDate(nextEndDate.getDate() + 4);
this.startDate = utils.getDateAsPythonDate(nextMonday);
this.endDate = utils.getDateAsPythonDate(nextEndDate);
},
},
};
</script>