improved code quality of start page scrollers

This commit is contained in:
vabene1111
2024-12-19 21:16:15 +01:00
parent 1c9d19fc76
commit 3e3780028b
37 changed files with 200 additions and 447 deletions

View File

@@ -50,29 +50,12 @@
<link rel="stylesheet" href="{% static 'themes/select2-bootstrap-theme.css' %}"/> <link rel="stylesheet" href="{% static 'themes/select2-bootstrap-theme.css' %}"/>
<script type="text/javascript">
$.fn.select2.defaults.set("theme", "bootstrap");
{% if request.user.is_authenticated %}
window.ACTIVE_SPACE_ID = '{{request.space.id}}';
{% endif %}
</script>
<!-- Fontawesome icons --> <!-- Fontawesome icons -->
<link rel="stylesheet" href="{% static "fontawesome/fontawesome_all.min.css" %}"> <link rel="stylesheet" href="{% static "fontawesome/fontawesome_all.min.css" %}">
{% block extra_head %} <!-- block for templates to put stuff into header --> {% block extra_head %} <!-- block for templates to put stuff into header -->
{% endblock %} {% endblock %}
<style>
{% if request.user.userpreference.left_handed %}
@media screen and (max-width: 600px) {
#switcher .btn-circle {
left: 80px !important;
}
}
{% endif %}
</style>
</head> </head>
<body> <body>
@@ -93,317 +76,8 @@
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
{% if request.user.userpreference.left_handed %}
{% if not request.user.is_authenticated or request.user.userpreference.nav_show_logo %}
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
aria-label="Tandoor">
<img class="brand-icon" src="{{ theme_values.nav_logo }}" alt="Logo">
</a>
{% endif %}
{% endif %}
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item {% if request.resolver_match.url_name in 'view_search' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_search' %}"><i
class="fas fa-fw fa-book"></i> {% trans 'Recipes' %}</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name in 'view_plan' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_plan' %}"><i
class="fas fa-fw fa-calendar"></i> {% trans 'Meal-Plan' %}</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name in 'list_shopping_list,view_shopping' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_shopping' %}"><i
class="fas fa-fw fa-shopping-cart"></i> {% trans 'Shopping' %}</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name in 'view_books' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_books' %}"><i
class="fas fa-fw fa-book-open"></i> {% trans 'Books' %}</a>
</li>
{% plugin_main_nav_templates as plugin_main_nav_templates %}
{% for pn in plugin_main_nav_templates %}
{% include pn %}
{% endfor %}
</ul>
<ul class="navbar-nav ml-auto">
{% if user.is_authenticated %}
{% page_help request.resolver_match.url_name as help_button %}
{% if help_button %}{{ help_button|safe }}{% endif %}
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'list_keyword,list_food,list_unit,view_supermarket,data_batch_edit,view_history' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<i class="fas fa-fw fa-toolbox fa-lg"></i>
</a>
<div class="dropdown-menu dropdown-menu-center dropdown-menu-center-large">
<div class="row m-0 mt-1 mt-md-0">
<div class="col-4">
<a href="{% url 'list_keyword' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-tags fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Keyword' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'list_food' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-leaf fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Foods' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'list_unit' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-balance-scale fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Units' %}
</div>
</div>
</a>
</div>
</div>
<div class="row m-0 mt-1 mt-md-0">
<div class="col-4">
<a href="{% url 'list_supermarket' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-store-alt fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Supermarket' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'list_supermarket_category' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-cubes fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Supermarket Category' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'list_automation' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-robot fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Automations' %}
</div>
</div>
</a>
</div>
</div>
<div class="row m-0 mt-1 mt-md-0">
<div class="col-4">
<a href="{% url 'list_user_file' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-file fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Files' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'data_batch_edit' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-edit fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Batch Edit' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'view_history' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-history fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'History' %}
</div>
</div>
</a>
</div>
</div>
<div class="row m-0 mt-1 mt-md-0">
<div class="col-4">
<a href="{% url 'view_ingredient_editor' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-th-list fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Ingredient Editor' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'view_export' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-file-export fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Export' %}
</div>
</div>
</a>
</div>
<div class="col-4">
<a href="{% url 'list_property_type' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-database fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Properties' %}
</div>
</div>
</a>
</div>
</div>
<div class="row m-0 mt-1 mt-md-0">
<div class="col-4">
<a href="{% url 'list_unit_conversion' %}" class="p-0 p-md-1">
<div class="card p-1 pt-2 no-gutters border-0">
<div class="card-body text-center p-0 no-gutters">
<i class="fas fa-exchange-alt fa-2x"></i>
</div>
<div class="card-body text-break text-center p-0 no-gutters text-muted menu-dropdown-text">
{% trans 'Unit Conversions' %}
</div>
</div>
</a>
</div>
</div>
</div>
</li>
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'data_import_url,new_recipe' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"><i
class="fas fa-fw fa-plus fa-lg"></i>
</a>
<div class="dropdown-menu dropdown-menu-center"
aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="{% url 'data_import_url' %}"><i
class="fas fa-file-import"></i> {% trans 'Import Recipe' %}</a>
<a class="dropdown-item " href="{% url 'new_recipe' %}"><i
class="fas fa-plus"></i> {% trans 'Create' %}</a>
</div>
</li>
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'view_space,view_settings,view_history,view_system,docs_markdown' %}active{% endif %}">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"><i
class="fas fa-fw fa-user-alt"></i> {{ user.get_user_display_name }}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="{% url 'view_settings' %}"><i
class="fas fa-user-cog fa-fw"></i> {% trans 'Settings' %}</a>
<a class="dropdown-item" href="{% url 'data_sync' %}"><i
class="fas fa-sync-alt fa-fw"></i> {% trans 'External Recipes' %}</a>
{% if request.user == request.space.created_by %}
<a class="dropdown-item" href="{% url 'view_space_manage' request.space.pk %}"><i
class="fas fa-server fa-fw"></i> {% trans 'Space Settings' %}</a>
{% endif %}
{% if not DISABLE_EXTERNAL_CONNECTORS and request.user == request.space.created_by or not DISABLE_EXTERNAL_CONNECTORS and user.is_superuser %}
<a class="dropdown-item" href="{% url 'list_connector_config' %}"><i
class="fas fa-sync-alt fa-fw"></i> {% trans 'External Connectors' %}</a>
{% endif %}
{% if user.is_superuser %}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'view_system' %}"><i
class="fas fa-server fa-fw"></i> {% trans 'System' %}</a>
<a class="dropdown-item" href="{% url 'admin:index' %}"><i
class="fas fa-user-shield fa-fw"></i> {% trans 'Admin' %}</a>
{% endif %}
{% if request.user.is_authenticated %}
<div class="dropdown-divider"></div>
<h6 class="dropdown-header">{% trans 'Your Spaces' %}</h6>
{% for us in request.user.userspace_set.all %}
<a class="dropdown-item" href="{% url 'view_switch_space' us.space.id %}">
{% if us.active %}
<i class="far fa-dot-circle fa-fw"></i>
{% else %}
<i class="far fa-circle fa-fw"></i>
{% endif %}
{{ us.space.name }}</a>
{% endfor %}
<a class="dropdown-item" href="{% url 'view_space_overview' %}"><i
class="fas fa-list"></i> {% trans 'Overview' %}</a>
{% endif %}
{% plugin_dropdown_nav_templates as plugin_dropdown_nav_templates %}
{% for pn in plugin_dropdown_nav_templates %}
<div class="dropdown-divider"></div>
{% include pn %}
{% endfor %}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'docs_markdown' %}"><i
class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Guide' %}</a>
<a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a>
<a class="dropdown-item" href="https://translate.tandoor.dev/projects/tandoor/"><i class="fas fa-language fa-fw"></i> {% trans 'Translate Tandoor' %}</a>
<a class="dropdown-item" href="{% url 'docs_api' %}"><i class="fas fa-passport fa-fw"></i> {% trans 'API ReDoc Documentation' %}</a>
<a class="dropdown-item" href="{% url 'docs_swagger' %}"><i class="fas fa-passport fa-fw"></i> {% trans 'API Swagger Documentation' %}</a>
<a class="dropdown-item" href="{% url 'api:api-root' %}"><i class="fas fa-file-code fa-fw"></i> {% trans 'API Browser' %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'account_logout' %}"><i class="fas fa-sign-out-alt fa-fw"></i> {% trans 'Log out' %}</a>
</div>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'account_login' %}">{% trans 'Login' %} <i
class="fas fa-fw fa-sign-in-alt"></i></a>
</li>
{% endif %}
</ul>
</div>
</nav> </nav>
{% message_of_the_day request as message_of_the_day %}
{% if message_of_the_day %}
<div class="bg-info" style=" width: 100%; text-align: center!important; color: #ffffff; padding: 8px">
{{ message_of_the_day | markdown |safe }}
</div>
{% endif %}
{% if HOSTED and request.space.max_recipes == 10 %}
<div class="bg-warning" style=" width: 100%; text-align: center!important; color: #ffffff; padding: 8px">
{% trans 'You are using the free version of Tandoor' %} <a class="btn-success btn-sm"
href="https://tandoor.dev/manage">{% trans 'Upgrade Now' %}</a>
</div>
{% endif %}
<div class="container-fluid mt-2 mt-md-3 mt-xl-3 mt-lg-3{% if request.user.userpreference.left_handed %} left-handed {% endif %}" <div class="container-fluid mt-2 mt-md-3 mt-xl-3 mt-lg-3{% if request.user.userpreference.left_handed %} left-handed {% endif %}"
id="id_base_container"> id="id_base_container">
<div class="row"> <div class="row">

View File

@@ -34,25 +34,12 @@ from recipes.settings import PLUGINS
def index(request): def index(request):
return HttpResponseRedirect(reverse('vue3'))
with scopes_disabled(): with scopes_disabled():
if not request.user.is_authenticated: if not request.user.is_authenticated:
if User.objects.count() < 1 and 'django.contrib.auth.backends.RemoteUserBackend' not in settings.AUTHENTICATION_BACKENDS: if User.objects.count() < 1 and 'django.contrib.auth.backends.RemoteUserBackend' not in settings.AUTHENTICATION_BACKENDS:
return HttpResponseRedirect(reverse_lazy('view_setup')) return HttpResponseRedirect(reverse_lazy('view_setup'))
return HttpResponseRedirect(reverse_lazy('view_search'))
try: return HttpResponseRedirect(reverse('vue3'))
page_map = {
UserPreference.SEARCH: reverse_lazy('view_search'),
UserPreference.PLAN: reverse_lazy('view_plan'),
UserPreference.BOOKS: reverse_lazy('view_books'),
UserPreference.SHOPPING: reverse_lazy('view_shopping'),
}
return HttpResponseRedirect(page_map.get(request.user.userpreference.default_page))
except UserPreference.DoesNotExist:
return HttpResponseRedirect(reverse('view_search'))
def search(request): def search(request):

View File

@@ -6,6 +6,15 @@
</router-link> </router-link>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<global-search-dialog></global-search-dialog> <global-search-dialog></global-search-dialog>
<v-btn icon="$add">
<v-icon icon="$add"></v-icon>
<v-menu activator="parent">
<v-list>
<v-list-item prepend-icon="$add" :to="{ name: 'ModelEditPage', params: {model: 'Recipe'} }">{{ $t('Create Recipe') }}</v-list-item>
<v-list-item prepend-icon="fa-solid fa-globe" :to="{ name: 'RecipeImportPage', params: {} }">{{ $t('Import Recipe') }}</v-list-item>
</v-list>
</v-menu>
</v-btn>
<v-avatar color="primary" class="me-2">{{ useUserPreferenceStore().userSettings.user.displayName.charAt(0) }} <v-avatar color="primary" class="me-2">{{ useUserPreferenceStore().userSettings.user.displayName.charAt(0) }}
<v-menu activator="parent"> <v-menu activator="parent">

View File

@@ -1,71 +1,159 @@
<template> <template>
<v-row justify="space-between"> <template v-if="loading || recipes.length > 0">
<v-col> <v-row justify="space-between">
<h4><i v-if="icon != 'undefined'" :class="icon + ' fa-fw'"></i> {{ title }}</h4> <v-col>
</v-col> <h4><i :class="icon + ' fa-fw'"></i> {{ title }}</h4>
</v-row> </v-col>
</v-row>
<v-row class="mt-0" v-if="recipeWindows.length > 0"> <v-row class="mt-0" v-if="recipeWindows.length > 0">
<v-col> <v-col>
<v-window show-arrows> <v-window show-arrows>
<v-window-item v-for="w in recipeWindows" class="pt-1 pb-1"> <v-window-item v-for="w in recipeWindows" class="pt-1 pb-1">
<v-row> <v-row>
<v-col v-for="r in w" :key="r.id"> <v-col v-for="r in w" :key="r.id">
<recipe-card :recipe="r" :show_description="true" :show_keywords="true" style="height: 20vh"></recipe-card> <recipe-card :recipe="r" :show_description="true" :show_keywords="true" style="height: 20vh"></recipe-card>
</v-col> </v-col>
</v-row> </v-row>
</v-window-item> </v-window-item>
</v-window> </v-window>
</v-col> </v-col>
</v-row> </v-row>
<v-row v-if="recipeWindows.length == 0 && skeletons > 0">
<v-col>
<v-window>
<v-window-item>
<v-row>
<v-col v-for="n in skeletons">
<v-skeleton-loader :elevation="3" type="card"></v-skeleton-loader>
</v-col>
</v-row>
</v-window-item>
</v-window>
</v-col>
</v-row>
<v-row v-if="skeletons > 0 && loading">
<v-col>
<v-window>
<v-window-item>
<v-row>
<v-col v-for="n in skeletons">
<v-skeleton-loader :elevation="3" type="card"></v-skeleton-loader>
</v-col>
</v-row>
</v-window-item>
</v-window>
</v-col>
</v-row>
</template>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {computed, PropType, toRefs} from 'vue' import {computed, onMounted, PropType, ref, toRefs} from 'vue'
import RecipeCard from "@/components/display/RecipeCard.vue"; import RecipeCard from "@/components/display/RecipeCard.vue";
import {DisplayBreakpoint, useDisplay} from "vuetify"; import {DisplayBreakpoint, useDisplay} from "vuetify";
import {Recipe, RecipeOverview} from "@/openapi"; import {ApiApi, ApiRecipeListRequest, Keyword, Recipe, RecipeOverview} from "@/openapi";
import {homePageCols} from "@/utils/breakpoint_utils"; import {homePageCols} from "@/utils/breakpoint_utils";
import {useI18n} from "vue-i18n";
//TODO mode ideas "last year/month/cooked long ago"
const props = defineProps( const props = defineProps(
{ {
title: {type: String as PropType<undefined | String>, required: true}, mode: {type: String as PropType<'recent' | 'new' | 'keyword' | 'rating'>, required: true},
icon: {type: String, required: false},
skeletons: {type: Number, default: 0}, skeletons: {type: Number, default: 0},
recipes: {
type: Array as PropType<Recipe[] | RecipeOverview[]>,
required: true
},
} }
) )
const {title, recipes} = toRefs(props)
let numberOfCols = computed(() => { const {t} = useI18n()
const {name} = useDisplay() const {name} = useDisplay()
const loading = ref(true)
const recipes = ref([] as Recipe[] | RecipeOverview[])
const keyword = ref({} as Keyword)
/**
* determine title based on type
*/
const title = computed(() => {
switch (props.mode) {
case 'recent':
return t('Recently_Viewed')
case 'new':
return t('New')
case 'rating':
return t('Rating')
case 'keyword':
if (Object.keys(keyword.value).length > 0) {
return keyword.value.label
}
return t('Keyword')
}
})
/**
* determine icon based on type
*/
const icon = computed(() => {
switch (props.mode) {
case 'recent':
return 'fa-solid fa-eye'
case 'new':
return 'fa-solid fa-calendar-alt'
case 'rating':
return 'fa-solid fa-star'
case 'keyword':
return 'fa-solid fa-tags'
}
})
/**
* number of columns to show depending on display size
*/
const numberOfCols = computed(() => {
return homePageCols(name.value) return homePageCols(name.value)
}) })
type CustomWindow = { onMounted(() => {
id: number, loadRecipes()
recipes: Array<Recipe | RecipeOverview> })
/**
* load recipes depending on type by creating request parameters and executing request function
*/
function loadRecipes() {
let api = new ApiApi()
let requestParameters = {pageSize: 16} as ApiRecipeListRequest
switch (props.mode) {
case 'recent':
// TODO implement correct parameter
requestParameters._new = 'true'
break;
case 'new':
requestParameters._new = 'true'
break;
case 'rating':
requestParameters.rating = 4
break;
case 'keyword':
api.apiKeywordList({random: "true", limit: "1"}).then((r) => {
if (r.count > 0) {
keyword.value = r.results[0]
requestParameters.keywords = [keyword.value.id!]
doRecipeRequest(requestParameters)
}
})
return;
}
doRecipeRequest(requestParameters)
} }
/**
* actual request function requesting the recipes from the server based on the given parameters
* @param params
*/
function doRecipeRequest(params: ApiRecipeListRequest) {
let api = new ApiApi()
api.apiRecipeList(params).then((r) => {
recipes.value = r.results
}).finally(() => {
loading.value = false
})
}
/**
* split the list of recipes into windows for display based on column count
*/
let recipeWindows = computed(() => { let recipeWindows = computed(() => {
let windows = [] let windows = []
let current_window = [] let current_window = []

View File

@@ -113,6 +113,7 @@
"Food_Alias": "", "Food_Alias": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "", "GroupBy": "",
"Hide_Food": "", "Hide_Food": "",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -110,6 +110,7 @@
"Food_Alias": "Псевдоним на храната", "Food_Alias": "Псевдоним на храната",
"Foods": "Храни", "Foods": "Храни",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Групирай по", "GroupBy": "Групирай по",
"Hide_Food": "Скриване на храна", "Hide_Food": "Скриване на храна",
"Hide_Keyword": "Скриване на ключови думи", "Hide_Keyword": "Скриване на ключови думи",

View File

@@ -153,6 +153,7 @@
"Food_Replace": "", "Food_Replace": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "", "GroupBy": "",
"Hide_Food": "", "Hide_Food": "",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -153,6 +153,7 @@
"Food_Replace": "Nahrazení v potravině", "Food_Replace": "Nahrazení v potravině",
"Foods": "Potraviny", "Foods": "Potraviny",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Seskupit podle", "GroupBy": "Seskupit podle",
"Hide_Food": "Skrýt potravinu", "Hide_Food": "Skrýt potravinu",
"Hide_Keyword": "Skrýt štítky", "Hide_Keyword": "Skrýt štítky",

View File

@@ -141,6 +141,7 @@
"Food_Replace": "Erstat ingrediens", "Food_Replace": "Erstat ingrediens",
"Foods": "Mad", "Foods": "Mad",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Grupper efter", "GroupBy": "Grupper efter",
"Hide_Food": "Skjul mad", "Hide_Food": "Skjul mad",
"Hide_Keyword": "Skjul nøgleord", "Hide_Keyword": "Skjul nøgleord",

View File

@@ -155,6 +155,7 @@
"Food_Replace": "Essen Ersetzen", "Food_Replace": "Essen Ersetzen",
"Foods": "Lebensmittel", "Foods": "Lebensmittel",
"Friday": "Freitag", "Friday": "Freitag",
"GettingStarted": "Erste Schritte",
"GroupBy": "Gruppieren nach", "GroupBy": "Gruppieren nach",
"Hide_Food": "Lebensmittel verbergen", "Hide_Food": "Lebensmittel verbergen",
"Hide_Keyword": "Schlüsselwörter verbergen", "Hide_Keyword": "Schlüsselwörter verbergen",

View File

@@ -136,6 +136,7 @@
"Food_Alias": "Ψευδώνυμο φαγητού", "Food_Alias": "Ψευδώνυμο φαγητού",
"Foods": "Φαγητά", "Foods": "Φαγητά",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Ομαδοποίηση κατά", "GroupBy": "Ομαδοποίηση κατά",
"Hide_Food": "Απόκρυψη φαγητού", "Hide_Food": "Απόκρυψη φαγητού",
"Hide_Keyword": "Απόκρυψη λέξεων-κλειδί", "Hide_Keyword": "Απόκρυψη λέξεων-κλειδί",

View File

@@ -154,6 +154,7 @@
"Food_Replace": "Food Replace", "Food_Replace": "Food Replace",
"Foods": "Foods", "Foods": "Foods",
"Friday": "Friday", "Friday": "Friday",
"GettingStarted": "Getting Started",
"GroupBy": "Group By", "GroupBy": "Group By",
"Hide_Food": "Hide Food", "Hide_Food": "Hide Food",
"Hide_Keyword": "Hide keywords", "Hide_Keyword": "Hide keywords",

View File

@@ -154,6 +154,7 @@
"Food_Replace": "Sustituir Alimento", "Food_Replace": "Sustituir Alimento",
"Foods": "Comida", "Foods": "Comida",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Agrupar por", "GroupBy": "Agrupar por",
"Hide_Food": "Esconder ingrediente", "Hide_Food": "Esconder ingrediente",
"Hide_Keyword": "Esconder Palabras Clave", "Hide_Keyword": "Esconder Palabras Clave",

View File

@@ -83,6 +83,7 @@
"Food": "Ruoka", "Food": "Ruoka",
"Food_Alias": "Ruoan nimimerkki", "Food_Alias": "Ruoan nimimerkki",
"Friday": "", "Friday": "",
"GettingStarted": "",
"Hide_Food": "Piilota ruoka", "Hide_Food": "Piilota ruoka",
"Hide_Keyword": "Piilota avainsana", "Hide_Keyword": "Piilota avainsana",
"Hide_Keywords": "Piilota Avainsana", "Hide_Keywords": "Piilota Avainsana",

View File

@@ -153,6 +153,7 @@
"Food_Replace": "Remplacer l'aliment", "Food_Replace": "Remplacer l'aliment",
"Foods": "Aliments", "Foods": "Aliments",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Grouper par", "GroupBy": "Grouper par",
"Hide_Food": "Cacher laliment", "Hide_Food": "Cacher laliment",
"Hide_Keyword": "masquer les mots clefs", "Hide_Keyword": "masquer les mots clefs",

View File

@@ -154,6 +154,7 @@
"Food_Replace": "החלף אוכל", "Food_Replace": "החלף אוכל",
"Foods": "מאכלים", "Foods": "מאכלים",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "אסוף לפי", "GroupBy": "אסוף לפי",
"Hide_Food": "הסתר אוכל", "Hide_Food": "הסתר אוכל",
"Hide_Keyword": "הסתר מילות מפתח", "Hide_Keyword": "הסתר מילות מפתח",

View File

@@ -137,6 +137,7 @@
"Food_Replace": "Étel cseréje", "Food_Replace": "Étel cseréje",
"Foods": "Alapanyagok", "Foods": "Alapanyagok",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Csoportosítva", "GroupBy": "Csoportosítva",
"Hide_Food": "Alapanyag elrejtése", "Hide_Food": "Alapanyag elrejtése",
"Hide_Keyword": "Kulcsszavak elrejtése", "Hide_Keyword": "Kulcsszavak elrejtése",

View File

@@ -62,6 +62,7 @@
"Files": "", "Files": "",
"Food": "Սննդամթերք", "Food": "Սննդամթերք",
"Friday": "", "Friday": "",
"GettingStarted": "",
"Hide_Food": "Թաքցնել սննդամթերքը", "Hide_Food": "Թաքցնել սննդամթերքը",
"Hide_Keywords": "Թաքցնել բանալի բառը", "Hide_Keywords": "Թաքցնել բանալի բառը",
"Hide_Recipes": "Թաքցնել բաղադրատոմսերը", "Hide_Recipes": "Թաքցնել բաղադրատոմսերը",

View File

@@ -125,6 +125,7 @@
"Food_Alias": "", "Food_Alias": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "", "GroupBy": "",
"Hide_Food": "", "Hide_Food": "",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -153,6 +153,7 @@
"Food_Replace": "", "Food_Replace": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "", "GroupBy": "",
"Hide_Food": "", "Hide_Food": "",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -130,6 +130,7 @@
"Food_Alias": "Alias Alimento", "Food_Alias": "Alias Alimento",
"Foods": "Alimenti", "Foods": "Alimenti",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Raggruppa per", "GroupBy": "Raggruppa per",
"Hide_Food": "Nascondi alimento", "Hide_Food": "Nascondi alimento",
"Hide_Keyword": "Nascondi parole chiave", "Hide_Keyword": "Nascondi parole chiave",

View File

@@ -139,6 +139,7 @@
"Food_Replace": "", "Food_Replace": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "", "GroupBy": "",
"Hide_Food": "", "Hide_Food": "",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -134,6 +134,7 @@
"Food_Alias": "Matrett Alias", "Food_Alias": "Matrett Alias",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Grupér", "GroupBy": "Grupér",
"Hide_Food": "Skjul Matrett", "Hide_Food": "Skjul Matrett",
"Hide_Keyword": "Skjul nøkkelord", "Hide_Keyword": "Skjul nøkkelord",

View File

@@ -138,6 +138,7 @@
"Food_Alias": "Eten Alias", "Food_Alias": "Eten Alias",
"Foods": "Ingrediënten", "Foods": "Ingrediënten",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Groepeer per", "GroupBy": "Groepeer per",
"Hide_Food": "Verberg Eten", "Hide_Food": "Verberg Eten",
"Hide_Keyword": "Verberg etiketten", "Hide_Keyword": "Verberg etiketten",

View File

@@ -155,6 +155,7 @@
"Food_Replace": "Zastąp produkt", "Food_Replace": "Zastąp produkt",
"Foods": "Żywność", "Foods": "Żywność",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Grupuj według", "GroupBy": "Grupuj według",
"Hide_Food": "Ukryj żywność", "Hide_Food": "Ukryj żywność",
"Hide_Keyword": "Ukryj słowa kluczowe", "Hide_Keyword": "Ukryj słowa kluczowe",

View File

@@ -112,6 +112,7 @@
"Food_Alias": "Alcunha da comida", "Food_Alias": "Alcunha da comida",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Agrupar por", "GroupBy": "Agrupar por",
"Hide_Food": "Esconder comida", "Hide_Food": "Esconder comida",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -149,6 +149,7 @@
"Food_Replace": "Substituir Alimento", "Food_Replace": "Substituir Alimento",
"Foods": "Alimentos", "Foods": "Alimentos",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Agrupar Por", "GroupBy": "Agrupar Por",
"Hide_Food": "Esconder Comida", "Hide_Food": "Esconder Comida",
"Hide_Keyword": "Oculta palavras-chave", "Hide_Keyword": "Oculta palavras-chave",

View File

@@ -132,6 +132,7 @@
"Food_Alias": "Pseudonim mâncare", "Food_Alias": "Pseudonim mâncare",
"Foods": "Alimente", "Foods": "Alimente",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Grupat de", "GroupBy": "Grupat de",
"Hide_Food": "Ascunde mâncare", "Hide_Food": "Ascunde mâncare",
"Hide_Keyword": "Ascunde cuvintele cheie", "Hide_Keyword": "Ascunde cuvintele cheie",

View File

@@ -101,6 +101,7 @@
"FoodOnHand": "{food} у вас в наличии.", "FoodOnHand": "{food} у вас в наличии.",
"Food_Alias": "Наименование еды", "Food_Alias": "Наименование еды",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Сгруппировать по", "GroupBy": "Сгруппировать по",
"Hide_Food": "Скрыть еду", "Hide_Food": "Скрыть еду",
"Hide_Keyword": "Скрыть ключевые слова", "Hide_Keyword": "Скрыть ключевые слова",

View File

@@ -101,6 +101,7 @@
"FoodOnHand": "Imaš {food} v roki.", "FoodOnHand": "Imaš {food} v roki.",
"Food_Alias": "Vzdevek hrane", "Food_Alias": "Vzdevek hrane",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Združi po", "GroupBy": "Združi po",
"Hide_Food": "Skrij hrano", "Hide_Food": "Skrij hrano",
"Hide_Keyword": "Skrij ključne besede", "Hide_Keyword": "Skrij ključne besede",

View File

@@ -155,6 +155,7 @@
"Food_Replace": "Ersätt ingrediens", "Food_Replace": "Ersätt ingrediens",
"Foods": "Livsmedel", "Foods": "Livsmedel",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Gruppera enligt", "GroupBy": "Gruppera enligt",
"Hide_Food": "Dölj livsmedel", "Hide_Food": "Dölj livsmedel",
"Hide_Keyword": "Dölj nyckelord", "Hide_Keyword": "Dölj nyckelord",

View File

@@ -154,6 +154,7 @@
"Food_Replace": "Yiyecek Değiştir", "Food_Replace": "Yiyecek Değiştir",
"Foods": "Yiyecekler", "Foods": "Yiyecekler",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "Gruplandırma Ölçütü", "GroupBy": "Gruplandırma Ölçütü",
"Hide_Food": "Yiyeceği Gizle", "Hide_Food": "Yiyeceği Gizle",
"Hide_Keyword": "Anahtar kelimeleri gizle", "Hide_Keyword": "Anahtar kelimeleri gizle",

View File

@@ -119,6 +119,7 @@
"Food_Alias": "", "Food_Alias": "",
"Foods": "", "Foods": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "По Групі", "GroupBy": "По Групі",
"Hide_Food": "Сховати Їжу", "Hide_Food": "Сховати Їжу",
"Hide_Keyword": "", "Hide_Keyword": "",

View File

@@ -150,6 +150,7 @@
"Food_Replace": "食物替换", "Food_Replace": "食物替换",
"Foods": "食物", "Foods": "食物",
"Friday": "", "Friday": "",
"GettingStarted": "",
"GroupBy": "分组", "GroupBy": "分组",
"Hide_Food": "隐藏食物", "Hide_Food": "隐藏食物",
"Hide_Keyword": "隐藏关键词", "Hide_Keyword": "隐藏关键词",

View File

@@ -50,6 +50,7 @@
"File": "", "File": "",
"Files": "", "Files": "",
"Friday": "", "Friday": "",
"GettingStarted": "",
"Hide_as_header": "隱藏為標題", "Hide_as_header": "隱藏為標題",
"History": "", "History": "",
"HostedFreeVersion": "", "HostedFreeVersion": "",

View File

@@ -109,7 +109,7 @@
<vue-draggable v-model="s.ingredients" group="ingredients" drag-class="drag-handle"> <vue-draggable v-model="s.ingredients" group="ingredients" drag-class="drag-handle">
<v-list-item v-for="i in s.ingredients" border> <v-list-item v-for="i in s.ingredients" border>
<v-icon size="small" class="drag-handle cursor-grab" icon="$dragHandle"></v-icon> <v-icon size="small" class="drag-handle cursor-grab" icon="$dragHandle"></v-icon>
{{ i.amount }} {{ i.unit.name }} {{ i.food.name }} {{ i.amount }} <span v-if="i.unit">{{ i.unit.name }}</span> <span v-if="i.food">{{ i.food.name }}</span>
<template #append> <template #append>
<v-btn size="small" color="edit" @click="editingIngredient = i; dialog=true"> <v-btn size="small" color="edit" @click="editingIngredient = i; dialog=true">
<v-icon icon="$edit"></v-icon> <v-icon icon="$edit"></v-icon>

View File

@@ -2,74 +2,38 @@
<v-container> <v-container>
<horizontal-meal-plan-window></horizontal-meal-plan-window> <horizontal-meal-plan-window></horizontal-meal-plan-window>
<!--TODO ideas for "start page": new recipes, meal plan, "last year/month/cooked long ago", high rated, random keyword --> <v-card v-if="totalRecipes == 0" class="mt-5 mb-5">
<!--TODO if nothing comes up for a category, hide the element, probably move fetch logic into component --> <v-card-title><i class="fa-solid fa-eye-slash"></i> {{ $t('search_no_recipes') }}</v-card-title>
<horizontal-recipe-scroller title="New Recipes" :skeletons="4" :recipes="new_recipes" icon="fas fa-calendar-alt"></horizontal-recipe-scroller> <v-card-text>
<horizontal-recipe-scroller title="Top Rated" :skeletons="2" :recipes="high_rated_recipes" icon="fas fa-star"></horizontal-recipe-scroller> <v-btn-group divided>
<horizontal-recipe-scroller <v-btn size="large" color="success" prepend-icon="$create" :to="{ name: 'ModelEditPage', params: {model: 'Recipe'} }">{{ $t('Create Recipe') }}</v-btn>
:title="random_keyword.label" <v-btn size="large" color="primary" prepend-icon="fa-solid fa-globe" :to="{ name: 'RecipeImportPage', params: {} }">{{ $t('Import Recipe') }}</v-btn>
:skeletons="4" </v-btn-group>
:recipes="random_keyword_recipes" </v-card-text>
icon="fas fa-tags" </v-card>
v-if="random_keyword.label"
></horizontal-recipe-scroller> <horizontal-recipe-scroller :skeletons="4" mode="recent"></horizontal-recipe-scroller>
<horizontal-recipe-scroller :skeletons="4" mode="new"></horizontal-recipe-scroller>
<horizontal-recipe-scroller :skeletons="2" mode="rating"></horizontal-recipe-scroller>
<horizontal-recipe-scroller :skeletons="4" mode="keyword"></horizontal-recipe-scroller>
</v-container> </v-container>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent, ref} from "vue" import {onMounted, ref} from "vue"
import {ApiApi, Keyword, Recipe, RecipeOverview} from "@/openapi" import {ApiApi} from "@/openapi"
import KeywordsComponent from "@/components/display/KeywordsBar.vue"
import RecipeCardComponent from "@/components/display/RecipeCard.vue"
import GlobalSearchDialog from "@/components/inputs/GlobalSearchDialog.vue"
import RecipeCard from "@/components/display/RecipeCard.vue"
import HorizontalRecipeScroller from "@/components/display/HorizontalRecipeWindow.vue" import HorizontalRecipeScroller from "@/components/display/HorizontalRecipeWindow.vue"
import HorizontalMealPlanWindow from "@/components/display/HorizontalMealPlanWindow.vue" import HorizontalMealPlanWindow from "@/components/display/HorizontalMealPlanWindow.vue"
import ModelSelect from "@/components/inputs/ModelSelect.vue"
export default defineComponent({ const totalRecipes = ref(-1)
name: "StartPage",
components: {ModelSelect, HorizontalMealPlanWindow, HorizontalRecipeScroller, RecipeCard, GlobalSearchDialog, RecipeCardComponent, KeywordsComponent},
computed: {},
data() {
return {
recipes: [] as Recipe[],
items: Array.from({length: 50}, (k, v) => v + 1),
new_recipes: [] as RecipeOverview[], onMounted(() => {
high_rated_recipes: [] as RecipeOverview[], const api = new ApiApi()
random_keyword: {} as Keyword,
random_keyword_recipes: [] as RecipeOverview[],
}
},
mounted() {
const api = new ApiApi()
api.apiRecipeList({_new: "true", pageSize: 16}).then((r) => { api.apiRecipeList({pageSize: 1}).then((r) => {
if (r.results != undefined) { totalRecipes.value = r.count
// TODO openapi generator makes arrays nullable for some reason })
this.new_recipes = r.results
}
})
api.apiRecipeList({rating: 4, pageSize: 16}).then((r) => {
if (r.results != undefined) {
this.high_rated_recipes = r.results
}
})
api.apiKeywordList({random: "true", limit: "1"}).then((r) => {
if (r.results != undefined && r.results.length > 0) {
this.random_keyword = r.results[0]
api.apiRecipeList({keywords: r.results[0].id}).then((r) => {
if (r.results != undefined) {
this.random_keyword_recipes = r.results
}
})
}
})
},
methods: {},
}) })
</script> </script>