enrecipes/app/components/MealPlanner.vue
2021-04-19 00:04:17 +05:30

391 lines
10 KiB
Vue

<template>
<Page @loaded="onPageLoad" actionBarHidden="true">
<GridLayout rows="*, auto" columns="*">
<ScrollView
@scroll="!edit && onScroll($event)"
rowSpan="2"
scrollBarIndicatorVisible="false"
>
<StackLayout>
<Label class="pageTitle" :text="'planner' | L" />
<GridLayout
class="calendar"
columns="*, *, *, *, *, *, *"
rows="auto, auto, auto, auto, auto, auto, auto, auto"
>
<Button class="ico navBtn" :text="icon.left" @tap="prevMonth" />
<Label
class="monthName"
col="1"
colSpan="5"
:text="$options.filters.L(mNames[month]) + ' ' + year"
/>
<Button
class="ico navBtn"
col="6"
:text="icon.right"
@tap="nextMonth"
/>
<Label
class="dayName"
row="1"
:col="i"
v-for="(d, i) in getDayNames"
:key="d"
:text="d | L"
/>
<Button
class="min day"
:class="{
tb: isToday(d),
activeDay: isActive(d),
hasPlans: hasPlans(d),
}"
:row="getrow(i)"
:col="i % 7"
v-for="(d, i) in getCal"
:key="i"
:text="d ? d : null"
@tap="setToday(d)"
/>
</GridLayout>
<StackLayout class="dayPlan">
<StackLayout
v-for="(mealType, index) in mealTimesWithRecipes"
:key="'mealType' + index"
>
<GridLayout columns="auto, auto">
<Label
class="periodLabel tb"
:class="mealType"
:text="mealType | L"
/>
<Button
:visibility="edit ? 'visible' : 'hidden'"
col="1"
class="ico"
:text="icon.plus"
@tap="addRecipe(mealType)"
/>
</GridLayout>
<GridLayout
:columns="`*, ${edit ? 'auto' : 0}`"
v-for="(recipeID, index) in getRecipes[mealType]"
:key="mealType + index"
>
<Button
class="recipeTitle"
:text="getRecipeTitle(recipeID)"
@tap="viewRecipe(recipeID)"
/>
<Button
:visibility="edit ? 'visible' : 'hidden'"
col="1"
class="ico x"
:text="icon.x"
@tap="removeRecipe(recipeID, mealType)"
/>
</GridLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
<GridLayout
row="1"
@loaded="onAppBarLoad"
class="appbar"
:hidden="showUndo"
columns="auto, *, auto, auto"
>
<Button class="ico" :text="icon.back" @tap="$navigateBack()" />
<Button
class="ico"
:text="icon.tod"
:hidden="isExactlyToday"
@tap="goToToday"
col="2"
/>
<Button
class="ico fab"
:text="edit ? icon.done : icon.edit"
@tap="toggleEditMode"
col="3"
/>
</GridLayout>
<GridLayout
row="1"
class="appbar snackBar"
:hidden="!showUndo"
columns="auto, *, auto"
>
<Button :text="countdown" class="ico countdown tb" />
<Label class="title" col="1" :text="snackMsg | L" />
<Button class="ico fab" :text="icon.undo" @tap="undoDel" col="3" />
</GridLayout>
</GridLayout>
</Page>
</template>
<script>
import { ApplicationSettings, Observable, CoreTypes } from "@nativescript/core";
import { mapState, mapActions } from "vuex";
import ViewRecipe from "./ViewRecipe.vue";
import ActionDialogWithSearch from "./modal/ActionDialogWithSearch.vue";
let undoTimer;
export default {
data() {
return {
appTheme: "Light",
mealTimes: ["breakfast", "lunch", "dinner", "snacks"],
dNames: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
year: 2021,
mNames: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
month: 0,
today: null,
edit: false,
scrollPos: 1,
appbar: null,
countdown: 5,
snackMsg: null,
showUndo: false,
undo: false,
};
},
computed: {
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]),
todaysTime() {
return new Date(this.year, this.month, this.today, 0).getTime();
},
getRecipes() {
if (this.mealPlans.length) {
return this.mealPlans.reduce((acc, e) => {
if (e.date == this.todaysTime) {
acc[e.type] = [...(acc[e.type] || []), e.title];
}
return acc;
}, {});
} else return 0;
},
getDayNames() {
if (!this.mondayFirst) this.dNames.unshift(this.dNames.pop());
return this.dNames;
},
getCal() {
let y = this.year;
let m = this.month;
let ds = new Date(y, m + 1, 0).getDate();
let fd = new Date(y, m, 1).getDay();
if (this.mondayFirst) fd -= 1;
let days = new Array(fd).fill(0);
for (let i = 1; i <= ds; i++) {
days.push(i);
}
return days;
},
isExactlyToday() {
let d = new Date();
return (
this.year == d.getFullYear() &&
this.month == d.getMonth() &&
this.today == d.getDate()
);
},
mealTimesWithRecipes() {
return this.mealTimes.filter(
(e) => (this.getRecipes[e] && this.getRecipes[e].length) || this.edit
);
},
},
methods: {
...mapActions([
"setComponent",
"addMealPlanAction",
"deleteMealPlanAction",
]),
onPageLoad(args) {
const page = args.object;
page.bindingContext = new Observable();
this.setComponent("MealPlanner");
if (!this.today || this.today === new Date().getDate()) this.goToToday();
},
onAppBarLoad({ object }) {
this.appbar = object;
},
onScroll(args) {
let scrollUp;
let y = args.scrollY;
if (y) {
scrollUp = y < this.scrollPos;
this.scrollPos = Math.abs(y);
let ab = this.appbar.translateY;
if (!scrollUp && ab == 0) {
this.appbar.animate({
translate: { x: 0, y: 64 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
} else if (scrollUp && ab == 64) {
this.appbar.animate({
translate: { x: 0, y: 0 },
duration: 250,
curve: CoreTypes.AnimationCurve.ease,
});
}
}
},
// HELPERS
showAppBar() {
this.appbar.translateY = 0;
},
getrow(i) {
return Math.floor(2 + i / 7);
},
getDate(index) {
let date = new Date();
date.setDate(date.getDate() + index);
return date.getTime();
},
getRecipeTitle(id) {
let recipe = this.recipes.filter((e) => e.id === id)[0];
return recipe ? recipe.title : `[ ${this.$options.filters.L("resNF")} ]`;
},
// NAVIGATION HANDLERS
viewRecipe(recipeID) {
let recipe = this.recipes.filter((e) => e.id === recipeID)[0];
if (recipe) {
this.$navigateTo(ViewRecipe, {
props: {
filterTrylater: true,
recipeID,
},
});
}
},
// CALENDAR
prevMonth() {
if (this.month == 0) {
this.year--;
this.month = 11;
} else this.month--;
this.showAppBar();
},
nextMonth() {
if (this.month == 11) {
this.year++;
this.month = 0;
} else this.month++;
this.showAppBar();
},
goToToday() {
let d = new Date();
this.year = d.getFullYear();
this.month = d.getMonth();
this.today = d.getDate();
this.showAppBar();
},
isToday(date) {
let d = new Date();
return (
this.year == d.getFullYear() &&
this.month == d.getMonth() &&
date == d.getDate()
);
},
isActive(date) {
return this.today == date;
},
hasPlans(date) {
let d = new Date(this.year, this.month, date, 0).getTime();
return this.mealPlans.filter((e) => e.date == d).length;
},
setToday(date) {
if (date) this.today = date;
},
newMealPlan(title, date, type, index) {
this.addMealPlanAction({
title,
date: date ? date : this.todaysTime,
type,
index,
});
},
toggleEditMode() {
this.edit = !this.edit;
},
// DATA HANDLERS
addRecipe(type) {
let filteredRecipes = this.recipes.filter((e) =>
this.getRecipes[type] ? !this.getRecipes[type].includes(e.id) : true
);
this.$showModal(ActionDialogWithSearch, {
props: {
title: "selRec",
recipes: filteredRecipes,
},
}).then((title) => {
title && this.newMealPlan(title, null, type, null);
});
},
removeRecipe(title, type) {
let date = this.todaysTime;
let index = this.mealPlans.findIndex(
(e) => e.title === title && e.type === type && e.date === date
);
let mealPlan = {
title,
date,
type,
index,
};
this.deleteMealPlanAction(mealPlan);
this.showUndoBar("recRm").then((res) =>
this.newMealPlan(title, date, type, index)
);
},
showUndoBar(message) {
return new Promise((resolve, reject) => {
clearTimeout(undoTimer);
this.showUndo = true;
this.snackMsg = message;
this.countdown = 5;
let a = 5;
undoTimer = setInterval(() => {
if (this.undo) {
this.showUndo = this.undo = false;
clearTimeout(undoTimer);
resolve(true);
}
this.countdown = Math.round((a -= 0.1));
if (this.countdown < 1) {
this.showUndo = false;
clearTimeout(undoTimer);
reject(true);
}
}, 100);
});
},
undoDel() {
this.undo = true;
},
},
created() {
this.appTheme = ApplicationSettings.getString("appTheme", "Light");
},
};
</script>