Merge branch 'develop' into remove_facets
This commit is contained in:
commit
3f63eab68c
@ -277,7 +277,7 @@ admin.site.register(RecipeBookEntry, RecipeBookEntryAdmin)
|
||||
|
||||
|
||||
class MealPlanAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'recipe', 'meal_type', 'date')
|
||||
list_display = ('user', 'recipe', 'meal_type', 'from_date', 'to_date')
|
||||
|
||||
@staticmethod
|
||||
def user(obj):
|
||||
|
@ -323,49 +323,6 @@ class ImportRecipeForm(forms.ModelForm):
|
||||
}
|
||||
|
||||
|
||||
# TODO deprecate
|
||||
class MealPlanForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
space = kwargs.pop('space')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['recipe'].queryset = Recipe.objects.filter(space=space).all()
|
||||
self.fields['meal_type'].queryset = MealType.objects.filter(space=space).all()
|
||||
self.fields['shared'].queryset = User.objects.filter(userpreference__space=space).all()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(MealPlanForm, self).clean()
|
||||
|
||||
if cleaned_data['title'] == '' and cleaned_data['recipe'] is None:
|
||||
raise forms.ValidationError(
|
||||
_('You must provide at least a recipe or a title.')
|
||||
)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = MealPlan
|
||||
fields = (
|
||||
'recipe', 'title', 'meal_type', 'note',
|
||||
'servings', 'date', 'shared'
|
||||
)
|
||||
|
||||
help_texts = {
|
||||
'shared': _('You can list default users to share recipes with in the settings.'),
|
||||
'note': _('You can use markdown to format this field. See the <a href="/docs/markdown/">docs here</a>')
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'recipe': SelectWidget,
|
||||
'date': DateWidget,
|
||||
'shared': MultiSelectWidget
|
||||
}
|
||||
field_classes = {
|
||||
'recipe': SafeModelChoiceField,
|
||||
'meal_type': SafeModelChoiceField,
|
||||
'shared': SafeModelMultipleChoiceField,
|
||||
}
|
||||
|
||||
|
||||
class InviteLinkForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop('user')
|
||||
|
@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.1.10 on 2023-09-08 12:20
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.models import F
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
|
||||
def apply_migration(apps, schema_editor):
|
||||
with scopes_disabled():
|
||||
MealPlan = apps.get_model('cookbook', 'MealPlan')
|
||||
MealPlan.objects.update(to_date=F('from_date'))
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('cookbook', '0200_alter_propertytype_options_remove_keyword_icon_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='mealplan',
|
||||
old_name='date',
|
||||
new_name='from_date',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='mealplan',
|
||||
name='to_date',
|
||||
field=models.DateField(blank=True, null=True),
|
||||
),
|
||||
migrations.RunPython(apply_migration),
|
||||
migrations.AlterField(
|
||||
model_name='mealplan',
|
||||
name='to_date',
|
||||
field=models.DateField(),
|
||||
),
|
||||
]
|
@ -991,7 +991,8 @@ class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, Permission
|
||||
shared = models.ManyToManyField(User, blank=True, related_name='plan_share')
|
||||
meal_type = models.ForeignKey(MealType, on_delete=models.CASCADE)
|
||||
note = models.TextField(blank=True)
|
||||
date = models.DateField()
|
||||
from_date = models.DateField()
|
||||
to_date = models.DateField()
|
||||
|
||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||
objects = ScopedManager(space='space')
|
||||
@ -1005,7 +1006,7 @@ class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, Permission
|
||||
return self.meal_type.name
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.get_label()} - {self.date} - {self.meal_type.name}'
|
||||
return f'{self.get_label()} - {self.from_date} - {self.meal_type.name}'
|
||||
|
||||
|
||||
class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), models.Model, PermissionModelMixin):
|
||||
|
@ -993,7 +993,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
|
||||
model = MealPlan
|
||||
fields = (
|
||||
'id', 'title', 'recipe', 'servings', 'note', 'note_markdown',
|
||||
'date', 'meal_type', 'created_by', 'shared', 'recipe_name',
|
||||
'from_date', 'to_date', 'meal_type', 'created_by', 'shared', 'recipe_name',
|
||||
'meal_type_name', 'shopping'
|
||||
)
|
||||
read_only_fields = ('created_by',)
|
||||
|
@ -12,6 +12,7 @@ from cookbook.tests.factories import RecipeFactory
|
||||
LIST_URL = 'api:mealplan-list'
|
||||
DETAIL_URL = 'api:mealplan-detail'
|
||||
|
||||
|
||||
# NOTE: auto adding shopping list from meal plan is tested in test_shopping_recipe as tests are identical
|
||||
|
||||
|
||||
@ -22,13 +23,13 @@ def meal_type(space_1, u1_s1):
|
||||
|
||||
@pytest.fixture()
|
||||
def obj_1(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, date=datetime.now(),
|
||||
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
||||
created_by=auth.get_user(u1_s1))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def obj_2(space_1, recipe_1_s1, meal_type, u1_s1):
|
||||
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, date=datetime.now(),
|
||||
return MealPlan.objects.create(recipe=recipe_1_s1, space=space_1, meal_type=meal_type, from_date=datetime.now(), to_date=datetime.now(),
|
||||
created_by=auth.get_user(u1_s1))
|
||||
|
||||
|
||||
@ -109,7 +110,7 @@ def test_add(arg, request, u1_s2, recipe_1_s1, meal_type):
|
||||
r = c.post(
|
||||
reverse(LIST_URL),
|
||||
{'recipe': {'id': recipe_1_s1.id, 'name': recipe_1_s1.name, 'keywords': []}, 'meal_type': {'id': meal_type.id, 'name': meal_type.name},
|
||||
'date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': []},
|
||||
'from_date': (datetime.now()).strftime("%Y-%m-%d"), 'to_date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': []},
|
||||
content_type='application/json'
|
||||
)
|
||||
response = json.loads(r.content)
|
||||
@ -151,7 +152,7 @@ def test_add_with_shopping(u1_s1, meal_type):
|
||||
r = u1_s1.post(
|
||||
reverse(LIST_URL),
|
||||
{'recipe': {'id': recipe.id, 'name': recipe.name, 'keywords': []}, 'meal_type': {'id': meal_type.id, 'name': meal_type.name},
|
||||
'date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': [], 'addshopping': True},
|
||||
'from_date': (datetime.now()).strftime("%Y-%m-%d"), 'to_date': (datetime.now()).strftime("%Y-%m-%d"), 'servings': 1, 'title': 'test', 'shared': [], 'addshopping': True},
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
|
@ -246,7 +246,8 @@ class MealPlanFactory(factory.django.DjangoModelFactory):
|
||||
meal_type = factory.SubFactory(
|
||||
MealTypeFactory, space=factory.SelfAttribute('..space'))
|
||||
note = factory.LazyAttribute(lambda x: faker.paragraph())
|
||||
date = factory.LazyAttribute(lambda x: faker.future_date())
|
||||
from_date = factory.LazyAttribute(lambda x: faker.future_date())
|
||||
to_date = factory.LazyAttribute(lambda x: faker.future_date())
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
class Params:
|
||||
|
@ -166,7 +166,7 @@ urlpatterns = [
|
||||
]
|
||||
|
||||
generic_models = (
|
||||
Recipe, RecipeImport, Storage, RecipeBook, MealPlan, SyncLog, Sync,
|
||||
Recipe, RecipeImport, Storage, RecipeBook, SyncLog, Sync,
|
||||
Comment, RecipeBookEntry, ShoppingList, InviteLink, UserSpace, Space
|
||||
)
|
||||
|
||||
|
@ -666,11 +666,11 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
||||
|
||||
from_date = self.request.query_params.get('from_date', None)
|
||||
if from_date is not None:
|
||||
queryset = queryset.filter(date__gte=from_date)
|
||||
queryset = queryset.filter(to_date__gte=from_date)
|
||||
|
||||
to_date = self.request.query_params.get('to_date', None)
|
||||
if to_date is not None:
|
||||
queryset = queryset.filter(date__lte=to_date)
|
||||
queryset = queryset.filter(to_date__lte=to_date)
|
||||
return queryset
|
||||
|
||||
|
||||
@ -707,7 +707,7 @@ class AutoPlanViewSet(viewsets.ViewSet):
|
||||
args = {'recipe_id': recipe['id'], 'servings': servings,
|
||||
'created_by': request.user,
|
||||
'meal_type_id': serializer.validated_data['meal_type_id'],
|
||||
'note': '', 'date': day, 'space': request.space}
|
||||
'note': '', 'from_date': day, 'to_date': day, 'space': request.space}
|
||||
|
||||
m = MealPlan(**args)
|
||||
meal_plans.append(m)
|
||||
@ -1633,8 +1633,11 @@ def get_plan_ical(request, from_date, to_date):
|
||||
for p in queryset:
|
||||
event = Event()
|
||||
event['uid'] = p.id
|
||||
event.add('dtstart', p.date)
|
||||
event.add('dtend', p.date)
|
||||
event.add('dtstart', p.from_date)
|
||||
if p.to_date:
|
||||
event.add('dtend', p.to_date)
|
||||
else:
|
||||
event.add('dtend', p.from_date)
|
||||
event['summary'] = f'{p.meal_type.name}: {p.get_label()}'
|
||||
event['description'] = p.note
|
||||
cal.add_component(event)
|
||||
|
@ -8,7 +8,7 @@ from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
from cookbook.forms import CommentForm, ExternalRecipeForm, MealPlanForm, StorageForm, SyncForm
|
||||
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required, above_space_limit
|
||||
from cookbook.models import (Comment, MealPlan, MealType, Recipe, RecipeImport, Storage, Sync,
|
||||
UserPreference)
|
||||
@ -192,26 +192,6 @@ class ImportUpdate(GroupRequiredMixin, UpdateView):
|
||||
return context
|
||||
|
||||
|
||||
class MealPlanUpdate(OwnerRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
template_name = "generic/edit_template.html"
|
||||
model = MealPlan
|
||||
form_class = MealPlanForm
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('view_plan_entry', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = self.form_class(**self.get_form_kwargs())
|
||||
form.fields['meal_type'].queryset = MealType.objects \
|
||||
.filter(created_by=self.request.user).all()
|
||||
return form
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(MealPlanUpdate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Meal-Plan")
|
||||
return context
|
||||
|
||||
|
||||
class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
model = Recipe
|
||||
|
@ -12,7 +12,7 @@ from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import CreateView
|
||||
|
||||
from cookbook.forms import ImportRecipeForm, InviteLinkForm, MealPlanForm, Storage, StorageForm
|
||||
from cookbook.forms import ImportRecipeForm, InviteLinkForm, Storage, StorageForm
|
||||
from cookbook.helper.permission_helper import GroupRequiredMixin, group_required, above_space_limit
|
||||
from cookbook.models import (InviteLink, MealPlan, MealType, Recipe, RecipeBook, RecipeImport,
|
||||
ShareLink, Step, UserPreference, UserSpace)
|
||||
@ -135,55 +135,3 @@ def create_new_external_recipe(request, import_id):
|
||||
return render(request, 'forms/edit_import_recipe.html', {'form': form})
|
||||
|
||||
|
||||
class MealPlanCreate(GroupRequiredMixin, CreateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/new_template.html"
|
||||
model = MealPlan
|
||||
form_class = MealPlanForm
|
||||
success_url = reverse_lazy('view_plan')
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = self.form_class(**self.get_form_kwargs())
|
||||
form.fields['meal_type'].queryset = MealType.objects.filter(created_by=self.request.user,
|
||||
space=self.request.space).all()
|
||||
return form
|
||||
|
||||
def get_initial(self):
|
||||
return dict(
|
||||
meal_type=(
|
||||
self.request.GET['meal']
|
||||
if 'meal' in self.request.GET
|
||||
else None
|
||||
),
|
||||
date=(
|
||||
datetime.strptime(self.request.GET['date'], '%Y-%m-%d')
|
||||
if 'date' in self.request.GET
|
||||
else None
|
||||
),
|
||||
shared=(
|
||||
self.request.user.userpreference.plan_share.all()
|
||||
if self.request.user.userpreference.plan_share
|
||||
else None
|
||||
)
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('view_plan'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(MealPlanCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Meal-Plan")
|
||||
|
||||
recipe = self.request.GET.get('recipe')
|
||||
if recipe:
|
||||
if re.match(r'^([0-9])+$', recipe):
|
||||
if Recipe.objects.filter(pk=int(recipe), space=self.request.space).exists():
|
||||
context['default_recipe'] = Recipe.objects.get(pk=int(recipe), space=self.request.space)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
@ -1,29 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-tabs content-class="mt-3" v-model="current_tab">
|
||||
<b-tab :title="$t('Planner')" active>
|
||||
<div class="d-none d-lg-block">
|
||||
<div class="row ">
|
||||
<div class="col col-2">
|
||||
<h4>{{ $t('Meal_Types') }}</h4>
|
||||
|
||||
<b-form-checkbox :button-variant="danger" v-model="mt.checked" size="lg" v-for="mt in meal_types" v-bind:key="mt.id">
|
||||
<div class="d-flex flex-row align-items-center border" v-for="mt in meal_types" v-bind:key="mt.id">
|
||||
<div class="flex-column" style="width: 2.5rem; height: 2.5rem;" :style="{'background-color': mt.color}"></div>
|
||||
<div class="flex-column flex-grow-1 align-middle justify-content-center">
|
||||
<div class="card-body p-2 align-middle">
|
||||
{{ mt.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-badge variant="primary" :style="{'background-color':mt.color}">{{ mt.name }}</b-badge>
|
||||
</b-form-checkbox>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<button class="btn btn-success shadow-none mt-1 btn-block" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Create") }}
|
||||
</button>
|
||||
<button class="btn btn-primary shadow-none mt-1 btn-block" @click="createAutoPlan(new Date())"><i
|
||||
<button class="btn btn-warning shadow-none mt-1 btn-block" @click="createAutoPlan(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Auto_Planner") }}
|
||||
</button>
|
||||
<a class="btn btn-primary shadow-none mt-1 btn-blockmt-1 btn-block" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t("Export_To_ICal") }}
|
||||
</a>
|
||||
|
||||
|
||||
<a class="btn btn-info shadow-none mt-1 btn-block" :href="resolveDjangoUrl('view_settings')">
|
||||
<i class="fas fa-cogs"></i> {{ $t("Settings") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col col-10">
|
||||
<div class="row calender-row ">
|
||||
@ -74,9 +79,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-block d-lg-none">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row d-block d-lg-none">
|
||||
<div class="">
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<div class="col-12 d-flex justify-content-center mt-2">
|
||||
@ -152,68 +159,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</b-tab>
|
||||
<b-tab :title="$t('Settings')">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-md-9 col-lg-6">
|
||||
<h5>{{ $t("Meal_Types") }}</h5>
|
||||
<div>
|
||||
<draggable :list="meal_types" group="meal_types" :empty-insert-threshold="10"
|
||||
@sort="sortMealTypes()" ghost-class="ghost">
|
||||
<b-card no-body class="mt-1 list-group-item p-2" style="cursor: move"
|
||||
v-for="(meal_type, index) in meal_types" v-hover :key="meal_type.id">
|
||||
<b-card-header class="p-2 border-0">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-lg shadow-none"><i
|
||||
class="fas fa-arrows-alt-v"></i></button>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<h5 class="mt-1 mb-1">
|
||||
{{ meal_type.name }}<span class="float-right text-primary" style="cursor: pointer"
|
||||
><i class="fa"
|
||||
v-bind:class="{ 'fa-pen': !meal_type.editing, 'fa-save': meal_type.editing }"
|
||||
@click="editOrSaveMealType(index)" aria-hidden="true"></i
|
||||
></span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</b-card-header>
|
||||
<b-card-body class="p-4" v-if="meal_type.editing">
|
||||
<div class="form-group">
|
||||
<label>{{ $t("Name") }}</label>
|
||||
<input class="form-control" :placeholder="$t('Name')"
|
||||
v-model="meal_type.name"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ $t("Color") }}</label>
|
||||
<input class="form-control" type="color" name="Name"
|
||||
:value="meal_type.color"
|
||||
@change="meal_type.color = $event.target.value"/>
|
||||
</div>
|
||||
<b-form-checkbox id="checkbox-1" v-model="meal_type.default"
|
||||
name="default_checkbox" class="mb-2">
|
||||
{{ $t("Default") }}
|
||||
</b-form-checkbox>
|
||||
<button class="btn btn-danger" @click="deleteMealType(index)">{{
|
||||
$t("Delete")
|
||||
}}
|
||||
</button>
|
||||
<button class="btn btn-primary float-right" @click="editOrSaveMealType(index)">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</draggable>
|
||||
<button class="btn btn-success float-right mt-1" @click="newMealType">
|
||||
<i class="fas fa-plus"></i>
|
||||
{{ $t("New_Meal_Type") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
|
||||
<ContextMenu ref="menu">
|
||||
<template #menu="{ contextData }">
|
||||
<ContextMenuItem
|
||||
@ -336,7 +282,6 @@ export default {
|
||||
ContextMenu,
|
||||
ContextMenuItem,
|
||||
MealPlanCalenderHeader,
|
||||
draggable,
|
||||
BottomNavigationBar,
|
||||
},
|
||||
mixins: [CalendarMathMixin, ApiMixin, ResolveUrlMixin],
|
||||
@ -362,7 +307,6 @@ export default {
|
||||
displayWeekNumbers: true,
|
||||
},
|
||||
dragged_item: null,
|
||||
current_tab: 0,
|
||||
meal_types: [],
|
||||
current_context_menu_item: null,
|
||||
options: {
|
||||
@ -404,7 +348,7 @@ export default {
|
||||
},
|
||||
item_height: function () {
|
||||
if (this.settings.displayPeriodUom === "week") {
|
||||
return "10rem"
|
||||
return "3rem"
|
||||
} else {
|
||||
return "1.6rem"
|
||||
}
|
||||
@ -428,7 +372,7 @@ export default {
|
||||
date: moment_date,
|
||||
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
||||
date_label: moment_date.format('ddd DD.MM'),
|
||||
plan_entries: this.plan_items.filter((m) => moment(m.startDate).isSame(moment_date, 'day'))
|
||||
plan_entries: this.plan_items.filter((m) => moment_date.isBetween(moment(m.startDate), moment(m.endDate), 'day', '[]'))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -436,18 +380,14 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(function () {
|
||||
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
|
||||
this.settings = Object.assign({}, this.settings, this.$cookies.get(SETTINGS_COOKIE_NAME))
|
||||
}
|
||||
})
|
||||
this.settings = useMealPlanStore().client_settings
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
moment.locale(window.CUSTOM_LOCALE)
|
||||
},
|
||||
watch: {
|
||||
settings: {
|
||||
handler() {
|
||||
this.$cookies.set(SETTINGS_COOKIE_NAME, this.settings, "360d")
|
||||
useMealPlanStore().updateClientSettings(this.settings)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@ -554,12 +494,16 @@ export default {
|
||||
moveEntry(null_object, target_date, drag_event) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === this.dragged_item.id) {
|
||||
let fromToDiff = Math.abs(moment(entry.to_date).diff(moment(entry.from_date), 'days'))
|
||||
|
||||
if (drag_event.ctrlKey) {
|
||||
let new_entry = Object.assign({}, entry)
|
||||
new_entry.date = target_date
|
||||
new_entry.from_date = target_date
|
||||
new_entry.to_date = moment(target_date).add(fromToDiff, 'd')
|
||||
this.createEntry(new_entry)
|
||||
} else {
|
||||
entry.date = target_date
|
||||
entry.from_date = target_date
|
||||
entry.to_date = moment(target_date).add(fromToDiff, 'd')
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
}
|
||||
@ -568,7 +512,8 @@ export default {
|
||||
moveEntryLeft(data) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === data.id) {
|
||||
entry.date = moment(entry.date).subtract(1, "d")
|
||||
entry.from_date = moment(entry.from_date).subtract(1, "d")
|
||||
entry.to_date = moment(entry.to_date).subtract(1, "d")
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
})
|
||||
@ -576,7 +521,8 @@ export default {
|
||||
moveEntryRight(data) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === data.id) {
|
||||
entry.date = moment(entry.date).add(1, "d")
|
||||
entry.from_date = moment(entry.from_date).add(1, "d")
|
||||
entry.to_date = moment(entry.to_date).add(1, "d")
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
})
|
||||
@ -594,7 +540,8 @@ export default {
|
||||
openEntryEdit(entry) {
|
||||
this.$bvModal.show(`id_meal_plan_edit_modal`)
|
||||
this.entryEditing = entry
|
||||
this.entryEditing.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
this.entryEditing.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
this.entryEditing.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
if (this.entryEditing.recipe != null) {
|
||||
this.entryEditing.title_placeholder = this.entryEditing.recipe.name
|
||||
}
|
||||
@ -617,21 +564,32 @@ export default {
|
||||
})
|
||||
},
|
||||
saveEntry(entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
entry.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
|
||||
if (entry.from_date > entry.to_date) {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE)
|
||||
entry.to_date = entry.from_date
|
||||
} else {
|
||||
useMealPlanStore().updateObject(entry)
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
createEntry(entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
entry.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
|
||||
useMealPlanStore().createObject(entry)
|
||||
},
|
||||
buildItem(plan_entry) {
|
||||
//dirty hack to order items within a day
|
||||
let date = moment(plan_entry.date).add(plan_entry.meal_type.order, "m")
|
||||
let from_date = moment(plan_entry.from_date).add(plan_entry.meal_type.order, "m")
|
||||
let to_date = moment(plan_entry.to_date).add(plan_entry.meal_type.order, "m")
|
||||
return {
|
||||
id: plan_entry.id,
|
||||
startDate: date,
|
||||
endDate: date,
|
||||
startDate: from_date,
|
||||
endDate: to_date,
|
||||
entry: plan_entry,
|
||||
}
|
||||
},
|
||||
@ -684,7 +642,7 @@ export default {
|
||||
}
|
||||
|
||||
.calender-row {
|
||||
height: calc(100vh - 240px);
|
||||
height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
.calender-parent {
|
||||
|
@ -723,8 +723,8 @@
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div
|
||||
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); column-gap: 0.5rem; row-gap: 0.5rem; grid-auto-rows: max-content"
|
||||
>
|
||||
|
||||
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); column-gap: 0.5rem;row-gap: 0.5rem; grid-auto-rows: max-content; ">
|
||||
<div v-for="day in meal_plan_grid" v-bind:key="day.day" :class="{'d-none d-sm-block': day.plan_entries.length === 0}">
|
||||
<b-list-group>
|
||||
<b-list-group-item class="hover-div pb-0">
|
||||
@ -739,16 +739,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div">
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div p-0 pr-2">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div>
|
||||
<b-img
|
||||
style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="plan.recipe.image"
|
||||
rounded="circle"
|
||||
v-if="plan.recipe?.image"
|
||||
></b-img>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" rounded="circle" v-else></b-img>
|
||||
|
||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="plan.recipe.image" v-if="plan.recipe?.image"/>
|
||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" v-else/>
|
||||
|
||||
</div>
|
||||
<div class="flex-grow-1 ml-2" style="text-overflow: ellipsis; overflow-wrap: anywhere">
|
||||
<span class="two-row-text">
|
||||
@ -986,8 +983,8 @@ export default {
|
||||
grid.push({
|
||||
date: moment_date,
|
||||
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
||||
date_label: moment_date.format("ddd DD.MM"),
|
||||
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment(m.date).isSame(moment_date, "day")),
|
||||
date_label: moment_date.format('ddd DD.MM'),
|
||||
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment_date.isBetween(moment(m.from_date), moment(m.to_date), 'day', '[]'))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1204,22 +1201,6 @@ export default {
|
||||
console.log("loadMealpLan")
|
||||
this.meal_plan_store = useMealPlanStore()
|
||||
this.meal_plan_store.refreshFromAPI(moment().format("YYYY-MM-DD"), moment().add(this.ui.meal_plan_days, "days").format("YYYY-MM-DD"))
|
||||
|
||||
// if (this.ui.show_meal_plan) {
|
||||
// let params = {
|
||||
// options: {
|
||||
// query: {
|
||||
// from_date: moment().format("YYYY-MM-DD"),
|
||||
// to_date: moment().add(this.ui.meal_plan_days, "days").format("YYYY-MM-DD"),
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// this.genericAPI(this.Models.MEAL_PLAN, this.Actions.LIST, params).then((result) => {
|
||||
// this.meal_plans = result.data
|
||||
// })
|
||||
// } else {
|
||||
// this.meal_plans = []
|
||||
// }
|
||||
},
|
||||
genericSelectChanged: function (obj) {
|
||||
if (obj.var.includes("::")) {
|
||||
|
@ -29,7 +29,7 @@
|
||||
<b-form-select class="ml-1" id="UomInput" v-model="settings.displayPeriodCount" :options="options.displayPeriodCount"></b-form-select>
|
||||
</span>
|
||||
<span
|
||||
class="delete-area text-danger p-1 mr-2 ml-1 d-none d-sm-flex align-items-center"
|
||||
class="delete-area text-danger p-1 mr-2 ml-1 d-none d-sm-flex align-items-center rounded"
|
||||
@drop.prevent="onDeleteDrop($event)"
|
||||
@dragenter.prevent="onDeleteDragEnter($event)"
|
||||
@dragleave.prevent="onDeleteDragLeave($event)"
|
||||
|
@ -1,52 +1,34 @@
|
||||
<template>
|
||||
<div
|
||||
v-hover
|
||||
|
||||
<div v-hover
|
||||
class="card cv-item meal-plan-card p-0"
|
||||
:key="value.id"
|
||||
:draggable="true"
|
||||
:style="`top:${top};max-height:${item_height}`"
|
||||
:style="{'top': top, 'height': item_height, 'border-color': entry.entry.meal_type.color}"
|
||||
@dragstart="onDragItemStart(value, $event)"
|
||||
@click="onClickItem(value, $event)"
|
||||
:aria-grabbed="value == currentDragItem"
|
||||
:class="value.classes"
|
||||
@contextmenu.prevent="$emit('open-context-menu', $event, value)"
|
||||
>
|
||||
<div class="card-header p-1 text-center text-primary border-bottom-0" v-if="detailed" :style="`background-color: ${background_color}`">
|
||||
<span class="font-light text-center" v-if="entry.entry.meal_type.icon != null">{{ entry.entry.meal_type.icon }}</span>
|
||||
<span class="font-light d-none d-md-inline">{{ entry.entry.meal_type.name }}</span>
|
||||
<span v-if="entry.entry.shopping" class="font-light"><i class="fas fa-shopping-cart fa-xs float-left" v-b-tooltip.hover.top :title="$t('in_shopping')"/></span>
|
||||
@contextmenu.prevent="$emit('open-context-menu', $event, value)">
|
||||
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div class="flex-column">
|
||||
<img class="" style="object-fit: cover" :style="{'height': item_height, 'width': item_height}" :src="entry.entry.recipe.image"
|
||||
v-if="hasRecipe && detailed"/>
|
||||
<img class="" style="object-fit: cover" :style="{'height': item_height, 'width': item_height}" :src="image_placeholder"
|
||||
v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"/>
|
||||
</div>
|
||||
<div class="card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right p-0" v-if="detailed">
|
||||
<a>
|
||||
<div style="position: static">
|
||||
<div class="dropdown b-dropdown position-static btn-group">
|
||||
<button
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
type="button"
|
||||
class="btn btn-link text-decoration-none text-body pr-2 dropdown-toggle-no-caret"
|
||||
@click.stop="$emit('open-context-menu', $event, value)"
|
||||
>
|
||||
<i class="fas fa-ellipsis-v fa-lg"></i>
|
||||
</button>
|
||||
<div class="flex-column flex-grow-0 align-middle justify-content-center">
|
||||
<div class="card-body p-0 pl-1 align-middle">
|
||||
|
||||
<span class="font-light" :class="{'two-line-text': detailed,'one-line-text': !detailed,}">
|
||||
<i class="fas fa-shopping-cart fa-xs float-left" v-b-tooltip.hover.top :title="$t('in_shopping')" v-if="entry.entry.shopping"/>
|
||||
{{ title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-header p-1 text-center" v-if="detailed" :style="`background-color: ${background_color}`">
|
||||
<span class="font-light">{{ title }}</span>
|
||||
</div>
|
||||
<b-img fluid class="card-img-bottom" :src="entry.entry.recipe.image" v-if="hasRecipe && detailed"></b-img>
|
||||
<b-img fluid class="card-img-bottom" :src="image_placeholder" v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"></b-img>
|
||||
<div class="card-body p-1" v-if="detailed && entry.entry.recipe == null" :style="`background-color: ${background_color}`">
|
||||
<p>{{ entry.entry.note }}</p>
|
||||
</div>
|
||||
|
||||
<div class="row p-1 flex-nowrap" v-if="!detailed" :style="`background-color: ${background_color}`">
|
||||
<div class="col-10 d-inline-block text-truncate" :style="`max-height:${item_height}`">
|
||||
<span class="font-light">{{ title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -131,4 +113,22 @@ export default {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.two-line-text {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.one-line-text {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
@ -6,14 +6,13 @@
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-6 col-lg-9">
|
||||
<div class="col-12">
|
||||
<b-input-group>
|
||||
<b-form-input id="TitleInput" v-model="entryEditing.title"
|
||||
:placeholder="entryEditing.title_placeholder"
|
||||
@change="missing_recipe = false"></b-form-input>
|
||||
<b-input-group-append class="d-none d-lg-block">
|
||||
<b-button variant="primary" @click="entryEditing.title = ''"><i
|
||||
class="fa fa-eraser"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.title = ''"><i class="fa fa-eraser"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<span class="text-danger" v-if="missing_recipe">{{
|
||||
@ -23,13 +22,30 @@
|
||||
$t("Title")
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="col-6 col-lg-3">
|
||||
<input type="date" id="DateInput" class="form-control" v-model="entryEditing.date"/>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("Date") }}</small>
|
||||
<div class="col-12 col-lg-6">
|
||||
<b-input-group>
|
||||
<b-form-input type="date" v-model="entryEditing.from_date"></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="secondary" @click="entryEditing.from_date = changeDate(entryEditing.from_date, -1)"><i class="fas fa-minus"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.from_date = changeDate(entryEditing.from_date, 1)"><i class="fas fa-plus"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("StartDate") }}</small>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<b-input-group>
|
||||
<b-form-input type="date" v-model="entryEditing.to_date"></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="secondary" @click="entryEditing.to_date = changeDate(entryEditing.to_date, -1)"><i class="fas fa-minus"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.to_date = changeDate(entryEditing.to_date, 1)"><i class="fas fa-plus"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("EndDate") }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b-form-group>
|
||||
<generic-multiselect
|
||||
@change="selectRecipe"
|
||||
@ -43,7 +59,11 @@
|
||||
></generic-multiselect>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("Recipe") }}</small>
|
||||
</b-form-group>
|
||||
<b-form-group class="mt-3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<b-form-group class="">
|
||||
<generic-multiselect
|
||||
required
|
||||
@change="selectMealType"
|
||||
@ -142,6 +162,7 @@ const {ApiApiFactory} = require("@/utils/openapi/api")
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||
import ShoppingModal from "@/components/Modals/ShoppingModal.vue";
|
||||
import moment from "moment";
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
Vue.use(VueCookies)
|
||||
@ -221,7 +242,8 @@ export default {
|
||||
}
|
||||
|
||||
if (this.create_date) {
|
||||
this.entryEditing.date = this.create_date
|
||||
this.entryEditing.from_date = this.create_date
|
||||
this.entryEditing.to_date = this.create_date
|
||||
}
|
||||
|
||||
useUserPreferenceStore().getData().then(userPreference => {
|
||||
@ -290,6 +312,9 @@ export default {
|
||||
this.entryEditing.servings = 1
|
||||
}
|
||||
},
|
||||
changeDate(date, change){
|
||||
return moment(date).add(change, 'd').format("YYYY-MM-DD")
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -184,7 +184,7 @@ export default {
|
||||
localStorage.setItem("pinned_recipes", JSON.stringify(pinnedRecipes))
|
||||
},
|
||||
saveMealPlan: function (entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
let reviewshopping = entry.addshopping && entry.reviewshopping
|
||||
entry.addshopping = entry.addshopping && !entry.reviewshopping
|
||||
|
||||
@ -208,7 +208,7 @@ export default {
|
||||
createMealPlan(data) {
|
||||
this.entryEditing = this.options.entryEditing
|
||||
this.entryEditing.recipe = this.recipe
|
||||
this.entryEditing.date = moment(new Date()).format("YYYY-MM-DD")
|
||||
this.entryEditing.from_date = moment(new Date()).format("YYYY-MM-DD")
|
||||
this.$nextTick(function () {
|
||||
this.$bvModal.show(`modal-meal-plan_${this.modal_id}`)
|
||||
})
|
||||
|
@ -14,24 +14,24 @@
|
||||
|
||||
<hr/>
|
||||
|
||||
<b-form v-if="meal_plan_store">
|
||||
<b-form v-if="settings">
|
||||
<b-form-group id="UomInput" :label="$t('Period')" :description="$t('Plan_Period_To_Show')"
|
||||
label-for="UomInput">
|
||||
<b-form-select id="UomInput" v-model="meal_plan_store.client_settings.displayPeriodUom"
|
||||
<b-form-select id="UomInput" v-model="settings.displayPeriodUom"
|
||||
:options="calendar_options.displayPeriodUom"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="PeriodInput" :label="$t('Periods')"
|
||||
:description="$t('Plan_Show_How_Many_Periods')" label-for="PeriodInput">
|
||||
<b-form-select id="PeriodInput" v-model="meal_plan_store.client_settings.displayPeriodCount"
|
||||
<b-form-select id="PeriodInput" v-model="settings.displayPeriodCount"
|
||||
:options="calendar_options.displayPeriodCount"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="DaysInput" :label="$t('Starting_Day')" :description="$t('Starting_Day')"
|
||||
label-for="DaysInput">
|
||||
<b-form-select id="DaysInput" v-model="meal_plan_store.client_settings.startingDayOfWeek"
|
||||
<b-form-select id="DaysInput" v-model="settings.startingDayOfWeek"
|
||||
:options="dayNames()"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="WeekNumInput" :label="$t('Week_Numbers')">
|
||||
<b-form-checkbox v-model="meal_plan_store.client_settings.displayWeekNumbers" name="week_num">
|
||||
<b-form-checkbox v-model="settings.displayWeekNumbers" name="week_num">
|
||||
{{ $t("Show_Week_Numbers") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
@ -96,7 +96,7 @@ export default {
|
||||
return {
|
||||
user_preferences: undefined,
|
||||
languages: [],
|
||||
meal_plan_store: undefined,
|
||||
settings: undefined,
|
||||
calendar_options: {
|
||||
displayPeriodUom: [
|
||||
{text: this.$t("Week"), value: "week"},
|
||||
@ -108,14 +108,24 @@ export default {
|
||||
meal_types: [],
|
||||
generic_action: null,
|
||||
editing_meal_type: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
settings: {
|
||||
handler() {
|
||||
useMealPlanStore().updateClientSettings(this.settings)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.user_preferences = this.preferences
|
||||
this.languages = window.AVAILABLE_LANGUAGES
|
||||
this.loadSettings()
|
||||
|
||||
this.meal_plan_store = useMealPlanStore()
|
||||
this.settings = useMealPlanStore().client_settings
|
||||
|
||||
|
||||
this.loadMealTypes()
|
||||
},
|
||||
|
@ -100,6 +100,8 @@
|
||||
"Energy": "Energy",
|
||||
"Nutrition": "Nutrition",
|
||||
"Date": "Date",
|
||||
"StartDate": "Start Date",
|
||||
"EndDate": "End Date",
|
||||
"Share": "Share",
|
||||
"Automation": "Automation",
|
||||
"Parameter": "Parameter",
|
||||
|
@ -478,5 +478,7 @@
|
||||
"Auto_Sort": "自动分类",
|
||||
"Auto_Sort_Help": "将所有食材移动到最恰当的步骤。",
|
||||
"Create Recipe": "创建食谱",
|
||||
"Import Recipe": "导入食谱"
|
||||
"Import Recipe": "导入食谱",
|
||||
"recipe_property_info": "您也可以为食物添加属性,以便根据食谱自动计算!",
|
||||
"per_serving": "每份"
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {defineStore} from 'pinia'
|
||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||
|
||||
const _STORE_ID = 'meal_plan_store'
|
||||
const _LOCAL_STORAGE_KEY = 'MEAL_PLAN_CLIENT_SETTINGS'
|
||||
import Vue from "vue"
|
||||
import {StandardToasts} from "@/utils/utils";
|
||||
/*
|
||||
@ -12,12 +13,7 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
state: () => ({
|
||||
plans: {},
|
||||
currently_updating: null,
|
||||
client_settings: {
|
||||
displayPeriodUom: "week",
|
||||
displayPeriodCount: 2,
|
||||
startingDayOfWeek: 1,
|
||||
displayWeekNumbers: true,
|
||||
},
|
||||
settings: null,
|
||||
}),
|
||||
getters: {
|
||||
plan_list: function () {
|
||||
@ -29,7 +25,8 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
},
|
||||
empty_meal_plan: function () {
|
||||
return {
|
||||
date: null,
|
||||
from_date: null,
|
||||
to_date: null,
|
||||
id: -1,
|
||||
meal_type: null,
|
||||
note: "",
|
||||
@ -40,6 +37,12 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
title: "",
|
||||
title_placeholder: 'Title', // meal plan edit modal should be improved to not need this
|
||||
}
|
||||
},
|
||||
client_settings: function () {
|
||||
if (this.settings === null) {
|
||||
this.settings = this.loadClientSettings()
|
||||
}
|
||||
return this.settings
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -90,6 +93,23 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
|
||||
})
|
||||
},
|
||||
updateClientSettings(settings) {
|
||||
this.settings = settings
|
||||
localStorage.setItem(_LOCAL_STORAGE_KEY, JSON.stringify(this.settings))
|
||||
},
|
||||
loadClientSettings() {
|
||||
let s = localStorage.getItem(_LOCAL_STORAGE_KEY)
|
||||
if (s === null || s === {}) {
|
||||
return {
|
||||
displayPeriodUom: "week",
|
||||
displayPeriodCount: 3,
|
||||
startingDayOfWeek: 1,
|
||||
displayWeekNumbers: true,
|
||||
}
|
||||
} else {
|
||||
return JSON.parse(s)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
@ -1812,7 +1812,13 @@ export interface MealPlan {
|
||||
* @type {string}
|
||||
* @memberof MealPlan
|
||||
*/
|
||||
date: string;
|
||||
from_date: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlan
|
||||
*/
|
||||
to_date?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {MealPlanMealType}
|
||||
|
@ -1165,7 +1165,9 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
|
||||
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
|
||||
|
||||
version "7.22.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
|
||||
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
|
||||
@ -3128,13 +3130,13 @@ asn1.js@^5.2.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
assert@^1.1.1:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
|
||||
integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
|
||||
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.1.tgz#038ab248e4ff078e7bc2485ba6e6388466c78f76"
|
||||
integrity sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
util "0.10.3"
|
||||
object.assign "^4.1.4"
|
||||
util "^0.10.4"
|
||||
|
||||
assign-symbols@^1.0.0:
|
||||
version "1.0.0"
|
||||
@ -3290,6 +3292,12 @@ babel-generator@^6.26.0:
|
||||
source-map "^0.5.7"
|
||||
trim-right "^1.0.1"
|
||||
|
||||
|
||||
babel-helper-vue-jsx-merge-props@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
|
||||
integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==
|
||||
|
||||
babel-helpers@^6.24.1:
|
||||
version "6.24.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
|
||||
@ -3892,9 +3900,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520:
|
||||
version "1.0.30001528"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz#479972fc705b996f1114336c0032418a215fd0aa"
|
||||
integrity sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==
|
||||
version "1.0.30001529"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001529.tgz#c1f2a411e85fdaace4b1560e1bad078b00ac3181"
|
||||
integrity sha512-n2pUQYGAkrLG4QYj2desAh+NqsJpHbNmVZz87imptDdxLAtjxary7Df/psdfyDGmskJK/9Dt9cPnx5RZ3CU4Og==
|
||||
|
||||
|
||||
canvg@^3.0.6:
|
||||
version "3.0.10"
|
||||
@ -5010,6 +5019,11 @@ duplexify@^3.4.2, duplexify@^3.6.0:
|
||||
readable-stream "^2.0.0"
|
||||
stream-shift "^1.0.0"
|
||||
|
||||
easings-css@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/easings-css/-/easings-css-1.0.0.tgz#dde569003bb7a4a0c0b77878f5db3e0be5679c81"
|
||||
integrity sha512-7Uq7NdazNfVtr0RNmPAys8it0zKCuaqxJStYKEl72D3j4gbvXhhaM7iWNbqhA4C94ygCye6VuyhzBRQC4szeBg==
|
||||
|
||||
easy-stack@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.1.tgz#8afe4264626988cabb11f3c704ccd0c835411066"
|
||||
@ -5033,9 +5047,9 @@ ejs@^3.1.6:
|
||||
jake "^10.8.5"
|
||||
|
||||
electron-to-chromium@^1.4.477:
|
||||
version "1.4.510"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.510.tgz#446c50d7533c1e71a84b00a3b37ab06dd601d890"
|
||||
integrity sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g==
|
||||
version "1.4.512"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.512.tgz#f6c14d4d2ddacf064f1de36dbd3f6a469821a7ee"
|
||||
integrity sha512-1W8wRbYlQE4ph7eoj3TJ+uqwO6+xvAE/L+KGU7WTQQvX3tnSIGZAb90MTsMoJqzntamiwJhBAj4WZmygXhsOUg==
|
||||
|
||||
elliptic@^6.5.3:
|
||||
version "6.5.4"
|
||||
@ -5830,9 +5844,9 @@ flatted@^3.2.7:
|
||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||
|
||||
flow-parser@0.*:
|
||||
version "0.216.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.216.0.tgz#719ca725ef08c7e6ad69fa09181f3e9f90a185a8"
|
||||
integrity sha512-ozczvnbZ++wfBJFseeV0FvINkJ0C6TmRBmb7U7FY1RledNQZuCDTMywRi6txkp8gdzFCJPUxzrU4E27txAktbA==
|
||||
version "0.216.1"
|
||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.216.1.tgz#eeba9b0b689deeccc34a6b7d2b1f97b8f943afc0"
|
||||
integrity sha512-wstw46/C/8bRv/8RySCl15lK376j8DHxm41xFjD9eVL+jSS1UmVpbdLdA0LzGuS2v5uGgQiBLEj6mgSJQwW+MA==
|
||||
|
||||
flush-write-stream@^1.0.0:
|
||||
version "1.1.1"
|
||||
@ -5999,6 +6013,12 @@ functions-have-names@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
|
||||
fuzzysearch@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fuzzysearch/-/fuzzysearch-1.0.3.tgz#dffc80f6d6b04223f2226aa79dd194231096d008"
|
||||
integrity sha512-s+kNWQuI3mo9OALw0HJ6YGmMbLqEufCh2nX/zzV5CrICQ/y4AwPxM+6TIiF9ItFCHXFCyM/BfCCmN57NTIJuPg==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
@ -6633,11 +6653,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
@ -7603,7 +7618,7 @@ lodash.uniqby@^4.7.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302"
|
||||
integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==
|
||||
|
||||
lodash@4, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4:
|
||||
lodash@4, lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@ -7745,6 +7760,12 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
|
||||
material-colors@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
|
||||
integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==
|
||||
|
||||
mavon-editor@^2.10.4:
|
||||
version "2.10.4"
|
||||
resolved "https://registry.yarnpkg.com/mavon-editor/-/mavon-editor-2.10.4.tgz#58d6c4dc208933f0ac4595c10c60655899ba8ba8"
|
||||
@ -8236,7 +8257,7 @@ nth-check@^2.0.1:
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
|
||||
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||
@ -11060,12 +11081,13 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
util@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
||||
integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==
|
||||
|
||||
util@^0.10.4:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
|
||||
dependencies:
|
||||
inherits "2.0.1"
|
||||
inherits "2.0.3"
|
||||
|
||||
util@^0.11.0:
|
||||
version "0.11.1"
|
||||
@ -11322,6 +11344,12 @@ vuedraggable@^2.24.3:
|
||||
dependencies:
|
||||
sortablejs "1.10.2"
|
||||
|
||||
|
||||
watch-size@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/watch-size/-/watch-size-2.0.0.tgz#096ee28d0365bd7ea03d9c8bf1f2f50a73be1474"
|
||||
integrity sha512-M92R89dNoTPWyCD+HuUEDdhaDnh9jxPGOwlDc0u51jAgmjUvzqaEMynXSr3BaWs+QdHYk4KzibPy1TFtjLmOZQ==
|
||||
|
||||
watchpack-chokidar2@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
||||
|
Loading…
Reference in New Issue
Block a user