Skip to content

Commit

Permalink
Preview version of recipe categories (#368)
Browse files Browse the repository at this point in the history
* Add recipeList component and update index.vue to use it

* **src/components/recipeList.vue**
  - Create a new component to list recipes.
  - Accept a parameter for the folder name.
  - Display recipes based on the folder name.

* **src/pages/index.vue**
  - Import and use the new `recipeList` component.
  - Display folders or the `recipeList` component based on configuration.
  - When displaying folders, show a grid with 2 columns on mobile displays and more columns on bigger displays.
  - When displaying folders, each grid item shows an image and the folder name.

* **src/pages/options.vue**
  - Remove configuration option to enable/disable folders.

* Adjusted file casing

* WIP

* Ready to start reviewing approach

* Adjusted minor issues

* Fixed sonar issues

* Fixed existing tests

* Tanner's feedback implementation

* Added category edits

* Small fix

* Fixed translation

* Fixed broken test

* Image processing modal

* Added categories tests

* Fine tuning + tests

* Added tests

* Fixed sonar cloud issues

* Fixed backups

* Fixed tests

* Few more general fixes to tests

* Try to fix flakyness of tests

* Adjusted more tests

* More test adjustments and removed firefox from tests
  • Loading branch information
jlucaspains authored Dec 20, 2024
1 parent 0dd48a5 commit 1e57666
Show file tree
Hide file tree
Showing 36 changed files with 2,282 additions and 1,123 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ghcr.io/jlaundry/devcontainer-features/azure-functions-core-tools:1": {}
},
"tasks": {
"test": "yarn && npx playwright install chromium firefox webkit && yarn dev && npx playwright test",
"test": "yarn && npx playwright install chromium webkit && yarn dev && npx playwright test",
"launch": "yarn && yarn dev"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:

- name: Run Playwright tests
run: |
npx playwright install chromium firefox webkit
npx playwright install chromium webkit
HOME=/root npx playwright test
- name: Create test summary
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Sharp Cooking leverages [Playwright](https://playwright.dev/) for end to end tes

Install playwright browsers:
```powershell
npx playwright install chromium firefox webkit
npx playwright install chromium webkit
```

Run tests locally:
Expand Down
13 changes: 5 additions & 8 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const config: PlaywrightTestConfig = {
trace: 'on-first-retry',
video: 'off',
screenshot: 'only-on-failure',
contextOptions: {
serviceWorkers: "block"
}
},

/* Configure projects for major browsers */
Expand All @@ -62,22 +65,16 @@ const config: PlaywrightTestConfig = {
...devices['Desktop Safari'],
},
},
{
name: 'firefox',
use: {
...devices['DeskDesktop Firefox'],
},
},
{
name: 'Mobile Chrome',
use: {
...devices['Pixel 5'],
...devices['Pixel 7'],
},
},
{
name: 'Mobile Safari',
use: {
...devices['iPhone 12'],
...devices['iPhone 15'],
},
},
],
Expand Down
25 changes: 23 additions & 2 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@
"aiModelName": "AI Model Name",
"aiModelNameDescription": "The name of the OpenAI model to use for the AI Chat service.",
"editInSingleTextArea": "Enable simplified recipe editor",
"editInSingleTextAreaDescription": "Use one text area to edit ingredients and another for steps."
"editInSingleTextAreaDescription": "Use one text area to edit ingredients and another for steps.",
"enableCategoryDisplay": "Enable Category Display",
"enableCategoryDisplayDescription": "When enabled, show a list of categories on the main page.",
"categories": "Categories",
"categoriesDescription": "Maintain list of categories"
},
"recipe": {
"id": {
Expand Down Expand Up @@ -134,7 +138,9 @@
"sugar": "Sugar (g)",
"protein": "Protein (g)",
"changeLanguage": "Change Language",
"changeLanguageTitle": "Change recipe language"
"changeLanguageTitle": "Change recipe language",
"category": "Category",
"selectCategory": "Select category..."
},
"gallery": {
"recipeImage": "Recipe Image"
Expand Down Expand Up @@ -247,6 +253,21 @@
"chat": {
"abortError": "Ok. I won't answer that question.",
"unableToAnswer": "I'm sorry, I'm unable to answer that question right now."
},
"categories": {
"title": "Categories",
"newTitle": "New Category",
"selectImage": "Select an image...",
"delete": "Delete...",
"edit": "Edit...",
"deleteModalTitle": "Delete this category?",
"deleteModalBody": "Are you sure? This action cannot be undone.",
"deleteYes": "Yes, delete",
"categoryDeleted": "Category deleted successfully.",
"categoryDeleteFailed": "Failed to delete the category.",
"processImage1": "Compressing image...",
"processImage2": "Still working on the image. This may take a while",
"selectAnItem": "Please select an item."
}
},
"initialRecipes": [
Expand Down
25 changes: 23 additions & 2 deletions public/locales/pt/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@
"aiModelName": "Nome do Modelo de IA",
"aiModelNameDescription": "O nome do modelo OpenAI a ser usado para o serviço de Chat de IA.",
"editInSingleTextArea": "Habilitar editor de receitas simplificado",
"editInSingleTextAreaDescription": "Use uma área de texto para editar os ingredientes e outra para os passos."
"editInSingleTextAreaDescription": "Use uma área de texto para editar os ingredientes e outra para os passos.",
"enableCategoryDisplay": "Ativar Categorias",
"enableCategoryDisplayDescription": "Quando ativado, mostra uma lista de categories na página principal.",
"categories": "Categorias",
"categoriesDescription": "Manter lista de categorias"
},
"recipe": {
"id": {
Expand Down Expand Up @@ -133,7 +137,9 @@
"sugar": "Açúcares (g)",
"protein": "Proteínas (g)",
"changeLanguage": "Alterar idioma",
"changeLanguageTitle": "Alterar idioma da receita"
"changeLanguageTitle": "Alterar idioma da receita",
"category": "Categoria",
"selectCategory": "Selecione categoria..."
},
"gallery": {
"recipeImage": "Imagem de Receita"
Expand Down Expand Up @@ -245,6 +251,21 @@
"chat": {
"abortError": "Ok. Eu não vou responder essa pergunta.",
"unableToAnswer": "Desculpe, eu não consigo responder sua pergunta no momento."
},
"categories": {
"title": "Categorias",
"newTitle": "Nova categoria",
"selectImage": "Selecione uma imagem...",
"delete": "Remover...",
"edit": "Editar...",
"deleteModalTitle": "Excluir esta categoria?",
"deleteYes": "Sim, exclua",
"deleteModalBody": "Essa ação não pode ser desfeita.",
"categoryDeleted": "Categoria excluída com sucesso.",
"categoryDeleteFailed": "Falhou em excluir a categoria",
"processImage1": "Comprimindo imagem...",
"processImage2": "Ainda processando. Isso pode demorar um pouco...",
"selectAnItem": "Selecione um item."
}
},
"initialRecipes": [
Expand Down
64 changes: 64 additions & 0 deletions src/components/AddButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script setup lang="ts">
import { Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/vue";
const props = defineProps<{
items: Array<{ name: string, text: string, action: () => void }>;
}>();
</script>

<template>
<Menu as="div" class="p-0 w-14 h-14 fixed bottom-6 right-6">
<div>
<MenuButton data-testid="add-menu-button" class="
w-12
h-12
m-1
rounded-full
bg-theme-primary
hover:bg-theme-secondary
focus:bg-theme-secondary
focus:shadow-lg
shadow-md
hover:shadow-lg
transition duration-150 ease-in-out
">
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="w-6 h-6 inline-block">
<path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
C15.952,9,16,9.447,16,10z" />
</svg>
</MenuButton>
</div>

<transition enter-active-class="transition duration-100 ease-out" enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100" leave-active-class="transition duration-75 ease-in"
leave-from-class="transform scale-100 opacity-100" leave-to-class="transform scale-95 opacity-0">
<MenuItems class="
-top-2
transform
-translate-y-full
absolute
right-0
w-56
origin-top-right
bg-white
divide-y divide-gray-100
rounded-md
shadow-lg
ring-1 ring-black ring-opacity-5
focus:outline-none
">
<div class="px-1 py-1">
<MenuItem :key="child.name" v-for="child in props.items" v-slot="{ active }">
<button @click="child.action" :class="[
active ? 'bg-theme-secondary text-white' : 'text-gray-900',
'group flex w-full items-center rounded-md px-2 py-2 text-sm',
]">
{{ child.text }}
</button>
</MenuItem>
</div>
</MenuItems>
</transition>
</Menu>
</template>
32 changes: 32 additions & 0 deletions src/components/CategoryList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useTranslation } from "i18next-vue";
import { useState } from "../services/store";
import RecipeCard from "./RecipeCard.vue";
import { Category } from "../services/category";
import { getCategories } from "../services/dataService";
const router = useRouter();
const { t } = useTranslation();
const state = useState()!;
const categories = ref([] as Array<Category>);
onMounted(async () => {
categories.value = await getCategories();
});
function goToCategory(id: number) {
router.push(`/category/${id}`);
}
</script>

<template>
<div class="bg-white text-slate-900 dark:bg-theme-gray dark:text-white">
<div class="grid md:grid-cols-2 lg:grid-cols-3 my-4 gap-5">
<recipe-card v-for="category in categories" :title="category.name" :image="category.image"
:imageAvailable="category.image != undefined" :recipeCount="category.recipeCount"
@click="goToCategory(category.id)" :rating="0" />
</div>
</div>
</template>
109 changes: 109 additions & 0 deletions src/components/ConfigSwitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
const props = defineProps<{
modelValue: boolean;
displayName: string;
displayDescription: string;
testId: string;
}>();
const emit = defineEmits<{
(e: "update:modelValue", value: boolean): void;
}>();
const localValue = ref(false);
watch(
() => props.modelValue,
(newValue: boolean) => {
localValue.value = newValue;
}
);
onMounted(() => {
localValue.value = props.modelValue;
});
function updateValue(value: boolean) {
emit("update:modelValue", value);
}
</script>

<template>
<div>
<span class="dark:text-white">{{ props.displayName }}</span>
<label :data-testid="props.testId" class="switch float-right align-middle">
<input :checked="localValue" type="checkbox" @change="evt => updateValue((evt.target as HTMLInputElement).checked)">
<span class="slider round"></span>
</label>
<div>
<span class="text-gray-500 text-sm">{{ props.displayDescription }}</span>
</div>
</div>
</template>

<style>
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 30px;
height: 17px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 13px;
width: 13px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked+.slider {
background-color: #2196F3;
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(13px);
-ms-transform: translateX(13px);
transform: translateX(13px);
}
/* Rounded sliders */
.slider.round {
border-radius: 17px;
}
.slider.round:before {
border-radius: 50%;
}
</style>
Loading

0 comments on commit 1e57666

Please sign in to comment.