Skip to content

Commit

Permalink
changed dish input form to allow for inputting a diet type
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Ruf committed Jul 15, 2024
1 parent 2219201 commit 656e1f8
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 46 deletions.
4 changes: 4 additions & 0 deletions src/Mealz/MealBundle/Controller/DishController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\Mealz\MealBundle\Entity\Dish;
use App\Mealz\MealBundle\Entity\DishCollection;
use App\Mealz\MealBundle\Enum\Diet;
use App\Mealz\MealBundle\Repository\CategoryRepositoryInterface;
use App\Mealz\MealBundle\Repository\DishRepositoryInterface;
use App\Mealz\MealBundle\Service\ApiService;
Expand Down Expand Up @@ -88,6 +89,9 @@ public function new(Request $request): JsonResponse
if (true === $this->apiService->isParamValid($parameters, 'category', 'integer')) {
$dish->setCategory($this->categoryRepository->find($parameters['category']));
}
if (true === $this->apiService->isParamValid($parameters, 'diet', 'string')) {
$dish->setDiet(Diet::tryFrom($parameters['diet']));
}

$this->em->persist($dish);
$this->em->flush();
Expand Down
4 changes: 4 additions & 0 deletions src/Mealz/MealBundle/Service/DishService.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Mealz\MealBundle\Entity\Dish;
use App\Mealz\MealBundle\Entity\DishVariation;
use App\Mealz\MealBundle\Entity\Meal;
use App\Mealz\MealBundle\Enum\Diet;
use App\Mealz\MealBundle\Repository\CategoryRepository;
use App\Mealz\MealBundle\Repository\DishRepository;
use Exception;
Expand Down Expand Up @@ -81,6 +82,9 @@ public function updateHelper(Dish $dish, array $parameters): void
if (true === $this->apiService->isParamValid($parameters, 'category', 'integer')) {
$dish->setCategory($this->categoryRepository->find($parameters['category']));
}
if (true === $this->apiService->isParamValid($parameters, 'diet', 'string')) {
$dish->setDiet(Diet::tryFrom($parameters['diet']));
}
}

public function getDishCount(): array
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/src/api/postCreateDish.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IMessage } from '@/interfaces/IMessage';
import useApi from './api';
import { Diet } from '@/enums/Diet';

export interface CreateDishDTO {
titleDe: string;
Expand All @@ -8,6 +9,7 @@ export interface CreateDishDTO {
descriptionDe?: string;
descriptionEn?: string;
category?: number;
diet?: Diet
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/Resources/src/api/postCreateDishVariation.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { IMessage } from '@/interfaces/IMessage';
import useApi from './api';
import { Diet } from '@/enums/Diet';

export interface CreateDishVariationDTO {
titleDe?: string;
titleEn?: string;
diet?: Diet
}

/**
Expand Down
37 changes: 21 additions & 16 deletions src/Resources/src/components/categories/CategoriesDropDown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</ListboxLabel>
<ListboxButton
:class="open ? 'rounded-t-[23px] border-x-2 border-t-2' : 'rounded-full border-2'"
class="focus-visible:ring-offset-orange-300 relative flex w-full items-center border-[#CAD6E1] bg-white px-4 py-2 text-left text-[14px] font-medium text-[#B4C1CE] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2"
class="focus-visible:ring-offset-orange-300 flex w-full items-center border-[#CAD6E1] bg-white px-4 py-2 text-left text-[14px] font-medium text-[#B4C1CE] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2"
>
<span class="w-full truncate text-[#9CA3AF]">
{{ locale === 'en' ? selectedCategory.titleEn : selectedCategory.titleDe }}
Expand All @@ -21,24 +21,29 @@
aria-hidden="true"
/>
</ListboxButton>
<ListboxOptions
class="absolute max-h-60 w-full overflow-hidden rounded-b-[23px] border-x-2 border-b-2 border-[#CAD6E1] bg-white shadow-lg focus:outline-none"
<div
v-if="open"
class="absolute z-10 w-full"
>
<ListboxOption
v-for="category in CategoriesState.categories"
:key="category.id"
v-slot="{ selected }"
:value="category"
class="cursor-pointer truncate text-[14px] text-[#9CA3AF] hover:bg-[#FAFAFA]"
<ListboxOptions
class="max-h-60 w-full overflow-hidden rounded-b-[23px] border-x-2 border-b-2 border-[#CAD6E1] bg-white shadow-lg focus:outline-none"
>
<span
class="inline-block size-full px-4 py-2"
:class="selected ? 'bg-[#F4F4F4] font-medium' : 'font-normal'"
<ListboxOption
v-for="category in CategoriesState.categories"
:key="category.id"
v-slot="{ selected }"
:value="category"
class="cursor-pointer truncate text-[14px] text-[#9CA3AF] hover:bg-[#FAFAFA]"
>
{{ locale === 'en' ? category.titleEn : category.titleDe }}
</span>
</ListboxOption>
</ListboxOptions>
<span
class="inline-block size-full px-4 py-2"
:class="selected ? 'bg-[#F4F4F4] font-medium' : 'font-normal'"
>
{{ locale === 'en' ? category.titleEn : category.titleDe }}
</span>
</ListboxOption>
</ListboxOptions>
</div>
</Listbox>
</template>

Expand Down
92 changes: 62 additions & 30 deletions src/Resources/src/components/dishes/DishesCreationPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,31 @@
</h3>
<div class="z-0 grid w-full grid-cols-2 sm:gap-2">
<InputLabel
v-model="titleDeInput"
v-model="dishInput.titleDe"
:label-text="t('dish.popover.german')"
:required="required"
class="z-[1] col-span-2 row-start-1 sm:col-span-1 sm:col-start-1 sm:row-start-1"
/>
<InputLabel
v-model="titleEnInput"
v-model="dishInput.titleEn"
:label-text="t('dish.popover.english')"
:required="required"
class="z-[1] col-span-2 row-start-2 sm:col-span-1 sm:col-start-2 sm:row-start-1"
/>
<InputLabel
v-model="descriptionDeInput"
v-model="dishInput.descriptionDe"
:label-text="t('dish.popover.descriptionDe')"
class="z-[1] col-span-2 row-start-3 sm:col-span-1 sm:col-start-1 sm:row-start-2"
/>
<InputLabel
v-model="descriptionEnInput"
v-model="dishInput.descriptionEn"
:label-text="t('dish.popover.descriptionEn')"
class="z-[1] col-span-2 row-start-4 sm:col-span-1 sm:col-start-2 sm:row-start-2"
/>
<CategoriesDropDown
ref="categoryDropDown"
:category-id="categoryId"
class="z-[2] col-span-1 col-start-1 row-start-5 sm:row-start-3"
class="col-span-1 col-start-1 row-start-5 sm:row-start-3"
/>
<SwitchGroup>
<div class="col-span-1 col-start-2 row-start-5 flex flex-col items-start pt-2 sm:row-start-3">
Expand All @@ -43,11 +43,18 @@
:sr="t('dish.popover.oneSizeServing')"
:initial="oneSizeServing"
class="my-auto ml-4"
@toggle="(value) => setOneSizeServing(value)"
@toggle="(value) => dishInput.oneServingSize = value"
/>
</div>
</SwitchGroup>
<SubmitButton class="z-[1] col-span-2 row-start-6 sm:col-start-1 sm:row-start-4" />
<ListOptionsDropDown
v-model="dietInput"
:list-options="dietOptions"
class="col-span-1 col-start-1 row-start-6 items-start pt-2 sm:col-span-1 sm:row-start-4"
>
{{ t('dish.diet.diet') }}
</ListOptionsDropDown>
<SubmitButton class="relative col-span-2 row-start-7 sm:col-start-1 sm:row-start-5" />
</div>
</form>
</template>
Expand All @@ -57,12 +64,14 @@ import { useI18n } from 'vue-i18n';
import SubmitButton from '../misc/SubmitButton.vue';
import InputLabel from '../misc/InputLabel.vue';
import { useDishes } from '@/stores/dishesStore';
import { onMounted, ref } from 'vue';
import { computed, onMounted, reactive, ref, watch } from 'vue';
import { CreateDishDTO } from '@/api/postCreateDish';
import CategoriesDropDown from '../categories/CategoriesDropDown.vue';
import { useCategories } from '@/stores/categoriesStore';
import Switch from '@/components/misc/Switch.vue';
import { SwitchGroup, SwitchLabel } from '@headlessui/vue';
import ListOptionsDropDown from '@/components/misc/ListOptionsDropDown.vue';
import { Diet } from '@/enums/Diet';
const { t } = useI18n();
const { createDish, updateDish } = useDishes();
Expand All @@ -76,6 +85,7 @@ const props = withDefaults(
descriptionEn?: string | null;
categoryId?: number | null;
oneSizeServing?: boolean;
diet?: Diet,
dishId?: number | null;
edit?: boolean;
}>(),
Expand All @@ -86,6 +96,7 @@ const props = withDefaults(
descriptionEn: null,
categoryId: null,
oneSizeServing: false,
diet: Diet.MEAT,
dishId: null,
edit: false
}
Expand All @@ -97,40 +108,61 @@ onMounted(async () => {
await fetchCategories();
});
const titleDeInput = ref(props.titleDe);
const titleEnInput = ref(props.titleEn);
const descriptionDeInput = ref(props.descriptionDe);
const descriptionEnInput = ref(props.descriptionEn);
const dietOptions = computed(() => {
return [Diet.MEAT, Diet.VEGAN, Diet.VEGETARIAN].map(diet => {
return {
value: diet,
label: getDietOption(diet)
};
});
});
const dietInput = ref({
value: props.diet,
label: getDietOption(props.diet)
});
const categoryDropDown = ref<InstanceType<typeof CategoriesDropDown> | null>(null);
const oneSizeServingInput = ref(props.oneSizeServing);
const required = ref(false);
const dishInput = reactive<CreateDishDTO>({
titleDe: props.titleDe,
titleEn: props.titleEn,
descriptionDe: props.descriptionDe,
descriptionEn: props.descriptionEn,
oneServingSize: props.oneSizeServing,
diet: props.diet,
category: null
});
watch(
() => categoryDropDown.value?.selectedCategory.id,
() => {
dishInput.category = categoryDropDown.value?.selectedCategory.id;
}
);
watch(
() => dietInput.value,
() => {
dishInput.diet = dietInput.value.value
}
);
async function onSubmit() {
required.value = true;
if (titleDeInput.value === '' || titleEnInput.value === '') {
if (dishInput.titleDe === '' || dishInput.titleEn === '') {
return;
}
if (props.edit === true) {
await updateDish(props.dishId, createDishDtoObject());
await updateDish(props.dishId, dishInput);
} else {
await createDish(createDishDtoObject());
await createDish(dishInput);
}
emit('closePanel');
}
function createDishDtoObject() {
const dish: CreateDishDTO = {
titleDe: titleDeInput.value,
titleEn: titleEnInput.value,
oneServingSize: oneSizeServingInput.value,
descriptionDe: descriptionDeInput.value,
descriptionEn: descriptionEnInput.value,
category: categoryDropDown.value?.selectedCategory.id
};
return dish;
}
function setOneSizeServing(state: boolean) {
oneSizeServingInput.value = state;
function getDietOption(diet: Diet): string {
return t(`dish.diet.${diet}`);
}
</script>
77 changes: 77 additions & 0 deletions src/Resources/src/components/misc/ListOptionsDropDown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<template>
<Listbox
v-slot="{ open }"
v-model="selectedOption"
as="span"
class="relative w-full"
>
<div class="relative">
<ListboxLabel class="w-full px-4 text-start text-xs font-medium text-[#173D7A]">
<slot />
</ListboxLabel>
<ListboxButton
:class="open ? 'rounded-t-[23px] border-x-2 border-t-2' : 'rounded-full border-2'"
class="focus-visible:ring-offset-orange-300 flex w-full items-center border-[#CAD6E1] bg-white px-4 py-2 text-left text-[14px] font-medium text-[#B4C1CE] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2"
>
<span class="w-full truncate text-[#9CA3AF]">
{{ selectedOption.label }}
</span>
<ChevronDownIcon
class="h-full w-5 justify-self-end text-[#9CA3AF]"
:class="open ? 'rotate-180 transform' : ''"
aria-hidden="true"
/>
</ListboxButton>
<div
v-if="open"
class="absolute z-10 w-full"
>
<ListboxOptions
class="max-h-60 w-full overflow-hidden rounded-b-[23px] border-x-2 border-b-2 border-[#CAD6E1] bg-white shadow-lg focus:outline-none"
>
<ListboxOption
v-for="option in listOptions"
:key="option.label"
v-slot="{ selected }"
:value="option"
class="cursor-pointer truncate text-[14px] text-[#9CA3AF] hover:bg-[#FAFAFA]"
>
<span
class="inline-block size-full px-4 py-2"
:class="selected ? 'bg-[#F4F4F4] font-medium' : 'font-normal'"
>
{{ option.label }}
</span>
</ListboxOption>
</ListboxOptions>
</div>
</div>
</Listbox>
</template>

<script setup lang="ts" generic="T">
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from '@headlessui/vue';
import { computed } from 'vue';
import { ChevronDownIcon } from '@heroicons/vue/solid';
export type IListOption<T> = {
value: T,
label: string
}
const props = defineProps<{
listOptions: IListOption<T>[],
modelValue: IListOption<T>
}>();
const emit = defineEmits(['update:modelValue'])
const selectedOption = computed({
get() {
return props.modelValue;
},
set(value) {
emit('update:modelValue', value);
}
})
</script>
6 changes: 6 additions & 0 deletions src/Resources/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@
"edit": "Variation editieren",
"new": "Variation erstellen"
}
},
"diet": {
"diet": "Ernährungsweise",
"vegan": "Vegan",
"vegetarian": "Vegetarisch",
"meat": "Beinhaltet Fleisch"
}
},
"event": {
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@
"edit": "Edit Variation",
"new": "Create Variation"
}
},
"diet": {
"diet": "Diet",
"vegan": "Vegan",
"vegetarian": "Vegetarisch",
"meat": "Beinhaltet Fleisch"
}
},
"event": {
Expand Down

0 comments on commit 656e1f8

Please sign in to comment.