mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-10-26 15:54:20 -04:00
chore: Add Stricter Frontend Formatting (#6262)
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -59,8 +59,11 @@
|
||||
"netlify.toml": "runtime.txt",
|
||||
"README.md": "LICENSE, SECURITY.md"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.formatOnSave": false
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[python]": {
|
||||
"editor.formatOnSave": true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@@ -105,12 +106,16 @@ def main():
|
||||
# Flatten list of lists
|
||||
all_children = [item for sublist in all_children for item in sublist]
|
||||
|
||||
out_path = GENERATED / "__init__.py"
|
||||
render_python_template(
|
||||
TEMPLATE,
|
||||
GENERATED / "__init__.py",
|
||||
out_path,
|
||||
{"children": all_children},
|
||||
)
|
||||
|
||||
subprocess.run(["poetry", "run", "ruff", "check", str(out_path), "--fix"])
|
||||
subprocess.run(["poetry", "run", "ruff", "format", str(out_path)])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from utils import PROJECT_DIR, log, render_python_template
|
||||
@@ -84,16 +85,23 @@ def find_modules(root: pathlib.Path) -> list[Modules]:
|
||||
return modules
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
modules = find_modules(SCHEMA_PATH)
|
||||
|
||||
template_paths: list[pathlib.Path] = []
|
||||
for module in modules:
|
||||
log.debug(f"Module: {module.directory.name}")
|
||||
for file in module.files:
|
||||
log.debug(f" File: {file.import_path}")
|
||||
log.debug(f" Classes: [{', '.join(file.classes)}]")
|
||||
|
||||
render_python_template(template, module.directory / "__init__.py", {"module": module})
|
||||
template_path = module.directory / "__init__.py"
|
||||
template_paths.append(template_path)
|
||||
render_python_template(template, template_path, {"module": module})
|
||||
|
||||
path_args = (str(p) for p in template_paths)
|
||||
subprocess.run(["poetry", "run", "ruff", "check", *path_args, "--fix"])
|
||||
subprocess.run(["poetry", "run", "ruff", "format", *path_args])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Template
|
||||
@@ -189,6 +190,7 @@ def generate_typescript_types() -> None: # noqa: C901
|
||||
skipped_dirs: list[Path] = []
|
||||
failed_modules: list[Path] = []
|
||||
|
||||
out_paths: list[Path] = []
|
||||
for module in schema_path.iterdir():
|
||||
if module.is_dir() and module.stem in ignore_dirs:
|
||||
skipped_dirs.append(module)
|
||||
@@ -205,10 +207,18 @@ def generate_typescript_types() -> None: # noqa: C901
|
||||
path_as_module = path_to_module(module)
|
||||
generate_typescript_defs(path_as_module, str(out_path), exclude=("MealieModel")) # type: ignore
|
||||
clean_output_file(out_path)
|
||||
out_paths.append(out_path)
|
||||
except Exception:
|
||||
failed_modules.append(module)
|
||||
log.exception(f"Module Error: {module}")
|
||||
|
||||
# Run ESLint --fix on the files to clean up any formatting issues
|
||||
subprocess.run(
|
||||
["yarn", "lint", "--fix", *(str(path) for path in out_paths)],
|
||||
check=True,
|
||||
cwd=PROJECT_DIR / "frontend",
|
||||
)
|
||||
|
||||
log.debug("\n📁 Skipped Directories:")
|
||||
for skipped_dir in skipped_dirs:
|
||||
log.debug(f" 📁 {skipped_dir.name}")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@@ -23,11 +22,6 @@ def render_python_template(template_file: Path | str, dest: Path, data: dict):
|
||||
|
||||
dest.write_text(text)
|
||||
|
||||
# lint/format file with Ruff
|
||||
log.info(f"Formatting {dest}")
|
||||
subprocess.run(["poetry", "run", "ruff", "check", str(dest), "--fix"])
|
||||
subprocess.run(["poetry", "run", "ruff", "format", str(dest)])
|
||||
|
||||
|
||||
@dataclass
|
||||
class CodeSlicer:
|
||||
@@ -37,7 +31,7 @@ class CodeSlicer:
|
||||
indentation: str | None
|
||||
text: list[str]
|
||||
|
||||
_next_line = None
|
||||
_next_line: int | None = None
|
||||
|
||||
def purge_lines(self) -> None:
|
||||
start = self.start + 1
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
>
|
||||
<div class="d-flex align-center w-100 mb-2">
|
||||
<v-toolbar-title class="headline mb-0">
|
||||
<v-icon size="large" class="mr-3">
|
||||
{{ $globals.icons.pages }}
|
||||
</v-icon>
|
||||
<v-icon size="large" class="mr-3">
|
||||
{{ $globals.icons.pages }}
|
||||
</v-icon>
|
||||
{{ book.name }}
|
||||
</v-toolbar-title>
|
||||
<BaseButton
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
<template>
|
||||
<div v-if="preferences">
|
||||
<BaseCardSectionTitle :title="$t('household.household-preferences')" />
|
||||
<div class="mb-6">
|
||||
<v-checkbox v-model="preferences.privateHousehold" hide-details density="compact" :label="$t('household.private-household')" color="primary" />
|
||||
<div class="ml-8">
|
||||
<p class="text-subtitle-2 my-0 py-0">
|
||||
{{ $t("household.private-household-description") }}
|
||||
</p>
|
||||
<DocLink class="mt-2" link="/documentation/getting-started/faq/#how-do-private-groups-and-recipes-work" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<v-checkbox v-model="preferences.lockRecipeEditsFromOtherHouseholds" hide-details density="compact" :label="$t('household.lock-recipe-edits-from-other-households')" color="primary" />
|
||||
<div class="ml-8">
|
||||
<p class="text-subtitle-2 my-0 py-0">
|
||||
{{ $t("household.lock-recipe-edits-from-other-households-description") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<v-select
|
||||
v-model="preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
variant="underlined"
|
||||
flat
|
||||
/>
|
||||
<div v-if="preferences">
|
||||
<BaseCardSectionTitle :title="$t('household.household-preferences')" />
|
||||
<div class="mb-6">
|
||||
<v-checkbox v-model="preferences.privateHousehold" hide-details density="compact" :label="$t('household.private-household')" color="primary" />
|
||||
<div class="ml-8">
|
||||
<p class="text-subtitle-2 my-0 py-0">
|
||||
{{ $t("household.private-household-description") }}
|
||||
</p>
|
||||
<DocLink class="mt-2" link="/documentation/getting-started/faq/#how-do-private-groups-and-recipes-work" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<v-checkbox v-model="preferences.lockRecipeEditsFromOtherHouseholds" hide-details density="compact" :label="$t('household.lock-recipe-edits-from-other-households')" color="primary" />
|
||||
<div class="ml-8">
|
||||
<p class="text-subtitle-2 my-0 py-0">
|
||||
{{ $t("household.lock-recipe-edits-from-other-households-description") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<v-select
|
||||
v-model="preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-title="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
variant="underlined"
|
||||
flat
|
||||
/>
|
||||
|
||||
<BaseCardSectionTitle class="mt-5" :title="$t('household.household-recipe-preferences')" />
|
||||
<div class="preference-container">
|
||||
<div v-for="p in recipePreferences" :key="p.key">
|
||||
<v-checkbox v-model="preferences[p.key]" hide-details density="compact" :label="p.label" color="primary" />
|
||||
<p class="ml-8 text-subtitle-2 my-0 py-0">
|
||||
{{ p.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BaseCardSectionTitle class="mt-5" :title="$t('household.household-recipe-preferences')" />
|
||||
<div class="preference-container">
|
||||
<div v-for="p in recipePreferences" :key="p.key">
|
||||
<v-checkbox v-model="preferences[p.key]" hide-details density="compact" :label="p.label" color="primary" />
|
||||
<p class="ml-8 text-subtitle-2 my-0 py-0">
|
||||
{{ p.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -432,9 +432,9 @@ function removeField(index: number) {
|
||||
|
||||
const fieldsUpdater = useDebounceFn((/* newFields: typeof fields.value */) => {
|
||||
/* newFields.forEach((field, index) => {
|
||||
const updatedField = getFieldFromFieldDef(field);
|
||||
fields.value[index] = updatedField; // recursive!!!
|
||||
}); */
|
||||
const updatedField = getFieldFromFieldDef(field);
|
||||
fields.value[index] = updatedField; // recursive!!!
|
||||
}); */
|
||||
|
||||
const qf = buildQueryFilterString(fields.value, state.showAdvanced);
|
||||
if (qf) {
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
density="compact"
|
||||
elevation="0"
|
||||
>
|
||||
<BaseDialog v-model="deleteDialog" :title="$t('recipe.delete-recipe')" color="error"
|
||||
:icon="$globals.icons.alertCircle" can-confirm @confirm="emitDelete()">
|
||||
<BaseDialog
|
||||
v-model="deleteDialog"
|
||||
:title="$t('recipe.delete-recipe')"
|
||||
color="error"
|
||||
:icon="$globals.icons.alertCircle"
|
||||
can-confirm
|
||||
@confirm="emitDelete()"
|
||||
>
|
||||
<v-card-text>
|
||||
{{ $t("recipe.delete-confirmation") }}
|
||||
</v-card-text>
|
||||
@@ -15,7 +21,14 @@
|
||||
<v-spacer />
|
||||
<div v-if="!open" class="custom-btn-group ma-1">
|
||||
<RecipeFavoriteBadge v-if="loggedIn" color="info" button-style :recipe-id="recipe.id!" show-always />
|
||||
<RecipeTimelineBadge v-if="loggedIn" class="ml-1" color="info" button-style :slug="recipe.slug" :recipe-name="recipe.name!" />
|
||||
<RecipeTimelineBadge
|
||||
v-if="loggedIn"
|
||||
class="ml-1"
|
||||
color="info"
|
||||
button-style
|
||||
:slug="recipe.slug"
|
||||
:recipe-name="recipe.name!"
|
||||
/>
|
||||
<div v-if="loggedIn">
|
||||
<v-tooltip v-if="canEdit" location="bottom" color="info">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
<template>
|
||||
<!-- Wrap v-hover with a div to provide a proper DOM element for the transition -->
|
||||
<div>
|
||||
<v-hover
|
||||
v-slot="{ isHovering, props: hoverProps }"
|
||||
:open-delay="50"
|
||||
<div>
|
||||
<v-hover
|
||||
v-slot="{ isHovering, props: hoverProps }"
|
||||
:open-delay="50"
|
||||
>
|
||||
<v-card
|
||||
v-bind="hoverProps"
|
||||
:class="{ 'on-hover': isHovering }"
|
||||
:style="{ cursor }"
|
||||
:elevation="isHovering ? 12 : 2"
|
||||
:to="recipeRoute"
|
||||
:min-height="imageHeight + 75"
|
||||
@click.self="$emit('click')"
|
||||
>
|
||||
<v-card
|
||||
v-bind="hoverProps"
|
||||
:class="{ 'on-hover': isHovering }"
|
||||
:style="{ cursor }"
|
||||
:elevation="isHovering ? 12 : 2"
|
||||
:to="recipeRoute"
|
||||
:min-height="imageHeight + 75"
|
||||
@click.self="$emit('click')"
|
||||
<RecipeCardImage
|
||||
:icon-size="imageHeight"
|
||||
:height="imageHeight"
|
||||
:slug="slug"
|
||||
:recipe-id="recipeId"
|
||||
size="small"
|
||||
:image-version="image"
|
||||
>
|
||||
<RecipeCardImage
|
||||
:icon-size="imageHeight"
|
||||
:height="imageHeight"
|
||||
:slug="slug"
|
||||
:recipe-id="recipeId"
|
||||
size="small"
|
||||
:image-version="image"
|
||||
>
|
||||
<v-expand-transition v-if="description">
|
||||
<div
|
||||
v-if="isHovering"
|
||||
class="d-flex transition-fast-in-fast-out bg-secondary v-card--reveal"
|
||||
style="height: 100%"
|
||||
>
|
||||
<v-card-text class="v-card--text-show white--text">
|
||||
<div class="descriptionWrapper">
|
||||
<SafeMarkdown :source="description" />
|
||||
</div>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</RecipeCardImage>
|
||||
<v-card-title class="mb-n3 px-4">
|
||||
<div class="headerClass">
|
||||
{{ name }}
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
||||
<slot name="actions">
|
||||
<v-card-actions
|
||||
v-if="showRecipeContent"
|
||||
class="px-1"
|
||||
<v-expand-transition v-if="description">
|
||||
<div
|
||||
v-if="isHovering"
|
||||
class="d-flex transition-fast-in-fast-out bg-secondary v-card--reveal"
|
||||
style="height: 100%"
|
||||
>
|
||||
<RecipeFavoriteBadge
|
||||
v-if="isOwnGroup"
|
||||
class="absolute"
|
||||
:recipe-id="recipeId"
|
||||
show-always
|
||||
/>
|
||||
<div v-else class="px-1" /> <!-- Empty div to keep the layout consistent -->
|
||||
<v-card-text class="v-card--text-show white--text">
|
||||
<div class="descriptionWrapper">
|
||||
<SafeMarkdown :source="description" />
|
||||
</div>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-expand-transition>
|
||||
</RecipeCardImage>
|
||||
<v-card-title class="mb-n3 px-4">
|
||||
<div class="headerClass">
|
||||
{{ name }}
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
||||
<RecipeCardRating
|
||||
:model-value="rating"
|
||||
:recipe-id="recipeId"
|
||||
/>
|
||||
<v-spacer />
|
||||
<RecipeChips
|
||||
:truncate="true"
|
||||
:items="tags"
|
||||
:title="false"
|
||||
:limit="2"
|
||||
small
|
||||
url-prefix="tags"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
<slot name="actions">
|
||||
<v-card-actions
|
||||
v-if="showRecipeContent"
|
||||
class="px-1"
|
||||
>
|
||||
<RecipeFavoriteBadge
|
||||
v-if="isOwnGroup"
|
||||
class="absolute"
|
||||
:recipe-id="recipeId"
|
||||
show-always
|
||||
/>
|
||||
<div v-else class="px-1" /> <!-- Empty div to keep the layout consistent -->
|
||||
|
||||
<!-- If we're not logged-in, no items display, so we hide this menu -->
|
||||
<RecipeContextMenu
|
||||
v-if="isOwnGroup && showRecipeContent"
|
||||
color="grey-darken-2"
|
||||
:slug="slug"
|
||||
:menu-icon="$globals.icons.dotsVertical"
|
||||
:name="name"
|
||||
:recipe-id="recipeId"
|
||||
:use-items="{
|
||||
delete: false,
|
||||
edit: false,
|
||||
download: true,
|
||||
mealplanner: true,
|
||||
shoppingList: true,
|
||||
print: false,
|
||||
printPreferences: false,
|
||||
share: true,
|
||||
}"
|
||||
@deleted="$emit('delete', slug)"
|
||||
/>
|
||||
</v-card-actions>
|
||||
</slot>
|
||||
<slot />
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</div>
|
||||
<RecipeCardRating
|
||||
:model-value="rating"
|
||||
:recipe-id="recipeId"
|
||||
/>
|
||||
<v-spacer />
|
||||
<RecipeChips
|
||||
:truncate="true"
|
||||
:items="tags"
|
||||
:title="false"
|
||||
:limit="2"
|
||||
small
|
||||
url-prefix="tags"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
|
||||
<!-- If we're not logged-in, no items display, so we hide this menu -->
|
||||
<RecipeContextMenu
|
||||
v-if="isOwnGroup && showRecipeContent"
|
||||
color="grey-darken-2"
|
||||
:slug="slug"
|
||||
:menu-icon="$globals.icons.dotsVertical"
|
||||
:name="name"
|
||||
:recipe-id="recipeId"
|
||||
:use-items="{
|
||||
delete: false,
|
||||
edit: false,
|
||||
download: true,
|
||||
mealplanner: true,
|
||||
shoppingList: true,
|
||||
print: false,
|
||||
printPreferences: false,
|
||||
share: true,
|
||||
}"
|
||||
@deleted="$emit('delete', slug)"
|
||||
/>
|
||||
</v-card-actions>
|
||||
</slot>
|
||||
<slot />
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
@deleted="$emit('delete', slug)"
|
||||
/>
|
||||
</v-card-actions>
|
||||
</slot>
|
||||
</slot>
|
||||
</v-list-item>
|
||||
<slot />
|
||||
</v-card>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
hide-header
|
||||
:first-day-of-week="firstDayOfWeek"
|
||||
:local="$i18n.locale"
|
||||
@update:model-value="pickerMenu = false"
|
||||
@update:model-value="pickerMenu = false"
|
||||
/>
|
||||
</v-menu>
|
||||
<v-select
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<BaseDialog v-model="dialog" :title="$t('data-pages.manage-aliases')" :icon="$globals.icons.edit"
|
||||
:submit-icon="$globals.icons.check" :submit-text="$t('general.confirm')" can-submit @submit="saveAliases"
|
||||
@cancel="$emit('cancel')">
|
||||
<BaseDialog
|
||||
v-model="dialog"
|
||||
:title="$t('data-pages.manage-aliases')"
|
||||
:icon="$globals.icons.edit"
|
||||
:submit-icon="$globals.icons.check"
|
||||
:submit-text="$t('general.confirm')"
|
||||
can-submit
|
||||
@submit="saveAliases"
|
||||
@cancel="$emit('cancel')"
|
||||
>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row v-for="alias, i in aliases" :key="i">
|
||||
@@ -10,13 +17,16 @@
|
||||
<v-text-field v-model="alias.name" :label="$t('general.name')" :rules="[validators.required]" />
|
||||
</v-col>
|
||||
<v-col cols="2">
|
||||
<BaseButtonGroup :buttons="[
|
||||
{
|
||||
icon: $globals.icons.delete,
|
||||
text: $t('general.delete'),
|
||||
event: 'delete',
|
||||
},
|
||||
]" @delete="deleteAlias(i)" />
|
||||
<BaseButtonGroup
|
||||
:buttons="[
|
||||
{
|
||||
icon: $globals.icons.delete,
|
||||
text: $t('general.delete'),
|
||||
event: 'delete',
|
||||
},
|
||||
]"
|
||||
@delete="deleteAlias(i)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
@@ -113,9 +113,13 @@
|
||||
/>
|
||||
<v-divider />
|
||||
</v-col>
|
||||
<v-col class="overflow-y-auto"
|
||||
:class="$vuetify.display.smAndDown ? 'py-2': 'py-6'"
|
||||
style="height: 100%" cols="12" sm="7">
|
||||
<v-col
|
||||
class="overflow-y-auto"
|
||||
:class="$vuetify.display.smAndDown ? '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>
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
@upload="uploadImage"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-select
|
||||
v-model="recipe.userId"
|
||||
class="my-2"
|
||||
max-width="300"
|
||||
:items="allUsers"
|
||||
:item-props="itemsProps"
|
||||
:label="$t('general.owner')"
|
||||
:disabled="!canEditOwner"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
>
|
||||
<template #prepend>
|
||||
<UserAvatar
|
||||
:user-id="recipe.userId"
|
||||
:tooltip="false"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
<v-select
|
||||
v-model="recipe.userId"
|
||||
class="my-2"
|
||||
max-width="300"
|
||||
:items="allUsers"
|
||||
:item-props="itemsProps"
|
||||
:label="$t('general.owner')"
|
||||
:disabled="!canEditOwner"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
>
|
||||
<template #prepend>
|
||||
<UserAvatar
|
||||
:user-id="recipe.userId"
|
||||
:tooltip="false"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
class="mb-2 mx-n2"
|
||||
>
|
||||
<v-card-title class="text-h5 font-weight-medium opacity-80">
|
||||
{{ $t('recipe.api-extras') }}
|
||||
</v-card-title>
|
||||
{{ $t('recipe.api-extras') }}
|
||||
</v-card-title>
|
||||
<v-divider class="ml-4" />
|
||||
<v-card-text>
|
||||
{{ $t('recipe.api-extras-description') }}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
>
|
||||
<v-card-text class="w-100">
|
||||
<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 }}
|
||||
</v-card-title>
|
||||
<RecipeRating
|
||||
<v-card-title class="text-h5 font-weight-regular pa-0 text-wrap text-center opacity-80">
|
||||
{{ recipe.name }}
|
||||
</v-card-title>
|
||||
<RecipeRating
|
||||
:key="recipe.slug"
|
||||
:value="recipe.rating"
|
||||
:recipe-id="recipe.id"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!-- eslint-disable vue/no-mutating-props -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-4">
|
||||
|
||||
@@ -79,8 +79,8 @@
|
||||
<BaseButton
|
||||
v-if="
|
||||
currentMissingUnit
|
||||
&& currentIng.ingredient.unit?.id
|
||||
&& currentMissingUnit.toLowerCase() != currentIng.ingredient.unit?.name.toLowerCase()
|
||||
&& currentIng.ingredient.unit?.id
|
||||
&& currentMissingUnit.toLowerCase() != currentIng.ingredient.unit?.name.toLowerCase()
|
||||
"
|
||||
color="warning"
|
||||
size="small"
|
||||
@@ -99,8 +99,8 @@
|
||||
<BaseButton
|
||||
v-if="
|
||||
currentMissingFood
|
||||
&& currentIng.ingredient.food?.id
|
||||
&& currentMissingFood.toLowerCase() != currentIng.ingredient.food?.name.toLowerCase()
|
||||
&& currentIng.ingredient.food?.id
|
||||
&& currentMissingFood.toLowerCase() != currentIng.ingredient.food?.name.toLowerCase()
|
||||
"
|
||||
color="warning"
|
||||
size="small"
|
||||
@@ -176,9 +176,9 @@
|
||||
:text="$t(currentIngShouldDelete ? 'recipe.parser.delete-item' : 'general.next')"
|
||||
@click="nextIngredient"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Review -->
|
||||
<div v-else>
|
||||
<div v-else>
|
||||
<BaseButton
|
||||
create
|
||||
:text="$t('general.save')"
|
||||
@@ -186,7 +186,7 @@
|
||||
:loading="state.loading.save"
|
||||
@click="saveIngs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
</template>
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
<section>
|
||||
<v-container class="ma-0 pa-0">
|
||||
<v-row>
|
||||
<v-col v-if="preferences.imagePosition && preferences.imagePosition != ImagePosition.hidden"
|
||||
:order="preferences.imagePosition == ImagePosition.left ? -1 : 1"
|
||||
cols="4"
|
||||
align-self="center"
|
||||
<v-col
|
||||
v-if="preferences.imagePosition && preferences.imagePosition != ImagePosition.hidden"
|
||||
:order="preferences.imagePosition == ImagePosition.left ? -1 : 1"
|
||||
cols="4"
|
||||
align-self="center"
|
||||
>
|
||||
<img :key="imageKey"
|
||||
:src="recipeImageUrl"
|
||||
style="min-height: 50; max-width: 100%;"
|
||||
<img
|
||||
:key="imageKey"
|
||||
:src="recipeImageUrl"
|
||||
style="min-height: 50; max-width: 100%;"
|
||||
>
|
||||
</v-col>
|
||||
<v-col order="0">
|
||||
<v-card-title class="headline pl-0">
|
||||
<v-icon start
|
||||
color="primary"
|
||||
<v-icon
|
||||
start
|
||||
color="primary"
|
||||
>
|
||||
{{ $globals.icons.primary }}
|
||||
</v-icon>
|
||||
@@ -36,17 +39,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<v-row class="d-flex justify-start">
|
||||
<RecipeTimeCard :prep-time="recipe.prepTime"
|
||||
:total-time="recipe.totalTime"
|
||||
:perform-time="recipe.performTime"
|
||||
small
|
||||
color="white"
|
||||
class="ml-4"
|
||||
<RecipeTimeCard
|
||||
:prep-time="recipe.prepTime"
|
||||
:total-time="recipe.totalTime"
|
||||
:perform-time="recipe.performTime"
|
||||
small
|
||||
color="white"
|
||||
class="ml-4"
|
||||
/>
|
||||
</v-row>
|
||||
|
||||
<v-card-text v-if="preferences.showDescription"
|
||||
class="px-0"
|
||||
<v-card-text
|
||||
v-if="preferences.showDescription"
|
||||
class="px-0"
|
||||
>
|
||||
<SafeMarkdown :source="recipe.description" />
|
||||
</v-card-text>
|
||||
@@ -60,24 +65,29 @@
|
||||
<v-card-title class="headline pl-0">
|
||||
{{ $t("recipe.ingredients") }}
|
||||
</v-card-title>
|
||||
<div v-for="(ingredientSection, sectionIndex) in ingredientSections"
|
||||
:key="`ingredient-section-${sectionIndex}`"
|
||||
class="print-section"
|
||||
<div
|
||||
v-for="(ingredientSection, sectionIndex) in ingredientSections"
|
||||
:key="`ingredient-section-${sectionIndex}`"
|
||||
class="print-section"
|
||||
>
|
||||
<h4 v-if="ingredientSection.ingredients[0].title"
|
||||
class="ingredient-title mt-2"
|
||||
<h4
|
||||
v-if="ingredientSection.ingredients[0].title"
|
||||
class="ingredient-title mt-2"
|
||||
>
|
||||
{{ ingredientSection.ingredients[0].title }}
|
||||
</h4>
|
||||
<div class="ingredient-grid"
|
||||
:style="{ gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
|
||||
<div
|
||||
class="ingredient-grid"
|
||||
:style="{ gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
|
||||
>
|
||||
<template v-for="(ingredient, ingredientIndex) in ingredientSection.ingredients"
|
||||
:key="`ingredient-${ingredientIndex}`"
|
||||
<template
|
||||
v-for="(ingredient, ingredientIndex) in ingredientSection.ingredients"
|
||||
:key="`ingredient-${ingredientIndex}`"
|
||||
>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p class="ingredient-body"
|
||||
v-html="parseText(ingredient)"
|
||||
<p
|
||||
class="ingredient-body"
|
||||
v-html="parseText(ingredient)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
@@ -86,22 +96,26 @@
|
||||
|
||||
<!-- Instructions -->
|
||||
<section>
|
||||
<div v-for="(instructionSection, sectionIndex) in instructionSections"
|
||||
:key="`instruction-section-${sectionIndex}`"
|
||||
:class="{ 'print-section': instructionSection.sectionName }"
|
||||
<div
|
||||
v-for="(instructionSection, sectionIndex) in instructionSections"
|
||||
:key="`instruction-section-${sectionIndex}`"
|
||||
:class="{ 'print-section': instructionSection.sectionName }"
|
||||
>
|
||||
<v-card-title v-if="!sectionIndex"
|
||||
class="headline pl-0"
|
||||
<v-card-title
|
||||
v-if="!sectionIndex"
|
||||
class="headline pl-0"
|
||||
>
|
||||
{{ $t("recipe.instructions") }}
|
||||
</v-card-title>
|
||||
<div v-for="(step, stepIndex) in instructionSection.instructions"
|
||||
:key="`instruction-${stepIndex}`"
|
||||
<div
|
||||
v-for="(step, stepIndex) in instructionSection.instructions"
|
||||
:key="`instruction-${stepIndex}`"
|
||||
>
|
||||
<div class="print-section">
|
||||
<h4 v-if="step.title"
|
||||
:key="`instruction-title-${stepIndex}`"
|
||||
class="instruction-title mb-2"
|
||||
<h4
|
||||
v-if="step.title"
|
||||
:key="`instruction-title-${stepIndex}`"
|
||||
class="instruction-title mb-2"
|
||||
>
|
||||
{{ step.title }}
|
||||
</h4>
|
||||
@@ -112,8 +126,9 @@
|
||||
+ 1,
|
||||
}) }}
|
||||
</h5>
|
||||
<SafeMarkdown :source="step.text"
|
||||
class="recipe-step-body"
|
||||
<SafeMarkdown
|
||||
:source="step.text"
|
||||
class="recipe-step-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,18 +137,21 @@
|
||||
|
||||
<!-- Notes -->
|
||||
<div v-if="preferences.showNotes">
|
||||
<v-divider v-if="hasNotes"
|
||||
class="grey my-4"
|
||||
<v-divider
|
||||
v-if="hasNotes"
|
||||
class="grey my-4"
|
||||
/>
|
||||
|
||||
<section>
|
||||
<div v-for="(note, index) in recipe.notes"
|
||||
:key="index + 'note'"
|
||||
<div
|
||||
v-for="(note, index) in recipe.notes"
|
||||
:key="index + 'note'"
|
||||
>
|
||||
<div class="print-section">
|
||||
<h4>{{ note.title }}</h4>
|
||||
<SafeMarkdown :source="note.text"
|
||||
class="note-body"
|
||||
<SafeMarkdown
|
||||
:source="note.text"
|
||||
class="note-body"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,8 +168,9 @@
|
||||
<div class="print-section">
|
||||
<table class="nutrition-table">
|
||||
<tbody>
|
||||
<tr v-for="(value, key) in recipe.nutrition"
|
||||
:key="key"
|
||||
<tr
|
||||
v-for="(value, key) in recipe.nutrition"
|
||||
:key="key"
|
||||
>
|
||||
<template v-if="value">
|
||||
<td>{{ labels[key].label }}</td>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<div @click.prevent>
|
||||
<!-- User Rating -->
|
||||
<v-hover v-slot="{ isHovering, props }">
|
||||
<v-rating v-if="isOwnGroup && (userRating || isHovering || !ratingsLoaded)"
|
||||
<v-rating
|
||||
v-if="isOwnGroup && (userRating || isHovering || !ratingsLoaded)"
|
||||
v-bind="props"
|
||||
:model-value="userRating"
|
||||
active-color="secondary"
|
||||
@@ -15,7 +16,8 @@
|
||||
@update:model-value="updateRating(+$event)"
|
||||
/>
|
||||
<!-- Group Rating -->
|
||||
<v-rating v-else
|
||||
<v-rating
|
||||
v-else
|
||||
v-bind="props"
|
||||
:model-value="groupRating"
|
||||
:half-increments="true"
|
||||
|
||||
@@ -84,12 +84,12 @@
|
||||
:buttons="[
|
||||
...(allowDelete
|
||||
? [
|
||||
{
|
||||
icon: $globals.icons.delete,
|
||||
text: $t('general.delete'),
|
||||
event: 'delete',
|
||||
},
|
||||
]
|
||||
{
|
||||
icon: $globals.icons.delete,
|
||||
text: $t('general.delete'),
|
||||
event: 'delete',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
icon: $globals.icons.close,
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<v-list-item-subtitle class="font-weight-medium" style="font-size: small;">
|
||||
{{ item.subtitle }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</template>
|
||||
</v-list>
|
||||
|
||||
@@ -33,20 +33,39 @@
|
||||
<template v-for="nav in topLink">
|
||||
<div v-if="!nav.restricted || isOwnGroup" :key="nav.key || nav.title">
|
||||
<!-- Multi Items -->
|
||||
<v-list-group v-if="nav.children" :key="(nav.key || nav.title) + 'multi-item'"
|
||||
v-model="dropDowns[nav.title]" color="primary" :prepend-icon="nav.icon" :fluid="true">
|
||||
<v-list-group
|
||||
v-if="nav.children"
|
||||
:key="(nav.key || nav.title) + 'multi-item'"
|
||||
v-model="dropDowns[nav.title]"
|
||||
color="primary"
|
||||
:prepend-icon="nav.icon"
|
||||
:fluid="true"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-list-item v-bind="props" :prepend-icon="nav.icon" :title="nav.title" />
|
||||
</template>
|
||||
|
||||
<v-list-item v-for="child in nav.children" :key="child.key || child.title" exact :to="child.to"
|
||||
:prepend-icon="child.icon" :title="child.title" class="ml-4" />
|
||||
<v-list-item
|
||||
v-for="child in nav.children"
|
||||
:key="child.key || child.title"
|
||||
exact
|
||||
:to="child.to"
|
||||
:prepend-icon="child.icon"
|
||||
:title="child.title"
|
||||
class="ml-4"
|
||||
/>
|
||||
</v-list-group>
|
||||
|
||||
<!-- Single Item -->
|
||||
<template v-else>
|
||||
<v-list-item :key="(nav.key || nav.title) + 'single-item'" exact link :to="nav.to"
|
||||
:prepend-icon="nav.icon" :title="nav.title" />
|
||||
<v-list-item
|
||||
:key="(nav.key || nav.title) + 'single-item'"
|
||||
exact
|
||||
link
|
||||
:to="nav.to"
|
||||
:prepend-icon="nav.icon"
|
||||
:title="nav.title"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -60,14 +79,27 @@
|
||||
<template v-for="nav in secondaryLinks">
|
||||
<div v-if="!nav.restricted || isOwnGroup" :key="nav.key || nav.title">
|
||||
<!-- Multi Items -->
|
||||
<v-list-group v-if="nav.children" :key="(nav.key || nav.title) + 'multi-item'"
|
||||
v-model="dropDowns[nav.title]" color="primary" :prepend-icon="nav.icon" fluid>
|
||||
<v-list-group
|
||||
v-if="nav.children"
|
||||
:key="(nav.key || nav.title) + 'multi-item'"
|
||||
v-model="dropDowns[nav.title]"
|
||||
color="primary"
|
||||
:prepend-icon="nav.icon"
|
||||
fluid
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-list-item v-bind="props" :prepend-icon="nav.icon" :title="nav.title" />
|
||||
</template>
|
||||
|
||||
<v-list-item v-for="child in nav.children" :key="child.key || child.title" exact :to="child.to"
|
||||
class="ml-2" :prepend-icon="child.icon" :title="child.title" />
|
||||
<v-list-item
|
||||
v-for="child in nav.children"
|
||||
:key="child.key || child.title"
|
||||
exact
|
||||
:to="child.to"
|
||||
class="ml-2"
|
||||
:prepend-icon="child.icon"
|
||||
:title="child.title"
|
||||
/>
|
||||
</v-list-group>
|
||||
|
||||
<!-- Single Item -->
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
:hide-details="!inputField.hint"
|
||||
:persistent-hint="!!inputField.hint"
|
||||
density="comfortable"
|
||||
@change="emitBlur">
|
||||
@change="emitBlur"
|
||||
>
|
||||
<template #label>
|
||||
<span class="ml-4">
|
||||
{{ inputField.label }}
|
||||
|
||||
@@ -200,8 +200,8 @@ function open() {
|
||||
}
|
||||
|
||||
/* function close() {
|
||||
dialog.value = false;
|
||||
logDeprecatedProp("close");
|
||||
dialog.value = false;
|
||||
logDeprecatedProp("close");
|
||||
} */
|
||||
|
||||
function logDeprecatedProp(val: string) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<!-- eslint-disable-next-line vue/no-v-html is safe here because all HTML is sanitized with DOMPurify in setup() -->
|
||||
<div v-html="value" />
|
||||
<!-- eslint-disable-next-line vue/no-v-html is safe here because all HTML is sanitized with DOMPurify in setup() -->
|
||||
<div v-html="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -29,20 +29,20 @@ interface PageState {
|
||||
editMode: ComputedRef<EditorMode>;
|
||||
|
||||
/**
|
||||
* true is the page is in edit mode and the edit mode is in form mode.
|
||||
*/
|
||||
* true is the page is in edit mode and the edit mode is in form mode.
|
||||
*/
|
||||
isEditForm: ComputedRef<boolean>;
|
||||
/**
|
||||
* true is the page is in edit mode and the edit mode is in json mode.
|
||||
*/
|
||||
* true is the page is in edit mode and the edit mode is in json mode.
|
||||
*/
|
||||
isEditJSON: ComputedRef<boolean>;
|
||||
/**
|
||||
* true is the page is in view mode.
|
||||
*/
|
||||
* true is the page is in view mode.
|
||||
*/
|
||||
isEditMode: ComputedRef<boolean>;
|
||||
/**
|
||||
* true is the page is in cook mode.
|
||||
*/
|
||||
* true is the page is in cook mode.
|
||||
*/
|
||||
isCookMode: ComputedRef<boolean>;
|
||||
/**
|
||||
* true if the recipe is currently being parsed.
|
||||
|
||||
@@ -29,8 +29,8 @@ export function useGroupRecipeActionData() {
|
||||
}
|
||||
|
||||
export const useGroupRecipeActions = function (
|
||||
orderBy: string | null = "title",
|
||||
orderDirection: string | null = "asc",
|
||||
orderBy: string | null = "title",
|
||||
orderDirection: string | null = "asc",
|
||||
) {
|
||||
const api = useUserApi();
|
||||
|
||||
|
||||
@@ -17,19 +17,19 @@ export interface OrganizerBase {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type FieldType =
|
||||
| "string"
|
||||
| "number"
|
||||
| "boolean"
|
||||
| "date"
|
||||
| RecipeOrganizer;
|
||||
export type FieldType
|
||||
= | "string"
|
||||
| "number"
|
||||
| "boolean"
|
||||
| "date"
|
||||
| RecipeOrganizer;
|
||||
|
||||
export type FieldValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| Date
|
||||
| Organizer;
|
||||
export type FieldValue
|
||||
= | string
|
||||
| number
|
||||
| boolean
|
||||
| Date
|
||||
| Organizer;
|
||||
|
||||
export interface SelectableItem {
|
||||
label: string;
|
||||
|
||||
@@ -177,8 +177,8 @@ export function useShoppingListItemActions(shoppingListId: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the queue items and returns whether the processing was successful.
|
||||
*/
|
||||
* Processes the queue items and returns whether the processing was successful.
|
||||
*/
|
||||
async function processQueueItems(
|
||||
action: (items: ShoppingListItemOut[]) => Promise<RequestResponse<any>>,
|
||||
itemQueueType: ItemQueueType,
|
||||
|
||||
@@ -6,21 +6,20 @@ export default withNuxt({
|
||||
plugins: {
|
||||
"@stylistic": stylistic,
|
||||
},
|
||||
// Your custom configs here
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/no-mutating-props": "warn",
|
||||
"vue/no-v-html": "warn",
|
||||
"object-curly-newline": "off",
|
||||
"consistent-list-newline": "off",
|
||||
"vue/first-attribute-linebreak": "off",
|
||||
"@stylistic/no-tabs": ["error", { allowIndentationTabs: true }],
|
||||
"@stylistic/no-tabs": ["error"],
|
||||
"@stylistic/no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/html-indent": "off",
|
||||
"vue/html-closing-bracket-newline": "off",
|
||||
// TODO: temporarily off to get this PR in without a crazy diff
|
||||
"@stylistic/indent": "off",
|
||||
"@stylistic/operator-linebreak": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"vue/first-attribute-linebreak": "error",
|
||||
"vue/html-closing-bracket-newline": "error",
|
||||
"vue/max-attributes-per-line": [
|
||||
"error",
|
||||
{
|
||||
singleline: 5,
|
||||
multiline: 1,
|
||||
},
|
||||
],
|
||||
"vue/no-mutating-props": "error",
|
||||
"vue/no-v-html": "error",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<v-app v-if="ready"
|
||||
dark
|
||||
<v-app
|
||||
v-if="ready"
|
||||
dark
|
||||
>
|
||||
<v-card-title>
|
||||
<slot>
|
||||
@@ -14,9 +15,10 @@
|
||||
<p class="primary--text">
|
||||
4
|
||||
</p>
|
||||
<v-icon color="primary"
|
||||
class="mx-auto mb-0"
|
||||
size="200"
|
||||
<v-icon
|
||||
color="primary"
|
||||
class="mx-auto mb-0"
|
||||
size="200"
|
||||
>
|
||||
{{ $globals.icons.primary }}
|
||||
</v-icon>
|
||||
@@ -28,11 +30,12 @@
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<slot name="actions">
|
||||
<v-btn v-for="(button, index) in buttons"
|
||||
:key="index"
|
||||
nuxt
|
||||
:to="button.to"
|
||||
color="primary"
|
||||
<v-btn
|
||||
v-for="(button, index) in buttons"
|
||||
:key="index"
|
||||
nuxt
|
||||
:to="button.to"
|
||||
color="primary"
|
||||
>
|
||||
<v-icon start>
|
||||
{{ button.icon }}
|
||||
@@ -121,9 +124,9 @@ export default defineNuxtComponent({
|
||||
|
||||
useSeoMeta({
|
||||
title:
|
||||
props.error.statusCode === 404
|
||||
? (i18n.t("page.404-not-found") as string)
|
||||
: (i18n.t("page.an-error-occurred") as string),
|
||||
props.error.statusCode === 404
|
||||
? (i18n.t("page.404-not-found") as string)
|
||||
: (i18n.t("page.an-error-occurred") as string),
|
||||
});
|
||||
|
||||
const buttons = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -24,12 +24,12 @@ export interface PaginationData<T> {
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export type RecipeOrganizer =
|
||||
| "categories"
|
||||
| "tags"
|
||||
| "tools"
|
||||
| "foods"
|
||||
| "households";
|
||||
export type RecipeOrganizer
|
||||
= | "categories"
|
||||
| "tags"
|
||||
| "tools"
|
||||
| "foods"
|
||||
| "households";
|
||||
|
||||
export enum Organizer {
|
||||
Category = "categories",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<v-container fluid class="narrow-container">
|
||||
<BaseDialog v-model="state.storageDetails" :title="$t('admin.maintenance.storage-details')"
|
||||
<BaseDialog
|
||||
v-model="state.storageDetails"
|
||||
:title="$t('admin.maintenance.storage-details')"
|
||||
:icon="$globals.icons.folderOutline"
|
||||
>
|
||||
>
|
||||
<div class="py-2">
|
||||
<template v-for="(value, key, idx) in storageDetails" :key="`item-${key}`">
|
||||
<v-list-item>
|
||||
@@ -55,9 +57,11 @@
|
||||
</v-card>
|
||||
</section>
|
||||
<section>
|
||||
<BaseCardSectionTitle class="pb-0 mt-8" :icon="$globals.icons.wrench"
|
||||
<BaseCardSectionTitle
|
||||
class="pb-0 mt-8"
|
||||
:icon="$globals.icons.wrench"
|
||||
:title="$t('admin.mainentance.actions-title')"
|
||||
>
|
||||
>
|
||||
<i18n-t keypath="admin.maintenance.actions-description">
|
||||
<template #destructive_in_bold>
|
||||
<b>{{ $t("admin.maintenance.actions-description-destructive") }}</b>
|
||||
@@ -78,11 +82,11 @@
|
||||
</v-list-item-title>
|
||||
<template #append>
|
||||
<BaseButton color="info" @click="action.handler">
|
||||
<template #icon>
|
||||
{{ $globals.icons.robot }}
|
||||
</template>
|
||||
{{ $t("general.run") }}
|
||||
</BaseButton>
|
||||
<template #icon>
|
||||
{{ $globals.icons.robot }}
|
||||
</template>
|
||||
{{ $t("general.run") }}
|
||||
</BaseButton>
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-divider class="mx-2" />
|
||||
|
||||
@@ -18,218 +18,219 @@
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<!-- Stepper Wizard -->
|
||||
<v-stepper v-model="currentPage" mobile-breakpoint="sm">
|
||||
<v-stepper-header>
|
||||
<v-stepper-item
|
||||
:value="Pages.LANDING"
|
||||
:complete="currentPage > Pages.LANDING"
|
||||
:title="$t('general.start')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.USER_INFO"
|
||||
:complete="currentPage > Pages.USER_INFO"
|
||||
:title="$t('user-registration.account-details')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.PAGE_2"
|
||||
:complete="currentPage > Pages.PAGE_2"
|
||||
:title="$t('settings.site-settings')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.CONFIRM"
|
||||
:complete="currentPage > Pages.CONFIRM"
|
||||
:title="$t('admin.maintenance.summary-title')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.END"
|
||||
:complete="currentPage > Pages.END"
|
||||
:title="$t('admin.setup.setup-complete')"
|
||||
/>
|
||||
</v-stepper-header>
|
||||
<v-progress-linear
|
||||
v-if="isSubmitting && currentPage === Pages.CONFIRM"
|
||||
color="primary"
|
||||
indeterminate
|
||||
class="mb-2"
|
||||
<!-- Stepper Wizard -->
|
||||
<v-stepper v-model="currentPage" mobile-breakpoint="sm">
|
||||
<v-stepper-header>
|
||||
<v-stepper-item
|
||||
:value="Pages.LANDING"
|
||||
:complete="currentPage > Pages.LANDING"
|
||||
:title="$t('general.start')"
|
||||
/>
|
||||
|
||||
<v-stepper-window :transition="false" class="stepper-window">
|
||||
<!-- LANDING -->
|
||||
<v-stepper-window-item :value="Pages.LANDING">
|
||||
<v-container class="mb-12">
|
||||
<AppLogo />
|
||||
<v-card-title class="text-h4 justify-center text-center text-break text-pre-wrap">
|
||||
{{ $t('admin.setup.welcome-to-mealie-get-started') }}
|
||||
</v-card-title>
|
||||
<v-btn
|
||||
:to="groupSlug ? `/g/${groupSlug}` : '/login'"
|
||||
rounded
|
||||
variant="outlined"
|
||||
color="grey-lighten-1"
|
||||
class="text-subtitle-2 d-flex mx-auto"
|
||||
style="width: fit-content;"
|
||||
>
|
||||
{{ $t('admin.setup.already-set-up-bring-to-homepage') }}
|
||||
</v-btn>
|
||||
</v-container>
|
||||
|
||||
<v-card-actions class="justify-center flex-column py-8">
|
||||
<BaseButton
|
||||
size="large"
|
||||
color="primary"
|
||||
:icon="$globals.icons.translate"
|
||||
@click="langDialog = true"
|
||||
>
|
||||
{{ $t('language-dialog.choose-language') }}
|
||||
</BaseButton>
|
||||
</v-card-actions>
|
||||
|
||||
<v-stepper-actions
|
||||
class="justify-end"
|
||||
:disabled="isSubmitting"
|
||||
next-text="general.next"
|
||||
@click:next="onNext"
|
||||
>
|
||||
<template #prev />
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
|
||||
<!-- USER INFO -->
|
||||
<v-stepper-window-item :value="Pages.USER_INFO" eager>
|
||||
<v-container max-width="880">
|
||||
<UserRegistrationForm />
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
next-text="general.next"
|
||||
@click:prev="onPrev"
|
||||
@click:next="onNext"
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.USER_INFO"
|
||||
:complete="currentPage > Pages.USER_INFO"
|
||||
:title="$t('user-registration.account-details')"
|
||||
/>
|
||||
</v-stepper-window-item>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.PAGE_2"
|
||||
:complete="currentPage > Pages.PAGE_2"
|
||||
:title="$t('settings.site-settings')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.CONFIRM"
|
||||
:complete="currentPage > Pages.CONFIRM"
|
||||
:title="$t('admin.maintenance.summary-title')"
|
||||
/>
|
||||
<v-divider />
|
||||
<v-stepper-item
|
||||
:value="Pages.END"
|
||||
:complete="currentPage > Pages.END"
|
||||
:title="$t('admin.setup.setup-complete')"
|
||||
/>
|
||||
</v-stepper-header>
|
||||
<v-progress-linear
|
||||
v-if="isSubmitting && currentPage === Pages.CONFIRM"
|
||||
color="primary"
|
||||
indeterminate
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<!-- COMMON SETTINGS -->
|
||||
<v-stepper-window-item :value="Pages.PAGE_2">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="headline pa-0">
|
||||
{{ $t('admin.setup.common-settings-for-new-sites') }}
|
||||
</v-card-title>
|
||||
<AutoForm
|
||||
v-model="commonSettings"
|
||||
:items="commonSettingsForm"
|
||||
<v-stepper-window :transition="false" class="stepper-window">
|
||||
<!-- LANDING -->
|
||||
<v-stepper-window-item :value="Pages.LANDING">
|
||||
<v-container class="mb-12">
|
||||
<AppLogo />
|
||||
<v-card-title class="text-h4 justify-center text-center text-break text-pre-wrap">
|
||||
{{ $t('admin.setup.welcome-to-mealie-get-started') }}
|
||||
</v-card-title>
|
||||
<v-btn
|
||||
:to="groupSlug ? `/g/${groupSlug}` : '/login'"
|
||||
rounded
|
||||
variant="outlined"
|
||||
color="grey-lighten-1"
|
||||
class="text-subtitle-2 d-flex mx-auto"
|
||||
style="width: fit-content;"
|
||||
>
|
||||
{{ $t('admin.setup.already-set-up-bring-to-homepage') }}
|
||||
</v-btn>
|
||||
</v-container>
|
||||
|
||||
<v-card-actions class="justify-center flex-column py-8">
|
||||
<BaseButton
|
||||
size="large"
|
||||
color="primary"
|
||||
:icon="$globals.icons.translate"
|
||||
@click="langDialog = true"
|
||||
>
|
||||
{{ $t('language-dialog.choose-language') }}
|
||||
</BaseButton>
|
||||
</v-card-actions>
|
||||
|
||||
<v-stepper-actions
|
||||
class="justify-end"
|
||||
:disabled="isSubmitting"
|
||||
next-text="general.next"
|
||||
@click:next="onNext"
|
||||
>
|
||||
<template #prev />
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
|
||||
<!-- USER INFO -->
|
||||
<v-stepper-window-item :value="Pages.USER_INFO" eager>
|
||||
<v-container max-width="880">
|
||||
<UserRegistrationForm />
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
next-text="general.next"
|
||||
@click:prev="onPrev"
|
||||
@click:next="onNext"
|
||||
/>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
next-text="general.next"
|
||||
@click:prev="onPrev"
|
||||
@click:next="onNext"
|
||||
/>
|
||||
</v-stepper-window-item>
|
||||
</v-stepper-window-item>
|
||||
|
||||
<!-- CONFIRMATION -->
|
||||
<v-stepper-window-item :value="Pages.CONFIRM">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="headline pa-0">
|
||||
{{ $t('general.confirm-how-does-everything-look') }}
|
||||
</v-card-title>
|
||||
<v-list>
|
||||
<template v-for="(item, idx) in confirmationData">
|
||||
<v-list-item
|
||||
v-if="item.display"
|
||||
:key="idx"
|
||||
class="px-0"
|
||||
>
|
||||
<v-list-item-title>{{ item.text }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ item.value }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-divider
|
||||
v-if="idx !== confirmationData.length - 1"
|
||||
:key="`divider-${idx}`"
|
||||
<!-- COMMON SETTINGS -->
|
||||
<v-stepper-window-item :value="Pages.PAGE_2">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="headline pa-0">
|
||||
{{ $t('admin.setup.common-settings-for-new-sites') }}
|
||||
</v-card-title>
|
||||
<AutoForm
|
||||
v-model="commonSettings"
|
||||
:items="commonSettingsForm"
|
||||
/>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
next-text="general.next"
|
||||
@click:prev="onPrev"
|
||||
@click:next="onNext"
|
||||
/>
|
||||
</v-stepper-window-item>
|
||||
|
||||
<!-- CONFIRMATION -->
|
||||
<v-stepper-window-item :value="Pages.CONFIRM">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="headline pa-0">
|
||||
{{ $t('general.confirm-how-does-everything-look') }}
|
||||
</v-card-title>
|
||||
<v-list>
|
||||
<template v-for="(item, idx) in confirmationData">
|
||||
<v-list-item
|
||||
v-if="item.display"
|
||||
:key="idx"
|
||||
class="px-0"
|
||||
>
|
||||
<v-list-item-title>{{ item.text }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ item.value }}</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-divider
|
||||
v-if="idx !== confirmationData.length - 1"
|
||||
:key="`divider-${idx}`"
|
||||
/>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
@click:prev="onPrev"
|
||||
>
|
||||
<template #next>
|
||||
<BaseButton
|
||||
create
|
||||
flat
|
||||
:disabled="isSubmitting"
|
||||
:loading="isSubmitting"
|
||||
:icon="$globals.icons.check"
|
||||
:text="$t('general.submit')"
|
||||
@click="onNext"
|
||||
/>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
@click:prev="onPrev"
|
||||
>
|
||||
<template #next>
|
||||
<BaseButton
|
||||
create flat
|
||||
:disabled="isSubmitting"
|
||||
:loading="isSubmitting"
|
||||
:icon="$globals.icons.check"
|
||||
:text="$t('general.submit')"
|
||||
@click="onNext"
|
||||
/>
|
||||
</template>
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
|
||||
<!-- END -->
|
||||
<v-stepper-window-item :value="Pages.END">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="text-h4 justify-center">
|
||||
{{ $t('admin.setup.setup-complete') }}
|
||||
</v-card-title>
|
||||
<v-card-title class="text-h6 justify-center">
|
||||
{{ $t('admin.setup.here-are-a-few-things-to-help-you-get-started') }}
|
||||
</v-card-title>
|
||||
<div
|
||||
v-for="link, idx in setupCompleteLinks"
|
||||
:key="idx"
|
||||
class="px-4 pt-4"
|
||||
>
|
||||
<div v-if="link.section">
|
||||
<v-divider v-if="idx" />
|
||||
<v-card-text class="headline pl-0">
|
||||
{{ link.section }}
|
||||
<!-- END -->
|
||||
<v-stepper-window-item :value="Pages.END">
|
||||
<v-container max-width="880">
|
||||
<v-card-title class="text-h4 justify-center">
|
||||
{{ $t('admin.setup.setup-complete') }}
|
||||
</v-card-title>
|
||||
<v-card-title class="text-h6 justify-center">
|
||||
{{ $t('admin.setup.here-are-a-few-things-to-help-you-get-started') }}
|
||||
</v-card-title>
|
||||
<div
|
||||
v-for="link, idx in setupCompleteLinks"
|
||||
:key="idx"
|
||||
class="px-4 pt-4"
|
||||
>
|
||||
<div v-if="link.section">
|
||||
<v-divider v-if="idx" />
|
||||
<v-card-text class="headline pl-0">
|
||||
{{ link.section }}
|
||||
</v-card-text>
|
||||
</div>
|
||||
<v-btn
|
||||
:to="link.to"
|
||||
color="info"
|
||||
>
|
||||
{{ link.text }}
|
||||
</v-btn>
|
||||
<v-card-text class="subtitle px-0 py-2">
|
||||
{{ link.description }}
|
||||
</v-card-text>
|
||||
</div>
|
||||
<v-btn
|
||||
:to="link.to"
|
||||
color="info"
|
||||
>
|
||||
{{ link.text }}
|
||||
</v-btn>
|
||||
<v-card-text class="subtitle px-0 py-2">
|
||||
{{ link.description }}
|
||||
</v-card-text>
|
||||
</div>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
@click:prev="onPrev"
|
||||
>
|
||||
<template #next>
|
||||
<BaseButton
|
||||
flat
|
||||
color="primary"
|
||||
:disabled="isSubmitting"
|
||||
:loading="isSubmitting"
|
||||
:icon="$globals.icons.home"
|
||||
:text="$t('general.home')"
|
||||
@click="onFinish"
|
||||
/>
|
||||
</template>
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
</v-stepper-window>
|
||||
</v-stepper>
|
||||
</v-container>
|
||||
<v-stepper-actions
|
||||
:disabled="isSubmitting"
|
||||
prev-text="general.back"
|
||||
@click:prev="onPrev"
|
||||
>
|
||||
<template #next>
|
||||
<BaseButton
|
||||
flat
|
||||
color="primary"
|
||||
:disabled="isSubmitting"
|
||||
:loading="isSubmitting"
|
||||
:icon="$globals.icons.home"
|
||||
:text="$t('general.home')"
|
||||
@click="onFinish"
|
||||
/>
|
||||
</template>
|
||||
</v-stepper-actions>
|
||||
</v-stepper-window-item>
|
||||
</v-stepper-window>
|
||||
</v-stepper>
|
||||
|
||||
<!-- Dialog Language -->
|
||||
<LanguageDialog v-model="langDialog" />
|
||||
</v-card>
|
||||
</v-container>
|
||||
<!-- Dialog Language -->
|
||||
<LanguageDialog v-model="langDialog" />
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -7,151 +7,151 @@
|
||||
<v-card-text>
|
||||
{{ $t('recipe.recipe-bulk-importer-description') }}
|
||||
</v-card-text>
|
||||
<div class="px-4">
|
||||
<section class="mt-2">
|
||||
<v-row
|
||||
v-for="(_, idx) in bulkUrls"
|
||||
:key="'bulk-url' + idx"
|
||||
class="my-1"
|
||||
density="compact"
|
||||
>
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="12"
|
||||
md="12"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="bulkUrls[idx].url"
|
||||
:label="$t('new-recipe.recipe-url')"
|
||||
<div class="px-4">
|
||||
<section class="mt-2">
|
||||
<v-row
|
||||
v-for="(_, idx) in bulkUrls"
|
||||
:key="'bulk-url' + idx"
|
||||
class="my-1"
|
||||
density="compact"
|
||||
single-line
|
||||
validate-on="blur"
|
||||
autofocus
|
||||
variant="solo-filled"
|
||||
hide-details
|
||||
clearable
|
||||
:prepend-inner-icon="$globals.icons.link"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
>
|
||||
<template #append>
|
||||
<v-btn
|
||||
style="margin-top: -2px"
|
||||
icon
|
||||
size="small"
|
||||
@click="bulkUrls.splice(idx, 1)"
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="12"
|
||||
md="12"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="bulkUrls[idx].url"
|
||||
:label="$t('new-recipe.recipe-url')"
|
||||
density="compact"
|
||||
single-line
|
||||
validate-on="blur"
|
||||
autofocus
|
||||
variant="solo-filled"
|
||||
hide-details
|
||||
clearable
|
||||
:prepend-inner-icon="$globals.icons.link"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<template #append>
|
||||
<v-btn
|
||||
style="margin-top: -2px"
|
||||
icon
|
||||
size="small"
|
||||
@click="bulkUrls.splice(idx, 1)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<template v-if="showCatTags">
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="6"
|
||||
class="py-0"
|
||||
>
|
||||
<RecipeOrganizerSelector
|
||||
v-model="bulkUrls[idx].categories"
|
||||
selector-type="categories"
|
||||
:input-attrs="{
|
||||
variant: 'filled',
|
||||
singleLine: true,
|
||||
density: 'compact',
|
||||
rounded: true,
|
||||
class: 'rounded-lg',
|
||||
hideDetails: true,
|
||||
clearable: true,
|
||||
}"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="6"
|
||||
class="pt-0 pb-4"
|
||||
>
|
||||
<RecipeOrganizerSelector
|
||||
v-model="bulkUrls[idx].tags"
|
||||
selector-type="tags"
|
||||
:input-attrs="{
|
||||
variant: 'filled',
|
||||
singleLine: true,
|
||||
density: 'compact',
|
||||
rounded: true,
|
||||
class: 'rounded-lg',
|
||||
hideDetails: true,
|
||||
clearable: true,
|
||||
}"
|
||||
/>
|
||||
</v-col>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<template v-if="showCatTags">
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="6"
|
||||
class="py-0"
|
||||
>
|
||||
<RecipeOrganizerSelector
|
||||
v-model="bulkUrls[idx].categories"
|
||||
selector-type="categories"
|
||||
:input-attrs="{
|
||||
variant: 'filled',
|
||||
singleLine: true,
|
||||
density: 'compact',
|
||||
rounded: true,
|
||||
class: 'rounded-lg',
|
||||
hideDetails: true,
|
||||
clearable: true,
|
||||
}"
|
||||
</v-row>
|
||||
<v-card-actions class="justify-end flex-wrap mt-3 pa-0">
|
||||
<BaseButton
|
||||
class="mt-1 pr-4"
|
||||
delete
|
||||
@click="
|
||||
bulkUrls = [];
|
||||
lockBulkImport = false;
|
||||
"
|
||||
>
|
||||
{{ $t('general.clear') }}
|
||||
</BaseButton>
|
||||
<v-spacer />
|
||||
<BaseButton
|
||||
class="mr-1 mb-1"
|
||||
color="info"
|
||||
@click="bulkUrls.push({ url: '', categories: [], tags: [] })"
|
||||
>
|
||||
<template #icon>
|
||||
{{ $globals.icons.createAlt }}
|
||||
</template>
|
||||
{{ $t('general.new') }}
|
||||
</BaseButton>
|
||||
<RecipeDialogBulkAdd
|
||||
v-model="bulkDialog"
|
||||
class="mr-1 mr-sm-0 mb-1"
|
||||
@bulk-data="assignUrls"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
xs="12"
|
||||
sm="6"
|
||||
class="pt-0 pb-4"
|
||||
>
|
||||
<RecipeOrganizerSelector
|
||||
v-model="bulkUrls[idx].tags"
|
||||
selector-type="tags"
|
||||
:input-attrs="{
|
||||
variant: 'filled',
|
||||
singleLine: true,
|
||||
density: 'compact',
|
||||
rounded: true,
|
||||
class: 'rounded-lg',
|
||||
hideDetails: true,
|
||||
clearable: true,
|
||||
}"
|
||||
</v-card-actions>
|
||||
<div class="px-0">
|
||||
<v-checkbox
|
||||
v-model="showCatTags"
|
||||
hide-details
|
||||
:label="$t('recipe.set-categories-and-tags')"
|
||||
/>
|
||||
</v-col>
|
||||
</template>
|
||||
</v-row>
|
||||
<v-card-actions class="justify-end flex-wrap mt-3 pa-0">
|
||||
<BaseButton
|
||||
class="mt-1 pr-4"
|
||||
delete
|
||||
@click="
|
||||
bulkUrls = [];
|
||||
lockBulkImport = false;
|
||||
"
|
||||
>
|
||||
{{ $t('general.clear') }}
|
||||
</BaseButton>
|
||||
<v-spacer />
|
||||
<BaseButton
|
||||
class="mr-1 mb-1"
|
||||
color="info"
|
||||
@click="bulkUrls.push({ url: '', categories: [], tags: [] })"
|
||||
>
|
||||
<template #icon>
|
||||
{{ $globals.icons.createAlt }}
|
||||
</template>
|
||||
{{ $t('general.new') }}
|
||||
</BaseButton>
|
||||
<RecipeDialogBulkAdd
|
||||
v-model="bulkDialog"
|
||||
class="mr-1 mr-sm-0 mb-1"
|
||||
@bulk-data="assignUrls"
|
||||
/>
|
||||
</v-card-actions>
|
||||
<div class="px-0">
|
||||
<v-checkbox
|
||||
v-model="showCatTags"
|
||||
hide-details
|
||||
:label="$t('recipe.set-categories-and-tags')"
|
||||
/>
|
||||
</div>
|
||||
<v-card-actions class="justify-center">
|
||||
<div style="width: 250px">
|
||||
<BaseButton
|
||||
:disabled="bulkUrls.length === 0 || lockBulkImport"
|
||||
rounded
|
||||
block
|
||||
@click="bulkCreate"
|
||||
>
|
||||
<template #icon>
|
||||
{{ $globals.icons.check }}
|
||||
</template>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</section>
|
||||
<section class="mt-12">
|
||||
<BaseCardSectionTitle :title="$t('recipe.bulk-imports')" />
|
||||
<ReportTable
|
||||
:items="reports"
|
||||
@delete="deleteReport"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
<v-card-actions class="justify-center">
|
||||
<div style="width: 250px">
|
||||
<BaseButton
|
||||
:disabled="bulkUrls.length === 0 || lockBulkImport"
|
||||
rounded
|
||||
block
|
||||
@click="bulkCreate"
|
||||
>
|
||||
<template #icon>
|
||||
{{ $globals.icons.check }}
|
||||
</template>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</section>
|
||||
<section class="mt-12">
|
||||
<BaseCardSectionTitle :title="$t('recipe.bulk-imports')" />
|
||||
<ReportTable
|
||||
:items="reports"
|
||||
@delete="deleteReport"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<v-container v-if="household"
|
||||
class="narrow-container"
|
||||
<v-container
|
||||
v-if="household"
|
||||
class="narrow-container"
|
||||
>
|
||||
<BasePageTitle class="mb-5">
|
||||
<template #header>
|
||||
@@ -53,105 +54,105 @@ export default defineNuxtComponent({
|
||||
|
||||
const refHouseholdEditForm = ref<VForm | null>(null);
|
||||
|
||||
type Preference = {
|
||||
key: keyof ReadHouseholdPreferences;
|
||||
value: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
type Preference = {
|
||||
key: keyof ReadHouseholdPreferences;
|
||||
value: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const preferencesEditor = computed<Preference[]>(() => {
|
||||
if (!household.value || !household.value.preferences) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: "recipePublic",
|
||||
value: household.value.preferences.recipePublic || false,
|
||||
label: i18n.t("household.allow-users-outside-of-your-household-to-see-your-recipes"),
|
||||
description: i18n.t("household.allow-users-outside-of-your-household-to-see-your-recipes-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowNutrition",
|
||||
value: household.value.preferences.recipeShowNutrition || false,
|
||||
label: i18n.t("group.show-nutrition-information"),
|
||||
description: i18n.t("group.show-nutrition-information-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowAssets",
|
||||
value: household.value.preferences.recipeShowAssets || false,
|
||||
label: i18n.t("group.show-recipe-assets"),
|
||||
description: i18n.t("group.show-recipe-assets-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeLandscapeView",
|
||||
value: household.value.preferences.recipeLandscapeView || false,
|
||||
label: i18n.t("group.default-to-landscape-view"),
|
||||
description: i18n.t("group.default-to-landscape-view-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeDisableComments",
|
||||
value: household.value.preferences.recipeDisableComments || false,
|
||||
label: i18n.t("group.disable-users-from-commenting-on-recipes"),
|
||||
description: i18n.t("group.disable-users-from-commenting-on-recipes-description"),
|
||||
} as Preference,
|
||||
];
|
||||
});
|
||||
const preferencesEditor = computed<Preference[]>(() => {
|
||||
if (!household.value || !household.value.preferences) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: "recipePublic",
|
||||
value: household.value.preferences.recipePublic || false,
|
||||
label: i18n.t("household.allow-users-outside-of-your-household-to-see-your-recipes"),
|
||||
description: i18n.t("household.allow-users-outside-of-your-household-to-see-your-recipes-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowNutrition",
|
||||
value: household.value.preferences.recipeShowNutrition || false,
|
||||
label: i18n.t("group.show-nutrition-information"),
|
||||
description: i18n.t("group.show-nutrition-information-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowAssets",
|
||||
value: household.value.preferences.recipeShowAssets || false,
|
||||
label: i18n.t("group.show-recipe-assets"),
|
||||
description: i18n.t("group.show-recipe-assets-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeLandscapeView",
|
||||
value: household.value.preferences.recipeLandscapeView || false,
|
||||
label: i18n.t("group.default-to-landscape-view"),
|
||||
description: i18n.t("group.default-to-landscape-view-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeDisableComments",
|
||||
value: household.value.preferences.recipeDisableComments || false,
|
||||
label: i18n.t("group.disable-users-from-commenting-on-recipes"),
|
||||
description: i18n.t("group.disable-users-from-commenting-on-recipes-description"),
|
||||
} as Preference,
|
||||
];
|
||||
});
|
||||
|
||||
const allDays = [
|
||||
{
|
||||
name: i18n.t("general.sunday"),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.monday"),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.tuesday"),
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.wednesday"),
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.thursday"),
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.friday"),
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.saturday"),
|
||||
value: 6,
|
||||
},
|
||||
];
|
||||
const allDays = [
|
||||
{
|
||||
name: i18n.t("general.sunday"),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.monday"),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.tuesday"),
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.wednesday"),
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.thursday"),
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.friday"),
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.saturday"),
|
||||
value: 6,
|
||||
},
|
||||
];
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!refHouseholdEditForm.value?.validate() || !household.value?.preferences) {
|
||||
console.log(refHouseholdEditForm.value?.validate());
|
||||
return;
|
||||
}
|
||||
async function handleSubmit() {
|
||||
if (!refHouseholdEditForm.value?.validate() || !household.value?.preferences) {
|
||||
console.log(refHouseholdEditForm.value?.validate());
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await householdActions.updatePreferences();
|
||||
if (data) {
|
||||
alert.success(i18n.t("settings.settings-updated"));
|
||||
}
|
||||
else {
|
||||
alert.error(i18n.t("settings.settings-update-failed"));
|
||||
}
|
||||
}
|
||||
const data = await householdActions.updatePreferences();
|
||||
if (data) {
|
||||
alert.success(i18n.t("settings.settings-updated"));
|
||||
}
|
||||
else {
|
||||
alert.error(i18n.t("settings.settings-update-failed"));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
household,
|
||||
householdActions,
|
||||
allDays,
|
||||
preferencesEditor,
|
||||
refHouseholdEditForm,
|
||||
handleSubmit,
|
||||
};
|
||||
return {
|
||||
household,
|
||||
householdActions,
|
||||
allDays,
|
||||
preferencesEditor,
|
||||
refHouseholdEditForm,
|
||||
handleSubmit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
if (newMeal.existing) {
|
||||
actions.updateOne({ ...newMeal, date: newMealDateString });
|
||||
}
|
||||
else {
|
||||
else {
|
||||
actions.createOne({ ...newMeal, date: newMealDateString });
|
||||
}
|
||||
resetDialog();
|
||||
@@ -147,7 +147,14 @@
|
||||
</v-btn>
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ props }">
|
||||
<v-chip v-bind="props" label variant="elevated" size="small" color="accent" @click.prevent>
|
||||
<v-chip
|
||||
v-bind="props"
|
||||
label
|
||||
variant="elevated"
|
||||
size="small"
|
||||
color="accent"
|
||||
@click.prevent
|
||||
>
|
||||
<v-icon start>
|
||||
{{ $globals.icons.tags }}
|
||||
</v-icon>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<v-container class="mx-0 my-3 pa">
|
||||
<v-row>
|
||||
<v-col v-for="(day, index) in plan" :key="index" cols="12" sm="12" md="4" lg="4" xl="2"
|
||||
class="col-borders my-1 d-flex flex-column">
|
||||
<v-col
|
||||
v-for="(day, index) in plan"
|
||||
:key="index"
|
||||
cols="12"
|
||||
sm="12"
|
||||
md="4"
|
||||
lg="4"
|
||||
xl="2"
|
||||
class="col-borders my-1 d-flex flex-column"
|
||||
>
|
||||
<v-card class="mb-2 border-left-primary rounded-sm px-2">
|
||||
<v-container class="px-0 d-flex align-center" height="56px">
|
||||
<v-row no-gutters style="width: 100%;">
|
||||
@@ -25,13 +33,17 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<RecipeCardMobile v-for="mealplan in section.meals" :key="mealplan.id"
|
||||
:recipe-id="mealplan.recipe ? mealplan.recipe.id! : ''" class="mb-2"
|
||||
<RecipeCardMobile
|
||||
v-for="mealplan in section.meals"
|
||||
:key="mealplan.id"
|
||||
:recipe-id="mealplan.recipe ? mealplan.recipe.id! : ''"
|
||||
class="mb-2"
|
||||
:rating="mealplan.recipe ? mealplan.recipe.rating! : 0"
|
||||
:slug="mealplan.recipe ? mealplan.recipe.slug! : mealplan.title!"
|
||||
:description="mealplan.recipe ? mealplan.recipe.description! : mealplan.text!"
|
||||
:name="mealplan.recipe ? mealplan.recipe.name! : mealplan.title!"
|
||||
:tags="mealplan.recipe ? mealplan.recipe.tags! : []" />
|
||||
:tags="mealplan.recipe ? mealplan.recipe.tags! : []"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -92,32 +92,32 @@
|
||||
class="my-2 left-border"
|
||||
:to="`/shopping-lists/${list.id}`"
|
||||
>
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2">
|
||||
{{ $globals.icons.cartCheck }}
|
||||
</v-icon>
|
||||
<span class="flex-grow-1">
|
||||
{{ list.name }}
|
||||
</span>
|
||||
<v-btn
|
||||
icon
|
||||
variant="plain"
|
||||
@click.prevent="toggleOwnerDialog(list)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.user }}
|
||||
<v-card-title class="d-flex align-center">
|
||||
<v-icon class="mr-2">
|
||||
{{ $globals.icons.cartCheck }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
variant="plain"
|
||||
@click.prevent="openDelete(list.id)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<span class="flex-grow-1">
|
||||
{{ list.name }}
|
||||
</span>
|
||||
<v-btn
|
||||
icon
|
||||
variant="plain"
|
||||
@click.prevent="toggleOwnerDialog(list)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.user }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
variant="plain"
|
||||
@click.prevent="openDelete(list.id)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
</section>
|
||||
</v-container>
|
||||
|
||||
@@ -80,10 +80,10 @@
|
||||
/>
|
||||
<section class="d-flex flex-column">
|
||||
<v-list>
|
||||
<div
|
||||
v-for="(token, index) in user.tokens"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
v-for="(token, index) in user.tokens"
|
||||
:key="index"
|
||||
>
|
||||
<v-list-item>
|
||||
<v-list-item-title>
|
||||
{{ token.name }}
|
||||
|
||||
6
frontend/types/auth.d.ts
vendored
6
frontend/types/auth.d.ts
vendored
@@ -1,11 +1,11 @@
|
||||
import type { UserOut } from "~/lib/api/types/user";
|
||||
|
||||
declare module "#auth-utils" {
|
||||
type User = UserOut;
|
||||
type User = UserOut;
|
||||
|
||||
type UserSession = object;
|
||||
type UserSession = object;
|
||||
|
||||
type SecureSessionData = object;
|
||||
type SecureSessionData = object;
|
||||
}
|
||||
|
||||
export { };
|
||||
|
||||
Reference in New Issue
Block a user