390 lines
10 KiB
Vue
390 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>
|