Merge branch 'mealie-next' into upgrade-create-from-image-visuals

This commit is contained in:
Kuchenpirat 2025-06-28 17:54:49 +02:00 committed by GitHub
commit 3ce3243b96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 138 additions and 134 deletions

View file

@ -24,8 +24,7 @@
<v-container <v-container
v-if="book" v-if="book"
fluid class="my-0"
class="py-0 my-0"
> >
<v-sheet <v-sheet
color="transparent" color="transparent"
@ -33,13 +32,12 @@
elevation="0" elevation="0"
> >
<div class="d-flex align-center w-100 mb-2"> <div class="d-flex align-center w-100 mb-2">
<v-toolbar-title class="headline mb-0">
<v-icon size="large" class="mr-3"> <v-icon size="large" class="mr-3">
{{ $globals.icons.pages }} {{ $globals.icons.pages }}
</v-icon> </v-icon>
<v-toolbar-title class="headline mb-0">
{{ book.name }} {{ book.name }}
</v-toolbar-title> </v-toolbar-title>
<v-spacer />
<BaseButton <BaseButton
v-if="canEdit" v-if="canEdit"
class="mx-1" class="mx-1"

View file

@ -69,22 +69,22 @@ export default defineNuxtComponent({
const i18n = useI18n(); const i18n = useI18n();
const MEAL_TYPE_OPTIONS = [ const MEAL_TYPE_OPTIONS = [
{ text: i18n.t("meal-plan.breakfast"), value: "breakfast" }, { title: i18n.t("meal-plan.breakfast"), value: "breakfast" },
{ text: i18n.t("meal-plan.lunch"), value: "lunch" }, { title: i18n.t("meal-plan.lunch"), value: "lunch" },
{ text: i18n.t("meal-plan.dinner"), value: "dinner" }, { title: i18n.t("meal-plan.dinner"), value: "dinner" },
{ text: i18n.t("meal-plan.side"), value: "side" }, { title: i18n.t("meal-plan.side"), value: "side" },
{ text: i18n.t("meal-plan.type-any"), value: "unset" }, { title: i18n.t("meal-plan.type-any"), value: "unset" },
]; ];
const MEAL_DAY_OPTIONS = [ const MEAL_DAY_OPTIONS = [
{ text: i18n.t("general.monday"), value: "monday" }, { title: i18n.t("general.monday"), value: "monday" },
{ text: i18n.t("general.tuesday"), value: "tuesday" }, { title: i18n.t("general.tuesday"), value: "tuesday" },
{ text: i18n.t("general.wednesday"), value: "wednesday" }, { title: i18n.t("general.wednesday"), value: "wednesday" },
{ text: i18n.t("general.thursday"), value: "thursday" }, { title: i18n.t("general.thursday"), value: "thursday" },
{ text: i18n.t("general.friday"), value: "friday" }, { title: i18n.t("general.friday"), value: "friday" },
{ text: i18n.t("general.saturday"), value: "saturday" }, { title: i18n.t("general.saturday"), value: "saturday" },
{ text: i18n.t("general.sunday"), value: "sunday" }, { title: i18n.t("general.sunday"), value: "sunday" },
{ text: i18n.t("meal-plan.day-any"), value: "unset" }, { title: i18n.t("meal-plan.day-any"), value: "unset" },
]; ];
const inputDay = computed({ const inputDay = computed({

View file

@ -189,6 +189,7 @@
:show-add="false" :show-add="false"
:show-label="false" :show-label="false"
:show-icon="false" :show-icon="false"
variant="underlined"
@update:model-value="setOrganizerValues(field, index, $event)" @update:model-value="setOrganizerValues(field, index, $event)"
/> />
<RecipeOrganizerSelector <RecipeOrganizerSelector
@ -198,6 +199,7 @@
:show-add="false" :show-add="false"
:show-label="false" :show-label="false"
:show-icon="false" :show-icon="false"
variant="underlined"
@update:model-value="setOrganizerValues(field, index, $event)" @update:model-value="setOrganizerValues(field, index, $event)"
/> />
<RecipeOrganizerSelector <RecipeOrganizerSelector
@ -207,6 +209,7 @@
:show-add="false" :show-add="false"
:show-label="false" :show-label="false"
:show-icon="false" :show-icon="false"
variant="underlined"
@update:model-value="setOrganizerValues(field, index, $event)" @update:model-value="setOrganizerValues(field, index, $event)"
/> />
<RecipeOrganizerSelector <RecipeOrganizerSelector
@ -216,6 +219,7 @@
:show-add="false" :show-add="false"
:show-label="false" :show-label="false"
:show-icon="false" :show-icon="false"
variant="underlined"
@update:model-value="setOrganizerValues(field, index, $event)" @update:model-value="setOrganizerValues(field, index, $event)"
/> />
<RecipeOrganizerSelector <RecipeOrganizerSelector
@ -225,6 +229,7 @@
:show-add="false" :show-add="false"
:show-label="false" :show-label="false"
:show-icon="false" :show-icon="false"
variant="underlined"
@update:model-value="setOrganizerValues(field, index, $event)" @update:model-value="setOrganizerValues(field, index, $event)"
/> />
</v-col> </v-col>

View file

@ -74,6 +74,7 @@
:size="$vuetify.display.xs ? 'small' : undefined" :size="$vuetify.display.xs ? 'small' : undefined"
:color="btn.color" :color="btn.color"
variant="elevated" variant="elevated"
:icon="$vuetify.display.xs"
@click="emitHandler(btn.event)" @click="emitHandler(btn.event)"
> >
<v-icon :left="!$vuetify.display.xs"> <v-icon :left="!$vuetify.display.xs">

View file

@ -10,7 +10,7 @@
v-for="category in items.slice(0, limit)" v-for="category in items.slice(0, limit)"
:key="category.name" :key="category.name"
label label
class="ma-1" class="mr-1 mt-1"
color="accent" color="accent"
variant="flat" variant="flat"
:size="small ? 'small' : 'default'" :size="small ? 'small' : 'default'"

View file

@ -17,7 +17,7 @@
id="arrow-search" id="arrow-search"
v-model="search.query.value" v-model="search.query.value"
autofocus autofocus
variant="solo-filled" variant="solo"
flat flat
autocomplete="off" autocomplete="off"
bg-color="primary-lighten-1" bg-color="primary-lighten-1"

View file

@ -1,7 +1,7 @@
<template> <template>
<v-container <v-container
fluid fluid
class="pa-0" class="px-0"
> >
<div class="search-container pb-8"> <div class="search-container pb-8">
<form <form

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="ma-0 pa-0 text-subtitle-1 dense-markdown ingredient-item"> <div class="text-subtitle-1 dense-markdown ingredient-item">
<SafeMarkdown <SafeMarkdown
v-if="parsedIng.quantity" v-if="parsedIng.quantity"
class="d-inline" class="d-inline"

View file

@ -28,6 +28,7 @@
</template> </template>
<v-list-item <v-list-item
density="compact" density="compact"
class="pa-0"
@click.stop="toggleChecked(index)" @click.stop="toggleChecked(index)"
> >
<template #prepend> <template #prepend>

View file

@ -59,7 +59,7 @@
<v-app-bar <v-app-bar
color="transparent" color="transparent"
flat flat
class="mt-n1 rounded align-center px-4 position-relative w-100 left-0 top-0" class="mt-n1 rounded align-center position-relative w-100 left-0 top-0"
> >
<v-icon <v-icon
size="large" size="large"

View file

@ -9,9 +9,9 @@
closable-chips closable-chips
item-title="name" item-title="name"
multiple multiple
variant="underlined" :variant="variant"
:prepend-inner-icon="icon" :prepend-inner-icon="icon"
:append-icon="$globals.icons.create" :append-icon="showAdd ? $globals.icons.create : undefined"
return-object return-object
auto-select-first auto-select-first
class="pa-0" class="pa-0"
@ -93,6 +93,10 @@ export default defineNuxtComponent({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
variant: {
type: String as () => "filled" | "underlined" | "outlined" | "plain" | "solo" | "solo-inverted" | "solo-filled",
default: "outlined",
},
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],

View file

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<v-container v-show="!isCookMode" key="recipe-page" class="pt-0" :class="{ 'pa-0': $vuetify.display.smAndDown.value }"> <v-container v-show="!isCookMode" key="recipe-page" class="px-0" :class="{ 'pa-0': $vuetify.display.smAndDown.value }">
<v-card :flat="$vuetify.display.smAndDown.value" class="d-print-none"> <v-card :flat="$vuetify.display.smAndDown.value" class="d-print-none">
<RecipePageHeader <RecipePageHeader
:recipe="recipe" :recipe="recipe"
@ -87,11 +87,10 @@
/> />
<RecipePrintContainer :recipe="recipe" :scale="scale" /> <RecipePrintContainer :recipe="recipe" :scale="scale" />
</v-container> </v-container>
<!-- Cook mode displayes two columns with ingredients and instructions side by side, each being scrolled individually, allowing to view both at the same timer --> <!-- Cook mode displayes two columns with ingredients and instructions side by side, each being scrolled individually, allowing to view both at the same time -->
<v-sheet <v-sheet
v-show="isCookMode && !hasLinkedIngredients" v-show="isCookMode && !hasLinkedIngredients"
key="cookmode" key="cookmode"
:style="{ height: $vuetify.display.smAndUp ? 'calc(100vh - 48px)' : '' }"
> >
<!-- the calc is to account for the toolbar a more dynamic solution could be needed --> <!-- the calc is to account for the toolbar a more dynamic solution could be needed -->
<v-row style="height: 100%" no-gutters class="overflow-hidden"> <v-row style="height: 100%" no-gutters class="overflow-hidden">
@ -107,7 +106,12 @@
/> />
<v-divider /> <v-divider />
</v-col> </v-col>
<v-col class="overflow-y-auto py-2" style="height: 100%" cols="12" sm="7"> <v-col class="overflow-y-auto"
:class="$vuetify.display.smAndDown.value ? 'py-2': 'py-6'"
style="height: 100%" cols="12" sm="7">
<h2 class="text-h5 px-4 font-weight-medium opacity-80">
{{ $t('recipe.instructions') }}
</h2>
<RecipePageInstructions <RecipePageInstructions
v-model="recipe.recipeInstructions" v-model="recipe.recipeInstructions"
v-model:assets="recipe.assets" v-model:assets="recipe.assets"

View file

@ -1,31 +1,28 @@
<template> <template>
<div class="d-flex justify-start align-top py-2"> <div class="d-flex justify-start align-top flex-wrap">
<RecipeImageUploadBtn <RecipeImageUploadBtn
class="my-1" class="my-2"
:slug="recipe.slug" :slug="recipe.slug"
@upload="uploadImage" @upload="uploadImage"
@refresh="imageKey++" @refresh="imageKey++"
/> />
<RecipeSettingsMenu <RecipeSettingsMenu
v-model="recipe.settings" v-model="recipe.settings"
class="my-1 mx-1" class="my-2 mx-1"
:is-owner="recipe.userId == user.id" :is-owner="recipe.userId == user.id"
@upload="uploadImage" @upload="uploadImage"
/> />
<v-spacer /> <v-spacer />
<v-container
class="py-0"
style="width: 40%;"
>
<v-select <v-select
v-model="recipe.userId" v-model="recipe.userId"
class="my-2"
max-width="300"
:items="allUsers" :items="allUsers"
item-title="fullName" :item-props="itemsProps"
item-value="id"
:label="$t('general.owner')" :label="$t('general.owner')"
hide-details
:disabled="!canEditOwner" :disabled="!canEditOwner"
variant="underlined" variant="outlined"
density="compact"
> >
<template #prepend> <template #prepend>
<UserAvatar <UserAvatar
@ -34,17 +31,7 @@
/> />
</template> </template>
</v-select> </v-select>
<v-card-text </div>
v-if="ownerHousehold"
class="pa-0 d-flex"
style="align-items: flex-end;"
>
<v-spacer />
<v-icon>{{ $globals.icons.household }}</v-icon>
<span class="pl-1">{{ ownerHousehold.name }}</span>
</v-card-text>
</v-container>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -71,13 +58,15 @@ const canEditOwner = computed(() => {
const { store: allUsers } = useUserStore(); const { store: allUsers } = useUserStore();
const { store: households } = useHouseholdStore(); const { store: households } = useHouseholdStore();
const ownerHousehold = computed(() => {
const owner = allUsers.value.find(u => u.id === recipe.value.userId); function itemsProps(item: any) {
if (!owner) { const owner = allUsers.value.find(u => u.id === item.id);
return null; return {
} value: item.id,
return households.value.find(h => h.id === owner.householdId); title: item.fullName,
}); subtitle: owner ? households.value.find(h => h.id === owner.householdId)?.name || "" : "",
};
}
async function uploadImage(fileObject: File) { async function uploadImage(fileObject: File) {
if (!recipe.value || !recipe.value.slug) { if (!recipe.value || !recipe.value.slug) {

View file

@ -11,15 +11,17 @@
class="d-flex flex-column justify-center align-center" class="d-flex flex-column justify-center align-center"
> >
<v-card-text class="w-100"> <v-card-text class="w-100">
<v-card-title class="text-h5 font-weight-regular pa-0 d-flex flex-column align-center justify-center opacity-80"> <div class="d-flex flex-column align-center">
<v-card-title class="text-h5 font-weight-regular pa-0 text-wrap text-center opacity-80">
{{ recipe.name }} {{ recipe.name }}
</v-card-title>
<RecipeRating <RecipeRating
:key="recipe.slug" :key="recipe.slug"
:value="recipe.rating" :value="recipe.rating"
:recipe-id="recipe.id" :recipe-id="recipe.id"
:slug="recipe.slug" :slug="recipe.slug"
/> />
</v-card-title> </div>
<v-divider class="my-2" /> <v-divider class="my-2" />
<SafeMarkdown :source="recipe.description" class="my-3" /> <SafeMarkdown :source="recipe.description" class="my-3" />
<v-divider v-if="recipe.description" /> <v-divider v-if="recipe.description" />
@ -52,7 +54,7 @@
</v-col> </v-col>
</v-row> </v-row>
</div> </div>
<div class="mx-6"> <div v-if="recipe.prepTime || recipe.totalTime || recipe.performTime" class="mx-6">
<RecipeTimeCard <RecipeTimeCard
container-class="d-flex flex-wrap justify-center" container-class="d-flex flex-wrap justify-center"
:prep-time="recipe.prepTime" :prep-time="recipe.prepTime"

View file

@ -15,12 +15,13 @@
v-for="(tool, index) in recipe.tools" v-for="(tool, index) in recipe.tools"
:key="index" :key="index"
density="compact" density="compact"
class="px-1"
> >
<template #prepend> <template #prepend>
<v-checkbox <v-checkbox
v-model="recipeTools[index].onHand" v-model="recipeTools[index].onHand"
hide-details hide-details
class="pt-0 my-auto py-auto" class="pt-0 py-auto"
color="secondary" color="secondary"
density="compact" density="compact"
@change="updateTool(index)" @change="updateTool(index)"

View file

@ -60,7 +60,7 @@
{{ $t('tool.required-tools') }} {{ $t('tool.required-tools') }}
</v-card-title> </v-card-title>
<v-divider class="mx-2" /> <v-divider class="mx-2" />
<v-card-text class="pt-0"> <v-card-text>
<RecipeOrganizerSelector <RecipeOrganizerSelector
v-model="recipe.tools" v-model="recipe.tools"
selector-type="tools" selector-type="tools"

View file

@ -141,6 +141,9 @@ export default defineNuxtComponent({
</script> </script>
<style scoped> <style scoped>
.text-center {
font-size: smaller;
}
.time-card-flex { .time-card-flex {
width: fit-content; width: fit-content;
} }

View file

@ -125,6 +125,7 @@
<v-alert <v-alert
type="info" type="info"
:text="$t('search.no-results')" :text="$t('search.no-results')"
class="mb-0"
/> />
</div> </div>
</v-card-text> </v-card-text>

View file

@ -32,11 +32,11 @@
<v-row> <v-row>
<v-col cols="9"> <v-col cols="9">
<v-text-field <v-text-field
v-model="generatedSignupLink"
:label="$t('profile.invite-link')" :label="$t('profile.invite-link')"
type="text" type="text"
readonly readonly
variant="filled" variant="filled"
:value="generatedSignupLink"
/> />
</v-col> </v-col>
<v-col <v-col

View file

@ -86,9 +86,8 @@ import { usePasswordField } from "~/composables/use-passwords";
import UserPasswordStrength from "~/components/Domain/User/UserPasswordStrength.vue"; import UserPasswordStrength from "~/components/Domain/User/UserPasswordStrength.vue";
const inputAttrs = { const inputAttrs = {
rounded: true,
validateOnBlur: true, validateOnBlur: true,
class: "rounded-lg pb-1", class: "pb-1",
variant: "solo-filled" as any, variant: "solo-filled" as any,
}; };

View file

@ -105,7 +105,7 @@
</v-list-item> </v-list-item>
</template> </template>
</AppSidebar> </AppSidebar>
<v-main class="pt-16"> <v-main class="pt-12">
<v-scroll-x-transition> <v-scroll-x-transition>
<div> <div>
<NuxtPage /> <NuxtPage />

View file

@ -10,7 +10,6 @@
<v-col <v-col
v-for="(inputField, index) in items" v-for="(inputField, index) in items"
:key="index" :key="index"
class="py-0"
cols="12" cols="12"
sm="12" sm="12"
> >
@ -75,7 +74,6 @@
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))" :disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
variant="solo-filled" variant="solo-filled"
flat flat
class="rounded-lg"
rows="3" rows="3"
auto-grow auto-grow
density="comfortable" density="comfortable"
@ -95,7 +93,6 @@
:disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))" :disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))"
variant="solo-filled" variant="solo-filled"
flat flat
class="rounded-lg"
:prepend-icon="inputField.icons ? modelValue[inputField.varName] : null" :prepend-icon="inputField.icons ? modelValue[inputField.varName] : null"
:label="inputField.label" :label="inputField.label"
:name="inputField.varName" :name="inputField.varName"

View file

@ -10,6 +10,7 @@
<v-card-title class="text-h5 pl-0 py-0" style="font-weight: normal;"> <v-card-title class="text-h5 pl-0 py-0" style="font-weight: normal;">
<v-icon <v-icon
v-if="icon" v-if="icon"
size="small"
start start
> >
{{ icon }} {{ icon }}
@ -24,7 +25,7 @@
<slot /> <slot />
</p> </p>
</v-card-text> </v-card-text>
<v-divider class="mb-3" /> <v-divider class="mt-1 mb-3" />
</v-card> </v-card>
</template> </template>

View file

@ -29,7 +29,6 @@
<v-toolbar-title class="headline"> <v-toolbar-title class="headline">
{{ title }} {{ title }}
</v-toolbar-title> </v-toolbar-title>
<v-spacer />
</v-toolbar> </v-toolbar>
<v-progress-linear <v-progress-linear
v-if="loading" v-if="loading"

View file

@ -10,7 +10,7 @@
style="margin-bottom: 4rem" style="margin-bottom: 4rem"
dark dark
> >
<v-toolbar-title class="headline text-h4"> <v-toolbar-title class="headline text-h4 text-center mx-0">
Mealie Mealie
</v-toolbar-title> </v-toolbar-title>
</v-toolbar> </v-toolbar>

View file

@ -10,7 +10,6 @@
:min="min" :min="min"
:max="max" :max="max"
type="number" type="number"
class="rounded-xl"
size="small" size="small"
variant="plain" variant="plain"
/> />

View file

@ -150,6 +150,7 @@ import {
mdiCodeTags, mdiCodeTags,
mdiKnife, mdiKnife,
mdiCookie, mdiCookie,
mdiBellPlus,
} from "@mdi/js"; } from "@mdi/js";
export const icons = { export const icons = {
@ -174,6 +175,7 @@ export const icons = {
arrowUpDown: mdiDrag, arrowUpDown: mdiDrag,
backupRestore: mdiBackupRestore, backupRestore: mdiBackupRestore,
bellAlert: mdiBellAlert, bellAlert: mdiBellAlert,
bellPlus: mdiBellPlus,
broom: mdiBroom, broom: mdiBroom,
calendar: mdiCalendar, calendar: mdiCalendar,
calendarMinus: mdiCalendarMinus, calendarMinus: mdiCalendarMinus,

View file

@ -26,7 +26,7 @@
<section> <section>
<BaseCardSectionTitle class="pb-0" :icon="$globals.icons.wrench" :title="$t('admin.maintenance.summary-title')" /> <BaseCardSectionTitle class="pb-0" :icon="$globals.icons.wrench" :title="$t('admin.maintenance.summary-title')" />
<div class="mb-6 ml-2 d-flex" style="gap: 0.3rem"> <div class="mb-6 d-flex" style="gap: 0.3rem">
<BaseButton color="info" @click="getSummary"> <BaseButton color="info" @click="getSummary">
<template #icon> <template #icon>
{{ $globals.icons.tools }} {{ $globals.icons.tools }}
@ -40,7 +40,7 @@
{{ $t("admin.maintenance.button-label-open-details") }} {{ $t("admin.maintenance.button-label-open-details") }}
</BaseButton> </BaseButton>
</div> </div>
<v-card class="ma-2" :loading="state.fetchingInfo"> <v-card class="" :loading="state.fetchingInfo">
<template v-for="(value, idx) in info" :key="`item-${idx}`"> <template v-for="(value, idx) in info" :key="`item-${idx}`">
<v-list-item> <v-list-item>
<v-list-item-title class="py-2"> <v-list-item-title class="py-2">
@ -67,21 +67,23 @@
</template> </template>
</i18n-t> </i18n-t>
</BaseCardSectionTitle> </BaseCardSectionTitle>
<v-card class="ma-2" :loading="state.actionLoading"> <v-card class="ma-0" flat :loading="state.actionLoading">
<template v-for="(action, idx) in actions" :key="`item-${idx}`"> <template v-for="(action, idx) in actions" :key="`item-${idx}`">
<v-list-item class="py-1"> <v-list-item class="py-2 px-0">
<v-list-item-title> <v-list-item-title>
<div>{{ action.name }}</div> <div>{{ action.name }}</div>
<v-list-item-subtitle class="wrap-word"> <v-list-item-subtitle class="wrap-word">
{{ action.subtitle }} {{ action.subtitle }}
</v-list-item-subtitle> </v-list-item-subtitle>
</v-list-item-title> </v-list-item-title>
<template #append>
<BaseButton color="info" @click="action.handler"> <BaseButton color="info" @click="action.handler">
<template #icon> <template #icon>
{{ $globals.icons.robot }} {{ $globals.icons.robot }}
</template> </template>
{{ $t("general.run") }} {{ $t("general.run") }}
</BaseButton> </BaseButton>
</template>
</v-list-item> </v-list-item>
<v-divider class="mx-2" /> <v-divider class="mx-2" />
</template> </template>

View file

@ -33,7 +33,6 @@
:items="groups" :items="groups"
variant="solo-filled" variant="solo-filled"
flat flat
class="rounded-lg"
item-title="name" item-title="name"
item-value="id" item-value="id"
:return-object="false" :return-object="false"

View file

@ -12,8 +12,6 @@
v-if="groups" v-if="groups"
v-model="createHouseholdForm.data.groupId" v-model="createHouseholdForm.data.groupId"
:items="groups" :items="groups"
rounded
class="rounded-lg"
item-title="name" item-title="name"
item-value="id" item-value="id"
:return-object="false" :return-object="false"

View file

@ -24,8 +24,6 @@
v-if="groups" v-if="groups"
v-model="selectedGroupId" v-model="selectedGroupId"
:items="groups" :items="groups"
rounded
class="rounded-lg"
item-title="name" item-title="name"
item-value="id" item-value="id"
:return-object="false" :return-object="false"
@ -37,8 +35,6 @@
v-model="newUserData.household" v-model="newUserData.household"
:disabled="!selectedGroupId" :disabled="!selectedGroupId"
:items="households" :items="households"
rounded
class="rounded-lg"
item-title="name" item-title="name"
item-value="name" item-value="name"
:return-object="false" :return-object="false"

View file

@ -17,10 +17,10 @@
<v-form @submit.prevent="requestLink()"> <v-form @submit.prevent="requestLink()">
<v-text-field <v-text-field
v-model="email" v-model="email"
variant="filled" :prepend-inner-icon="$globals.icons.email"
rounded variant="solo-filled"
flat
autofocus autofocus
class="rounded-lg"
name="login" name="login"
:label="$t('user.email')" :label="$t('user.email')"
type="text" type="text"

View file

@ -7,7 +7,7 @@
<v-card-text> <v-card-text>
{{ $t('recipe.recipe-bulk-importer-description') }} {{ $t('recipe.recipe-bulk-importer-description') }}
</v-card-text> </v-card-text>
</div> <div class="px-4">
<section class="mt-2"> <section class="mt-2">
<v-row <v-row
v-for="(_, idx) in bulkUrls" v-for="(_, idx) in bulkUrls"
@ -54,6 +54,7 @@
cols="12" cols="12"
xs="12" xs="12"
sm="6" sm="6"
class="py-0"
> >
<RecipeOrganizerSelector <RecipeOrganizerSelector
v-model="bulkUrls[idx].categories" v-model="bulkUrls[idx].categories"
@ -73,6 +74,7 @@
cols="12" cols="12"
xs="12" xs="12"
sm="6" sm="6"
class="pt-0 pb-4"
> >
<RecipeOrganizerSelector <RecipeOrganizerSelector
v-model="bulkUrls[idx].tags" v-model="bulkUrls[idx].tags"
@ -90,8 +92,9 @@
</v-col> </v-col>
</template> </template>
</v-row> </v-row>
<v-card-actions class="justify-end flex-wrap mb-1"> <v-card-actions class="justify-end flex-wrap mt-3 pa-0">
<BaseButton <BaseButton
class="mt-1 pr-4"
delete delete
@click=" @click="
bulkUrls = []; bulkUrls = [];
@ -117,23 +120,26 @@
@bulk-data="assignUrls" @bulk-data="assignUrls"
/> />
</v-card-actions> </v-card-actions>
<div class="px-1"> <div class="px-0">
<v-checkbox <v-checkbox
v-model="showCatTags" v-model="showCatTags"
hide-details hide-details
:label="$t('recipe.set-categories-and-tags')" :label="$t('recipe.set-categories-and-tags')"
/> />
</div> </div>
<v-card-actions class="justify-end"> <v-card-actions class="justify-center">
<div style="width: 250px">
<BaseButton <BaseButton
:disabled="bulkUrls.length === 0 || lockBulkImport" :disabled="bulkUrls.length === 0 || lockBulkImport"
rounded
block
@click="bulkCreate" @click="bulkCreate"
> >
<template #icon> <template #icon>
{{ $globals.icons.check }} {{ $globals.icons.check }}
</template> </template>
{{ $t('general.submit') }}
</BaseButton> </BaseButton>
</div>
</v-card-actions> </v-card-actions>
</section> </section>
<section class="mt-12"> <section class="mt-12">
@ -144,6 +150,8 @@
/> />
</section> </section>
</div> </div>
</div>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View file

@ -61,7 +61,6 @@
<div style="width: 250px"> <div style="width: 250px">
<BaseButton <BaseButton
:disabled="!newRecipeData" :disabled="!newRecipeData"
large
rounded rounded
block block
type="submit" type="submit"

View file

@ -25,7 +25,6 @@
<div style="width: 250px"> <div style="width: 250px">
<BaseButton <BaseButton
:disabled="newRecipeZip === null" :disabled="newRecipeZip === null"
large
rounded rounded
block block
:loading="loading" :loading="loading"

View file

@ -2,7 +2,7 @@
<div> <div>
<BasePageTitle <BasePageTitle
v-if="groupName" v-if="groupName"
class="bg-grey-darken-4 mt-n4 pt-8" class="mt-n4 pt-8"
> >
<template #header> <template #header>
<v-img <v-img

View file

@ -15,11 +15,12 @@
</template> </template>
{{ $t('migration.recipe-data-migrations-explanation') }} {{ $t('migration.recipe-data-migrations-explanation') }}
</BasePageTitle> </BasePageTitle>
<v-container> <v-container :class="$vuetify.display.smAndDown ? 'px-0': ''">
<BaseCardSectionTitle :title="$t('migration.new-migration')" /> <BaseCardSectionTitle :title="$t('migration.new-migration')" />
<v-card <v-card
variant="outlined" variant="outlined"
:loading="loading" :loading="loading"
style="border-color: lightgrey;"
> >
<v-card-title> {{ $t('migration.choose-migration-type') }} </v-card-title> <v-card-title> {{ $t('migration.choose-migration-type') }} </v-card-title>
<v-card-text <v-card-text
@ -83,7 +84,7 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-container> </v-container>
<v-container> <v-container class="$vuetify.display.smAndDown ? 'px-0': ''">
<BaseCardSectionTitle :title="$t('migration.previous-migrations')" /> <BaseCardSectionTitle :title="$t('migration.previous-migrations')" />
<ReportTable <ReportTable
:items="reports" :items="reports"

View file

@ -15,6 +15,7 @@
<BaseDialog <BaseDialog
v-model="createDialog" v-model="createDialog"
:title="$t('events.new-notification')" :title="$t('events.new-notification')"
:icon="$globals.icons.bellPlus"
can-submit can-submit
@submit="createNewNotifier" @submit="createNewNotifier"
> >
@ -95,7 +96,7 @@
> >
<v-expansion-panel-title <v-expansion-panel-title
disable-icon-rotate disable-icon-rotate
class="headline" class="text-h6"
> >
<div class="d-flex align-center"> <div class="d-flex align-center">
{{ notifier.name }} {{ notifier.name }}
@ -103,6 +104,7 @@
<template #actions> <template #actions>
<v-btn <v-btn
icon icon
flat
class="ml-2" class="ml-2"
> >
<v-icon> <v-icon>

View file

@ -47,7 +47,7 @@
<!-- Form Container --> <!-- Form Container -->
<div class="d-flex justify-center grow items-center my-4"> <div class="d-flex justify-center grow items-center my-4">
<template v-if="state.ctx.state === States.Initial"> <template v-if="state.ctx.state === States.Initial">
<div width="600px"> <v-container>
<v-card-title class="text-h5 my-4 mb-5 pb-0 text-center"> <v-card-title class="text-h5 my-4 mb-5 pb-0 text-center">
{{ $t("user-registration.user-registration") }} {{ $t("user-registration.user-registration") }}
</v-card-title> </v-card-title>
@ -60,7 +60,7 @@
color="primary" color="primary"
dark dark
hover hover
width="300px" width="320px"
@click="initial.joinGroup" @click="initial.joinGroup"
> >
<v-card-title class="d-flex align-center justify-center py-3"> <v-card-title class="d-flex align-center justify-center py-3">
@ -77,7 +77,7 @@
color="primary" color="primary"
dark dark
hover hover
width="300px" width="320px"
@click="initial.createGroup" @click="initial.createGroup"
> >
<v-card-title class="d-flex align-center justify-center py-3"> <v-card-title class="d-flex align-center justify-center py-3">
@ -92,7 +92,7 @@
</v-card-title> </v-card-title>
</v-card> </v-card>
</div> </div>
</div> </v-container>
</template> </template>
<template v-else-if="state.ctx.state === States.ProvideToken"> <template v-else-if="state.ctx.state === States.ProvideToken">
@ -333,9 +333,7 @@ import type { VForm } from "~/types/auto-forms";
const inputAttrs = { const inputAttrs = {
variant: "filled", variant: "filled",
rounded: true,
validateOnBlur: true, validateOnBlur: true,
class: "rounded-lg",
}; };
export default defineNuxtComponent({ export default defineNuxtComponent({

View file

@ -17,21 +17,19 @@
<v-form @submit.prevent="requestLink()"> <v-form @submit.prevent="requestLink()">
<v-text-field <v-text-field
v-model="email" v-model="email"
:prepend-icon="$globals.icons.email" :prepend-inner-icon="$globals.icons.email"
variant="filled" variant="solo-filled"
rounded flat
autofocus autofocus
class="rounded-lg"
name="login" name="login"
:label="$t('user.email')" :label="$t('user.email')"
type="text" type="text"
/> />
<v-text-field <v-text-field
v-model="password" v-model="password"
variant="filled" variant="solo-filled"
rounded flat
class="rounded-lg" :prepend-inner-icon="$globals.icons.lock"
:prepend-icon="$globals.icons.lock"
name="password" name="password"
:label="$t('user.password')" :label="$t('user.password')"
type="password" type="password"
@ -39,11 +37,10 @@
/> />
<v-text-field <v-text-field
v-model="passwordConfirm" v-model="passwordConfirm"
variant="filled" variant="solo-filled"
rounded flat
validate-on="blur" validate-on="blur"
class="rounded-lg" :prepend-inner-icon="$globals.icons.lock"
:prepend-icon="$globals.icons.lock"
name="password" name="password"
:label="$t('user.confirm-password')" :label="$t('user.confirm-password')"
type="password" type="password"

View file

@ -17,9 +17,10 @@
<section class="d-flex justify-center"> <section class="d-flex justify-center">
<v-card <v-card
class="mt-4" class="mt-4"
width="500px" width="100%"
flat
> >
<v-card-text> <v-card-text class="px-0">
<v-form <v-form
ref="domNewTokenForm" ref="domNewTokenForm"
@submit.prevent @submit.prevent
@ -38,16 +39,16 @@
readonly readonly
rows="3" rows="3"
/> />
<v-list-subheader class="text-center"> <p>
{{ {{
$t( $t(
"settings.token.copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again", "settings.token.copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again",
) )
}} }}
</v-list-subheader> </p>
</template> </template>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions class="px-0">
<BaseButton <BaseButton
v-if="createdToken" v-if="createdToken"
cancel cancel
@ -78,13 +79,10 @@
:title="$t('settings.token.active-tokens')" :title="$t('settings.token.active-tokens')"
/> />
<section class="d-flex flex-column"> <section class="d-flex flex-column">
<v-list>
<div <div
v-for="(token, index) in user.tokens" v-for="(token, index) in user.tokens"
:key="index" :key="index"
>
<v-card
variant="outlined"
class="mb-2"
> >
<v-list-item> <v-list-item>
<v-list-item-title> <v-list-item-title>
@ -93,16 +91,17 @@
<v-list-item-subtitle> <v-list-item-subtitle>
{{ $t('general.created-on-date', [$d(new Date(token.createdAt!))]) }} {{ $t('general.created-on-date', [$d(new Date(token.createdAt!))]) }}
</v-list-item-subtitle> </v-list-item-subtitle>
<v-list-item-action> <template #append>
<BaseButton <BaseButton
delete delete
small small
@click="deleteToken(token.id)" @click="deleteToken(token.id)"
/> />
</v-list-item-action> </template>
</v-list-item> </v-list-item>
</v-card> <v-divider class="mx-2 my-2" />
</div> </div>
</v-list>
</section> </section>
</v-container> </v-container>
</template> </template>

View file

@ -1,5 +1,5 @@
<template> <template>
<v-container v-if="user"> <v-container v-if="user" class="mb-8">
<section class="d-flex flex-column align-center mt-4"> <section class="d-flex flex-column align-center mt-4">
<UserAvatar <UserAvatar
:tooltip="false" :tooltip="false"
@ -7,7 +7,7 @@
:user-id="user.id" :user-id="user.id"
/> />
<h2 class="text-h4"> <h2 class="text-h4 text-center">
{{ $t('profile.welcome-user', [user.fullName]) }} {{ $t('profile.welcome-user', [user.fullName]) }}
</h2> </h2>
<p class="subtitle-1 mb-0 text-center"> <p class="subtitle-1 mb-0 text-center">

View file

@ -449,7 +449,7 @@ class AppSettings(AppLoggingSettings):
def app_settings_constructor(data_dir: Path, production: bool, env_file: Path, env_encoding="utf-8") -> AppSettings: def app_settings_constructor(data_dir: Path, production: bool, env_file: Path, env_encoding="utf-8") -> AppSettings:
""" """
app_settings_constructor is a factory function that returns an AppSettings object. It is used to inject the app_settings_constructor is a factory function that returns an AppSettings object. It is used to inject the
required dependencies into the AppSettings object and nested child objects. AppSettings should not be substantiated required dependencies into the AppSettings object and nested child objects. AppSettings should not be instantiated
directly, but rather through this factory function. directly, but rather through this factory function.
""" """
secret_settings = { secret_settings = {