diff --git a/.gitignore b/.gitignore
index d01991ff2..3b2646bfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,6 @@ mealie/temp/api.html
.temp/
.secret
-
dev/data/backups/*
dev/data/debug/*
dev/data/img/*
@@ -157,39 +156,6 @@ mealie/data/debug/last_recipe.json
*.sqlite
dev/data/db/test.db
scratch.py
-frontend/dist/favicon.ico
-frontend/dist/index.html
-frontend/dist/css/app.29fe0155.css
-frontend/dist/css/chunk-vendors.db944396.css
-frontend/dist/fonts/materialdesignicons-webfont.7a44ea19.woff2
-frontend/dist/fonts/materialdesignicons-webfont.64d4cf64.eot
-frontend/dist/fonts/materialdesignicons-webfont.147e3378.woff
-frontend/dist/fonts/materialdesignicons-webfont.174c02fc.ttf
-frontend/dist/fonts/roboto-latin-100.5cb7edfc.woff
-frontend/dist/fonts/roboto-latin-100.7370c367.woff2
-frontend/dist/fonts/roboto-latin-100italic.f8b1df51.woff2
-frontend/dist/fonts/roboto-latin-100italic.f9e8e590.woff
-frontend/dist/fonts/roboto-latin-300.b00849e0.woff
-frontend/dist/fonts/roboto-latin-300.ef7c6637.woff2
-frontend/dist/fonts/roboto-latin-300italic.4df32891.woff
-frontend/dist/fonts/roboto-latin-300italic.14286f3b.woff2
-frontend/dist/fonts/roboto-latin-400.60fa3c06.woff
-frontend/dist/fonts/roboto-latin-400.479970ff.woff2
-frontend/dist/fonts/roboto-latin-400italic.51521a2a.woff2
-frontend/dist/fonts/roboto-latin-400italic.fe65b833.woff
-frontend/dist/fonts/roboto-latin-500.020c97dc.woff2
-frontend/dist/fonts/roboto-latin-500.87284894.woff
-frontend/dist/fonts/roboto-latin-500italic.288ad9c6.woff
-frontend/dist/fonts/roboto-latin-500italic.db4a2a23.woff2
-frontend/dist/fonts/roboto-latin-700.2735a3a6.woff2
-frontend/dist/fonts/roboto-latin-700.adcde98f.woff
-frontend/dist/fonts/roboto-latin-700italic.81f57861.woff
-frontend/dist/fonts/roboto-latin-700italic.da0e7178.woff2
-frontend/dist/fonts/roboto-latin-900.9b3766ef.woff2
-frontend/dist/fonts/roboto-latin-900.bb1e4dc6.woff
-frontend/dist/fonts/roboto-latin-900italic.28f91510.woff
-frontend/dist/fonts/roboto-latin-900italic.ebf6d164.woff2
-frontend/dist/js/app.36f2760c.js
-frontend/dist/js/app.36f2760c.js.map
-frontend/dist/js/chunk-vendors.c93761e4.js
-frontend/dist/js/chunk-vendors.c93761e4.js.map
+dev/data/backups/dev_sample_data*.zip
+dev/data/backups/dev_sample_data*.zip
+!dev/data/backups/test*.zip
diff --git a/dev/data/backups/dev_sample_data_2021-Feb-13.zip b/dev/data/backups/test_backup_2021-Apr-27.zip
similarity index 72%
rename from dev/data/backups/dev_sample_data_2021-Feb-13.zip
rename to dev/data/backups/test_backup_2021-Apr-27.zip
index 7a79d98bc..bac642b1b 100644
Binary files a/dev/data/backups/dev_sample_data_2021-Feb-13.zip and b/dev/data/backups/test_backup_2021-Apr-27.zip differ
diff --git a/docs/docs/changelog/v0.5.0.md b/docs/docs/changelog/v0.5.0.md
index beccda8fe..5db08118b 100644
--- a/docs/docs/changelog/v0.5.0.md
+++ b/docs/docs/changelog/v0.5.0.md
@@ -21,6 +21,11 @@
- 'Dinner today' shows a warning when no meal is planned yet
### General
+- New Toolbox Page!
+ - Bulk assign categories and tags by keyword search
+ - Title case all Categories or Tags with 1 click
+ - Create/Rename/Delete Operations for Tags/Categories
+ - Remove Unused Categories or Tags with 1 click
- More localization
- Start date for Week is now selectable
- Languages are now managed through Crowdin
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index b3474e9eb..56b8c537a 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -46,6 +46,7 @@ export default {
this.darkModeAddEventListener();
this.$store.dispatch("requestAppInfo");
this.$store.dispatch("requestCustomPages");
+ this.$store.dispatch("requestSiteSettings");
},
methods: {
diff --git a/frontend/src/api/api-utils.js b/frontend/src/api/api-utils.js
index 50b9ed964..c8aff9f09 100644
--- a/frontend/src/api/api-utils.js
+++ b/frontend/src/api/api-utils.js
@@ -24,6 +24,16 @@ const apiReq = {
});
return response;
},
+ patch: async function(url, data) {
+ let response = await axios.patch(url, data).catch(function(error) {
+ if (error.response) {
+ processResponse(error.response);
+ return response;
+ } else return;
+ });
+ processResponse(response);
+ return response;
+ },
get: async function(url, data) {
let response = await axios.get(url, data).catch(function(error) {
diff --git a/frontend/src/api/category.js b/frontend/src/api/category.js
index 94118fc99..676a4488a 100644
--- a/frontend/src/api/category.js
+++ b/frontend/src/api/category.js
@@ -6,8 +6,10 @@ const prefix = baseURL + "categories";
const categoryURLs = {
getAll: `${prefix}`,
+ getEmpty: `${prefix}/empty`,
getCategory: category => `${prefix}/${category}`,
deleteCategory: category => `${prefix}/${category}`,
+ updateCategory: category => `${prefix}/${category}`,
};
export const categoryAPI = {
@@ -15,6 +17,10 @@ export const categoryAPI = {
let response = await apiReq.get(categoryURLs.getAll);
return response.data;
},
+ async getEmpty() {
+ let response = await apiReq.get(categoryURLs.getEmpty);
+ return response.data;
+ },
async create(name) {
let response = await apiReq.post(categoryURLs.getAll, { name: name });
store.dispatch("requestCategories");
@@ -24,9 +30,20 @@ export const categoryAPI = {
let response = await apiReq.get(categoryURLs.getCategory(category));
return response.data;
},
- async delete(category) {
+ async update(name, newName, overrideRequest = false) {
+ let response = await apiReq.put(categoryURLs.updateCategory(name), {
+ name: newName,
+ });
+ if (!overrideRequest) {
+ store.dispatch("requestCategories");
+ }
+ return response.data;
+ },
+ async delete(category, overrideRequest = false) {
let response = await apiReq.delete(categoryURLs.deleteCategory(category));
- store.dispatch("requestCategories");
+ if (!overrideRequest) {
+ store.dispatch("requestCategories");
+ }
return response;
},
};
@@ -35,8 +52,10 @@ const tagPrefix = baseURL + "tags";
const tagURLs = {
getAll: `${tagPrefix}`,
+ getEmpty: `${tagPrefix}/empty`,
getTag: tag => `${tagPrefix}/${tag}`,
deleteTag: tag => `${tagPrefix}/${tag}`,
+ updateTag: tag => `${tagPrefix}/${tag}`,
};
export const tagAPI = {
@@ -44,6 +63,10 @@ export const tagAPI = {
let response = await apiReq.get(tagURLs.getAll);
return response.data;
},
+ async getEmpty() {
+ let response = await apiReq.get(tagURLs.getEmpty);
+ return response.data;
+ },
async create(name) {
let response = await apiReq.post(tagURLs.getAll, { name: name });
store.dispatch("requestTags");
@@ -53,9 +76,20 @@ export const tagAPI = {
let response = await apiReq.get(tagURLs.getTag(tag));
return response.data;
},
- async delete(tag) {
+ async update(name, newName, overrideRequest = false) {
+ let response = await apiReq.put(tagURLs.updateTag(name), { name: newName });
+
+ if (!overrideRequest) {
+ store.dispatch("requestTags");
+ }
+
+ return response.data;
+ },
+ async delete(tag, overrideRequest = false) {
let response = await apiReq.delete(tagURLs.deleteTag(tag));
- store.dispatch("requestTags");
+ if (!overrideRequest) {
+ store.dispatch("requestTags");
+ }
return response.data;
},
};
diff --git a/frontend/src/api/recipe.js b/frontend/src/api/recipe.js
index 881990178..80385ae6d 100644
--- a/frontend/src/api/recipe.js
+++ b/frontend/src/api/recipe.js
@@ -66,7 +66,13 @@ export const recipeAPI = {
async update(data) {
let response = await apiReq.put(recipeURLs.update(data.slug), data);
- store.dispatch("requestRecentRecipes");
+ store.dispatch("patchRecipe", response.data);
+ return response.data.slug; // ! Temporary until I rewrite to refresh page without additional request
+ },
+
+ async patch(data) {
+ let response = await apiReq.patch(recipeURLs.update(data.slug), data);
+ store.dispatch("patchRecipe", response.data);
return response.data;
},
diff --git a/frontend/src/components/MealPlan/MealPlanNew.vue b/frontend/src/components/MealPlan/MealPlanNew.vue
index 292f8c71c..c81238c60 100644
--- a/frontend/src/components/MealPlan/MealPlanNew.vue
+++ b/frontend/src/components/MealPlan/MealPlanNew.vue
@@ -3,7 +3,8 @@
{{ $t("meal-plan.create-a-new-meal-plan") }}
- mdi-calendar-minus {{$t('meal-plan.quick-week')}}
+ mdi-calendar-minus
+ {{ $t("meal-plan.quick-week") }}
@@ -153,7 +154,7 @@ export default {
return recipes.length > 0 ? recipes : this.items;
},
allRecipes() {
- return this.$store.getters.getRecentRecipes;
+ return this.$store.getters.getAllRecipes;
},
},
diff --git a/frontend/src/components/UI/CardSection.vue b/frontend/src/components/UI/CardSection.vue
index d29a9178e..dd6efb0f3 100644
--- a/frontend/src/components/UI/CardSection.vue
+++ b/frontend/src/components/UI/CardSection.vue
@@ -60,10 +60,10 @@
@@ -79,14 +79,16 @@
-
+
+
+
@@ -109,6 +111,12 @@ export default {
hardLimit: {
default: 99999,
},
+ mobileCards: {
+ default: false,
+ },
+ singleColumn: {
+ defualt: false,
+ },
recipes: Array,
},
data() {
@@ -117,8 +125,14 @@ export default {
loading: false,
};
},
+ watch: {
+ recipes() {
+ this.bumpList();
+ },
+ },
computed: {
viewScale() {
+ if (this.mobileCards) return true;
switch (this.$vuetify.breakpoint.name) {
case "xs":
return true;
@@ -128,10 +142,16 @@ export default {
return false;
}
},
+ effectiveHardLimit() {
+ return Math.min(this.hardLimit, this.recipes.length);
+ },
},
methods: {
bumpList() {
- const newCardLimit = Math.min(this.cardLimit + 20, this.hardLimit);
+ const newCardLimit = Math.min(
+ this.cardLimit + 20,
+ this.effectiveHardLimit
+ );
if (this.loading === false && newCardLimit > this.cardLimit) {
this.setLoader();
@@ -141,7 +161,7 @@ export default {
},
async setLoader() {
this.loading = true;
- await new Promise(r => setTimeout(r, 3000));
+ await new Promise(r => setTimeout(r, 1000));
this.loading = false;
},
},
diff --git a/frontend/src/components/UI/Dialogs/BaseDialog.vue b/frontend/src/components/UI/Dialogs/BaseDialog.vue
index a1f64a258..23123fce4 100644
--- a/frontend/src/components/UI/Dialogs/BaseDialog.vue
+++ b/frontend/src/components/UI/Dialogs/BaseDialog.vue
@@ -1,21 +1,38 @@
-
-
-
- {{ titleIcon }}
-
-
+
+
+
+ {{ titleIcon }}
+
+ {{ title }}
+
+
+
-
- {{ title }}
-
-
+ color="primary"
+ >
+
+
+
+
+ Cancel
+
+
+
+ Submit
+
+
+
+
+
+
@@ -35,6 +52,12 @@ export default {
modalWidth: {
default: "500",
},
+ loading: {
+ default: false,
+ },
+ top: {
+ default: false,
+ },
},
data() {
return {
@@ -45,9 +68,15 @@ export default {
open() {
this.dialog = true;
},
+ close() {
+ this.dialog = false;
+ },
},
};
\ No newline at end of file
diff --git a/frontend/src/components/UI/Dialogs/NewCategoryTagDialog.vue b/frontend/src/components/UI/Dialogs/NewCategoryTagDialog.vue
index 8f91292e8..69c0c1588 100644
--- a/frontend/src/components/UI/Dialogs/NewCategoryTagDialog.vue
+++ b/frontend/src/components/UI/Dialogs/NewCategoryTagDialog.vue
@@ -1,8 +1,10 @@
-
- mdi-plus
-
+
+
+ mdi-plus
+
+
@@ -80,6 +82,9 @@ export default {
},
methods: {
+ open() {
+ this.dialog = true;
+ },
async select() {
const newItem = await (async () => {
if (this.tagDialog) {
diff --git a/frontend/src/components/UI/Search/FuseSearchBar.vue b/frontend/src/components/UI/Search/FuseSearchBar.vue
new file mode 100644
index 000000000..08152fbdf
--- /dev/null
+++ b/frontend/src/components/UI/Search/FuseSearchBar.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/UI/Search/SearchBar.vue b/frontend/src/components/UI/Search/SearchBar.vue
index afb7540d0..964fa72a0 100644
--- a/frontend/src/components/UI/Search/SearchBar.vue
+++ b/frontend/src/components/UI/Search/SearchBar.vue
@@ -132,7 +132,7 @@ export default {
},
computed: {
data() {
- return this.$store.getters.getRecentRecipes;
+ return this.$store.getters.getAllRecipes;
},
autoResults() {
return this.fuseResults.length > 1 ? this.fuseResults : this.results;
diff --git a/frontend/src/components/UI/TheSidebar.vue b/frontend/src/components/UI/TheSidebar.vue
index b9d3d6a45..d36e7066a 100644
--- a/frontend/src/components/UI/TheSidebar.vue
+++ b/frontend/src/components/UI/TheSidebar.vue
@@ -163,6 +163,11 @@ export default {
to: "/admin/settings",
title: this.$t("settings.site-settings"),
},
+ {
+ icon: "mdi-tools",
+ to: "/admin/toolbox",
+ title: this.$t("settings.toolbox.toolbox"),
+ },
{
icon: "mdi-account-group",
to: "/admin/manage-users",
diff --git a/frontend/src/components/UI/TheSiteMenu.vue b/frontend/src/components/UI/TheSiteMenu.vue
index 98c873387..d936b9b52 100644
--- a/frontend/src/components/UI/TheSiteMenu.vue
+++ b/frontend/src/components/UI/TheSiteMenu.vue
@@ -6,6 +6,7 @@
bottom
right
offset-y
+ offset-overflow
open-on-hover
close-delay="200"
>
@@ -72,7 +73,7 @@ export default {
},
{
icon: "mdi-logout",
- title: this.$t('user.logout'),
+ title: this.$t("user.logout"),
restricted: true,
nav: "/logout",
},
diff --git a/frontend/src/locales/messages/af-ZA.json b/frontend/src/locales/messages/af-ZA.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/af-ZA.json
+++ b/frontend/src/locales/messages/af-ZA.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ar-SA.json b/frontend/src/locales/messages/ar-SA.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ar-SA.json
+++ b/frontend/src/locales/messages/ar-SA.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ca-ES.json b/frontend/src/locales/messages/ca-ES.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ca-ES.json
+++ b/frontend/src/locales/messages/ca-ES.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/cs-CZ.json b/frontend/src/locales/messages/cs-CZ.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/cs-CZ.json
+++ b/frontend/src/locales/messages/cs-CZ.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/da-DK.json b/frontend/src/locales/messages/da-DK.json
index 348acd61f..86635b1b4 100644
--- a/frontend/src/locales/messages/da-DK.json
+++ b/frontend/src/locales/messages/da-DK.json
@@ -145,15 +145,15 @@
"view-recipe": "Se opskrift"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Webadresserne, der er anført nedenfor, modtager webhooks, der indeholder opskriftsdataene for måltidsplanen på den planlagte dag. \nWebhooks udføres i øjeblikket på {time} ",
"webhook-url": "Webhook adresse"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Tid",
"webhooks-enabled": "Webhooks Aktiveret"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/de-DE.json b/frontend/src/locales/messages/de-DE.json
index d4d67b939..d76054bf3 100644
--- a/frontend/src/locales/messages/de-DE.json
+++ b/frontend/src/locales/messages/de-DE.json
@@ -145,15 +145,15 @@
"view-recipe": "Rezept anschauen"
},
"search": {
- "and": "Und",
+ "search-mealie": "Mealie durchsuchen (/ drücken)",
+ "search-placeholder": "Suchen...",
+ "max-results": "Max. Ergebnisse",
"category-filter": "Kategoriefilter",
"exclude": "Ausschließen",
"include": "Einbeziehen",
- "max-results": "Max. Ergebnisse",
"or": "Oder",
+ "and": "and",
"search": "Suchen",
- "search-mealie": "Mealie durchsuchen",
- "search-placeholder": "Suchen...",
"tag-filter": "Schlagwortfilter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Teste Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Die unten stehenden URL's erhalten Webhooks welche die Rezeptdaten für den Menüplan am geplanten Tag enthalten. Derzeit werden die Webhooks ausgeführt um",
"webhook-url": "Webhook-URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Zeit",
"webhooks-enabled": "Webhooks aktiviert"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/el-GR.json b/frontend/src/locales/messages/el-GR.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/el-GR.json
+++ b/frontend/src/locales/messages/el-GR.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/en-US.json b/frontend/src/locales/messages/en-US.json
index 08d4d4715..a8d587162 100644
--- a/frontend/src/locales/messages/en-US.json
+++ b/frontend/src/locales/messages/en-US.json
@@ -180,6 +180,7 @@
"include": "Include",
"max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
"search-mealie": "Search Mealie (press /)",
"search-placeholder": "Search...",
@@ -264,6 +265,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"this": {},
diff --git a/frontend/src/locales/messages/es-ES.json b/frontend/src/locales/messages/es-ES.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/es-ES.json
+++ b/frontend/src/locales/messages/es-ES.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/fi-FI.json b/frontend/src/locales/messages/fi-FI.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/fi-FI.json
+++ b/frontend/src/locales/messages/fi-FI.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/fr-FR.json b/frontend/src/locales/messages/fr-FR.json
index ccc9994f1..90519673f 100644
--- a/frontend/src/locales/messages/fr-FR.json
+++ b/frontend/src/locales/messages/fr-FR.json
@@ -47,7 +47,7 @@
"reset": "Réinitialiser",
"saturday": "Samedi",
"save": "Sauvegarder",
- "settings": "Options",
+ "settings": "Paramètres",
"sort": "Trier",
"sort-alphabetically": "A-Z",
"submit": "Soumettre",
@@ -145,15 +145,15 @@
"view-recipe": "Voir la recette"
},
"search": {
- "and": "Et",
+ "search-mealie": "Rechercher dans Mealie (appuyez sur /)",
+ "search-placeholder": "Rechercher...",
+ "max-results": "Résultats max",
"category-filter": "Filtre par catégories",
"exclude": "Exclure",
"include": "Inclure",
- "max-results": "Résultats max",
"or": "Ou",
+ "and": "and",
"search": "Rechercher",
- "search-mealie": "Rechercher dans Mealie",
- "search-placeholder": "Rechercher...",
"tag-filter": "Filtre par mots-clés"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Tester les webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Les liens dans cette liste recevront les webhooks contenant les recettes pour le menu du jour. Actuellement, les webhooks se lancent à",
"webhook-url": "Lien du webhook"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Heure du Webhook",
"webhooks-enabled": "Webhooks activés"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/he-IL.json b/frontend/src/locales/messages/he-IL.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/he-IL.json
+++ b/frontend/src/locales/messages/he-IL.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/hu-HU.json b/frontend/src/locales/messages/hu-HU.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/hu-HU.json
+++ b/frontend/src/locales/messages/hu-HU.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/it-IT.json b/frontend/src/locales/messages/it-IT.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/it-IT.json
+++ b/frontend/src/locales/messages/it-IT.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ja-JP.json b/frontend/src/locales/messages/ja-JP.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ja-JP.json
+++ b/frontend/src/locales/messages/ja-JP.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ko-KR.json b/frontend/src/locales/messages/ko-KR.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ko-KR.json
+++ b/frontend/src/locales/messages/ko-KR.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/nl-NL.json b/frontend/src/locales/messages/nl-NL.json
index 6d06039d1..c2d00d238 100644
--- a/frontend/src/locales/messages/nl-NL.json
+++ b/frontend/src/locales/messages/nl-NL.json
@@ -145,15 +145,15 @@
"view-recipe": "Bekijk Recept"
},
"search": {
- "and": "En",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Zoeken...",
+ "max-results": "Max. resultaten",
"category-filter": "Categorie Filter",
"exclude": "Exclusief",
"include": "Inclusief",
- "max-results": "Max. resultaten",
"or": "Of",
+ "and": "and",
"search": "Zoek",
- "search-mealie": "Mealie Zoeken",
- "search-placeholder": "Zoeken...",
"tag-filter": "Label Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Webhooks testen",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "De onderstaande URL's ontvangen webhooks met receptgegevens voor het maaltijdplan op de geplande dag. Momenteel zullen Webhooks uitvoeren op",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Tijd",
"webhooks-enabled": "Webhooks ingeschakeld"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/no-NO.json b/frontend/src/locales/messages/no-NO.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/no-NO.json
+++ b/frontend/src/locales/messages/no-NO.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/pl-PL.json b/frontend/src/locales/messages/pl-PL.json
index cf016a7a3..0d9d3db81 100644
--- a/frontend/src/locales/messages/pl-PL.json
+++ b/frontend/src/locales/messages/pl-PL.json
@@ -145,15 +145,15 @@
"view-recipe": "Wyświetl przepis"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Przeszukaj Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Testuj webhooki",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Odnośniki poniżej otrzymają webhook zawierający dane o przepisie dla danego dnia. Aktualnie webhooki zostanę wykonane o",
"webhook-url": "Odnośnik webhooka"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/pt-BR.json b/frontend/src/locales/messages/pt-BR.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/pt-BR.json
+++ b/frontend/src/locales/messages/pt-BR.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/pt-PT.json b/frontend/src/locales/messages/pt-PT.json
index f05ba8d72..c04e97a45 100644
--- a/frontend/src/locales/messages/pt-PT.json
+++ b/frontend/src/locales/messages/pt-PT.json
@@ -145,15 +145,15 @@
"view-recipe": "Ver Receita"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Pesquisar Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Webhooks de Teste",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Os URLs apresentados abaixo receberão webhooks que contêm os dados da receita para o plano de refeições no dia marcado. Atualmente, os webhooks serão executados a ",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ro-RO.json b/frontend/src/locales/messages/ro-RO.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ro-RO.json
+++ b/frontend/src/locales/messages/ro-RO.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/ru-RU.json b/frontend/src/locales/messages/ru-RU.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/ru-RU.json
+++ b/frontend/src/locales/messages/ru-RU.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/sr-SP.json b/frontend/src/locales/messages/sr-SP.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/sr-SP.json
+++ b/frontend/src/locales/messages/sr-SP.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/sv-SE.json b/frontend/src/locales/messages/sv-SE.json
index 6b46e2a84..4dbee6358 100644
--- a/frontend/src/locales/messages/sv-SE.json
+++ b/frontend/src/locales/messages/sv-SE.json
@@ -145,15 +145,15 @@
"view-recipe": "Visa recept"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Testa Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Följande URLer kommer att mottaga webhooks med receptdata för dagens planerade måltid. Datan kommer att skickas klockan { time }",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/tr-TR.json b/frontend/src/locales/messages/tr-TR.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/tr-TR.json
+++ b/frontend/src/locales/messages/tr-TR.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/uk-UA.json b/frontend/src/locales/messages/uk-UA.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/uk-UA.json
+++ b/frontend/src/locales/messages/uk-UA.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/vi-VN.json b/frontend/src/locales/messages/vi-VN.json
index 1ad2d0efd..c897010d8 100644
--- a/frontend/src/locales/messages/vi-VN.json
+++ b/frontend/src/locales/messages/vi-VN.json
@@ -145,15 +145,15 @@
"view-recipe": "View Recipe"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "Search Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "Test Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
"webhook-url": "Webhook URL"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/zh-CN.json b/frontend/src/locales/messages/zh-CN.json
index 2ddc404ee..d48cb18f5 100644
--- a/frontend/src/locales/messages/zh-CN.json
+++ b/frontend/src/locales/messages/zh-CN.json
@@ -145,15 +145,15 @@
"view-recipe": "查看食谱"
},
"search": {
- "and": "与",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "搜索...",
+ "max-results": "最大结果",
"category-filter": "分类筛选",
"exclude": "排除",
"include": "包括",
- "max-results": "最大结果",
"or": "或",
+ "and": "and",
"search": "搜索",
- "search-mealie": "搜索Mealie",
- "search-placeholder": "搜索...",
"tag-filter": "标签筛选"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "测试Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "下方列出的网址将在预定日期接收到有关用餐计划的食谱资料。Webhooks执行将在",
"webhook-url": "Webhook网址"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook时间",
"webhooks-enabled": "Webhooks 启用"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/locales/messages/zh-TW.json b/frontend/src/locales/messages/zh-TW.json
index 03cb27186..b49cd4291 100644
--- a/frontend/src/locales/messages/zh-TW.json
+++ b/frontend/src/locales/messages/zh-TW.json
@@ -145,15 +145,15 @@
"view-recipe": "查看食譜"
},
"search": {
- "and": "And",
+ "search-mealie": "Search Mealie (press /)",
+ "search-placeholder": "Search...",
+ "max-results": "Max Results",
"category-filter": "Category Filter",
"exclude": "Exclude",
"include": "Include",
- "max-results": "Max Results",
"or": "Or",
+ "and": "and",
"search": "Search",
- "search-mealie": "搜索Mealie",
- "search-placeholder": "Search...",
"tag-filter": "Tag Filter"
},
"settings": {
@@ -218,6 +218,16 @@
"test-webhooks": "測試Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "下方列出的網址將在預定日期接收到有關用餐計劃的食譜資料。Webhooks將在{ time }執行",
"webhook-url": "Webhook網址"
+ },
+ "toolbox": {
+ "toolbox": "Toolbox",
+ "new-name": "New Name",
+ "recipes-effected": "Recipes Effected",
+ "title-case-all": "Title Case All",
+ "no-unused-items": "No Unused Items",
+ "remove-unused": "Remove Unused",
+ "assign-all": "Assign All",
+ "bulk-assign": "Bulk Assign"
}
},
"user": {
@@ -266,4 +276,4 @@
"webhook-time": "Webhook Time",
"webhooks-enabled": "Webhooks Enabled"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue
new file mode 100644
index 000000000..f04b11334
--- /dev/null
+++ b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/BulkAssign.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("general.cancel") }}
+
+
+
+ {{ $t("settings.toolbox.assign-all") }}
+
+
+
+
+
+
+
+
+
+ {{ $t("settings.toolbox.bulk-assign") }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue
new file mode 100644
index 000000000..12554c1a1
--- /dev/null
+++ b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/RemoveUnused.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+ {{ $t("settings.toolbox.no-unused-items") }}
+
+
+
+ {{ $t("general.cancel") }}
+
+
+
+ {{ $t("general.delete") }}
+
+
+
+
+
+ {{ $t("settings.toolbox.remove-unused") }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue
new file mode 100644
index 000000000..4215b9116
--- /dev/null
+++ b/frontend/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+ {{ renameTarget.recipes.length || 0 }}
+ {{ $t("settings.toolbox.recipes-effected") }}
+
+
+
+
+
+
+
+
+ {{ $t("general.create") }}
+
+
+
+
+
+
+ {{ $t("settings.toolbox.title-case-all") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ Edit
+
+ Delete
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/Admin/ToolBox/index.vue b/frontend/src/pages/Admin/ToolBox/index.vue
new file mode 100644
index 000000000..a65bc552f
--- /dev/null
+++ b/frontend/src/pages/Admin/ToolBox/index.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ {{ $t("recipe.categories") }}
+ mdi-tag-multiple-outline
+
+
+
+ {{ $t("recipe.tags") }}
+ mdi-tag-multiple-outline
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue
index f44888452..1aaab2c2b 100644
--- a/frontend/src/pages/HomePage.vue
+++ b/frontend/src/pages/HomePage.vue
@@ -38,8 +38,8 @@ export default {
return this.$store.getters.getSiteSettings;
},
recentRecipes() {
- let recipes = this.$store.getters.getRecentRecipes;
- return recipes.sort((a, b) => (a.dateAdded > b.dateAdded ? -1 : 1));
+ return this.$store.getters.getRecentRecipes;
+
},
},
async mounted() {
diff --git a/frontend/src/pages/Recipes/AllRecipes.vue b/frontend/src/pages/Recipes/AllRecipes.vue
index ebcf39eae..e516f623c 100644
--- a/frontend/src/pages/Recipes/AllRecipes.vue
+++ b/frontend/src/pages/Recipes/AllRecipes.vue
@@ -1,5 +1,10 @@
+
{
diff --git a/frontend/src/routes/admin.js b/frontend/src/routes/admin.js
index 5b51907f0..0c2a60428 100644
--- a/frontend/src/routes/admin.js
+++ b/frontend/src/routes/admin.js
@@ -7,9 +7,10 @@ import Profile from "@/pages/Admin/Profile";
import ManageUsers from "@/pages/Admin/ManageUsers";
import Settings from "@/pages/Admin/Settings";
import About from "@/pages/Admin/About";
+import ToolBox from "@/pages/Admin/ToolBox";
import { store } from "../store";
-export const adminRoutes = {
+export const adminRoutes = {
path: "/admin",
component: Admin,
beforeEnter: (to, _from, next) => {
@@ -72,6 +73,13 @@ export const adminRoutes = {
title: "settings.site-settings",
},
},
+ {
+ path: "toolbox",
+ component: ToolBox,
+ meta: {
+ title: "settings.toolbox.toolbox",
+ },
+ },
{
path: "about",
component: About,
diff --git a/frontend/src/routes/index.js b/frontend/src/routes/index.js
index f5cc21e31..1ed95dc7b 100644
--- a/frontend/src/routes/index.js
+++ b/frontend/src/routes/index.js
@@ -4,12 +4,12 @@ import { authRoutes } from "./auth";
import { recipeRoutes } from "./recipes";
import { mealRoutes } from "./meal";
import { generalRoutes } from "./general";
-import { store } from "../store";
+import { store } from "@/store";
import VueRouter from "vue-router";
import VueI18n from "@/i18n";
import Vuetify from "@/plugins/vuetify";
import Vue from "vue";
-import i18n from '@/i18n.js';
+import i18n from "@/i18n.js";
export const routes = [
...generalRoutes,
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index 627a0eee8..618e01507 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -5,6 +5,7 @@ import createPersistedState from "vuex-persistedstate";
import userSettings from "./modules/userSettings";
import language from "./modules/language";
import siteSettings from "./modules/siteSettings";
+import recipes from "./modules/recipes";
import groups from "./modules/groups";
Vue.use(Vuex);
@@ -20,6 +21,7 @@ const store = new Vuex.Store({
language,
siteSettings,
groups,
+ recipes,
},
state: {
// All Recipe Data Store
@@ -35,9 +37,6 @@ const store = new Vuex.Store({
},
mutations: {
- setRecentRecipes(state, payload) {
- state.recentRecipes = payload;
- },
setMealPlanCategories(state, payload) {
state.mealPlanCategories = payload;
},
@@ -53,18 +52,6 @@ const store = new Vuex.Store({
},
actions: {
- async requestRecentRecipes({ getters }) {
- const payload = await api.recipes.allSummary(0, 30);
- const recent = getters.getRecentRecipes;
- if (recent.length >= 30) return;
- this.commit("setRecentRecipes", payload);
- },
- async requestAllRecipes({ getters }) {
- const recent = getters.getRecentRecipes;
- const start = recent.length + 1;
- const payload = await api.recipes.allSummary(start, 9999);
- this.commit("setRecentRecipes", [...recent, ...payload]);
- },
async requestCategories({ commit }) {
const categories = await api.categories.getAll();
commit("setAllCategories", categories);
@@ -80,7 +67,6 @@ const store = new Vuex.Store({
},
getters: {
- getRecentRecipes: state => state.recentRecipes,
getMealPlanCategories: state => state.mealPlanCategories,
getAllCategories: state =>
state.allCategories.sort((a, b) => (a.slug > b.slug ? 1 : -1)),
diff --git a/frontend/src/store/modules/recipes.js b/frontend/src/store/modules/recipes.js
new file mode 100644
index 000000000..bcec72447
--- /dev/null
+++ b/frontend/src/store/modules/recipes.js
@@ -0,0 +1,73 @@
+import { api } from "@/api";
+
+const state = {
+ recentRecipes: [],
+ allRecipes: [],
+};
+
+const mutations = {
+ setRecentRecipes(state, payload) {
+ state.recentRecipes = payload;
+ },
+ patchRecentRecipes(state, payload) {
+ if (state.recentRecipes[payload.id]) {
+ state.recentRecipes[payload.id] = payload;
+ }
+ },
+ dropRecentRecipes(state, payload) {
+ if (state.recentRecipes[payload.id]) {
+ delete state.recentRecipes[payload.id];
+ }
+ },
+ setAllRecipes(state, payload) {
+ state.allRecipes = payload;
+ },
+ patchAllRecipes(state, payload) {
+ state.allRecipes[payload.id] = payload;
+ },
+ dropAllRecipes(state, payload) {
+ if (state.allRecipes[payload.id]) {
+ delete state.allRecipes[payload.id];
+ }
+ },
+};
+
+const actions = {
+ async requestRecentRecipes() {
+ const payload = await api.recipes.allSummary(0, 30);
+ payload.sort((a, b) => (a.dateAdded > b.dateAdded ? -1 : 1));
+ console.log(payload);
+ const hash = Object.fromEntries(payload.map(e => [e.id, e]));
+ this.commit("setRecentRecipes", hash);
+ },
+ async requestAllRecipes({ getters }) {
+ const all = getters.getAllRecipes;
+ const payload = await api.recipes.allSummary(all.length, 9999);
+ const hash = Object.fromEntries([...all, ...payload].map(e => [e.id, e]));
+ console.log(hash);
+
+ this.commit("setAllRecipes", hash);
+ },
+ patchRecipe({ commit }, payload) {
+ commit("patchAllRecipes", payload);
+ commit("patchRecentRecipes", payload);
+ },
+ dropRecipe({ commit }, payload) {
+ commit("dropAllRecipes", payload);
+ commit("dropRecentRecipes", payload);
+ },
+};
+
+const getters = {
+ getAllRecipes: state => Object.values(state.allRecipes),
+ getAllRecipesHash: state => state.allRecipes,
+ getRecentRecipes: state => Object.values(state.recentRecipes),
+ getRecentRecipesHash: state => state.recentRecipes,
+};
+
+export default {
+ state,
+ mutations,
+ actions,
+ getters,
+};
diff --git a/frontend/src/store/modules/siteSettings.js b/frontend/src/store/modules/siteSettings.js
index 11591a079..9bf647397 100644
--- a/frontend/src/store/modules/siteSettings.js
+++ b/frontend/src/store/modules/siteSettings.js
@@ -29,10 +29,10 @@ const actions = {
let settings = await api.siteSettings.get();
commit("setSettings", settings);
},
- async requestCustomPages({commit }) {
- const customPages = await api.siteSettings.getPages()
- commit("setCustomPages", customPages)
- }
+ async requestCustomPages({ commit }) {
+ const customPages = await api.siteSettings.getPages();
+ commit("setCustomPages", customPages);
+ },
};
const getters = {
diff --git a/mealie/db/database.py b/mealie/db/database.py
index 69e3830d1..b724006b7 100644
--- a/mealie/db/database.py
+++ b/mealie/db/database.py
@@ -1,3 +1,5 @@
+from logging import getLogger
+
from mealie.db.db_base import BaseDocument
from mealie.db.models.group import Group
from mealie.db.models.mealplan import MealPlanModel
@@ -16,12 +18,13 @@ from mealie.schema.theme import SiteTheme
from mealie.schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session
+logger = getLogger()
+
class _Recipes(BaseDocument):
def __init__(self) -> None:
self.primary_key = "slug"
self.sql_model: RecipeModel = RecipeModel
- self.orm_mode = True
self.schema: Recipe = Recipe
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
@@ -36,23 +39,26 @@ class _Categories(BaseDocument):
def __init__(self) -> None:
self.primary_key = "slug"
self.sql_model = Category
- self.orm_mode = True
self.schema = RecipeCategoryResponse
+ def get_empty(self, session: Session):
+ return session.query(Category).filter(~Category.recipes.any()).all()
+
class _Tags(BaseDocument):
def __init__(self) -> None:
self.primary_key = "slug"
self.sql_model = Tag
- self.orm_mode = True
self.schema = RecipeTagResponse
+ def get_empty(self, session: Session):
+ return session.query(Tag).filter(~Tag.recipes.any()).all()
+
class _Meals(BaseDocument):
def __init__(self) -> None:
self.primary_key = "uid"
self.sql_model = MealPlanModel
- self.orm_mode = True
self.schema = MealPlanInDB
@@ -60,7 +66,6 @@ class _Settings(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = SiteSettings
- self.orm_mode = True
self.schema = SiteSettingsSchema
@@ -68,7 +73,6 @@ class _Themes(BaseDocument):
def __init__(self) -> None:
self.primary_key = "name"
self.sql_model = SiteThemeModel
- self.orm_mode = True
self.schema = SiteTheme
@@ -76,7 +80,6 @@ class _Users(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = User
- self.orm_mode = True
self.schema = UserInDB
def update_password(self, session, id, password: str):
@@ -91,7 +94,6 @@ class _Groups(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = Group
- self.orm_mode = True
self.schema = GroupInDB
def get_meals(self, session: Session, match_value: str, match_key: str = "name") -> list[MealPlanInDB]:
@@ -116,7 +118,6 @@ class _SignUps(BaseDocument):
def __init__(self) -> None:
self.primary_key = "token"
self.sql_model = SignUp
- self.orm_mode = True
self.schema = SignUpOut
@@ -124,7 +125,6 @@ class _CustomPages(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = CustomPage
- self.orm_mode = True
self.schema = CustomPageOut
diff --git a/mealie/db/db_base.py b/mealie/db/db_base.py
index 8d6344fc8..ede800379 100644
--- a/mealie/db/db_base.py
+++ b/mealie/db/db_base.py
@@ -12,7 +12,6 @@ class BaseDocument:
self.primary_key: str
self.store: str
self.sql_model: SqlAlchemyBase
- self.orm_mode = False
self.schema: BaseModel
# TODO: Improve Get All Query Functionality
@@ -138,3 +137,4 @@ class BaseDocument:
session.delete(result)
session.commit()
+
diff --git a/mealie/db/models/recipe/category.py b/mealie/db/models/recipe/category.py
index 7a69044f6..ffaa48638 100644
--- a/mealie/db/models/recipe/category.py
+++ b/mealie/db/models/recipe/category.py
@@ -11,28 +11,28 @@ site_settings2categories = sa.Table(
"site_settings2categoories",
SqlAlchemyBase.metadata,
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
- sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
+ sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
)
group2categories = sa.Table(
"group2categories",
SqlAlchemyBase.metadata,
sa.Column("group_id", sa.Integer, sa.ForeignKey("groups.id")),
- sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
+ sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
)
recipes2categories = sa.Table(
"recipes2categories",
SqlAlchemyBase.metadata,
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
- sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
+ sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
)
custom_pages2categories = sa.Table(
"custom_pages2categories",
SqlAlchemyBase.metadata,
sa.Column("custom_page_id", sa.Integer, sa.ForeignKey("custom_pages.id")),
- sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
+ sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
)
@@ -52,6 +52,9 @@ class Category(SqlAlchemyBase):
self.name = name.strip()
self.slug = slugify(name)
+ def update(self, name, session=None) -> None:
+ self.__init__(name, session)
+
@staticmethod
def get_ref(session, slug: str):
return session.query(Category).filter(Category.slug == slug).one()
diff --git a/mealie/db/models/recipe/recipe.py b/mealie/db/models/recipe/recipe.py
index 28e512db0..3127a840b 100644
--- a/mealie/db/models/recipe/recipe.py
+++ b/mealie/db/models/recipe/recipe.py
@@ -86,6 +86,8 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
rating: int = None,
orgURL: str = None,
extras: dict = None,
+ *args,
+ **kwargs
) -> None:
self.name = name
self.description = description
@@ -139,6 +141,8 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
rating: int = None,
orgURL: str = None,
extras: dict = None,
+ *args,
+ **kwargs
):
"""Updated a database entry by removing nested rows and rebuilds the row through the __init__ functions"""
diff --git a/mealie/db/models/recipe/tag.py b/mealie/db/models/recipe/tag.py
index d0cc0d39f..435f22ba4 100644
--- a/mealie/db/models/recipe/tag.py
+++ b/mealie/db/models/recipe/tag.py
@@ -11,7 +11,7 @@ recipes2tags = sa.Table(
"recipes2tags",
SqlAlchemyBase.metadata,
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
- sa.Column("tag_slug", sa.Integer, sa.ForeignKey("tags.slug")),
+ sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")),
)
@@ -31,6 +31,9 @@ class Tag(SqlAlchemyBase):
self.name = name.strip()
self.slug = slugify(self.name)
+ def update(self, name, session=None) -> None:
+ self.__init__(name, session)
+
@staticmethod
def create_if_not_exist(session, name: str = None):
test_slug = slugify(name)
diff --git a/mealie/routes/recipe/category_routes.py b/mealie/routes/recipe/category_routes.py
index e9270c07f..db7701024 100644
--- a/mealie/routes/recipe/category_routes.py
+++ b/mealie/routes/recipe/category_routes.py
@@ -17,6 +17,18 @@ async def get_all_recipe_categories(session: Session = Depends(generate_session)
return db.categories.get_all_limit_columns(session, ["slug", "name"])
+@router.get("/empty")
+def get_empty_categories(session: Session = Depends(generate_session)):
+ """ Returns a list of categories that do not contain any recipes"""
+ return db.categories.get_empty(session)
+
+
+@router.get("/{category}", response_model=RecipeCategoryResponse)
+def get_all_recipes_by_category(category: str, session: Session = Depends(generate_session)):
+ """ Returns a list of recipes associated with the provided category. """
+ return db.categories.get(session, category)
+
+
@router.post("")
async def create_recipe_category(
category: CategoryIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
@@ -26,10 +38,16 @@ async def create_recipe_category(
return db.categories.create(session, category.dict())
-@router.get("/{category}", response_model=RecipeCategoryResponse)
-def get_all_recipes_by_category(category: str, session: Session = Depends(generate_session)):
- """ Returns a list of recipes associated with the provided category. """
- return db.categories.get(session, category)
+@router.put("/{category}", response_model=RecipeCategoryResponse)
+async def update_recipe_category(
+ category: str,
+ new_category: CategoryIn,
+ session: Session = Depends(generate_session),
+ current_user=Depends(get_current_user),
+):
+ """ Updates an existing Tag in the database """
+
+ return db.categories.update(session, category, new_category.dict())
@router.delete("/{category}")
diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py
index 5e3ba9c91..e6d5062f9 100644
--- a/mealie/routes/recipe/recipe_crud_routes.py
+++ b/mealie/routes/recipe/recipe_crud_routes.py
@@ -1,7 +1,5 @@
-import shutil
from enum import Enum
-import requests
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
from fastapi.responses import FileResponse
from mealie.db.database import db
@@ -12,10 +10,7 @@ from mealie.services.image.image import IMG_OPTIONS, delete_image, read_image, r
from mealie.services.scraper.scraper import create_from_url
from sqlalchemy.orm.session import Session
-router = APIRouter(
- prefix="/api/recipes",
- tags=["Recipe CRUD"],
-)
+router = APIRouter(prefix="/api/recipes", tags=["Recipe CRUD"])
@router.post("/create", status_code=201, response_model=str)
@@ -65,7 +60,30 @@ def update_recipe(
if recipe_slug != recipe.slug:
rename_image(original_slug=recipe_slug, new_slug=recipe.slug)
- return recipe.slug
+ return recipe
+
+
+@router.patch("/{recipe_slug}")
+def update_recipe(
+ recipe_slug: str,
+ data: dict,
+ session: Session = Depends(generate_session),
+ current_user=Depends(get_current_user),
+):
+ """ Updates a recipe by existing slug and data. """
+
+ existing_entry: Recipe = db.recipes.get(session, recipe_slug)
+
+ entry_dict = existing_entry.dict()
+ entry_dict.update(data)
+ updated_entry = Recipe(**entry_dict) # ! Surely there's a better way?
+
+ recipe: Recipe = db.recipes.update(session, recipe_slug, updated_entry.dict())
+
+ if recipe_slug != recipe.slug:
+ rename_image(original_slug=recipe_slug, new_slug=recipe.slug)
+
+ return recipe
@router.delete("/{recipe_slug}")
diff --git a/mealie/routes/recipe/tag_routes.py b/mealie/routes/recipe/tag_routes.py
index d7d851b77..5d62715bb 100644
--- a/mealie/routes/recipe/tag_routes.py
+++ b/mealie/routes/recipe/tag_routes.py
@@ -19,6 +19,18 @@ async def get_all_recipe_tags(session: Session = Depends(generate_session)):
return db.tags.get_all_limit_columns(session, ["slug", "name"])
+@router.get("/empty")
+def get_empty_tags(session: Session = Depends(generate_session)):
+ """ Returns a list of tags that do not contain any recipes"""
+ return db.tags.get_empty(session)
+
+
+@router.get("/{tag}", response_model=RecipeTagResponse)
+def get_all_recipes_by_tag(tag: str, session: Session = Depends(generate_session)):
+ """ Returns a list of recipes associated with the provided tag. """
+ return db.tags.get(session, tag)
+
+
@router.post("")
async def create_recipe_tag(
tag: TagIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
@@ -28,10 +40,13 @@ async def create_recipe_tag(
return db.tags.create(session, tag.dict())
-@router.get("/{tag}", response_model=RecipeTagResponse)
-def get_all_recipes_by_tag(tag: str, session: Session = Depends(generate_session)):
- """ Returns a list of recipes associated with the provided tag. """
- return db.tags.get(session, tag)
+@router.put("/{tag}", response_model=RecipeTagResponse)
+async def update_recipe_tag(
+ tag: str, new_tag: TagIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
+):
+ """ Updates an existing Tag in the database """
+
+ return db.tags.update(session, tag, new_tag.dict())
@router.delete("/{tag}")
diff --git a/mealie/schema/category.py b/mealie/schema/category.py
index 5e7f1c842..074e90cc6 100644
--- a/mealie/schema/category.py
+++ b/mealie/schema/category.py
@@ -2,6 +2,7 @@ from typing import List, Optional
from fastapi_camelcase import CamelModel
from mealie.schema.recipe import Recipe
+from pydantic.utils import GetterDict
class CategoryIn(CamelModel):
@@ -15,6 +16,13 @@ class CategoryBase(CategoryIn):
class Config:
orm_mode = True
+ @classmethod
+ def getter_dict(_cls, name_orm):
+ return {
+ **GetterDict(name_orm),
+ "total_recipes": len(name_orm.recipes),
+ }
+
class RecipeCategoryResponse(CategoryBase):
recipes: Optional[List[Recipe]]
diff --git a/mealie/schema/recipe.py b/mealie/schema/recipe.py
index 3ea0b9aee..994255a4b 100644
--- a/mealie/schema/recipe.py
+++ b/mealie/schema/recipe.py
@@ -35,6 +35,7 @@ class Nutrition(BaseModel):
class RecipeSummary(BaseModel):
+ id: Optional[int]
name: str
slug: Optional[str] = ""
image: Optional[Any]
diff --git a/tests/integration_tests/test_import_routes.py b/tests/integration_tests/test_import_routes.py
index 62125da97..f683a2791 100644
--- a/tests/integration_tests/test_import_routes.py
+++ b/tests/integration_tests/test_import_routes.py
@@ -8,18 +8,19 @@ from tests.app_routes import AppRoutes
@pytest.fixture
def backup_data():
return {
- "name": "dev_sample_data_2021-Feb-13.zip",
- "force": False,
+ "name": "test_backup_2021-Apr-27.zip",
+ "force": True,
"recipes": True,
"settings": False, # ! Broken
"themes": True,
"groups": True,
"users": True,
+ "pages": True,
}
def test_import(api_client: TestClient, api_routes: AppRoutes, backup_data, token):
- import_route = api_routes.backups_file_name_import("dev_sample_data_2021-Feb-13.zip")
+ import_route = api_routes.backups_file_name_import("test_backup_2021-Apr-27.zip")
response = api_client.post(import_route, json=backup_data, headers=token)
assert response.status_code == 200
for _, value in json.loads(response.content).items():
diff --git a/tests/integration_tests/test_recipe_routes.py b/tests/integration_tests/test_recipe_routes.py
index 0c886118e..b94d6d5f3 100644
--- a/tests/integration_tests/test_recipe_routes.py
+++ b/tests/integration_tests/test_recipe_routes.py
@@ -60,7 +60,7 @@ def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data,
response = api_client.put(recipe_url, json=recipe, headers=token)
assert response.status_code == 200
- assert json.loads(response.text) == recipe_data.expected_slug
+ assert json.loads(response.text).get("slug") == recipe_data.expected_slug
response = api_client.get(recipe_url)
@@ -84,7 +84,7 @@ def test_rename(api_client: TestClient, api_routes: AppRoutes, recipe_data, toke
response = api_client.put(recipe_url, json=recipe, headers=token)
assert response.status_code == 200
- assert json.loads(response.text) == new_slug
+ assert json.loads(response.text).get("slug") == new_slug
recipe_data.expected_slug = new_slug