Merge branch 'dev' of https://github.com/hay-kot/mealie into dev

This commit is contained in:
hay-kot 2021-05-03 20:40:42 -08:00
commit 2fc44018ec
118 changed files with 1770 additions and 421 deletions

View File

@ -10,8 +10,8 @@
encode gzip encode gzip
uri strip_suffix / uri strip_suffix /
handle_path /api/recipes/image/* { handle_path /api/recipes/media/* {
root * /app/data/img/ root * /app/data/recipes/
file_server file_server
} }

View File

@ -57,14 +57,16 @@ Mealie is a self hosted recipe manager and meal planner with a RestAPI backend a
## Key Features ## Key Features
- 🔍 Fuzzy search - 🔍 Fuzzy search
- 🏷️ Tag recipes with categories or tags to flexible sorting - 🏷️ Tag recipes with categories or tags for flexible sorting
- 🕸 Import recipes from around the web by URL - 🕸 Import recipes from around the web by URL
- 💪 Powerful bulk Category/Tag assignment
- 📱 Beautiful Mobile Views - 📱 Beautiful Mobile Views
- 📆 Create Meal Plans - 📆 Create Meal Plans
- 🛒 Generate shopping lists - 🛒 Generate shopping lists
- 🐳 Easy setup with Docker - 🐳 Easy setup with Docker
- 🎨 Customize your interface with color themes layouts - 🎨 Customize your interface with color themes
- 💾 Export all your data in any format with Jinja2 Templates, with easy data restoration from the user interface. - 💾 Export all your data in any format with Jinja2 Templates
- 🔒 Keep your data safe with automated backup and easy restore options
- 🌍 localized in many languages - 🌍 localized in many languages
- Plus tons more! - Plus tons more!
- Flexible API - Flexible API

View File

@ -11,6 +11,9 @@
#### Database #### Database
Database version has been bumped from v0.4.x -> v0.5.0. You will need to export and import your data. Database version has been bumped from v0.4.x -> v0.5.0. You will need to export and import your data.
#### Image Directory
the /data/img directory has been depreciated. All images are now stored in the /recipes/{slug}/image directory. Images should be migrated automatically, but you may experience issues related to this change.
## Bug Fixes ## Bug Fixes
- Fixed #332 - Language settings are saved for one browser - Fixed #332 - Language settings are saved for one browser
- Fixes #281 - Slow Handling of Large Sets of Recipes - Fixes #281 - Slow Handling of Large Sets of Recipes

59
frontend/src/api/about.js Normal file
View File

@ -0,0 +1,59 @@
import { baseURL } from "./api-utils";
import { apiReq } from "./api-utils";
const prefix = baseURL + "about";
const aboutURLs = {
version: `${prefix}/version`,
debug: `${prefix}`,
lastRecipe: `${prefix}/last-recipe-json`,
demo: `${prefix}/is-demo`,
log: num => `${prefix}/log/${num}`,
statistics: `${prefix}/statistics`,
events: `${prefix}/events`,
event: id => `${prefix}/events/${id}`,
};
export const aboutAPI = {
async getEvents() {
const resposne = await apiReq.get(aboutURLs.events);
return resposne.data;
},
async deleteEvent(id) {
const resposne = await apiReq.delete(aboutURLs.event(id));
return resposne.data;
},
async deleteAllEvents() {
const resposne = await apiReq.delete(aboutURLs.events);
return resposne.data;
},
// async getAppInfo() {
// const response = await apiReq.get(aboutURLs.version);
// return response.data;
// },
// async getDebugInfo() {
// const response = await apiReq.get(aboutURLs.debug);
// return response.data;
// },
// async getLogText(num) {
// const response = await apiReq.get(aboutURLs.log(num));
// return response.data;
// },
// async getLastJson() {
// const response = await apiReq.get(aboutURLs.lastRecipe);
// return response.data;
// },
// async getIsDemo() {
// const response = await apiReq.get(aboutURLs.demo);
// return response.data;
// },
// async getStatistics() {
// const response = await apiReq.get(aboutURLs.statistics);
// return response.data;
// },
};

View File

@ -11,6 +11,7 @@ import { userAPI } from "./users";
import { signupAPI } from "./signUps"; import { signupAPI } from "./signUps";
import { groupAPI } from "./groups"; import { groupAPI } from "./groups";
import { siteSettingsAPI } from "./siteSettings"; import { siteSettingsAPI } from "./siteSettings";
import { aboutAPI } from "./about";
/** /**
* The main object namespace for interacting with the backend database * The main object namespace for interacting with the backend database
@ -30,4 +31,5 @@ export const api = {
users: userAPI, users: userAPI,
signUps: signupAPI, signUps: signupAPI,
groups: groupAPI, groups: groupAPI,
about: aboutAPI,
}; };

View File

@ -8,11 +8,13 @@ const debugURLs = {
debug: `${prefix}`, debug: `${prefix}`,
lastRecipe: `${prefix}/last-recipe-json`, lastRecipe: `${prefix}/last-recipe-json`,
demo: `${prefix}/is-demo`, demo: `${prefix}/is-demo`,
log: num => `${prefix}/log/${num}`,
statistics: `${prefix}/statistics`,
}; };
export const metaAPI = { export const metaAPI = {
async getAppInfo() { async getAppInfo() {
let response = await apiReq.get(debugURLs.version); const response = await apiReq.get(debugURLs.version);
return response.data; return response.data;
}, },
@ -21,13 +23,23 @@ export const metaAPI = {
return response.data; return response.data;
}, },
async getLogText(num) {
const response = await apiReq.get(debugURLs.log(num));
return response.data;
},
async getLastJson() { async getLastJson() {
let response = await apiReq.get(debugURLs.lastRecipe); const response = await apiReq.get(debugURLs.lastRecipe);
return response.data; return response.data;
}, },
async getIsDemo() { async getIsDemo() {
let response = await apiReq.get(debugURLs.demo); const response = await apiReq.get(debugURLs.demo);
return response.data;
},
async getStatistics() {
const response = await apiReq.get(debugURLs.statistics);
return response.data; return response.data;
}, },
}; };

View File

@ -14,9 +14,9 @@ const recipeURLs = {
recipe: slug => prefix + slug, recipe: slug => prefix + slug,
update: slug => prefix + slug, update: slug => prefix + slug,
delete: slug => prefix + slug, delete: slug => prefix + slug,
createAsset: slug => `${prefix}media/${slug}/assets`,
recipeImage: slug => `${prefix}${slug}/image`, recipeImage: slug => `${prefix}${slug}/image`,
updateImage: slug => `${prefix}${slug}/image`, updateImage: slug => `${prefix}${slug}/image`,
createAsset: slug => `${prefix}${slug}/asset`,
}; };
export const recipeAPI = { export const recipeAPI = {
@ -84,7 +84,7 @@ export const recipeAPI = {
fd.append("extension", fileObject.name.split(".").pop()); fd.append("extension", fileObject.name.split(".").pop());
fd.append("name", name); fd.append("name", name);
fd.append("icon", icon); fd.append("icon", icon);
let response = apiReq.post(recipeURLs.createAsset(recipeSlug), fd); const response = apiReq.post(recipeURLs.createAsset(recipeSlug), fd);
return response; return response;
}, },
@ -135,14 +135,14 @@ export const recipeAPI = {
}, },
recipeImage(recipeSlug) { recipeImage(recipeSlug) {
return `/api/recipes/image/${recipeSlug}/original.webp`; return `/api/recipes/media/${recipeSlug}/image/original.webp`;
}, },
recipeSmallImage(recipeSlug) { recipeSmallImage(recipeSlug) {
return `/api/recipes/image/${recipeSlug}/min-original.webp`; return `/api/recipes/media/${recipeSlug}/image/min-original.webp`;
}, },
recipeTinyImage(recipeSlug) { recipeTinyImage(recipeSlug) {
return `/api/recipes/image/${recipeSlug}/tiny-original.webp`; return `/api/recipes/media/${recipeSlug}/image/tiny-original.webp`;
}, },
}; };

View File

@ -18,15 +18,20 @@
v-if="!edit" v-if="!edit"
color="primary" color="primary"
icon icon
:href="`/api/recipes/${slug}/asset?file_name=${item.fileName}`" :href="`/api/recipes/media/${slug}/assets/${item.fileName}`"
target="_blank" target="_blank"
top top
> >
<v-icon> mdi-download</v-icon> <v-icon> mdi-download</v-icon>
</v-btn> </v-btn>
<v-btn v-else color="error" icon @click="deleteAsset(i)" top> <div v-else>
<v-btn color="error" icon @click="deleteAsset(i)" top>
<v-icon>mdi-delete</v-icon> <v-icon>mdi-delete</v-icon>
</v-btn> </v-btn>
<v-btn color="primary" icon @click="copyLink(item.name, item.fileName)" top>
<v-icon>mdi-content-copy</v-icon>
</v-btn>
</div>
</v-list-item-action> </v-list-item-action>
</v-list-item> </v-list-item>
</v-list> </v-list>
@ -107,6 +112,11 @@ export default {
], ],
}; };
}, },
computed: {
baseURL() {
return window.location.origin;
},
},
methods: { methods: {
setFileObject(obj) { setFileObject(obj) {
this.fileObject = obj; this.fileObject = obj;
@ -124,6 +134,13 @@ export default {
deleteAsset(index) { deleteAsset(index) {
this.value.splice(index, 1); this.value.splice(index, 1);
}, },
copyLink(name, fileName) {
const copyText = `![${name}](${this.baseURL}/api/recipes/media/${this.slug}/assets/${fileName})`;
navigator.clipboard.writeText(copyText).then(
() => console.log("Copied", copyText),
() => console.log("Copied Failed", copyText)
);
},
}, },
}; };
</script> </script>

View File

@ -27,16 +27,27 @@
<v-row> <v-row>
<v-col cols="12" sm="12" md="4" lg="4"> <v-col cols="12" sm="12" md="4" lg="4">
<Ingredients :edit="true" v-model="value.recipeIngredient" /> <Ingredients :edit="true" v-model="value.recipeIngredient" />
<v-card class="mt-6">
<h2 class="mt-6">{{ $t("recipe.categories") }}</h2> <v-card-title class="py-2">
{{ $t("recipe.categories") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<CategoryTagSelector <CategoryTagSelector
:return-object="false" :return-object="false"
v-model="value.recipeCategory" v-model="value.recipeCategory"
:show-add="true" :show-add="true"
:show-label="false" :show-label="false"
/> />
</v-card-text>
</v-card>
<h2 class="mt-4">{{ $t("tag.tags") }}</h2> <v-card class="mt-2">
<v-card-title class="py-2">
{{ $t("tag.tags") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<CategoryTagSelector <CategoryTagSelector
:return-object="false" :return-object="false"
v-model="value.tags" v-model="value.tags"
@ -44,6 +55,8 @@
:tag-selector="true" :tag-selector="true"
:show-label="false" :show-label="false"
/> />
</v-card-text>
</v-card>
<Nutrition v-model="value.nutrition" :edit="true" /> <Nutrition v-model="value.nutrition" :edit="true" />
<Assets v-model="value.assets" :edit="true" :slug="value.slug" /> <Assets v-model="value.assets" :edit="true" :slug="value.slug" />
<ExtrasEditor :extras="value.extras" @save="saveExtras" /> <ExtrasEditor :extras="value.extras" @save="saveExtras" />

View File

@ -1,14 +1,14 @@
<template> <template>
<div> <div>
<v-card-title class="headline"> <v-card-title class="headline">
{{ name }} {{ recipe.name }}
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<vue-markdown :source="description"> </vue-markdown> <vue-markdown :source="recipe.description"> </vue-markdown>
<v-row dense disabled> <v-row dense disabled>
<v-col> <v-col>
<v-btn <v-btn
v-if="yields" v-if="recipe.yields"
dense dense
small small
:hover="false" :hover="false"
@ -21,59 +21,59 @@
{{ yields }} {{ yields }}
</v-btn> </v-btn>
</v-col> </v-col>
<Rating :value="rating" :name="name" :slug="slug" /> <Rating :value="recipe.rating" :name="recipe.name" :slug="recipe.slug" />
</v-row> </v-row>
<v-row> <v-row>
<v-col cols="12" sm="12" md="4" lg="4"> <v-col cols="12" sm="12" md="4" lg="4">
<Ingredients :value="ingredients" :edit="false" /> <Ingredients :value="recipe.recipeIngredient" :edit="false" />
<div v-if="medium"> <div v-if="medium">
<v-card class="mt-2" v-if="categories.length > 0"> <v-card class="mt-2" v-if="recipe.recipeCategory.length > 0">
<v-card-title class="py-2"> <v-card-title class="py-2">
{{ $t("recipe.categories") }} {{ $t("recipe.categories") }}
</v-card-title> </v-card-title>
<v-divider class="mx-2"></v-divider> <v-divider class="mx-2"></v-divider>
<v-card-text> <v-card-text>
<RecipeChips :items="categories" /> <RecipeChips :items="recipe.recipeCategory" />
</v-card-text> </v-card-text>
</v-card> </v-card>
<v-card class="mt-2" v-if="tags.length > 0"> <v-card class="mt-2" v-if="recipe.tags.length > 0">
<v-card-title class="py-2"> <v-card-title class="py-2">
{{ $t("tag.tags") }} {{ $t("tag.tags") }}
</v-card-title> </v-card-title>
<v-divider class="mx-2"></v-divider> <v-divider class="mx-2"></v-divider>
<v-card-text> <v-card-text>
<RecipeChips :items="tags" :isCategory="false" /> <RecipeChips :items="recipe.tags" :isCategory="false" />
</v-card-text> </v-card-text>
</v-card> </v-card>
<Nutrition :value="nutrition" :edit="false" /> <Nutrition v-if="recipe.settings.showNutrition" :value="recipe.nutrition" :edit="false" />
<Assets :value="assets" :edit="false" :slug="slug" /> <Assets v-if="recipe.settings.showAssets" :value="recipe.assets" :edit="false" :slug="recipe.slug" />
</div> </div>
</v-col> </v-col>
<v-divider v-if="medium" class="my-divider" :vertical="true"></v-divider> <v-divider v-if="medium" class="my-divider" :vertical="true"></v-divider>
<v-col cols="12" sm="12" md="8" lg="8"> <v-col cols="12" sm="12" md="8" lg="8">
<Instructions :value="instructions" :edit="false" /> <Instructions :value="recipe.recipeInstructions" :edit="false" />
<Notes :value="notes" :edit="false" /> <Notes :value="recipe.notes" :edit="false" />
</v-col> </v-col>
</v-row> </v-row>
<div v-if="!medium"> <div v-if="!medium">
<RecipeChips :title="$t('recipe.categories')" :items="categories" /> <RecipeChips :title="$t('recipe.categories')" :items="recipe.recipeCategory" />
<RecipeChips :title="$t('tag.tags')" :items="tags" /> <RecipeChips :title="$t('tag.tags')" :items="recipe.tags" />
<Nutrition :value="nutrition" :edit="false" /> <Nutrition v-if="recipe.settings.showNutrition" :value="recipe.nutrition" :edit="false" />
<Assets :value="assets" :edit="false" :slug="slug" /> <Assets v-if="recipe.settings.showAssets" :value="recipe.assets" :edit="false" :slug="recipe.slug" />
</div> </div>
<v-row class="mt-2 mb-1"> <v-row class="mt-2 mb-1">
<v-col></v-col> <v-col></v-col>
<v-btn <v-btn
v-if="orgURL" v-if="recipe.orgURL"
dense dense
small small
:hover="false" :hover="false"
type="label" type="label"
:ripple="false" :ripple="false"
elevation="0" elevation="0"
:href="orgURL" :href="recipe.orgURL"
color="secondary darken-1" color="secondary darken-1"
target="_blank" target="_blank"
class="rounded-sm mr-4" class="rounded-sm mr-4"
@ -107,19 +107,7 @@ export default {
Rating, Rating,
}, },
props: { props: {
name: String, recipe: Object,
slug: String,
description: String,
ingredients: Array,
instructions: Array,
categories: Array,
tags: Array,
notes: Array,
rating: Number,
yields: String,
orgURL: String,
nutrition: Object,
assets: Array,
}, },
data() { data() {
return { return {

View File

@ -1,7 +1,11 @@
<template> <template>
<div>
<slot v-bind="{ downloading, downloadFile }">
<v-btn color="accent" text :loading="downloading" @click="downloadFile"> <v-btn color="accent" text :loading="downloading" @click="downloadFile">
{{ showButtonText }} {{ showButtonText }}
</v-btn> </v-btn>
</slot>
</div>
</template> </template>
<script> <script>

View File

@ -1,10 +1,12 @@
<template> <template>
<v-form ref="file"> <v-form ref="file">
<input ref="uploader" class="d-none" type="file" @change="onFileChanged" /> <input ref="uploader" class="d-none" type="file" @change="onFileChanged" />
<slot v-bind="{ isSelecting, onButtonClick }">
<v-btn :loading="isSelecting" @click="onButtonClick" color="accent" :text="textBtn"> <v-btn :loading="isSelecting" @click="onButtonClick" color="accent" :text="textBtn">
<v-icon left> {{ icon }}</v-icon> <v-icon left> {{ icon }}</v-icon>
{{ text ? text : defaultText }} {{ text ? text : defaultText }}
</v-btn> </v-btn>
</slot>
</v-form> </v-form>
</template> </template>

View File

@ -0,0 +1,81 @@
<template>
<div class="mt-2">
<v-card>
<v-card-title class="headline">
Log
<v-spacer></v-spacer>
<v-text-field
class="ml-auto shrink mb-n7"
solo
label="Log Lines"
type="number"
append-icon="mdi-refresh-circle"
v-model="lines"
@click:append="getLogText"
suffix="lines"
single-line
>
</v-text-field>
<TheDownloadBtn :button-text="$t('about.download-log')" download-url="/api/debug/log">
<template v-slot:default="{ downloadFile }">
<v-btn bottom right relative fab icon color="primary" @click="downloadFile">
<v-icon> mdi-download </v-icon>
</v-btn>
</template>
</TheDownloadBtn>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<div v-for="(item, index) in splitText" :key="index" :class="getClass(item)">
{{ item }}
</div>
</v-card-text>
</v-card>
</div>
</template>
<script>
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn";
import { api } from "@/api";
export default {
components: { TheDownloadBtn },
data() {
return {
lines: 200,
text: "",
};
},
mounted() {
this.getLogText();
},
computed: {
splitText() {
return this.text.split("/n");
},
},
methods: {
async getLogText() {
this.text = await api.meta.getLogText(this.lines);
},
getClass(text) {
const isError = text.includes("ERROR:");
if (isError) {
return "log--error";
}
},
},
};
</script>
<style scoped>
.log-text {
background-color: #e0e0e077;
}
.log--error {
color: #ef5350;
}
.line-number {
color: black;
font-weight: bold;
}
</style>

View File

@ -147,6 +147,11 @@ export default {
}, },
adminLinks() { adminLinks() {
return [ return [
{
icon: "mdi-view-dashboard",
to: "/admin/dashboard",
title: this.$t("general.dashboard"),
},
{ {
icon: "mdi-cog", icon: "mdi-cog",
to: "/admin/settings", to: "/admin/settings",

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -9,5 +9,14 @@
"day": "numeric", "day": "numeric",
"weekday": "long", "weekday": "long",
"year": "numeric" "year": "numeric"
},
"long": {
"year": "numeric",
"month": "long",
"day": "numeric",
"weekday": "long",
"hour": "numeric",
"minute": "numeric",
"hour12": true
} }
} }

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Anvend", "apply": "Anvend",
@ -36,6 +37,7 @@
"confirm": "Bekræft", "confirm": "Bekræft",
"create": "Opret", "create": "Opret",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Slet", "delete": "Slet",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Hent", "download": "Hent",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Importere", "import": "Importere",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Mandag", "monday": "Mandag",
"name": "Navn", "name": "Navn",
"no": "Nej", "no": "Nej",

View File

@ -14,10 +14,10 @@
"demo-status": "Demostatus", "demo-status": "Demostatus",
"development": "Entwicklung", "development": "Entwicklung",
"download-log": "Protokoll herunterladen", "download-log": "Protokoll herunterladen",
"download-recipe-json": "Rezept JSON herunterladen", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Keine Demo", "not-demo": "Keine Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite Datei", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Entfernen der Kategorie fehlgeschlagen", "category-deletion-failed": "Entfernen der Kategorie fehlgeschlagen",
"category-filter": "Kategoriefilter", "category-filter": "Kategoriefilter",
"category-update-failed": "Aktualisieren der Kategorie fehlgeschlagen", "category-update-failed": "Aktualisieren der Kategorie fehlgeschlagen",
"category-updated": "Kategorie aktualisiert" "category-updated": "Kategorie aktualisiert",
"category": "Category"
}, },
"general": { "general": {
"apply": "Anwenden", "apply": "Anwenden",
@ -36,6 +37,7 @@
"confirm": "Bestätigen", "confirm": "Bestätigen",
"create": "Erstellen", "create": "Erstellen",
"current-parenthesis": "(Neueste)", "current-parenthesis": "(Neueste)",
"dashboard": "Dashboard",
"delete": "Löschen", "delete": "Löschen",
"disabled": "Deaktiviert", "disabled": "Deaktiviert",
"download": "Herunterladen", "download": "Herunterladen",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Importieren", "import": "Importieren",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Montag", "monday": "Montag",
"name": "Name", "name": "Name",
"no": "Nein", "no": "Nein",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",

View File

@ -1,60 +1,63 @@
{ {
"404": { "404": {
"page-not-found": "404 Page Not Found", "page-not-found": "404 Página No Encontrada",
"take-me-home": "Take me Home" "take-me-home": "Ir a Inicio"
}, },
"about": { "about": {
"about-mealie": "About Mealie", "about-mealie": "Acerca de Mealie",
"api-docs": "API Docs", "api-docs": "Documentación API",
"api-port": "API Port", "api-port": "Puerto API",
"application-mode": "Application Mode", "application-mode": "Modo de Aplicación",
"database-type": "Database Type", "database-type": "Tipo de base de datos",
"default-group": "Default Group", "default-group": "Grupo Predeterminado",
"demo": "Demo", "demo": "Versión Demo",
"demo-status": "Demo Status", "demo-status": "Estado Demo",
"development": "Development", "development": "Desarrollo",
"download-log": "Download Log", "download-log": "Descargar Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "No Demo",
"production": "Production", "production": "Producción",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Versión"
}, },
"category": { "category": {
"category-created": "Category created", "category-created": "Categoría creada",
"category-creation-failed": "Category creation failed", "category-creation-failed": "Error al crear categoría",
"category-deleted": "Category Deleted", "category-deleted": "Categoría Eliminada",
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Error al eliminar categoría",
"category-filter": "Category Filter", "category-filter": "Filtros de Categorías",
"category-update-failed": "Category update failed", "category-update-failed": "Error al actualizar categoría",
"category-updated": "Category updated" "category-updated": "Categoría actualizada",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Aplicar",
"cancel": "Cancel", "cancel": "Cancelar",
"close": "Close", "close": "Cerrar",
"confirm": "Confirm", "confirm": "Confirmar",
"create": "Create", "create": "Crear",
"current-parenthesis": "(Current)", "current-parenthesis": "(Actual)",
"delete": "Delete", "dashboard": "Dashboard",
"disabled": "Disabled", "delete": "Eliminar",
"download": "Download", "disabled": "Deshabilitado",
"edit": "Edit", "download": "Descargar",
"enabled": "Enabled", "edit": "Editar",
"exception": "Exception", "enabled": "Habilitado",
"failed-count": "Failed: {count}", "exception": "Excepción",
"failure-uploading-file": "Failure uploading file", "failed-count": "Error: {count}",
"field-required": "Field Required", "failure-uploading-file": "Error al cargar el archivo",
"file-folder-not-found": "File/folder not found", "field-required": "Campo Requerido",
"file-uploaded": "File uploaded", "file-folder-not-found": "No se ha encontrado el archivo o la carpeta",
"filter": "Filter", "file-uploaded": "Archivo cargado",
"friday": "Friday", "filter": "Filtro",
"get": "Get", "friday": "Viernes",
"groups": "Groups", "get": "Obtener",
"image": "Image", "groups": "Grupos",
"image-upload-failed": "Image upload failed", "image": "Imagen",
"import": "Import", "image-upload-failed": "Error al subir la imagen",
"keyword": "Keyword", "import": "Importar",
"keyword": "Etiqueta",
"link": "Enlace",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",
@ -84,23 +87,23 @@
"url": "URL", "url": "URL",
"users": "Users", "users": "Users",
"wednesday": "Wednesday", "wednesday": "Wednesday",
"yes": "Yes" "yes": "Si"
}, },
"group": { "group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?", "are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",
"cannot-delete-default-group": "Cannot delete default group", "cannot-delete-default-group": "No se puede eliminar el grupo predeterminado",
"cannot-delete-group-with-users": "Cannot delete group with users", "cannot-delete-group-with-users": "No se puede eliminar el grupo con usuarios",
"confirm-group-deletion": "Confirm Group Deletion", "confirm-group-deletion": "Confirmar Eliminación de Grupo",
"create-group": "Create Group", "create-group": "Crear Grupo",
"error-updating-group": "Error updating group", "error-updating-group": "Error al actualizar grupo",
"group": "Group", "group": "Grupo",
"group-deleted": "Group deleted", "group-deleted": "Grupo eliminado",
"group-deletion-failed": "Group deletion failed", "group-deletion-failed": "Error al eliminar grupo",
"group-id-with-value": "Group ID: {groupID}", "group-id-with-value": "ID del Grupo: {groupID}",
"group-name": "Group Name", "group-name": "Nombre del Grupo",
"group-not-found": "Group not found", "group-not-found": "Grupo no encontrado",
"groups": "Groups", "groups": "Grupos",
"groups-can-only-be-set-by-administrators": "Groups can only be set by administrators", "groups-can-only-be-set-by-administrators": "Los grupos sólo pueden ser establecidos por administradores",
"user-group": "User Group", "user-group": "User Group",
"user-group-created": "User Group Created", "user-group-created": "User Group Created",
"user-group-creation-failed": "User Group Creation Failed" "user-group-creation-failed": "User Group Creation Failed"
@ -109,13 +112,13 @@
"create-a-new-meal-plan": "Create a New Meal Plan", "create-a-new-meal-plan": "Create a New Meal Plan",
"dinner-this-week": "Dinner This Week", "dinner-this-week": "Dinner This Week",
"dinner-today": "Dinner Today", "dinner-today": "Dinner Today",
"edit-meal-plan": "Edit Meal Plan", "edit-meal-plan": "Editar Plan de Comida",
"end-date": "End Date", "end-date": "Fecha de Finalización",
"group": "Group (Beta)", "group": "Grupo (Beta)",
"meal-planner": "Meal Planner", "meal-planner": "Plan de Comidas",
"meal-plans": "Meal Plans", "meal-plans": "Planes de Comidas",
"mealplan-created": "Mealplan created", "mealplan-created": "Plan de Comida creado",
"mealplan-creation-failed": "Mealplan creation failed", "mealplan-creation-failed": "Error al crear Plan de Comida",
"mealplan-deleted": "Mealplan Deleted", "mealplan-deleted": "Mealplan Deleted",
"mealplan-deletion-failed": "Mealplan deletion failed", "mealplan-deletion-failed": "Mealplan deletion failed",
"mealplan-update-failed": "Mealplan update failed", "mealplan-update-failed": "Mealplan update failed",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Mode démo", "demo-status": "Mode démo",
"development": "Développement", "development": "Développement",
"download-log": "Télécharger les logs", "download-log": "Télécharger les logs",
"download-recipe-json": "Télécharger le JSON de la recette", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Non", "not-demo": "Non",
"production": "Production", "production": "Production",
"sqlite-file": "Fichier SQLite", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "La suppression de la catégorie a échoué", "category-deletion-failed": "La suppression de la catégorie a échoué",
"category-filter": "Filtre par catégories", "category-filter": "Filtre par catégories",
"category-update-failed": "La mise à jour de la catégorie a échoué", "category-update-failed": "La mise à jour de la catégorie a échoué",
"category-updated": "Catégorie mise à jour" "category-updated": "Catégorie mise à jour",
"category": "Category"
}, },
"general": { "general": {
"apply": "Appliquer", "apply": "Appliquer",
@ -36,6 +37,7 @@
"confirm": "Confirmer", "confirm": "Confirmer",
"create": "Créer", "create": "Créer",
"current-parenthesis": "(Actuel)", "current-parenthesis": "(Actuel)",
"dashboard": "Dashboard",
"delete": "Supprimer", "delete": "Supprimer",
"disabled": "Désactivé", "disabled": "Désactivé",
"download": "Télécharger", "download": "Télécharger",
@ -55,6 +57,7 @@
"image-upload-failed": "Le téléchargement de l'image a échoué", "image-upload-failed": "Le téléchargement de l'image a échoué",
"import": "Importer", "import": "Importer",
"keyword": "Mot-clé", "keyword": "Mot-clé",
"link": "Lien",
"monday": "Lundi", "monday": "Lundi",
"name": "Nom", "name": "Nom",
"no": "Non", "no": "Non",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo status", "demo-status": "Demo status",
"development": "Versies in ontwikkeling", "development": "Versies in ontwikkeling",
"download-log": "Logbestand downloaden", "download-log": "Logbestand downloaden",
"download-recipe-json": "Download Recept JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Niet Demo", "not-demo": "Niet Demo",
"production": "Productie", "production": "Productie",
"sqlite-file": "SQLite bestand", "database-url": "Database URL",
"version": "Versie" "version": "Versie"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Categorie verwijderen mislukt", "category-deletion-failed": "Categorie verwijderen mislukt",
"category-filter": "Categorie Filter", "category-filter": "Categorie Filter",
"category-update-failed": "Categorie bijwerken mislukt", "category-update-failed": "Categorie bijwerken mislukt",
"category-updated": "Categorie bijgewerkt" "category-updated": "Categorie bijgewerkt",
"category": "Category"
}, },
"general": { "general": {
"apply": "Toepassen", "apply": "Toepassen",
@ -36,6 +37,7 @@
"confirm": "Bevestigen", "confirm": "Bevestigen",
"create": "Maak", "create": "Maak",
"current-parenthesis": "(Huidig)", "current-parenthesis": "(Huidig)",
"dashboard": "Dashboard",
"delete": "Verwijderen", "delete": "Verwijderen",
"disabled": "Uitgeschakeld", "disabled": "Uitgeschakeld",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Afbeelding uploaden mislukt", "image-upload-failed": "Afbeelding uploaden mislukt",
"import": "Importeren", "import": "Importeren",
"keyword": "Trefwoord", "keyword": "Trefwoord",
"link": "Link",
"monday": "Maandag", "monday": "Maandag",
"name": "Naam", "name": "Naam",
"no": "Nee", "no": "Nee",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Status demo", "demo-status": "Status demo",
"development": "Wersja testowa", "development": "Wersja testowa",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Nie demo", "not-demo": "Nie demo",
"production": "Produkcyjna", "production": "Produkcyjna",
"sqlite-file": "Plik SQLite'a", "database-url": "Database URL",
"version": "Wersja" "version": "Wersja"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Zastosuj", "apply": "Zastosuj",
@ -36,6 +37,7 @@
"confirm": "Potwierdź", "confirm": "Potwierdź",
"create": "Utwórz", "create": "Utwórz",
"current-parenthesis": "(Bieżący)", "current-parenthesis": "(Bieżący)",
"dashboard": "Dashboard",
"delete": "Usuń", "delete": "Usuń",
"disabled": "Wyłączone", "disabled": "Wyłączone",
"download": "Pobierz", "download": "Pobierz",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Importuj", "import": "Importuj",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Poniedziałek", "monday": "Poniedziałek",
"name": "Nazwa", "name": "Nazwa",
"no": "Nie", "no": "Nie",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirmar", "confirm": "Confirmar",
"create": "Criar", "create": "Criar",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Eliminar", "delete": "Eliminar",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Transferir", "download": "Transferir",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Importar", "import": "Importar",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Nome", "name": "Nome",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Skapa", "create": "Skapa",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Ta bort", "delete": "Ta bort",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Ladda ner", "download": "Ladda ner",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Importera", "import": "Importera",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Namn", "name": "Namn",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "Confirm", "confirm": "Confirm",
"create": "Create", "create": "Create",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled", "disabled": "Disabled",
"download": "Download", "download": "Download",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "Import", "import": "Import",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "Name", "name": "Name",
"no": "No", "no": "No",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "应用", "apply": "应用",
@ -36,6 +37,7 @@
"confirm": "确定", "confirm": "确定",
"create": "创建", "create": "创建",
"current-parenthesis": "(当前)", "current-parenthesis": "(当前)",
"dashboard": "Dashboard",
"delete": "删除", "delete": "删除",
"disabled": "Disabled", "disabled": "Disabled",
"download": "下载", "download": "下载",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "导入", "import": "导入",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "名称", "name": "名称",
"no": "否", "no": "否",

View File

@ -14,10 +14,10 @@
"demo-status": "Demo Status", "demo-status": "Demo Status",
"development": "Development", "development": "Development",
"download-log": "Download Log", "download-log": "Download Log",
"download-recipe-json": "Download Recipe JSON", "download-recipe-json": "Last Scraped JSON",
"not-demo": "Not Demo", "not-demo": "Not Demo",
"production": "Production", "production": "Production",
"sqlite-file": "SQLite File", "database-url": "Database URL",
"version": "Version" "version": "Version"
}, },
"category": { "category": {
@ -27,7 +27,8 @@
"category-deletion-failed": "Category deletion failed", "category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter", "category-filter": "Category Filter",
"category-update-failed": "Category update failed", "category-update-failed": "Category update failed",
"category-updated": "Category updated" "category-updated": "Category updated",
"category": "Category"
}, },
"general": { "general": {
"apply": "Apply", "apply": "Apply",
@ -36,6 +37,7 @@
"confirm": "確定", "confirm": "確定",
"create": "創建", "create": "創建",
"current-parenthesis": "(Current)", "current-parenthesis": "(Current)",
"dashboard": "Dashboard",
"delete": "删除", "delete": "删除",
"disabled": "Disabled", "disabled": "Disabled",
"download": "下载", "download": "下载",
@ -55,6 +57,7 @@
"image-upload-failed": "Image upload failed", "image-upload-failed": "Image upload failed",
"import": "導入", "import": "導入",
"keyword": "Keyword", "keyword": "Keyword",
"link": "Link",
"monday": "Monday", "monday": "Monday",
"name": "名稱", "name": "名稱",
"no": "No", "no": "No",

View File

@ -22,19 +22,26 @@
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<TheDownloadBtn :button-text="$t('about.download-recipe-json')" download-url="/api/debug/last-recipe-json" /> <TheDownloadBtn download-url="/api/debug/last-recipe-json">
<TheDownloadBtn :button-text="$t('about.download-log')" download-url="/api/debug/log" /> <template v-slot:default="{ downloadFile }">
<v-btn color="primary" @click="downloadFile">
<v-icon left> mdi-code-braces </v-icon> {{ $t("about.download-recipe-json") }}
</v-btn>
</template>
</TheDownloadBtn>
</v-card-actions> </v-card-actions>
<v-divider></v-divider> <v-divider></v-divider>
</v-card> </v-card>
<LogCard />
</div> </div>
</template> </template>
<script> <script>
import { api } from "@/api"; import { api } from "@/api";
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn"; import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn";
import LogCard from "@/components/UI/LogCard.vue";
export default { export default {
components: { TheDownloadBtn }, components: { TheDownloadBtn, LogCard },
data() { data() {
return { return {
prettyInfo: [], prettyInfo: [],
@ -79,9 +86,9 @@ export default {
value: debugInfo.dbType, value: debugInfo.dbType,
}, },
{ {
name: this.$t("about.sqlite-file"), name: this.$t("about.database-url"),
icon: "mdi-file-cabinet", icon: "mdi-file-cabinet",
value: debugInfo.sqliteFile, value: debugInfo.dbUrl,
}, },
{ {
name: this.$t("about.default-group"), name: this.$t("about.default-group"),
@ -93,5 +100,3 @@ export default {
}, },
}; };
</script> </script>
<style lang="scss" scoped></style>

View File

@ -19,7 +19,7 @@
<div class="text-truncate"> <div class="text-truncate">
<strong>{{ backup.name }}</strong> <strong>{{ backup.name }}</strong>
</div> </div>
<div class="text-truncate">{{ $d(new Date(backup.date), "medium") }}</div> <div class="text-truncate">{{ $d(Date.parse(backup.date), "medium") }}</div>
</v-col> </v-col>
</v-row> </v-row>
</v-card-text> </v-card-text>

View File

@ -15,7 +15,7 @@
</v-toolbar-items> </v-toolbar-items>
</v-toolbar> </v-toolbar>
<v-card-title> {{ name }} </v-card-title> <v-card-title> {{ name }} </v-card-title>
<v-card-subtitle class="mb-n3"> {{ $d(new Date(date), "medium") }} </v-card-subtitle> <v-card-subtitle class="mb-n3" v-if="date"> {{ $d(new Date(date), "medium") }} </v-card-subtitle>
<v-divider></v-divider> <v-divider></v-divider>
<v-card-text> <v-card-text>

View File

@ -0,0 +1,144 @@
<template>
<div>
<ImportSummaryDialog ref="report" />
<ImportDialog
:name="selectedName"
:date="selectedDate"
ref="import_dialog"
@import="importBackup"
@delete="deleteBackup"
/>
<StatCard icon="mdi-backup-restore" :color="color">
<template v-slot:after-heading>
<div class="ml-auto text-right">
<div class="body-3 grey--text font-weight-light" v-text="'Backups'" />
<h3 class="display-2 font-weight-light text--primary">
<small> {{ total }}</small>
</h3>
</div>
</template>
<div class="d-flex row py-3 justify-end">
<TheUploadBtn url="/api/backups/upload" @uploaded="getAvailableBackups">
<template v-slot="{ isSelecting, onButtonClick }">
<v-btn :loading="isSelecting" class="mx-2" small :color="color" @click="onButtonClick">
<v-icon left> mdi-cloud-upload </v-icon> Upload
</v-btn>
</template>
</TheUploadBtn>
<v-btn :loading="loading" class="mx-2" small :color="color" @click="createBackup">
<v-icon left> mdi-plus </v-icon> Create
</v-btn>
</div>
<template v-slot:bottom>
<v-virtual-scroll height="290" item-height="70" :items="availableBackups">
<template v-slot:default="{ item }">
<v-list-item @click.prevent="openDialog(item)">
<v-list-item-avatar>
<v-icon large dark :color="color">
mdi-backup-restore
</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.name"></v-list-item-title>
<v-list-item-subtitle>
{{ $d(Date.parse(item.date), "medium") }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action class="ml-auto">
<v-btn large icon @click.stop="deleteBackup(item.name)">
<v-icon color="error">mdi-delete</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</template>
</v-virtual-scroll>
</template>
</StatCard>
</div>
</template>
<script>
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
import ImportSummaryDialog from "@/components/ImportSummaryDialog";
import { api } from "@/api";
import StatCard from "./StatCard";
import ImportDialog from "../Backup/ImportDialog";
export default {
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog },
data() {
return {
color: "secondary",
selectedName: "",
selectedDate: "",
loading: false,
events: [],
availableBackups: [],
};
},
computed: {
total() {
return this.availableBackups.length;
},
},
mounted() {
this.getAvailableBackups();
},
methods: {
async getAvailableBackups() {
const response = await api.backups.requestAvailable();
this.availableBackups = response.imports;
console.log(this.availableBackups);
},
async deleteBackup(name) {
this.loading = true;
await api.backups.delete(name);
this.loading = false;
this.getAvailableBackups();
},
openDialog(backup) {
this.selectedDate = backup.date;
this.selectedName = backup.name;
this.$refs.import_dialog.open();
},
async importBackup(data) {
this.loading = true;
const response = await api.backups.import(data.name, data);
if (response) {
const importData = response.data;
this.$refs.report.open(importData);
}
this.loading = false;
},
async createBackup() {
this.loading = true;
let data = {
tag: this.tag,
options: {
recipes: true,
settings: true,
themes: true,
users: true,
groups: true,
},
templates: [],
};
if (await api.backups.create(data)) {
this.getAvailableBackups();
}
this.loading = false;
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,110 @@
<template>
<div>
<StatCard icon="mdi-bell-ring" :color="color">
<template v-slot:after-heading>
<div class="ml-auto text-right">
<div class="body-3 grey--text font-weight-light" v-text="'Events'" />
<h3 class="display-2 font-weight-light text--primary">
<small> {{ total }} </small>
</h3>
</div>
</template>
<div class="d-flex row py-3 justify-end">
<v-btn class="mx-2" small :color="color" @click="deleteAll">
<v-icon left> mdi-notification-clear-all </v-icon> Clear
</v-btn>
</div>
<template v-slot:bottom>
<v-virtual-scroll height="290" item-height="70" :items="events">
<template v-slot:default="{ item }">
<v-list-item>
<v-list-item-avatar>
<v-icon large dark :color="icons[item.category].color">
{{ icons[item.category].icon }}
</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.title"></v-list-item-title>
<v-list-item-subtitle v-text="item.text"></v-list-item-subtitle>
<v-list-item-subtitle>
{{ $d(Date.parse(item.timeStamp), "long") }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action class="ml-auto">
<v-btn large icon @click="deleteEvent(item.id)">
<v-icon color="error">mdi-delete</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</template>
</v-virtual-scroll>
</template>
</StatCard>
</div>
</template>
<script>
import { api } from "@/api";
import StatCard from "./StatCard";
export default {
components: { StatCard },
data() {
return {
color: "secondary",
total: 0,
events: [],
icons: {
general: {
icon: "mdi-information",
color: "info",
},
recipe: {
icon: "mdi-silverware-fork-knife",
color: "primary",
},
backup: {
icon: "mdi-backup-restore",
color: "primary",
},
schedule: {
icon: "mdi-calendar-clock",
color: "primary",
},
migration: {
icon: "mdi-database-import",
color: "primary",
},
signup: {
icon: "mdi-account",
color: "primary",
},
},
};
},
mounted() {
this.getEvents();
},
methods: {
async getEvents() {
const events = await api.about.getEvents();
this.events = events.events;
this.total = events.total;
},
async deleteEvent(id) {
await api.about.deleteEvent(id);
this.getEvents();
},
async deleteAll() {
await api.about.deleteAllEvents();
this.getEvents();
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,100 @@
w<template>
<v-card v-bind="$attrs" :class="classes" class="v-card--material pa-3">
<div class="d-flex grow flex-wrap">
<v-sheet
:color="color"
:max-height="icon ? 90 : undefined"
:width="icon ? 'auto' : '100%'"
elevation="6"
class="text-start v-card--material__heading mb-n6 mt-n10 pa-7"
dark
>
<v-icon v-if="icon" size="40" v-text="icon" />
<div v-if="text" class="headline font-weight-thin" v-text="text" />
</v-sheet>
<div v-if="$slots['after-heading']" class="ml-auto">
<slot name="after-heading" />
</div>
</div>
<slot />
<template v-if="$slots.actions">
<v-divider class="mt-2" />
<v-card-actions class="pb-0">
<slot name="actions" />
</v-card-actions>
</template>
<template v-if="$slots.bottom">
<v-divider class="mt-2" />
<div class="pb-0">
<slot name="bottom" />
</div>
</template>
</v-card>
</template>
<script>
export default {
name: "MaterialCard",
props: {
avatar: {
type: String,
default: "",
},
color: {
type: String,
default: "primary",
},
icon: {
type: String,
default: undefined,
},
image: {
type: Boolean,
default: false,
},
text: {
type: String,
default: "",
},
title: {
type: String,
default: "",
},
},
computed: {
classes() {
return {
"v-card--material--has-heading": this.hasHeading,
};
},
hasHeading() {
return false;
},
hasAltHeading() {
return false;
},
},
};
</script>
<style lang="sass">
.v-card--material
&__avatar
position: relative
top: -64px
margin-bottom: -32px
&__heading
position: relative
top: -40px
transition: .3s ease
z-index: 1
</style>

View File

@ -0,0 +1,119 @@
<template>
<div class="mt-10">
<v-row>
<v-col cols="12" sm="12" md="4">
<StatCard icon="mdi-silverware-fork-knife">
<template v-slot:after-heading>
<div class="ml-auto text-right">
<div class="body-3 grey--text font-weight-light" v-text="'Recipes'" />
<h3 class="display-2 font-weight-light text--primary">
<small> {{ statistics.totalRecipes }}</small>
</h3>
</div>
</template>
<template v-slot:actions>
<div class="d-flex row py-3 justify-space-around">
<v-btn small color="primary" :to="{ path: '/admin/toolbox/', query: { tab: 'organize', filter: 'tag' } }">
<v-icon left> mdi-tag </v-icon> Untagged {{ statistics.untaggedRecipes }}
</v-btn>
<v-btn
small
color="primary"
:to="{ path: '/admin/toolbox/', query: { tab: 'organize', filter: 'category' } }"
>
<v-icon left> mdi-tag </v-icon> Uncategorized {{ statistics.uncategorizedRecipes }}
</v-btn>
</div>
</template>
</StatCard>
</v-col>
<v-col cols="12" sm="12" md="4">
<StatCard icon="mdi-account">
<template v-slot:after-heading>
<div class="ml-auto text-right">
<div class="body-3 grey--text font-weight-light" v-text="'Users'" />
<h3 class="display-2 font-weight-light text--primary">
<small> {{ statistics.totalUsers }}</small>
</h3>
</div>
</template>
<template v-slot:actions>
<div class="ml-auto">
<v-btn color="primary" small to="/admin/manage-users?tab=users">
<v-icon left>mdi-account</v-icon>
Manage Users
</v-btn>
</div>
</template>
</StatCard>
</v-col>
<v-col cols="12" sm="12" md="4">
<StatCard icon="mdi-account-group">
<template v-slot:after-heading>
<div class="ml-auto text-right">
<div class="body-3 grey--text font-weight-light" v-text="'Groups'" />
<h3 class="display-2 font-weight-light text--primary">
<small> {{ statistics.totalGroups }}</small>
</h3>
</div>
</template>
<template v-slot:actions>
<div class="ml-auto">
<v-btn color="primary" small to="/admin/manage-users?tab=groups">
<v-icon left>mdi-account-group</v-icon>
Manage Groups
</v-btn>
</div>
</template>
</StatCard>
</v-col>
</v-row>
<v-row class="mt-10">
<v-col cols="12" sm="12" lg="6">
<EventViewer />
</v-col>
<v-col cols="12" sm="12" lg="6"> <BackupViewer /> </v-col>
</v-row>
</div>
</template>
<script>
import { api } from "@/api";
import StatCard from "./StatCard";
import EventViewer from "./EventViewer";
import BackupViewer from "./BackupViewer";
export default {
components: { StatCard, EventViewer, BackupViewer },
data() {
return {
statistics: {
totalGroups: 0,
totalRecipes: 0,
totalUsers: 0,
uncategorizedRecipes: 0,
untaggedRecipes: 0,
},
};
},
mounted() {
this.getStatistics();
},
methods: {
async getStatistics() {
this.statistics = await api.meta.getStatistics();
},
},
};
</script>
<style>
.grid-style {
flex-grow: inherit;
display: inline-flex;
flex-wrap: wrap;
gap: 10px;
}
</style>

View File

@ -160,10 +160,10 @@ export default {
methods: { methods: {
updateClipboard(newClip) { updateClipboard(newClip) {
navigator.clipboard.writeText(newClip).then( navigator.clipboard.writeText(newClip).then(
function() { () => {
console.log("Copied", newClip); console.log("Copied", newClip);
}, },
function() { () => {
console.log("Copy Failed", newClip); console.log("Copy Failed", newClip);
} }
); );

View File

@ -4,30 +4,30 @@
<v-tabs v-model="tab" background-color="primary" centered dark icons-and-text> <v-tabs v-model="tab" background-color="primary" centered dark icons-and-text>
<v-tabs-slider></v-tabs-slider> <v-tabs-slider></v-tabs-slider>
<v-tab> <v-tab href="#users">
{{ $t("user.users") }} {{ $t("user.users") }}
<v-icon>mdi-account</v-icon> <v-icon>mdi-account</v-icon>
</v-tab> </v-tab>
<v-tab> <v-tab href="#sign-ups">
{{ $t("signup.sign-up-links") }} {{ $t("signup.sign-up-links") }}
<v-icon>mdi-account-plus-outline</v-icon> <v-icon>mdi-account-plus-outline</v-icon>
</v-tab> </v-tab>
<v-tab> <v-tab href="#groups">
{{ $t("group.groups") }} {{ $t("group.groups") }}
<v-icon>mdi-account-group</v-icon> <v-icon>mdi-account-group</v-icon>
</v-tab> </v-tab>
</v-tabs> </v-tabs>
<v-tabs-items v-model="tab"> <v-tabs-items v-model="tab">
<v-tab-item> <v-tab-item value="users">
<TheUserTable /> <TheUserTable />
</v-tab-item> </v-tab-item>
<v-tab-item> <v-tab-item value="sign-ups">
<TheSignUpTable /> <TheSignUpTable />
</v-tab-item> </v-tab-item>
<v-tab-item> <v-tab-item value="groups">
<GroupDashboard /> <GroupDashboard />
</v-tab-item> </v-tab-item>
</v-tabs-items> </v-tabs-items>
@ -42,9 +42,17 @@ import TheSignUpTable from "./TheSignUpTable";
export default { export default {
components: { TheUserTable, GroupDashboard, TheSignUpTable }, components: { TheUserTable, GroupDashboard, TheSignUpTable },
data() { data() {
return { return {};
tab: 0, },
}; computed: {
tab: {
set(tab) {
this.$router.replace({ query: { ...this.$route.query, tab } });
},
get() {
return this.$route.query.tab;
},
},
}, },
mounted() { mounted() {
this.$store.dispatch("requestAllGroups"); this.$store.dispatch("requestAllGroups");

View File

@ -0,0 +1,72 @@
<template>
<v-card outlined class="mt-n1">
<div class="d-flex justify-center align-center pa-2 flex-wrap">
<v-btn-toggle v-model="filter" mandatory color="primary">
<v-btn small value="category">
<v-icon>mdi-tag-multiple</v-icon>
{{ $t("category.category") }}
</v-btn>
<v-btn small value="tag">
<v-icon>mdi-tag-multiple</v-icon>
{{ $t("tag.tags") }}
</v-btn>
</v-btn-toggle>
<v-spacer v-if="!isMobile"> </v-spacer>
<FuseSearchBar :raw-data="allItems" @results="filterItems" :search="searchString">
<v-text-field
v-model="searchString"
clearable
solo
dense
class="mx-2"
hide-details
single-line
:placeholder="$t('search.search')"
prepend-inner-icon="mdi-magnify"
>
</v-text-field>
</FuseSearchBar>
</div>
</v-card>
</template>
<script>
import FuseSearchBar from "@/components/UI/Search/FuseSearchBar";
export default {
components: { FuseSearchBar },
data() {
return {
buttonToggle: 0,
allItems: [],
searchString: "",
searchResults: [],
};
},
computed: {
isMobile() {
return this.$vuetify.breakpoint.name === "xs";
},
isCategory() {
return this.buttonToggle === 0;
},
filter: {
set(filter) {
this.$router.replace({ query: { ...this.$route.query, filter } });
},
get() {
return this.$route.query.filter;
},
},
},
methods: {
filterItems(val) {
this.searchResults = val.map(x => x.item);
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@ -4,20 +4,25 @@
<v-tabs v-model="tab" background-color="primary" centered dark icons-and-text> <v-tabs v-model="tab" background-color="primary" centered dark icons-and-text>
<v-tabs-slider></v-tabs-slider> <v-tabs-slider></v-tabs-slider>
<v-tab> <v-tab href="#category-editor">
{{ $t("recipe.categories") }} {{ $t("recipe.categories") }}
<v-icon>mdi-tag-multiple-outline</v-icon> <v-icon>mdi-tag-multiple-outline</v-icon>
</v-tab> </v-tab>
<v-tab> <v-tab href="#tag-editor">
{{ $t("tag.tags") }} {{ $t("tag.tags") }}
<v-icon>mdi-tag-multiple-outline</v-icon> <v-icon>mdi-tag-multiple-outline</v-icon>
</v-tab> </v-tab>
<v-tab href="#organize">
Organize
<v-icon>mdi-broom</v-icon>
</v-tab>
</v-tabs> </v-tabs>
<v-tabs-items v-model="tab"> <v-tabs-items v-model="tab">
<v-tab-item><CategoryTagEditor :is-tags="false"/></v-tab-item> <v-tab-item value="category-editor"> <CategoryTagEditor :is-tags="false"/></v-tab-item>
<v-tab-item><CategoryTagEditor :is-tags="true" /> </v-tab-item> <v-tab-item value="tag-editor"> <CategoryTagEditor :is-tags="true" /> </v-tab-item>
<v-tab-item value="organize"> <RecipeOrganizer :is-tags="true" /> </v-tab-item>
</v-tabs-items> </v-tabs-items>
</v-card> </v-card>
</div> </div>
@ -25,14 +30,24 @@
<script> <script>
import CategoryTagEditor from "./CategoryTagEditor"; import CategoryTagEditor from "./CategoryTagEditor";
import RecipeOrganizer from "./RecipeOrganizer";
export default { export default {
components: { components: {
CategoryTagEditor, CategoryTagEditor,
RecipeOrganizer,
},
computed: {
tab: {
set(tab) {
this.$router.replace({ query: { ...this.$route.query, tab } });
},
get() {
return this.$route.query.tab;
},
},
}, },
data() { data() {
return { return {};
tab: 0,
};
}, },
}; };
</script> </script>

View File

@ -37,7 +37,6 @@ export default {
return this.$store.getters.getSiteSettings; return this.$store.getters.getSiteSettings;
}, },
recentRecipes() { recentRecipes() {
console.log("Recent Recipes");
return this.$store.getters.getRecentRecipes; return this.$store.getters.getRecentRecipes;
}, },
}, },

View File

@ -25,22 +25,7 @@
class="sticky" class="sticky"
/> />
<RecipeViewer <RecipeViewer v-if="!form" :recipe="recipeDetails" />
v-if="!form"
:name="recipeDetails.name"
:ingredients="recipeDetails.recipeIngredient"
:description="recipeDetails.description"
:instructions="recipeDetails.recipeInstructions"
:tags="recipeDetails.tags"
:categories="recipeDetails.recipeCategory"
:notes="recipeDetails.notes"
:rating="recipeDetails.rating"
:yields="recipeDetails.recipeYield"
:orgURL="recipeDetails.orgURL"
:nutrition="recipeDetails.nutrition"
:assets="recipeDetails.assets"
:slug="recipeDetails.slug"
/>
<VJsoneditor <VJsoneditor
@error="logError()" @error="logError()"
class="mt-10" class="mt-10"

View File

@ -8,6 +8,7 @@ import ManageUsers from "@/pages/Admin/ManageUsers";
import Settings from "@/pages/Admin/Settings"; import Settings from "@/pages/Admin/Settings";
import About from "@/pages/Admin/About"; import About from "@/pages/Admin/About";
import ToolBox from "@/pages/Admin/ToolBox"; import ToolBox from "@/pages/Admin/ToolBox";
import Dashboard from "@/pages/Admin/Dashboard";
import { store } from "../store"; import { store } from "../store";
export const adminRoutes = { export const adminRoutes = {
@ -87,5 +88,12 @@ export const adminRoutes = {
title: "general.about", title: "general.about",
}, },
}, },
{
path: "dashboard",
component: Dashboard,
meta: {
title: "general.dashboard",
},
},
], ],
}; };

View File

@ -4,11 +4,13 @@ from fastapi import FastAPI
from mealie.core import root_logger from mealie.core import root_logger
from mealie.core.config import APP_VERSION, settings from mealie.core.config import APP_VERSION, settings
from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes from mealie.routes import backup_routes, debug_routes, migration_routes, theme_routes, utility_routes
from mealie.routes.about import about_router
from mealie.routes.groups import groups from mealie.routes.groups import groups
from mealie.routes.mealplans import mealplans from mealie.routes.mealplans import mealplans
from mealie.routes.recipe import router as recipe_router from mealie.routes.recipe import router as recipe_router
from mealie.routes.site_settings import all_settings from mealie.routes.site_settings import all_settings
from mealie.routes.users import users from mealie.routes.users import users
from mealie.services.events import create_general_event
logger = root_logger.get_logger() logger = root_logger.get_logger()
@ -31,6 +33,7 @@ def api_routers():
app.include_router(groups.router) app.include_router(groups.router)
# Recipes # Recipes
app.include_router(recipe_router) app.include_router(recipe_router)
app.include_router(about_router)
# Meal Routes # Meal Routes
app.include_router(mealplans.router) app.include_router(mealplans.router)
# Settings Routes # Settings Routes
@ -53,6 +56,7 @@ def system_startup():
logger.info("-----SYSTEM STARTUP----- \n") logger.info("-----SYSTEM STARTUP----- \n")
logger.info("------APP SETTINGS------") logger.info("------APP SETTINGS------")
logger.info(settings.json(indent=4, exclude={"SECRET", "DEFAULT_PASSWORD", "SFTP_PASSWORD", "SFTP_USERNAME"})) logger.info(settings.json(indent=4, exclude={"SECRET", "DEFAULT_PASSWORD", "SFTP_PASSWORD", "SFTP_USERNAME"}))
create_general_event("Application Startup", f"Mealie API started on port {settings.API_PORT}")
def main(): def main():

View File

@ -4,8 +4,8 @@ import sys
from mealie.core.config import DATA_DIR from mealie.core.config import DATA_DIR
LOGGER_FILE = DATA_DIR.joinpath("mealie.log") LOGGER_FILE = DATA_DIR.joinpath("mealie.log")
LOGGER_FORMAT = "%(levelname)s: \t%(message)s"
DATE_FORMAT = "%d-%b-%y %H:%M:%S" DATE_FORMAT = "%d-%b-%y %H:%M:%S"
LOGGER_FORMAT = "%(levelname)s: %(asctime)s \t%(message)s"
logging.basicConfig(level=logging.INFO, format=LOGGER_FORMAT, datefmt="%d-%b-%y %H:%M:%S") logging.basicConfig(level=logging.INFO, format=LOGGER_FORMAT, datefmt="%d-%b-%y %H:%M:%S")
@ -30,6 +30,9 @@ def logger_init() -> logging.Logger:
return logger return logger
root_logger = logger_init()
def get_logger(module=None) -> logging.Logger: def get_logger(module=None) -> logging.Logger:
""" Returns a child logger for mealie """ """ Returns a child logger for mealie """
global root_logger global root_logger
@ -38,6 +41,3 @@ def get_logger(module=None) -> logging.Logger:
return root_logger return root_logger
return root_logger.getChild(module) return root_logger.getChild(module)
root_logger = logger_init()

View File

@ -1,6 +1,7 @@
from logging import getLogger from logging import getLogger
from mealie.db.db_base import BaseDocument from mealie.db.db_base import BaseDocument
from mealie.db.models.event import Event
from mealie.db.models.group import Group from mealie.db.models.group import Group
from mealie.db.models.mealplan import MealPlanModel from mealie.db.models.mealplan import MealPlanModel
from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag
@ -9,6 +10,7 @@ from mealie.db.models.sign_up import SignUp
from mealie.db.models.theme import SiteThemeModel from mealie.db.models.theme import SiteThemeModel
from mealie.db.models.users import User from mealie.db.models.users import User
from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
from mealie.schema.events import Event as EventSchema
from mealie.schema.meal import MealPlanInDB from mealie.schema.meal import MealPlanInDB
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from mealie.schema.settings import CustomPageOut from mealie.schema.settings import CustomPageOut
@ -18,7 +20,6 @@ from mealie.schema.theme import SiteTheme
from mealie.schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
logger = getLogger() logger = getLogger()
@ -35,6 +36,26 @@ class _Recipes(BaseDocument):
return f"{slug}.{extension}" return f"{slug}.{extension}"
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
eff_schema = override_schema or self.schema
if count:
return session.query(self.sql_model).filter(RecipeModel.recipe_category == None).count() # noqa: 711
else:
return [
eff_schema.from_orm(x)
for x in session.query(self.sql_model).filter(RecipeModel.tags == None).all() # noqa: 711
]
def count_untagged(self, session: Session, count=True, override_schema=None) -> int:
eff_schema = override_schema or self.schema
if count:
return session.query(self.sql_model).filter(RecipeModel.tags == None).count() # noqa: 711
else:
return [
eff_schema.from_orm(x)
for x in session.query(self.sql_model).filter(RecipeModel.tags == None).all() # noqa: 711
]
class _Categories(BaseDocument): class _Categories(BaseDocument):
def __init__(self) -> None: def __init__(self) -> None:
@ -110,8 +131,6 @@ class _Groups(BaseDocument):
""" """
group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none() group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none()
# Potentially not needed? column is sorted by SqlAlchemy based on startDate
# return sorted(group.mealplans, key=lambda mealplan: mealplan.startDate)
return group.mealplans return group.mealplans
@ -129,6 +148,13 @@ class _CustomPages(BaseDocument):
self.schema = CustomPageOut self.schema = CustomPageOut
class _Events(BaseDocument):
def __init__(self) -> None:
self.primary_key = "id"
self.sql_model = Event
self.schema = EventSchema
class Database: class Database:
def __init__(self) -> None: def __init__(self) -> None:
self.recipes = _Recipes() self.recipes = _Recipes()
@ -141,6 +167,7 @@ class Database:
self.sign_ups = _SignUps() self.sign_ups = _SignUps()
self.groups = _Groups() self.groups = _Groups()
self.custom_pages = _CustomPages() self.custom_pages = _CustomPages()
self.events = _Events()
db = Database() db = Database()

View File

@ -23,6 +23,14 @@ class BaseDocument:
) -> List[dict]: ) -> List[dict]:
eff_schema = override_schema or self.schema eff_schema = override_schema or self.schema
if order_by:
order_attr = getattr(self.sql_model, str(order_by))
return [
eff_schema.from_orm(x)
for x in session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
]
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()] return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]: def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]:
@ -154,3 +162,14 @@ class BaseDocument:
session.commit() session.commit()
return results_as_model return results_as_model
def delete_all(self, session: Session) -> None:
session.query(self.sql_model).delete()
session.commit()
def count_all(self, session: Session, match_key=None, match_value=None) -> int:
if None in [match_key, match_value]:
return session.query(self.sql_model).count()
else:
return session.query(self.sql_model).filter_by(**{match_key: match_value}).count()

View File

@ -5,6 +5,7 @@ from mealie.db.database import db
from mealie.db.db_setup import create_session from mealie.db.db_setup import create_session
from mealie.schema.settings import SiteSettings from mealie.schema.settings import SiteSettings
from mealie.schema.theme import SiteTheme from mealie.schema.theme import SiteTheme
from mealie.services.events import create_general_event
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
logger = root_logger.get_logger("init_db") logger = root_logger.get_logger("init_db")
@ -58,6 +59,7 @@ def main():
else: else:
print("Database Doesn't Exists, Initializing...") print("Database Doesn't Exists, Initializing...")
init_db() init_db()
create_general_event("Initialize Database", "Initialize database with default values", session)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,7 +1,8 @@
from mealie.db.models.event import *
from mealie.db.models.group import *
from mealie.db.models.mealplan import * from mealie.db.models.mealplan import *
from mealie.db.models.recipe.recipe import * from mealie.db.models.recipe.recipe import *
from mealie.db.models.settings import * from mealie.db.models.settings import *
from mealie.db.models.sign_up import *
from mealie.db.models.theme import * from mealie.db.models.theme import *
from mealie.db.models.users import * from mealie.db.models.users import *
from mealie.db.models.sign_up import *
from mealie.db.models.group import *

17
mealie/db/models/event.py Normal file
View File

@ -0,0 +1,17 @@
import sqlalchemy as sa
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
class Event(SqlAlchemyBase, BaseMixins):
__tablename__ = "events"
id = sa.Column(sa.Integer, primary_key=True)
title = sa.Column(sa.String)
text = sa.Column(sa.String)
time_stamp = sa.Column(sa.DateTime)
category = sa.Column(sa.String)
def __init__(self, title, text, time_stamp, category, *args, **kwargs) -> None:
self.title = title
self.text = text
self.time_stamp = time_stamp
self.category = category

View File

@ -4,5 +4,5 @@ SqlAlchemyBase = dec.declarative_base()
class BaseMixins: class BaseMixins:
def _pass_on_me(): def update(self, *args, **kwarg):
pass self.__init__(*args, **kwarg)

View File

@ -0,0 +1,7 @@
from fastapi import APIRouter
from .events import router as events_router
about_router = APIRouter(prefix="/api/about")
about_router.include_router(events_router)

View File

@ -0,0 +1,28 @@
from fastapi import APIRouter, Depends
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user
from mealie.schema.events import EventsOut
from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/events", tags=["App Events"])
@router.get("", response_model=EventsOut)
async def get_events(session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
""" Get event from the Database """
# Get Item
return EventsOut(total=db.events.count_all(session), events=db.events.get_all(session, order_by="time_stamp"))
@router.delete("")
async def delete_events(session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
""" Get event from the Database """
# Get Item
return db.events.delete_all(session)
@router.delete("/{id}")
async def delete_event(id: int, session: Session = Depends(generate_session), current_user=Depends(get_current_user)):
""" Delete event from the Database """
return db.events.delete(session, id)

View File

@ -1,17 +1,21 @@
import operator import operator
import shutil import shutil
from pathlib import Path
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from mealie.core.config import app_dirs from mealie.core.config import app_dirs
from mealie.core.root_logger import get_logger
from mealie.core.security import create_file_token from mealie.core.security import create_file_token
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user from mealie.routes.deps import get_current_user
from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
from mealie.services.backups import imports from mealie.services.backups import imports
from mealie.services.backups.exports import backup_all from mealie.services.backups.exports import backup_all
from mealie.services.events import create_backup_event
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)]) router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
logger = get_logger()
@router.get("/available", response_model=Imports) @router.get("/available", response_model=Imports)
@ -43,8 +47,10 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
export_users=data.options.users, export_users=data.options.users,
export_groups=data.options.groups, export_groups=data.options.groups,
) )
create_backup_event("Database Backup", f"Manual Backup Created '{Path(export_path).name}'", session)
return {"export_path": export_path} return {"export_path": export_path}
except Exception: except Exception as e:
logger.error(e)
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR) raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
@ -72,7 +78,7 @@ async def download_backup_file(file_name: str):
def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)): def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)):
""" Import a database backup file generated from Mealie. """ """ Import a database backup file generated from Mealie. """
return imports.import_database( db_import = imports.import_database(
session=session, session=session,
archive=import_data.name, archive=import_data.name,
import_recipes=import_data.recipes, import_recipes=import_data.recipes,
@ -84,6 +90,8 @@ def import_database(file_name: str, import_data: ImportJob, session: Session = D
force_import=import_data.force, force_import=import_data.force,
rebase=import_data.rebase, rebase=import_data.rebase,
) )
create_backup_event("Database Restore", f"Restored Database File {file_name}", session)
return db_import
@router.delete("/{file_name}/delete", status_code=status.HTTP_200_OK) @router.delete("/{file_name}/delete", status_code=status.HTTP_200_OK)

View File

@ -2,8 +2,11 @@ from fastapi import APIRouter, Depends
from mealie.core.config import APP_VERSION, app_dirs, settings from mealie.core.config import APP_VERSION, app_dirs, settings
from mealie.core.root_logger import LOGGER_FILE from mealie.core.root_logger import LOGGER_FILE
from mealie.core.security import create_file_token from mealie.core.security import create_file_token
from mealie.db.database import db
from mealie.db.db_setup import generate_session
from mealie.routes.deps import get_current_user from mealie.routes.deps import get_current_user
from mealie.schema.debug import AppInfo, DebugInfo from mealie.schema.about import AppInfo, AppStatistics, DebugInfo
from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/debug", tags=["Debug"]) router = APIRouter(prefix="/api/debug", tags=["Debug"])
@ -18,11 +21,23 @@ async def get_debug_info(current_user=Depends(get_current_user)):
demo_status=settings.IS_DEMO, demo_status=settings.IS_DEMO,
api_port=settings.API_PORT, api_port=settings.API_PORT,
api_docs=settings.API_DOCS, api_docs=settings.API_DOCS,
db_type=settings.DB_ENGINE,
db_url=settings.DB_URL, db_url=settings.DB_URL,
default_group=settings.DEFAULT_GROUP, default_group=settings.DEFAULT_GROUP,
) )
@router.get("/statistics")
async def get_app_statistics(session: Session = Depends(generate_session)):
return AppStatistics(
total_recipes=db.recipes.count_all(session),
uncategorized_recipes=db.recipes.count_uncategorized(session),
untagged_recipes=db.recipes.count_untagged(session),
total_users=db.users.count_all(session),
total_groups=db.groups.count_all(session),
)
@router.get("/version") @router.get("/version")
async def get_mealie_version(): async def get_mealie_version():
""" Returns the current version of mealie""" """ Returns the current version of mealie"""

Some files were not shown because too many files have changed in this diff Show More