461 lines
12 KiB
Vue
461 lines
12 KiB
Vue
<template>
|
|
<Page @loaded="pgLoad" actionBarHidden="true">
|
|
<GridLayout rows="*, auto" columns="*">
|
|
<ScrollView
|
|
@scroll="!edit && svLoad($event)"
|
|
rowSpan="2"
|
|
scrollBarIndicatorVisible="false"
|
|
>
|
|
<StackLayout>
|
|
<GridLayout rows="auto" columns="*, auto, 8">
|
|
<Label class="pageTitle" :text="'planner' | L" />
|
|
<Button
|
|
col="1"
|
|
class="ico"
|
|
:text="icon.cog"
|
|
@tap="$navigateTo(MPSettings)"
|
|
/>
|
|
</GridLayout>
|
|
<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"
|
|
@touch="touchMonthYearPicker"
|
|
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(mealType, recipeID)"
|
|
/>
|
|
</GridLayout>
|
|
</StackLayout>
|
|
</StackLayout>
|
|
</StackLayout>
|
|
</ScrollView>
|
|
<GridLayout
|
|
row="1"
|
|
@loaded="abLoad"
|
|
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>
|
|
<SnackBar
|
|
:hidden="!showUndo"
|
|
:count="countdown"
|
|
:msg="snackMsg"
|
|
:undo="undoDel"
|
|
:action="hideBar"
|
|
:onload="sbLoad"
|
|
/>
|
|
</GridLayout>
|
|
</Page>
|
|
</template>
|
|
|
|
<script>
|
|
import { Observable, CoreTypes } from "@nativescript/core";
|
|
import { mapState, mapActions } from "vuex";
|
|
import ViewRecipe from "./ViewRecipe";
|
|
import MPSettings from "./settings/MPSettings";
|
|
import ActionWithSearch from "./modals/ActionWithSearch";
|
|
import MonthYearPicker from "./modals/MonthYearPicker";
|
|
import SnackBar from "./sub/SnackBar";
|
|
let barTimer;
|
|
|
|
export default {
|
|
components: {
|
|
SnackBar,
|
|
},
|
|
data() {
|
|
return {
|
|
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,
|
|
date: null,
|
|
edit: false,
|
|
scrollPos: 1,
|
|
appbar: null,
|
|
snackbar: null,
|
|
countdown: 5,
|
|
snackMsg: null,
|
|
showUndo: false,
|
|
undo: false,
|
|
MPSettings: MPSettings,
|
|
temp: 0,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState(["icon", "recipes", "mealPlans", "mondayFirst"]),
|
|
todaysTime() {
|
|
return new Date(this.year, this.month, this.date, 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() {
|
|
let dNames = [...this.dNames];
|
|
if (!this.mondayFirst) dNames.unshift(dNames.pop());
|
|
return 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();
|
|
let ld = new Date(y, m, ds).getDay();
|
|
if (this.mondayFirst) fd -= 1;
|
|
let days = new Array(fd).fill(0);
|
|
// let prevDays = Array.from(
|
|
// { length: fd },
|
|
// (e, k) => k + new Date(!m ? y - 1 : y, m, 0).getDate() - fd + 1
|
|
// );
|
|
// let days = prevDays;
|
|
for (let i = 1; i <= ds; i++) days.push(i);
|
|
// for (let i = 1; i <= 6 - ld; i++) days.push(i);
|
|
return days;
|
|
},
|
|
isExactlyToday() {
|
|
let d = new Date();
|
|
return (
|
|
this.year == d.getFullYear() &&
|
|
this.month == d.getMonth() &&
|
|
this.date == d.getDate()
|
|
);
|
|
},
|
|
mealTimesWithRecipes() {
|
|
return this.mealTimes.filter(
|
|
(e) => (this.getRecipes[e] && this.getRecipes[e].length) || this.edit
|
|
);
|
|
},
|
|
},
|
|
methods: {
|
|
...mapActions([
|
|
"setComponent",
|
|
"addMealPlanAction",
|
|
"deleteMealPlanAction",
|
|
]),
|
|
pgLoad({ object }) {
|
|
object.bindingContext = new Observable();
|
|
this.setComponent("MealPlanner");
|
|
if (!this.date || this.date === new Date().getDate()) this.goToToday();
|
|
},
|
|
abLoad({ object }) {
|
|
this.appbar = object;
|
|
},
|
|
sbLoad({ object }) {
|
|
this.snackbar = object;
|
|
},
|
|
svLoad(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.date = 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.date == 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.date = date;
|
|
},
|
|
toggleEditMode() {
|
|
this.edit = !this.edit;
|
|
},
|
|
openMonthYearPicker() {
|
|
this.$showModal(MonthYearPicker, {
|
|
props: {
|
|
title: "gtD",
|
|
monthNames: this.mNames,
|
|
currentM: this.month,
|
|
currentY: this.year,
|
|
},
|
|
}).then((res) => {
|
|
if (res) {
|
|
this.month = res.month;
|
|
this.year = res.year;
|
|
}
|
|
});
|
|
},
|
|
|
|
// DATA HANDLERS
|
|
newMealPlan({ date, type, title, index, inDB }) {
|
|
this.addMealPlanAction({
|
|
date: date ? date : this.todaysTime,
|
|
type,
|
|
title,
|
|
index,
|
|
inDB,
|
|
});
|
|
},
|
|
addRecipe(type) {
|
|
let filteredRecipes = this.recipes.filter((e) =>
|
|
this.getRecipes[type] ? !this.getRecipes[type].includes(e.id) : true
|
|
);
|
|
this.$showModal(ActionWithSearch, {
|
|
props: {
|
|
title: "selRec",
|
|
recipes: filteredRecipes,
|
|
},
|
|
}).then(
|
|
(title) =>
|
|
title &&
|
|
this.newMealPlan({ date: 0, type, title, index: null, inDB: true })
|
|
);
|
|
},
|
|
deleteTempFromDB() {
|
|
if (this.temp) {
|
|
this.temp.inDB = 1;
|
|
this.deleteMealPlanAction(this.temp);
|
|
this.temp = 0;
|
|
}
|
|
},
|
|
removeRecipe(type, title) {
|
|
this.deleteTempFromDB();
|
|
let date = this.todaysTime;
|
|
let index = this.mealPlans.findIndex(
|
|
(e) => e.date == date && e.type == type && e.title == title
|
|
);
|
|
let mealPlan = {
|
|
date,
|
|
type,
|
|
title,
|
|
index,
|
|
};
|
|
let temp;
|
|
this.temp = temp = mealPlan;
|
|
this.deleteMealPlanAction(mealPlan);
|
|
this.showUndoBar("recRm")
|
|
.then(() => this.newMealPlan({ date, type, title, index }))
|
|
.catch(() => {
|
|
temp.inDB = 1;
|
|
console.log("deleting inDB after catch: ", temp);
|
|
this.deleteMealPlanAction(temp);
|
|
});
|
|
},
|
|
showUndoBar(message) {
|
|
return new Promise((resolve, reject) => {
|
|
this.animateBar(this.appbar, 0).then(() => {
|
|
this.showUndo = true;
|
|
this.snackMsg = message;
|
|
this.countdown = 5;
|
|
this.animateBar(this.snackbar, 1).then(() => {
|
|
let a = 5;
|
|
clearInterval(barTimer);
|
|
barTimer = setInterval(() => {
|
|
if (this.undo) {
|
|
this.hideBar();
|
|
resolve(true);
|
|
}
|
|
this.countdown = Math.round((a -= 0.1));
|
|
if (this.countdown < 1) {
|
|
this.hideBar();
|
|
reject(true);
|
|
}
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|
|
},
|
|
hideBar() {
|
|
clearInterval(barTimer);
|
|
this.animateBar(this.snackbar, 0).then(() => {
|
|
this.showUndo = this.undo = false;
|
|
this.animateBar(this.appbar, 1);
|
|
});
|
|
},
|
|
undoDel() {
|
|
this.undo = true;
|
|
},
|
|
|
|
//HELPERS
|
|
touchMonthYearPicker({ object, action }) {
|
|
object.className = action.match(/down|move/)
|
|
? "monthName fade"
|
|
: "monthName";
|
|
if (action == "up") this.openMonthYearPicker();
|
|
},
|
|
},
|
|
};
|
|
</script>
|