mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-23 17:02:55 -04:00
feat: Add Households to Mealie (#3970)
This commit is contained in:
parent
0c29cef17d
commit
eb170cc7e5
@ -13,8 +13,7 @@ from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
from mealie.db.models.group.shopping_list import ShoppingList
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "b04a08da2108"
|
||||
@ -23,6 +22,25 @@ branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_lists"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
class MultiPurposeLabel(SqlAlchemyBase):
|
||||
__tablename__ = "multi_purpose_labels"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
def populate_shopping_lists_multi_purpose_labels(
|
||||
shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session
|
||||
):
|
||||
|
@ -12,11 +12,11 @@ from typing import Any
|
||||
|
||||
import sqlalchemy as sa
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import Session, load_only
|
||||
|
||||
from alembic import op
|
||||
from mealie.db.models._model_base import SqlAlchemyBase
|
||||
from mealie.db.models.group.shopping_list import ShoppingListItem
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel
|
||||
|
||||
@ -27,6 +27,27 @@ branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
# Intermediate table definitions
|
||||
class SqlAlchemyBase(orm.DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_lists"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True)
|
||||
|
||||
|
||||
class ShoppingListItem(SqlAlchemyBase):
|
||||
__tablename__ = "shopping_list_items"
|
||||
|
||||
id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate)
|
||||
food_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_foods.id"))
|
||||
unit_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_units.id"))
|
||||
label_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("multi_purpose_labels.id"))
|
||||
|
||||
|
||||
@dataclass
|
||||
class TableMeta:
|
||||
tablename: str
|
||||
@ -42,7 +63,7 @@ def _is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def _get_duplicates(session: Session, model: SqlAlchemyBase) -> defaultdict[str, list]:
|
||||
def _get_duplicates(session: Session, model: orm.DeclarativeBase) -> defaultdict[str, list]:
|
||||
duplicate_map: defaultdict[str, list] = defaultdict(list)
|
||||
|
||||
query = session.execute(sa.text(f"SELECT id, group_id, name FROM {model.__tablename__}"))
|
||||
|
@ -0,0 +1,321 @@
|
||||
"""add households
|
||||
|
||||
Revision ID: feecc8ffb956
|
||||
Revises: 32d69327997b
|
||||
Create Date: 2024-07-12 16:16:29.973929
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import sqlalchemy as sa
|
||||
from slugify import slugify
|
||||
from sqlalchemy import orm
|
||||
|
||||
import mealie.db.migration_types
|
||||
from alembic import op
|
||||
from mealie.core.config import get_app_settings
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "feecc8ffb956"
|
||||
down_revision = "32d69327997b"
|
||||
branch_labels = None # type: ignore
|
||||
depends_on = None # type: ignore
|
||||
|
||||
settings = get_app_settings()
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def generate_id() -> str:
|
||||
"""See GUID.convert_value_to_guid"""
|
||||
val = uuid4()
|
||||
if is_postgres():
|
||||
return str(val)
|
||||
else:
|
||||
return f"{val.int:032x}"
|
||||
|
||||
|
||||
def dedupe_cookbook_slugs():
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
with session:
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
SELECT slug, group_id, COUNT(*)
|
||||
FROM cookbooks
|
||||
GROUP BY slug, group_id
|
||||
HAVING COUNT(*) > 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
rows = session.execute(sql).fetchall()
|
||||
|
||||
for slug, group_id, _ in rows:
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
SELECT id
|
||||
FROM cookbooks
|
||||
WHERE slug = :slug AND group_id = :group_id
|
||||
ORDER BY id
|
||||
"""
|
||||
)
|
||||
)
|
||||
cookbook_ids = session.execute(sql, {"slug": slug, "group_id": group_id}).fetchall()
|
||||
|
||||
for i, (cookbook_id,) in enumerate(cookbook_ids):
|
||||
if i == 0:
|
||||
continue
|
||||
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
"""
|
||||
UPDATE cookbooks
|
||||
SET slug = :slug || '-' || :i
|
||||
WHERE id = :id
|
||||
"""
|
||||
)
|
||||
)
|
||||
session.execute(sql, {"slug": slug, "i": i, "id": cookbook_id})
|
||||
|
||||
|
||||
def create_household(session: orm.Session, group_id: str) -> str:
|
||||
# create/insert household
|
||||
household_id = generate_id()
|
||||
timestamp = datetime.now(timezone.utc).isoformat()
|
||||
household_data = {
|
||||
"id": household_id,
|
||||
"name": settings.DEFAULT_HOUSEHOLD,
|
||||
"slug": slugify(settings.DEFAULT_HOUSEHOLD),
|
||||
"group_id": group_id,
|
||||
"created_at": timestamp,
|
||||
"update_at": timestamp,
|
||||
}
|
||||
columns = ", ".join(household_data.keys())
|
||||
placeholders = ", ".join(f":{key}" for key in household_data.keys())
|
||||
sql_statement = f"INSERT INTO households ({columns}) VALUES ({placeholders})"
|
||||
|
||||
session.execute(sa.text(sql_statement), household_data)
|
||||
|
||||
# fetch group preferences so we can copy them over to household preferences
|
||||
migrated_field_defaults = {
|
||||
"private_group": True, # this is renamed later
|
||||
"first_day_of_week": 0,
|
||||
"recipe_public": True,
|
||||
"recipe_show_nutrition": False,
|
||||
"recipe_show_assets": False,
|
||||
"recipe_landscape_view": False,
|
||||
"recipe_disable_comments": False,
|
||||
"recipe_disable_amount": True,
|
||||
}
|
||||
sql_statement = (
|
||||
f"SELECT {', '.join(migrated_field_defaults.keys())} FROM group_preferences WHERE group_id = :group_id"
|
||||
)
|
||||
group_preferences = session.execute(sa.text(sql_statement), {"group_id": group_id}).fetchone()
|
||||
|
||||
# build preferences data
|
||||
if group_preferences:
|
||||
preferences_data: dict[str, Any] = {}
|
||||
for i, (field, default_value) in enumerate(migrated_field_defaults.items()):
|
||||
value = group_preferences[i]
|
||||
preferences_data[field] = value if value is not None else default_value
|
||||
else:
|
||||
preferences_data = migrated_field_defaults
|
||||
|
||||
preferences_data["id"] = generate_id()
|
||||
preferences_data["household_id"] = household_id
|
||||
preferences_data["created_at"] = timestamp
|
||||
preferences_data["update_at"] = timestamp
|
||||
preferences_data["private_household"] = preferences_data.pop("private_group")
|
||||
|
||||
# insert preferences data
|
||||
columns = ", ".join(preferences_data.keys())
|
||||
placeholders = ", ".join(f":{key}" for key in preferences_data.keys())
|
||||
sql_statement = f"INSERT INTO household_preferences ({columns}) VALUES ({placeholders})"
|
||||
|
||||
session.execute(sa.text(sql_statement), preferences_data)
|
||||
|
||||
return household_id
|
||||
|
||||
|
||||
def create_households_for_groups() -> dict[str, str]:
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
group_id_household_id_map: dict[str, str] = {}
|
||||
with session:
|
||||
rows = session.execute(sa.text("SELECT id FROM groups")).fetchall()
|
||||
for row in rows:
|
||||
group_id = row[0]
|
||||
group_id_household_id_map[group_id] = create_household(session, group_id)
|
||||
|
||||
return group_id_household_id_map
|
||||
|
||||
|
||||
def _do_assignment(session: orm.Session, table: str, group_id: str, household_id: str):
|
||||
sql = sa.text(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE {table}
|
||||
SET household_id = :household_id
|
||||
WHERE group_id = :group_id
|
||||
""",
|
||||
)
|
||||
)
|
||||
session.execute(sql, {"group_id": group_id, "household_id": household_id})
|
||||
|
||||
|
||||
def assign_households(group_id_household_id_map: dict[str, str]):
|
||||
tables = [
|
||||
"cookbooks",
|
||||
"group_events_notifiers",
|
||||
"group_meal_plan_rules",
|
||||
"invite_tokens",
|
||||
"recipe_actions",
|
||||
"users",
|
||||
"webhook_urls",
|
||||
]
|
||||
|
||||
bind = op.get_bind()
|
||||
session = orm.Session(bind=bind)
|
||||
with session:
|
||||
for table in tables:
|
||||
for group_id, household_id in group_id_household_id_map.items():
|
||||
_do_assignment(session, table, group_id, household_id)
|
||||
|
||||
|
||||
def populate_household_data():
|
||||
group_id_household_id_map = create_households_for_groups()
|
||||
assign_households(group_id_household_id_map)
|
||||
|
||||
|
||||
def upgrade():
|
||||
dedupe_cookbook_slugs()
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"households",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("name", sa.String(), nullable=False),
|
||||
sa.Column("slug", sa.String(), nullable=True),
|
||||
sa.Column("group_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["group_id"],
|
||||
["groups.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("group_id", "name", name="household_name_group_id_key"),
|
||||
sa.UniqueConstraint("group_id", "slug", name="household_slug_group_id_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_households_created_at"), "households", ["created_at"], unique=False)
|
||||
op.create_index(op.f("ix_households_group_id"), "households", ["group_id"], unique=False)
|
||||
op.create_index(op.f("ix_households_name"), "households", ["name"], unique=False)
|
||||
op.create_index(op.f("ix_households_slug"), "households", ["slug"], unique=False)
|
||||
op.create_table(
|
||||
"household_preferences",
|
||||
sa.Column("id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=False),
|
||||
sa.Column("private_household", sa.Boolean(), nullable=True),
|
||||
sa.Column("first_day_of_week", sa.Integer(), nullable=True),
|
||||
sa.Column("recipe_public", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_nutrition", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_show_assets", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_landscape_view", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_comments", sa.Boolean(), nullable=True),
|
||||
sa.Column("recipe_disable_amount", sa.Boolean(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("update_at", sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["household_id"],
|
||||
["households.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f("ix_household_preferences_created_at"), "household_preferences", ["created_at"], unique=False)
|
||||
op.create_index(
|
||||
op.f("ix_household_preferences_household_id"), "household_preferences", ["household_id"], unique=False
|
||||
)
|
||||
|
||||
with op.batch_alter_table("cookbooks") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_cookbooks_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_cookbooks_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
# not directly related to households, but important for frontend routes
|
||||
batch_op.create_unique_constraint("cookbook_slug_group_id_key", ["slug", "group_id"])
|
||||
|
||||
with op.batch_alter_table("group_events_notifiers") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_group_events_notifiers_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_group_events_notifiers_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("group_meal_plan_rules") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_group_meal_plan_rules_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_group_meal_plan_rules_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("invite_tokens") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_invite_tokens_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_invite_tokens_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("recipe_actions") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_recipe_actions_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_recipe_actions_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("users") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_users_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_users_household_id", "households", ["household_id"], ["id"])
|
||||
|
||||
with op.batch_alter_table("webhook_urls") as batch_op:
|
||||
batch_op.add_column(sa.Column("household_id", mealie.db.migration_types.GUID(), nullable=True))
|
||||
batch_op.create_index(op.f("ix_webhook_urls_household_id"), ["household_id"], unique=False)
|
||||
batch_op.create_foreign_key("fk_webhook_urls_household_id", "households", ["household_id"], ["id"])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
populate_household_data()
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, "webhook_urls", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_webhook_urls_household_id"), table_name="webhook_urls")
|
||||
op.drop_column("webhook_urls", "household_id")
|
||||
op.drop_constraint(None, "users", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_users_household_id"), table_name="users")
|
||||
op.drop_column("users", "household_id")
|
||||
op.drop_constraint(None, "recipe_actions", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_recipe_actions_household_id"), table_name="recipe_actions")
|
||||
op.drop_column("recipe_actions", "household_id")
|
||||
op.drop_constraint(None, "invite_tokens", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_invite_tokens_household_id"), table_name="invite_tokens")
|
||||
op.drop_column("invite_tokens", "household_id")
|
||||
op.drop_constraint(None, "group_meal_plan_rules", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_group_meal_plan_rules_household_id"), table_name="group_meal_plan_rules")
|
||||
op.drop_column("group_meal_plan_rules", "household_id")
|
||||
op.drop_constraint(None, "group_events_notifiers", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_group_events_notifiers_household_id"), table_name="group_events_notifiers")
|
||||
op.drop_column("group_events_notifiers", "household_id")
|
||||
op.drop_constraint(None, "cookbooks", type_="foreignkey")
|
||||
op.drop_index(op.f("ix_cookbooks_household_id"), table_name="cookbooks")
|
||||
op.drop_column("cookbooks", "household_id")
|
||||
op.drop_constraint("cookbook_slug_group_id_key", "cookbooks", type_="unique")
|
||||
op.drop_index(op.f("ix_household_preferences_household_id"), table_name="household_preferences")
|
||||
op.drop_index(op.f("ix_household_preferences_created_at"), table_name="household_preferences")
|
||||
op.drop_table("household_preferences")
|
||||
op.drop_index(op.f("ix_households_slug"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_name"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_group_id"), table_name="households")
|
||||
op.drop_index(op.f("ix_households_created_at"), table_name="households")
|
||||
op.drop_table("households")
|
||||
# ### end Alembic commands ###
|
@ -67,7 +67,7 @@ def rename_non_compliant_paths():
|
||||
kabab case.
|
||||
"""
|
||||
|
||||
ignore_files = ["DS_Store", ".gitkeep"]
|
||||
ignore_files = ["DS_Store", ".gitkeep", "af-ZA.json", "en-US.json"]
|
||||
|
||||
ignore_extensions = [".pyc", ".pyo", ".py"]
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@ -23,6 +24,11 @@ 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:
|
||||
|
@ -173,7 +173,7 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic
|
||||
"dateAdded": "2022-09-03",
|
||||
"dateUpdated": "2022-09-10T15:18:19.866085",
|
||||
"createdAt": "2022-09-03T18:31:17.488118",
|
||||
"updateAt": "2022-09-10T15:18:19.869630",
|
||||
"updatedAt": "2022-09-10T15:18:19.869630",
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"id": "60ae53a3-b3ff-40ee-bae3-89fea0b1c637",
|
||||
|
@ -24,7 +24,7 @@ Make sure the url and port (`http://mealie:9000` ) matches your installation's a
|
||||
|
||||
```yaml
|
||||
- platform: rest
|
||||
resource: "http://mealie:9000/api/groups/mealplans/today"
|
||||
resource: "http://mealie:9000/api/households/mealplans/today"
|
||||
method: GET
|
||||
name: Mealie todays meal
|
||||
headers:
|
||||
@ -36,7 +36,7 @@ Make sure the url and port (`http://mealie:9000` ) matches your installation's a
|
||||
|
||||
```yaml
|
||||
- platform: rest
|
||||
resource: "http://mealie:9000/api/groups/mealplans/today"
|
||||
resource: "http://mealie:9000/api/households/mealplans/today"
|
||||
method: GET
|
||||
name: Mealie todays meal ID
|
||||
headers:
|
||||
|
@ -73,13 +73,13 @@ Mealie uses a calendar like view to help you plan your meals. It shows you the p
|
||||
!!! tip
|
||||
You can also add a "Note" type entry to your meal-plan when you want to include something that might not have a specific recipes. This is great for leftovers, or for ordering out.
|
||||
|
||||
[Mealplanner Demo](https://demo.mealie.io/group/mealplan/planner/view){ .md-button .md-button--primary }
|
||||
[Mealplanner Demo](https://demo.mealie.io/household/mealplan/planner/view){ .md-button .md-button--primary }
|
||||
|
||||
### Planner Rules
|
||||
|
||||
The meal planner has the concept of plan rules. These offer a flexible way to use your organizers to customize how a random recipe is inserted into your meal plan. You can set rules to restrict the pool of recipes based on the Tags and/or Categories of a recipe. Additionally, since meal plans have a Breakfast, Lunch, Dinner, and Snack labels, you can specifically set a rule to be active for a **specific meal type** or even a **specific day of the week.**
|
||||
|
||||
[Planner Settings Demo](https://demo.mealie.io/group/mealplan/settings){ .md-button .md-button--primary }
|
||||
[Planner Settings Demo](https://demo.mealie.io/household/mealplan/settings){ .md-button .md-button--primary }
|
||||
|
||||
## Shopping Lists
|
||||
|
||||
@ -105,13 +105,13 @@ Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), whi
|
||||
- `json` and `jsons`
|
||||
- `xml` and `xmls`
|
||||
|
||||
[Notifiers Demo](https://demo.mealie.io/group/notifiers){ .md-button .md-button--primary }
|
||||
[Notifiers Demo](https://demo.mealie.io/household/notifiers){ .md-button .md-button--primary }
|
||||
|
||||
### Webhooks
|
||||
|
||||
Unlike notifiers, which are event-driven notifications, Webhooks allow you to send scheduled notifications to your desired endpoint. Webhooks are sent on the day of a scheduled mealplan, at the specified time, and contain the mealplan data in the request.
|
||||
|
||||
[Webhooks Demo](https://demo.mealie.io/group/webhooks){ .md-button .md-button--primary }
|
||||
[Webhooks Demo](https://demo.mealie.io/household/webhooks){ .md-button .md-button--primary }
|
||||
|
||||
### Recipe Actions
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,30 +2,11 @@
|
||||
<div v-if="preferences">
|
||||
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
|
||||
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" :label="$t('group.private-group')"></v-checkbox>
|
||||
<v-select
|
||||
v-model="preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
/>
|
||||
|
||||
<BaseCardSectionTitle class="mt-5" :title="$tc('group.group-recipe-preferences')"></BaseCardSectionTitle>
|
||||
<template v-for="(_, key) in preferences">
|
||||
<v-checkbox
|
||||
v-if="labels[key]"
|
||||
:key="key"
|
||||
v-model="preferences[key]"
|
||||
class="mt-n4"
|
||||
:label="labels[key]"
|
||||
></v-checkbox>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -35,48 +16,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { i18n } = useContext();
|
||||
|
||||
const labels = {
|
||||
recipePublic: i18n.tc("group.allow-users-outside-of-your-group-to-see-your-recipes"),
|
||||
recipeShowNutrition: i18n.tc("group.show-nutrition-information"),
|
||||
recipeShowAssets: i18n.tc("group.show-recipe-assets"),
|
||||
recipeLandscapeView: i18n.tc("group.default-to-landscape-view"),
|
||||
recipeDisableComments: i18n.tc("group.disable-users-from-commenting-on-recipes"),
|
||||
recipeDisableAmount: i18n.tc("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
||||
};
|
||||
|
||||
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 preferences = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
@ -87,8 +26,6 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
return {
|
||||
allDays,
|
||||
labels,
|
||||
preferences,
|
||||
};
|
||||
},
|
||||
|
@ -39,7 +39,7 @@
|
||||
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
|
||||
import { ShoppingListSummary } from "~/lib/api/types/group";
|
||||
import { ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
|
||||
export interface ContextMenuItem {
|
@ -35,7 +35,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
||||
import { ReadWebhook } from "~/lib/api/types/group";
|
||||
import { ReadWebhook } from "~/lib/api/types/household";
|
||||
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
||||
|
||||
export default defineComponent({
|
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div v-if="preferences">
|
||||
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
|
||||
<v-checkbox v-model="preferences.privateHousehold" class="mt-n4" :label="$t('household.private-household')"></v-checkbox>
|
||||
<v-select
|
||||
v-model="preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
/>
|
||||
|
||||
<BaseCardSectionTitle class="mt-5" :title="$tc('household.household-recipe-preferences')"></BaseCardSectionTitle>
|
||||
<template v-for="(_, key) in preferences">
|
||||
<v-checkbox
|
||||
v-if="labels[key]"
|
||||
:key="key"
|
||||
v-model="preferences[key]"
|
||||
class="mt-n4"
|
||||
:label="labels[key]"
|
||||
></v-checkbox>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { i18n } = useContext();
|
||||
|
||||
const labels = {
|
||||
recipePublic: i18n.tc("household.allow-users-outside-of-your-household-to-see-your-recipes"),
|
||||
recipeShowNutrition: i18n.tc("group.show-nutrition-information"),
|
||||
recipeShowAssets: i18n.tc("group.show-recipe-assets"),
|
||||
recipeLandscapeView: i18n.tc("group.default-to-landscape-view"),
|
||||
recipeDisableComments: i18n.tc("group.disable-users-from-commenting-on-recipes"),
|
||||
recipeDisableAmount: i18n.tc("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
||||
};
|
||||
|
||||
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 preferences = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
context.emit("input", val);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
allDays,
|
||||
labels,
|
||||
preferences,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
@ -337,7 +337,7 @@ export default defineComponent({
|
||||
);
|
||||
break;
|
||||
case EVENTS.updated:
|
||||
setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false);
|
||||
setter("updated_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false);
|
||||
break;
|
||||
case EVENTS.lastMade:
|
||||
setter(
|
||||
|
@ -138,11 +138,11 @@ import RecipeDialogShare from "./RecipeDialogShare.vue";
|
||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useGroupRecipeActions } from "~/composables/use-group-recipe-actions";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { usePlanTypeOptions } from "~/composables/use-group-mealplan";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { GroupRecipeActionOut, ShoppingListSummary } from "~/lib/api/types/group";
|
||||
import { GroupRecipeActionOut, ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import { PlanEntryType } from "~/lib/api/types/meal-plan";
|
||||
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
||||
|
||||
@ -254,14 +254,14 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const { i18n, $auth, $globals } = useContext();
|
||||
const { group } = useGroupSelf();
|
||||
const { household } = useHouseholdSelf();
|
||||
const { isOwnGroup } = useLoggedInState();
|
||||
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
|
||||
// ===========================================================================
|
||||
|
@ -18,7 +18,7 @@
|
||||
</tr>
|
||||
</template>
|
||||
<template #item.name="{ item }">
|
||||
<a :href="`/r/${item.slug}`" style="color: inherit; text-decoration: inherit; " @click="$emit('click')">{{ item.name }}</a>
|
||||
<a :href="`/g/${groupSlug}/r/${item.slug}`" style="color: inherit; text-decoration: inherit; " @click="$emit('click')">{{ item.name }}</a>
|
||||
</template>
|
||||
<template #item.tags="{ item }">
|
||||
<RecipeChip small :items="item.tags" :is-category="false" url-prefix="tags" />
|
||||
@ -48,7 +48,7 @@ import UserAvatar from "../User/UserAvatar.vue";
|
||||
import RecipeChip from "./RecipeChips.vue";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
import { UserSummary } from "~/lib/api/types/user";
|
||||
|
||||
const INPUT_EVENT = "input";
|
||||
|
||||
@ -95,7 +95,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { i18n } = useContext();
|
||||
const { $auth, i18n } = useContext();
|
||||
const groupSlug = $auth.user?.groupSlug;
|
||||
|
||||
function setValue(value: Recipe[]) {
|
||||
context.emit(INPUT_EVENT, value);
|
||||
@ -134,7 +135,7 @@ export default defineComponent({
|
||||
// ============
|
||||
// Group Members
|
||||
const api = useUserApi();
|
||||
const members = ref<UserOut[]>([]);
|
||||
const members = ref<UserSummary[]>([]);
|
||||
|
||||
async function refreshMembers() {
|
||||
const { data } = await api.groups.fetchMembers();
|
||||
@ -149,13 +150,19 @@ export default defineComponent({
|
||||
|
||||
function getMember(id: string) {
|
||||
if (members.value[0]) {
|
||||
return members.value.find((m) => m.id === id)?.username;
|
||||
return members.value.find((m) => m.id === id)?.fullName;
|
||||
}
|
||||
|
||||
return i18n.t("general.none");
|
||||
}
|
||||
|
||||
return { setValue, headers, members, getMember };
|
||||
return {
|
||||
groupSlug,
|
||||
setValue,
|
||||
headers,
|
||||
members,
|
||||
getMember,
|
||||
};
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -127,14 +127,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api"
|
||||
import { toRefs } from "@vueuse/core"
|
||||
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue"
|
||||
import { useUserApi } from "~/composables/api"
|
||||
import { alert } from "~/composables/use-toast"
|
||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences"
|
||||
import { ShoppingListSummary } from "~/lib/api/types/group"
|
||||
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe"
|
||||
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api";
|
||||
import { toRefs } from "@vueuse/core";
|
||||
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
||||
import { ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface RecipeWithScale extends Recipe {
|
||||
scale: number;
|
||||
@ -209,7 +209,8 @@ export default defineComponent({
|
||||
watchEffect(
|
||||
() => {
|
||||
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
|
||||
openShoppingListIngredientDialog(shoppingListChoices.value[0]);
|
||||
selectedShoppingList.value = shoppingListChoices.value[0];
|
||||
openShoppingListIngredientDialog(selectedShoppingList.value);
|
||||
} else {
|
||||
ready.value = true;
|
||||
}
|
||||
@ -365,12 +366,8 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const successMessage = promises.length === 1
|
||||
? i18n.t("recipe.successfully-added-to-list") as string
|
||||
: i18n.t("recipe.failed-to-add-to-list") as string;
|
||||
|
||||
success ? alert.success(successMessage)
|
||||
: alert.error(i18n.t("failed-to-add-recipes-to-list") as string)
|
||||
success ? alert.success(i18n.tc("recipe.successfully-added-to-list"))
|
||||
: alert.error(i18n.tc("failed-to-add-recipes-to-list"))
|
||||
|
||||
state.shoppingListDialog = false;
|
||||
state.shoppingListIngredientDialog = false;
|
||||
|
@ -66,7 +66,7 @@ import { defineComponent, computed, toRefs, reactive, useContext, useRoute } fro
|
||||
import { useClipboard, useShare, whenever } from "@vueuse/core";
|
||||
import { RecipeShareToken } from "~/lib/api/types/recipe";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
|
||||
export default defineComponent({
|
||||
@ -113,12 +113,12 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const { $auth, i18n } = useContext();
|
||||
const { group } = useGroupSelf();
|
||||
const { household } = useHouseholdSelf();
|
||||
const route = useRoute();
|
||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
|
@ -349,7 +349,7 @@ export default defineComponent({
|
||||
{
|
||||
icon: $globals.icons.update,
|
||||
name: i18n.tc("general.updated"),
|
||||
value: "update_at",
|
||||
value: "updated_at",
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.diceMultiple,
|
||||
|
@ -11,7 +11,7 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
||||
import { RecipeIngredient } from "~/lib/api/types/group";
|
||||
import { RecipeIngredient } from "~/lib/api/types/household";
|
||||
import { useParsedIngredientText } from "~/composables/recipes";
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -114,7 +114,7 @@ import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@n
|
||||
import { whenever } from "@vueuse/core";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { Recipe, RecipeTimelineEventIn } from "~/lib/api/types/recipe";
|
||||
|
||||
export default defineComponent({
|
||||
@ -131,7 +131,7 @@ export default defineComponent({
|
||||
setup(props, context) {
|
||||
const madeThisDialog = ref(false);
|
||||
const userApi = useUserApi();
|
||||
const { group } = useGroupSelf();
|
||||
const { household } = useHouseholdSelf();
|
||||
const { $auth, i18n } = useContext();
|
||||
const domMadeThisForm = ref<VForm>();
|
||||
const newTimelineEvent = ref<RecipeTimelineEventIn>({
|
||||
@ -157,7 +157,7 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
|
||||
function clearImage() {
|
||||
|
@ -31,7 +31,7 @@
|
||||
import { computed, defineComponent, useContext, useRoute } from "@nuxtjs/composition-api";
|
||||
import DOMPurify from "dompurify";
|
||||
import { useFraction } from "~/composables/recipes/use-fraction";
|
||||
import { ShoppingListItemOut } from "~/lib/api/types/group";
|
||||
import { ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/group";
|
||||
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/household";
|
||||
|
||||
interface actions {
|
||||
text: string;
|
||||
|
@ -75,7 +75,7 @@
|
||||
<v-row v-if="listItem.checked" no-gutters class="mb-2">
|
||||
<v-col cols="auto">
|
||||
<div class="text-caption font-weight-light font-italic">
|
||||
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updateAt || "").toLocaleDateString($i18n.locale)}) }}
|
||||
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updatedAt || "").toLocaleDateString($i18n.locale)}) }}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@ -99,7 +99,7 @@ import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-
|
||||
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
|
||||
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
||||
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
|
||||
import { ShoppingListItemOut } from "~/lib/api/types/group";
|
||||
import { ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
|
||||
import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
|
||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||
|
@ -111,7 +111,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, watch } from "@nuxtjs/composition-api";
|
||||
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/group";
|
||||
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
||||
|
@ -183,7 +183,7 @@ export default defineComponent({
|
||||
{
|
||||
icon: $globals.icons.calendarMultiselect,
|
||||
title: i18n.tc("meal-plan.meal-planner"),
|
||||
to: "/group/mealplan/planner/view",
|
||||
to: "/household/mealplan/planner/view",
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-toolbar flat>
|
||||
<v-toolbar color="transparent" flat>
|
||||
<BaseButton color="null" rounded secondary @click="$router.go(-1)">
|
||||
<template #icon> {{ $globals.icons.arrowLeftBold }}</template>
|
||||
{{ $t('general.back') }}
|
||||
|
@ -85,6 +85,8 @@
|
||||
:label="inputField.label"
|
||||
:name="inputField.varName"
|
||||
:items="inputField.options"
|
||||
:item-text="inputField.itemText"
|
||||
:item-value="inputField.itemValue"
|
||||
:return-object="false"
|
||||
:hint="inputField.hint"
|
||||
persistent-hint
|
||||
|
@ -4,7 +4,7 @@ import { BaseCRUDAPI, BaseCRUDAPIReadOnly } from "~/lib/api/base/base-clients";
|
||||
import { QueryValue } from "~/lib/api/base/route";
|
||||
|
||||
type BoundT = {
|
||||
id?: string | number;
|
||||
id?: string | number | null;
|
||||
};
|
||||
|
||||
interface PublicStoreActions<T extends BoundT> {
|
||||
|
@ -160,6 +160,9 @@ export function usePageUser(): { user: UserOut } {
|
||||
group: "",
|
||||
groupId: "",
|
||||
groupSlug: "",
|
||||
household: "",
|
||||
householdId: "",
|
||||
householdSlug: "",
|
||||
cacheKey: "",
|
||||
email: "",
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
||||
}
|
||||
|
||||
const { quantity, food, unit, note } = ingredient;
|
||||
const usePluralUnit = quantity !== undefined && (quantity * scale > 1 || quantity * scale === 0);
|
||||
const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0);
|
||||
const usePluralFood = (!quantity) || quantity * scale > 1
|
||||
|
||||
let returnQty = "";
|
||||
@ -69,8 +69,8 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
||||
}
|
||||
}
|
||||
|
||||
const unitName = useUnitName(unit, usePluralUnit);
|
||||
const foodName = useFoodName(food, usePluralFood);
|
||||
const unitName = useUnitName(unit || undefined, usePluralUnit);
|
||||
const foodName = useFoodName(food || undefined, usePluralFood);
|
||||
|
||||
return {
|
||||
quantity: returnQty ? sanitizeIngredientHTML(returnQty) : undefined,
|
||||
|
@ -2,7 +2,7 @@ import { reactive, ref, Ref } from "@nuxtjs/composition-api";
|
||||
import { usePublicStoreActions, useStoreActions } from "../partials/use-actions-factory";
|
||||
import { usePublicExploreApi } from "../api/api-client";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { RecipeTag } from "~/lib/api/types/admin";
|
||||
import { RecipeTag } from "~/lib/api/types/recipe";
|
||||
|
||||
const items: Ref<RecipeTag[]> = ref([]);
|
||||
const publicStoreLoading = ref(false);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useAsync, ref, Ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { useAsyncKey } from "./use-utils";
|
||||
import { usePublicExploreApi } from "./api/api-client";
|
||||
import { useHouseholdSelf } from "./use-households";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
|
||||
|
||||
@ -67,6 +68,7 @@ export const usePublicCookbooks = function (groupSlug: string) {
|
||||
|
||||
export const useCookbooks = function () {
|
||||
const api = useUserApi();
|
||||
const { household } = useHouseholdSelf();
|
||||
const loading = ref(false);
|
||||
|
||||
const { i18n } = useContext();
|
||||
@ -100,7 +102,7 @@ export const useCookbooks = function () {
|
||||
async createOne() {
|
||||
loading.value = true;
|
||||
const { data } = await api.cookbooks.createOne({
|
||||
name: i18n.t("cookbook.cookbook-with-name", [String((cookbookStore?.value?.length ?? 0) + 1)]) as string,
|
||||
name: i18n.t("cookbook.household-cookbook-name", [household.value?.name || "", String((cookbookStore?.value?.length ?? 0) + 1)]) as string,
|
||||
});
|
||||
if (data && cookbookStore?.value) {
|
||||
cookbookStore.value.push(data);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { computed, reactive, ref } from "@nuxtjs/composition-api";
|
||||
import { useStoreActions } from "./partials/use-actions-factory";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { GroupRecipeActionOut, RecipeActionType } from "~/lib/api/types/group";
|
||||
import { GroupRecipeActionOut, GroupRecipeActionType } from "~/lib/api/types/household";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
const groupRecipeActions = ref<GroupRecipeActionOut[] | null>(null);
|
||||
@ -10,7 +10,7 @@ const loading = ref(false);
|
||||
export function useGroupRecipeActionData() {
|
||||
const data = reactive({
|
||||
id: "",
|
||||
actionType: "link" as RecipeActionType,
|
||||
actionType: "link" as GroupRecipeActionType,
|
||||
title: "",
|
||||
url: "",
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||
import { useAsyncKey } from "./use-utils";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { ReadWebhook } from "~/lib/api/types/group";
|
||||
import { ReadWebhook } from "~/lib/api/types/household";
|
||||
|
||||
export const useGroupWebhooks = function () {
|
||||
const api = useUserApi();
|
||||
|
@ -51,7 +51,7 @@ export const useGroups = function () {
|
||||
loading.value = true;
|
||||
const asyncKey = String(Date.now());
|
||||
const groups = useAsync(async () => {
|
||||
const { data } = await api.groups.getAll();
|
||||
const { data } = await api.groups.getAll(1, -1, {orderBy: "name", orderDirection: "asc"});;
|
||||
|
||||
if (data) {
|
||||
return data.items;
|
||||
@ -66,7 +66,7 @@ export const useGroups = function () {
|
||||
|
||||
async function refreshAllGroups() {
|
||||
loading.value = true;
|
||||
const { data } = await api.groups.getAll();
|
||||
const { data } = await api.groups.getAll(1, -1, {orderBy: "name", orderDirection: "asc"});;
|
||||
|
||||
if (data) {
|
||||
groups.value = data.items;
|
||||
|
117
frontend/composables/use-households.ts
Normal file
117
frontend/composables/use-households.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { computed, ref, Ref, useAsync } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { HouseholdCreate, HouseholdInDB } from "~/lib/api/types/household";
|
||||
|
||||
const householdSelfRef = ref<HouseholdInDB | null>(null);
|
||||
const loading = ref(false);
|
||||
|
||||
export const useHouseholdSelf = function () {
|
||||
const api = useUserApi();
|
||||
|
||||
async function refreshHouseholdSelf() {
|
||||
loading.value = true;
|
||||
const { data } = await api.households.getCurrentUserHousehold();
|
||||
householdSelfRef.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
const actions = {
|
||||
get() {
|
||||
if (!(householdSelfRef.value || loading.value)) {
|
||||
refreshHouseholdSelf();
|
||||
}
|
||||
|
||||
return householdSelfRef;
|
||||
},
|
||||
async updatePreferences() {
|
||||
if (!householdSelfRef.value) {
|
||||
await refreshHouseholdSelf();
|
||||
}
|
||||
if (!householdSelfRef.value?.preferences) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await api.households.setPreferences(householdSelfRef.value.preferences);
|
||||
|
||||
if (data) {
|
||||
householdSelfRef.value.preferences = data;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const household = actions.get();
|
||||
|
||||
return { actions, household };
|
||||
};
|
||||
|
||||
export const useHouseholds = function () {
|
||||
const api = useUserApi();
|
||||
const loading = ref(false);
|
||||
|
||||
function getAllHouseholds() {
|
||||
loading.value = true;
|
||||
const asyncKey = String(Date.now());
|
||||
const households = useAsync(async () => {
|
||||
const { data } = await api.households.getAll(1, -1, {orderBy: "name, group.name", orderDirection: "asc"});
|
||||
|
||||
if (data) {
|
||||
return data.items;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, asyncKey);
|
||||
|
||||
loading.value = false;
|
||||
return households;
|
||||
}
|
||||
|
||||
async function refreshAllHouseholds() {
|
||||
loading.value = true;
|
||||
const { data } = await api.households.getAll(1, -1, {orderBy: "name, group.name", orderDirection: "asc"});;
|
||||
|
||||
if (data) {
|
||||
households.value = data.items;
|
||||
} else {
|
||||
households.value = null;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
async function deleteHousehold(id: string | number) {
|
||||
loading.value = true;
|
||||
const { data } = await api.households.deleteOne(id);
|
||||
loading.value = false;
|
||||
refreshAllHouseholds();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function createHousehold(payload: HouseholdCreate) {
|
||||
loading.value = true;
|
||||
const { data } = await api.households.createOne(payload);
|
||||
|
||||
if (data && households.value) {
|
||||
households.value.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
const households = getAllHouseholds();
|
||||
function useHouseholdsInGroup(groupIdRef: Ref<string>) {
|
||||
return computed(
|
||||
() => {
|
||||
return (households.value && groupIdRef.value)
|
||||
? households.value.filter((h) => h.groupId === groupIdRef.value)
|
||||
: [];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
households,
|
||||
useHouseholdsInGroup,
|
||||
getAllHouseholds,
|
||||
refreshAllHouseholds,
|
||||
deleteHousehold,
|
||||
createHousehold,
|
||||
};
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
import { computed, reactive, watch } from "@nuxtjs/composition-api";
|
||||
import { useLocalStorage } from "@vueuse/core";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { ShoppingListItemOut, ShoppingListOut } from "~/lib/api/types/group";
|
||||
import { ShoppingListItemOut, ShoppingListOut } from "~/lib/api/types/household";
|
||||
import { RequestResponse } from "~/lib/api/types/non-generated";
|
||||
|
||||
const localStorageKey = "shopping-list-queue";
|
||||
@ -144,7 +144,7 @@ export function useShoppingListItemActions(shoppingListId: string) {
|
||||
|
||||
function checkUpdateState(list: ShoppingListOut) {
|
||||
const cutoffDate = new Date(queue.lastUpdate + queueTimeout).toISOString();
|
||||
if (list.updateAt && list.updateAt > cutoffDate) {
|
||||
if (list.updatedAt && list.updatedAt > cutoffDate) {
|
||||
// If the queue is too far behind the shopping list to reliably do updates, we clear the queue
|
||||
console.log("Out of sync with server; clearing queue");
|
||||
clearQueueItems("all");
|
||||
|
@ -8,6 +8,7 @@
|
||||
"database-type": "Database Type",
|
||||
"database-url": "Database URL",
|
||||
"default-group": "Default Group",
|
||||
"default-household": "Default Household",
|
||||
"demo": "Demo",
|
||||
"demo-status": "Demo Status",
|
||||
"development": "Development",
|
||||
@ -238,7 +239,7 @@
|
||||
"keep-my-recipes-private-description": "Sets your group and all recipes defaults to private. You can always change this later."
|
||||
},
|
||||
"manage-members": "Manage Members",
|
||||
"manage-members-description": "Manage the permissions of the members in your groups. {manage} allows the user to access the data-management page {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
|
||||
"manage-members-description": "Manage the permissions of the members in your household. {manage} allows the user to access the data-management page, and {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
|
||||
"manage": "Manage",
|
||||
"invite": "Invite",
|
||||
"looking-to-update-your-profile": "Looking to Update Your Profile?",
|
||||
@ -246,7 +247,7 @@
|
||||
"default-recipe-preferences": "Default Recipe Preferences",
|
||||
"group-preferences": "Group Preferences",
|
||||
"private-group": "Private Group",
|
||||
"private-group-description": "Setting your group to private will default all public view options to default. This overrides an individual recipes public view settings.",
|
||||
"private-group-description": "Setting your group to private will default all public view options to default. This overrides any individual households or recipes public view settings.",
|
||||
"enable-public-access": "Enable Public Access",
|
||||
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
|
||||
"allow-users-outside-of-your-group-to-see-your-recipes": "Allow users outside of your group to see your recipes",
|
||||
@ -260,7 +261,7 @@
|
||||
"disable-users-from-commenting-on-recipes": "Disable users from commenting on recipes",
|
||||
"disable-users-from-commenting-on-recipes-description": "Hides the comment section on the recipe page and disables commenting",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food": "Disable organizing recipe ingredients by units and food",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields.",
|
||||
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields",
|
||||
"general-preferences": "General Preferences",
|
||||
"group-recipe-preferences": "Group Recipe Preferences",
|
||||
"report": "Report",
|
||||
@ -268,7 +269,28 @@
|
||||
"group-management": "Group Management",
|
||||
"admin-group-management": "Admin Group Management",
|
||||
"admin-group-management-text": "Changes to this group will be reflected immediately.",
|
||||
"group-id-value": "Group Id: {0}"
|
||||
"group-id-value": "Group Id: {0}",
|
||||
"total-households": "Total Households"
|
||||
},
|
||||
"household": {
|
||||
"household": "Household",
|
||||
"households": "Households",
|
||||
"user-household": "User Household",
|
||||
"create-household": "Create Household",
|
||||
"household-name": "Household Name",
|
||||
"household-group": "Household Group",
|
||||
"household-management": "Household Management",
|
||||
"manage-households": "Manage Households",
|
||||
"admin-household-management": "Admin Household Management",
|
||||
"admin-household-management-text": "Changes to this household will be reflected immediately.",
|
||||
"household-id-value": "Household Id: {0}",
|
||||
"private-household": "Private Household",
|
||||
"private-household-description": "Setting your household to private will default all public view options to default. This overrides any individual recipes public view settings.",
|
||||
"household-recipe-preferences": "Household Recipe Preferences",
|
||||
"default-recipe-preferences-description": "These are the default settings when a new recipe is created in your household. These can be changed for individual recipes in the recipe settings menu.",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes": "Allow users outside of your household to see your recipes",
|
||||
"allow-users-outside-of-your-household-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your household or with a pre-generated private link",
|
||||
"household-preferences": "Household Preferences"
|
||||
},
|
||||
"meal-plan": {
|
||||
"create-a-new-meal-plan": "Create a New Meal Plan",
|
||||
@ -1230,6 +1252,8 @@
|
||||
"account-summary-description": "Here's a summary of your group's information.",
|
||||
"group-statistics": "Group Statistics",
|
||||
"group-statistics-description": "Your Group Statistics provide some insight how you're using Mealie.",
|
||||
"household-statistics": "Household Statistics",
|
||||
"household-statistics-description": "Your Household Statistics provide some insight how you're using Mealie.",
|
||||
"storage-capacity": "Storage Capacity",
|
||||
"storage-capacity-description": "Your storage capacity is a calculation of the images and assets you have uploaded.",
|
||||
"personal": "Personal",
|
||||
@ -1239,10 +1263,13 @@
|
||||
"api-tokens-description": "Manage your API Tokens for access from external applications.",
|
||||
"group-description": "These items are shared within your group. Editing one of them will change it for the whole group!",
|
||||
"group-settings": "Group Settings",
|
||||
"group-settings-description": "Manage your common group settings like mealplan and privacy settings.",
|
||||
"group-settings-description": "Manage your common group settings, like privacy settings.",
|
||||
"household-description": "These items are shared within your household. Editing one of them will change it for the whole household!",
|
||||
"household-settings": "Household Settings",
|
||||
"household-settings-description": "Manage your household settings, like mealplan and privacy settings.",
|
||||
"cookbooks-description": "Manage a collection of recipe categories and generate pages for them.",
|
||||
"members": "Members",
|
||||
"members-description": "See who's in your group and manage their permissions.",
|
||||
"members-description": "See who's in your household and manage their permissions.",
|
||||
"webhooks-description": "Set up webhooks that trigger on days that you have mealplans scheduled.",
|
||||
"notifiers": "Notifiers",
|
||||
"notifiers-description": "Set up email and push notifications that trigger on specific events.",
|
||||
@ -1277,6 +1304,7 @@
|
||||
"require-all-tools": "Require All Tools",
|
||||
"cookbook-name": "Cookbook Name",
|
||||
"cookbook-with-name": "Cookbook {0}",
|
||||
"household-cookbook-name": "{0} Cookbook {1}",
|
||||
"create-a-cookbook": "Create a Cookbook",
|
||||
"cookbook": "Cookbook"
|
||||
}
|
||||
|
@ -64,6 +64,12 @@ export default defineComponent({
|
||||
title: i18n.tc("user.users"),
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.household,
|
||||
to: "/admin/manage/households",
|
||||
title: i18n.tc("household.households"),
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.group,
|
||||
to: "/admin/manage/groups",
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-app dark>
|
||||
<v-app v-if="ready" dark>
|
||||
<v-card-title>
|
||||
<slot>
|
||||
<h1 class="mx-auto">{{ $t("page.404-page-not-found") }}</h1>
|
||||
@ -75,9 +75,21 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
async function handle404() {
|
||||
const normalizedRoute = route.value.fullPath.replace(/\/$/, "");
|
||||
const newRoute = normalizedRoute.replace(/^\/group\/(mealplan|members|notifiers|webhooks)(\/.*)?$/, "/household/$1$2");
|
||||
|
||||
if (newRoute !== normalizedRoute) {
|
||||
await router.replace(newRoute);
|
||||
} else {
|
||||
await insertGroupSlugIntoRoute();
|
||||
}
|
||||
|
||||
ready.value = true;
|
||||
}
|
||||
|
||||
if (props.error.statusCode === 404) {
|
||||
// see if adding the groupSlug fixes the error
|
||||
insertGroupSlugIntoRoute().then(() => { ready.value = true });
|
||||
handle404();
|
||||
} else {
|
||||
ready.value = true;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { RecipeAPI } from "./user/recipes";
|
||||
import { UserApi } from "./user/users";
|
||||
import { HouseholdAPI } from "./user/households";
|
||||
import { GroupAPI } from "./user/groups";
|
||||
import { BackupAPI } from "./user/backups";
|
||||
import { UploadFile } from "./user/upload";
|
||||
@ -28,6 +29,7 @@ import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||
export class UserApiClient {
|
||||
public recipes: RecipeAPI;
|
||||
public users: UserApi;
|
||||
public households: HouseholdAPI;
|
||||
public groups: GroupAPI;
|
||||
public backups: BackupAPI;
|
||||
public categories: CategoriesAPI;
|
||||
@ -63,6 +65,7 @@ export class UserApiClient {
|
||||
|
||||
// Users
|
||||
this.users = new UserApi(requests);
|
||||
this.households = new HouseholdAPI(requests);
|
||||
this.groups = new GroupAPI(requests);
|
||||
this.cookbooks = new CookbookAPI(requests);
|
||||
this.groupRecipeActions = new GroupRecipeActionsAPI(requests);
|
||||
|
@ -3,10 +3,11 @@ import { RecipeCookBook } from "~/lib/api/types/cookbook";
|
||||
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||
|
||||
const prefix = "/api";
|
||||
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||
|
||||
const routes = {
|
||||
cookbooksGroupSlug: (groupSlug: string | number) => `${prefix}/explore/cookbooks/${groupSlug}`,
|
||||
cookbooksGroupSlugCookbookId: (groupSlug: string | number, cookbookId: string | number) => `${prefix}/explore/cookbooks/${groupSlug}/${cookbookId}`,
|
||||
cookbooksGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/cookbooks`,
|
||||
cookbooksGroupSlugCookbookId: (groupSlug: string | number, cookbookId: string | number) => `${exploreGroupSlug(groupSlug)}/cookbooks/${cookbookId}`,
|
||||
};
|
||||
|
||||
export class PublicCookbooksApi extends BaseCRUDAPIReadOnly<RecipeCookBook> {
|
||||
|
@ -3,10 +3,11 @@ import { IngredientFood } from "~/lib/api/types/recipe";
|
||||
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||
|
||||
const prefix = "/api";
|
||||
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||
|
||||
const routes = {
|
||||
foodsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/foods/${groupSlug}`,
|
||||
foodsGroupSlugFoodId: (groupSlug: string | number, foodId: string | number) => `${prefix}/explore/foods/${groupSlug}/${foodId}`,
|
||||
foodsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/foods`,
|
||||
foodsGroupSlugFoodId: (groupSlug: string | number, foodId: string | number) => `${exploreGroupSlug(groupSlug)}/foods/${foodId}`,
|
||||
};
|
||||
|
||||
export class PublicFoodsApi extends BaseCRUDAPIReadOnly<IngredientFood> {
|
||||
|
@ -3,14 +3,15 @@ import { RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
|
||||
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||
|
||||
const prefix = "/api";
|
||||
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||
|
||||
const routes = {
|
||||
categoriesGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/categories`,
|
||||
categoriesGroupSlugCategoryId: (groupSlug: string | number, categoryId: string | number) => `${prefix}/explore/organizers/${groupSlug}/categories/${categoryId}`,
|
||||
tagsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/tags`,
|
||||
tagsGroupSlugTagId: (groupSlug: string | number, tagId: string | number) => `${prefix}/explore/organizers/${groupSlug}/tags/${tagId}`,
|
||||
toolsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/tools`,
|
||||
toolsGroupSlugToolId: (groupSlug: string | number, toolId: string | number) => `${prefix}/explore/organizers/${groupSlug}/tools/${toolId}`,
|
||||
categoriesGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/categories`,
|
||||
categoriesGroupSlugCategoryId: (groupSlug: string | number, categoryId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/categories/${categoryId}`,
|
||||
tagsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tags`,
|
||||
tagsGroupSlugTagId: (groupSlug: string | number, tagId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tags/${tagId}`,
|
||||
toolsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tools`,
|
||||
toolsGroupSlugToolId: (groupSlug: string | number, toolId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tools`,
|
||||
};
|
||||
|
||||
export class PublicCategoriesApi extends BaseCRUDAPIReadOnly<RecipeCategory> {
|
||||
|
@ -5,10 +5,11 @@ import { ApiRequestInstance, PaginationData } from "~/lib/api/types/non-generate
|
||||
import { RecipeSearchQuery } from "../../user/recipes/recipe";
|
||||
|
||||
const prefix = "/api";
|
||||
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||
|
||||
const routes = {
|
||||
recipesGroupSlug: (groupSlug: string | number) => `${prefix}/explore/recipes/${groupSlug}`,
|
||||
recipesGroupSlugRecipeSlug: (groupSlug: string | number, recipeSlug: string | number) => `${prefix}/explore/recipes/${groupSlug}/${recipeSlug}`,
|
||||
recipesGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/recipes`,
|
||||
recipesGroupSlugRecipeSlug: (groupSlug: string | number, recipeSlug: string | number) => `${exploreGroupSlug(groupSlug)}/recipes/${recipeSlug}`,
|
||||
};
|
||||
|
||||
export class PublicRecipeApi extends BaseCRUDAPIReadOnly<Recipe> {
|
||||
|
@ -10,6 +10,8 @@ export interface AdminAboutInfo {
|
||||
version: string;
|
||||
demoStatus: boolean;
|
||||
allowSignup: boolean;
|
||||
defaultGroupSlug?: string | null;
|
||||
defaultHouseholdSlug?: string | null;
|
||||
enableOidc: boolean;
|
||||
oidcRedirect: boolean;
|
||||
oidcProviderName: string;
|
||||
@ -18,8 +20,9 @@ export interface AdminAboutInfo {
|
||||
apiPort: number;
|
||||
apiDocs: boolean;
|
||||
dbType: string;
|
||||
dbUrl?: string;
|
||||
dbUrl?: string | null;
|
||||
defaultGroup: string;
|
||||
defaultHousehold: string;
|
||||
buildId: string;
|
||||
recipeScraperVersion: string;
|
||||
}
|
||||
@ -37,7 +40,8 @@ export interface AppInfo {
|
||||
version: string;
|
||||
demoStatus: boolean;
|
||||
allowSignup: boolean;
|
||||
defaultGroupSlug?: string;
|
||||
defaultGroupSlug?: string | null;
|
||||
defaultHouseholdSlug?: string | null;
|
||||
enableOidc: boolean;
|
||||
oidcRedirect: boolean;
|
||||
oidcProviderName: string;
|
||||
@ -51,6 +55,7 @@ export interface AppStartupInfo {
|
||||
export interface AppStatistics {
|
||||
totalRecipes: number;
|
||||
totalUsers: number;
|
||||
totalHouseholds: number;
|
||||
totalGroups: number;
|
||||
uncategorizedRecipes: number;
|
||||
untaggedRecipes: number;
|
||||
@ -93,16 +98,16 @@ export interface ChowdownURL {
|
||||
export interface CommentImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface CreateBackup {
|
||||
tag?: string;
|
||||
tag?: string | null;
|
||||
options: BackupOptions;
|
||||
templates?: string[];
|
||||
templates?: string[] | null;
|
||||
}
|
||||
export interface CustomPageBase {
|
||||
name: string;
|
||||
slug?: string;
|
||||
slug: string | null;
|
||||
position: number;
|
||||
categories?: RecipeCategoryResponse[];
|
||||
}
|
||||
@ -113,38 +118,41 @@ export interface RecipeCategoryResponse {
|
||||
recipes?: RecipeSummary[];
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTool {
|
||||
id: string;
|
||||
@ -155,11 +163,11 @@ export interface RecipeTool {
|
||||
export interface CustomPageImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface CustomPageOut {
|
||||
name: string;
|
||||
slug?: string;
|
||||
slug: string | null;
|
||||
position: number;
|
||||
categories?: RecipeCategoryResponse[];
|
||||
id: number;
|
||||
@ -169,7 +177,7 @@ export interface EmailReady {
|
||||
}
|
||||
export interface EmailSuccess {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
error?: string | null;
|
||||
}
|
||||
export interface EmailTest {
|
||||
email: string;
|
||||
@ -177,12 +185,12 @@ export interface EmailTest {
|
||||
export interface GroupImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface ImportBase {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface ImportJob {
|
||||
recipes?: boolean;
|
||||
@ -217,8 +225,8 @@ export interface MigrationFile {
|
||||
export interface MigrationImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
slug?: string;
|
||||
exception?: string | null;
|
||||
slug?: string | null;
|
||||
}
|
||||
export interface Migrations {
|
||||
type: string;
|
||||
@ -227,25 +235,26 @@ export interface Migrations {
|
||||
export interface NotificationImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface OIDCInfo {
|
||||
configurationUrl?: string;
|
||||
clientId?: string;
|
||||
configurationUrl: string | null;
|
||||
clientId: string | null;
|
||||
groupsClaim: string | null;
|
||||
}
|
||||
export interface RecipeImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
slug?: string;
|
||||
exception?: string | null;
|
||||
slug?: string | null;
|
||||
}
|
||||
export interface SettingsImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface UserImport {
|
||||
name: string;
|
||||
status: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
export interface CreateCookBook {
|
||||
name: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
slug?: string | null;
|
||||
position?: number;
|
||||
public?: boolean;
|
||||
categories?: CategoryBase[];
|
||||
@ -37,7 +37,7 @@ export interface RecipeTool {
|
||||
export interface ReadCookBook {
|
||||
name: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
slug?: string | null;
|
||||
position?: number;
|
||||
public?: boolean;
|
||||
categories?: CategoryBase[];
|
||||
@ -47,12 +47,13 @@ export interface ReadCookBook {
|
||||
requireAllTags?: boolean;
|
||||
requireAllTools?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface RecipeCookBook {
|
||||
name: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
slug?: string | null;
|
||||
position?: number;
|
||||
public?: boolean;
|
||||
categories?: CategoryBase[];
|
||||
@ -62,47 +63,51 @@ export interface RecipeCookBook {
|
||||
requireAllTags?: boolean;
|
||||
requireAllTools?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
recipes: RecipeSummary[];
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface SaveCookBook {
|
||||
name: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
slug?: string | null;
|
||||
position?: number;
|
||||
public?: boolean;
|
||||
categories?: CategoryBase[];
|
||||
@ -112,11 +117,12 @@ export interface SaveCookBook {
|
||||
requireAllTags?: boolean;
|
||||
requireAllTools?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
}
|
||||
export interface UpdateCookBook {
|
||||
name: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
slug?: string | null;
|
||||
position?: number;
|
||||
public?: boolean;
|
||||
categories?: CategoryBase[];
|
||||
@ -126,5 +132,6 @@ export interface UpdateCookBook {
|
||||
requireAllTags?: boolean;
|
||||
requireAllTools?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
|
@ -5,10 +5,6 @@
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
|
||||
export type RecipeActionType =
|
||||
| "link"
|
||||
| "post";
|
||||
export type WebhookType = "mealplan";
|
||||
export type SupportedMigrations =
|
||||
| "nextcloud"
|
||||
| "chowdown"
|
||||
@ -17,59 +13,23 @@ export type SupportedMigrations =
|
||||
| "mealie_alpha"
|
||||
| "tandoor"
|
||||
| "plantoeat"
|
||||
| "myrecipebox"
|
||||
| "recipekeeper";
|
||||
|
||||
export interface CreateGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
groupId: string;
|
||||
}
|
||||
export interface CreateGroupRecipeAction {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
export interface CreateInviteToken {
|
||||
uses: number;
|
||||
}
|
||||
export interface CreateWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
}
|
||||
export interface DataMigrationCreate {
|
||||
sourceType: SupportedMigrations;
|
||||
}
|
||||
export interface EmailInitationResponse {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
export interface EmailInvitation {
|
||||
email: string;
|
||||
token: string;
|
||||
}
|
||||
export interface GroupAdminUpdate {
|
||||
id: string;
|
||||
name: string;
|
||||
preferences?: UpdateGroupPreferences;
|
||||
preferences?: UpdateGroupPreferences | null;
|
||||
}
|
||||
export interface UpdateGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
}
|
||||
export interface GroupDataExport {
|
||||
id: string;
|
||||
@ -80,140 +40,6 @@ export interface GroupDataExport {
|
||||
size: string;
|
||||
expires: string;
|
||||
}
|
||||
export interface GroupEventNotifierCreate {
|
||||
name: string;
|
||||
appriseUrl: string;
|
||||
}
|
||||
/**
|
||||
* These events are in-sync with the EventTypes found in the EventBusService.
|
||||
* If you modify this, make sure to update the EventBusService as well.
|
||||
*/
|
||||
export interface GroupEventNotifierOptions {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
}
|
||||
/**
|
||||
* These events are in-sync with the EventTypes found in the EventBusService.
|
||||
* If you modify this, make sure to update the EventBusService as well.
|
||||
*/
|
||||
export interface GroupEventNotifierOptionsOut {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
id: string;
|
||||
}
|
||||
/**
|
||||
* These events are in-sync with the EventTypes found in the EventBusService.
|
||||
* If you modify this, make sure to update the EventBusService as well.
|
||||
*/
|
||||
export interface GroupEventNotifierOptionsSave {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
notifierId: string;
|
||||
}
|
||||
export interface GroupEventNotifierOut {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
groupId: string;
|
||||
options: GroupEventNotifierOptionsOut;
|
||||
}
|
||||
export interface GroupEventNotifierPrivate {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
groupId: string;
|
||||
options: GroupEventNotifierOptionsOut;
|
||||
appriseUrl: string;
|
||||
}
|
||||
export interface GroupEventNotifierSave {
|
||||
name: string;
|
||||
appriseUrl: string;
|
||||
enabled?: boolean;
|
||||
groupId: string;
|
||||
options?: GroupEventNotifierOptions;
|
||||
}
|
||||
export interface GroupEventNotifierUpdate {
|
||||
name: string;
|
||||
appriseUrl?: string;
|
||||
enabled?: boolean;
|
||||
groupId: string;
|
||||
options?: GroupEventNotifierOptions;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupRecipeActionOut {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupStatistics {
|
||||
totalRecipes: number;
|
||||
totalUsers: number;
|
||||
totalCategories: number;
|
||||
totalTags: number;
|
||||
totalTools: number;
|
||||
}
|
||||
export interface GroupStorage {
|
||||
usedStorageBytes: number;
|
||||
usedStorageStr: string;
|
||||
@ -222,408 +48,9 @@ export interface GroupStorage {
|
||||
}
|
||||
export interface ReadGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface ReadInviteToken {
|
||||
token: string;
|
||||
usesLeft: number;
|
||||
groupId: string;
|
||||
}
|
||||
export interface ReadWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface SaveGroupRecipeAction {
|
||||
actionType: RecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
}
|
||||
export interface SaveInviteToken {
|
||||
usesLeft: number;
|
||||
groupId: string;
|
||||
token: string;
|
||||
}
|
||||
export interface SaveWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
groupId: string;
|
||||
}
|
||||
export interface SeederConfig {
|
||||
locale: string;
|
||||
}
|
||||
export interface SetPermissions {
|
||||
userId: string;
|
||||
canManage?: boolean;
|
||||
canInvite?: boolean;
|
||||
canOrganize?: boolean;
|
||||
}
|
||||
export interface ShoppingListAddRecipeParams {
|
||||
recipeIncrementQuantity?: number;
|
||||
recipeIngredients?: RecipeIngredient[];
|
||||
}
|
||||
export interface RecipeIngredient {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
title?: string;
|
||||
originalText?: string;
|
||||
referenceId?: string;
|
||||
}
|
||||
export interface IngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: IngredientUnitAlias[];
|
||||
id: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
}
|
||||
export interface CreateIngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
id: string;
|
||||
label?: MultiPurposeLabelSummary;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface MultiPurposeLabelSummary {
|
||||
name: string;
|
||||
color?: string;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
}
|
||||
export interface CreateIngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface ShoppingListCreate {
|
||||
name?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface ShoppingListItemBase {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string;
|
||||
labelId?: string;
|
||||
unitId?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
}
|
||||
export interface ShoppingListItemCreate {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string;
|
||||
labelId?: string;
|
||||
unitId?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
recipeReferences?: ShoppingListItemRecipeRefCreate[];
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefCreate {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number;
|
||||
recipeNote?: string;
|
||||
}
|
||||
export interface ShoppingListItemOut {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit;
|
||||
food?: IngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string;
|
||||
labelId?: string;
|
||||
unitId?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
id: string;
|
||||
label?: MultiPurposeLabelSummary;
|
||||
recipeReferences?: ShoppingListItemRecipeRefOut[];
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefOut {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number;
|
||||
recipeNote?: string;
|
||||
id: string;
|
||||
shoppingListItemId: string;
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefUpdate {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number;
|
||||
recipeNote?: string;
|
||||
id: string;
|
||||
shoppingListItemId: string;
|
||||
}
|
||||
export interface ShoppingListItemUpdate {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string;
|
||||
labelId?: string;
|
||||
unitId?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
recipeReferences?: (ShoppingListItemRecipeRefCreate | ShoppingListItemRecipeRefUpdate)[];
|
||||
}
|
||||
/**
|
||||
* Only used for bulk update operations where the shopping list item id isn't already supplied
|
||||
*/
|
||||
export interface ShoppingListItemUpdateBulk {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string;
|
||||
labelId?: string;
|
||||
unitId?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
recipeReferences?: (ShoppingListItemRecipeRefCreate | ShoppingListItemRecipeRefUpdate)[];
|
||||
id: string;
|
||||
}
|
||||
/**
|
||||
* Container for bulk shopping list item changes
|
||||
*/
|
||||
export interface ShoppingListItemsCollectionOut {
|
||||
createdItems?: ShoppingListItemOut[];
|
||||
updatedItems?: ShoppingListItemOut[];
|
||||
deletedItems?: ShoppingListItemOut[];
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelCreate {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelOut {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
id: string;
|
||||
label: MultiPurposeLabelSummary;
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelUpdate {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
id: string;
|
||||
}
|
||||
export interface ShoppingListOut {
|
||||
name?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
listItems?: ShoppingListItemOut[];
|
||||
recipeReferences: ShoppingListRecipeRefOut[];
|
||||
labelSettings: ShoppingListMultiPurposeLabelOut[];
|
||||
}
|
||||
export interface ShoppingListRecipeRefOut {
|
||||
id: string;
|
||||
shoppingListId: string;
|
||||
recipeId: string;
|
||||
recipeQuantity: number;
|
||||
recipe: RecipeSummary;
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string;
|
||||
userId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
export interface RecipeTool {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
onHand?: boolean;
|
||||
}
|
||||
export interface ShoppingListRemoveRecipeParams {
|
||||
recipeDecrementQuantity?: number;
|
||||
}
|
||||
export interface ShoppingListSave {
|
||||
name?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
}
|
||||
export interface ShoppingListSummary {
|
||||
name?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
recipeReferences: ShoppingListRecipeRefOut[];
|
||||
labelSettings: ShoppingListMultiPurposeLabelOut[];
|
||||
}
|
||||
export interface ShoppingListUpdate {
|
||||
name?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
listItems?: ShoppingListItemOut[];
|
||||
}
|
||||
export interface RecipeIngredientBase {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
}
|
||||
|
664
frontend/lib/api/types/household.ts
Normal file
664
frontend/lib/api/types/household.ts
Normal file
@ -0,0 +1,664 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
export type GroupRecipeActionType = "link" | "post";
|
||||
export type WebhookType = "mealplan";
|
||||
|
||||
export interface CreateGroupRecipeAction {
|
||||
actionType: GroupRecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
export interface CreateHouseholdPreferences {
|
||||
privateHousehold?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
}
|
||||
export interface CreateInviteToken {
|
||||
uses: number;
|
||||
}
|
||||
export interface CreateWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
}
|
||||
export interface EmailInitationResponse {
|
||||
success: boolean;
|
||||
error?: string | null;
|
||||
}
|
||||
export interface EmailInvitation {
|
||||
email: string;
|
||||
token: string;
|
||||
}
|
||||
export interface GroupEventNotifierCreate {
|
||||
name: string;
|
||||
appriseUrl?: string | null;
|
||||
}
|
||||
/**
|
||||
* These events are in-sync with the EventTypes found in the EventBusService.
|
||||
* If you modify this, make sure to update the EventBusService as well.
|
||||
*/
|
||||
export interface GroupEventNotifierOptions {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
}
|
||||
export interface GroupEventNotifierOptionsOut {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupEventNotifierOptionsSave {
|
||||
testMessage?: boolean;
|
||||
webhookTask?: boolean;
|
||||
recipeCreated?: boolean;
|
||||
recipeUpdated?: boolean;
|
||||
recipeDeleted?: boolean;
|
||||
userSignup?: boolean;
|
||||
dataMigrations?: boolean;
|
||||
dataExport?: boolean;
|
||||
dataImport?: boolean;
|
||||
mealplanEntryCreated?: boolean;
|
||||
shoppingListCreated?: boolean;
|
||||
shoppingListUpdated?: boolean;
|
||||
shoppingListDeleted?: boolean;
|
||||
cookbookCreated?: boolean;
|
||||
cookbookUpdated?: boolean;
|
||||
cookbookDeleted?: boolean;
|
||||
tagCreated?: boolean;
|
||||
tagUpdated?: boolean;
|
||||
tagDeleted?: boolean;
|
||||
categoryCreated?: boolean;
|
||||
categoryUpdated?: boolean;
|
||||
categoryDeleted?: boolean;
|
||||
notifierId: string;
|
||||
}
|
||||
export interface GroupEventNotifierOut {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
options: GroupEventNotifierOptionsOut;
|
||||
}
|
||||
export interface GroupEventNotifierPrivate {
|
||||
id: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
options: GroupEventNotifierOptionsOut;
|
||||
appriseUrl: string;
|
||||
}
|
||||
export interface GroupEventNotifierSave {
|
||||
name: string;
|
||||
appriseUrl?: string | null;
|
||||
enabled?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
options?: GroupEventNotifierOptions;
|
||||
}
|
||||
export interface GroupEventNotifierUpdate {
|
||||
name: string;
|
||||
appriseUrl?: string | null;
|
||||
enabled?: boolean;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
options?: GroupEventNotifierOptions;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupRecipeActionOut {
|
||||
actionType: GroupRecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface HouseholdCreate {
|
||||
groupId?: string | null;
|
||||
name: string;
|
||||
}
|
||||
export interface HouseholdInDB {
|
||||
groupId: string;
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
preferences?: ReadHouseholdPreferences | null;
|
||||
group: string;
|
||||
users?: HouseholdUserSummary[] | null;
|
||||
webhooks?: ReadWebhook[];
|
||||
}
|
||||
export interface ReadHouseholdPreferences {
|
||||
privateHousehold?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface HouseholdUserSummary {
|
||||
id: string;
|
||||
fullName: string;
|
||||
}
|
||||
export interface ReadWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface HouseholdSave {
|
||||
groupId: string;
|
||||
name: string;
|
||||
}
|
||||
export interface HouseholdStatistics {
|
||||
totalRecipes: number;
|
||||
totalUsers: number;
|
||||
totalCategories: number;
|
||||
totalTags: number;
|
||||
totalTools: number;
|
||||
}
|
||||
export interface HouseholdSummary {
|
||||
groupId: string;
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
preferences?: ReadHouseholdPreferences | null;
|
||||
}
|
||||
export interface ReadInviteToken {
|
||||
token: string;
|
||||
usesLeft: number;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
}
|
||||
export interface SaveGroupRecipeAction {
|
||||
actionType: GroupRecipeActionType;
|
||||
title: string;
|
||||
url: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
}
|
||||
export interface SaveHouseholdPreferences {
|
||||
privateHousehold?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
householdId: string;
|
||||
}
|
||||
export interface SaveInviteToken {
|
||||
usesLeft: number;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
token: string;
|
||||
}
|
||||
export interface SaveWebhook {
|
||||
enabled?: boolean;
|
||||
name?: string;
|
||||
url?: string;
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
}
|
||||
export interface SetPermissions {
|
||||
userId: string;
|
||||
canManage?: boolean;
|
||||
canInvite?: boolean;
|
||||
canOrganize?: boolean;
|
||||
}
|
||||
export interface ShoppingListAddRecipeParams {
|
||||
recipeIncrementQuantity?: number;
|
||||
recipeIngredients?: RecipeIngredient[] | null;
|
||||
}
|
||||
export interface RecipeIngredient {
|
||||
quantity?: number | null;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean | null;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
title?: string | null;
|
||||
originalText?: string | null;
|
||||
referenceId?: string;
|
||||
}
|
||||
export interface IngredientUnit {
|
||||
id: string;
|
||||
name: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string | null;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: IngredientUnitAlias[];
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface IngredientUnitAlias {
|
||||
name: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string | null;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface CreateIngredientUnitAlias {
|
||||
name: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
id: string;
|
||||
name: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
labelId?: string | null;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
label?: MultiPurposeLabelSummary | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface IngredientFoodAlias {
|
||||
name: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface MultiPurposeLabelSummary {
|
||||
name: string;
|
||||
color?: string;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientFood {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
labelId?: string | null;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface CreateIngredientFoodAlias {
|
||||
name: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface ShoppingListCreate {
|
||||
name?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface ShoppingListItemBase {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string | null;
|
||||
labelId?: string | null;
|
||||
unitId?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
}
|
||||
export interface ShoppingListItemCreate {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string | null;
|
||||
labelId?: string | null;
|
||||
unitId?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
id?: string | null;
|
||||
recipeReferences?: ShoppingListItemRecipeRefCreate[];
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefCreate {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number | null;
|
||||
recipeNote?: string | null;
|
||||
}
|
||||
export interface ShoppingListItemOut {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | null;
|
||||
food?: IngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string | null;
|
||||
labelId?: string | null;
|
||||
unitId?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
id: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
label?: MultiPurposeLabelSummary | null;
|
||||
recipeReferences?: ShoppingListItemRecipeRefOut[];
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefOut {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number | null;
|
||||
recipeNote?: string | null;
|
||||
id: string;
|
||||
shoppingListItemId: string;
|
||||
}
|
||||
export interface ShoppingListItemRecipeRefUpdate {
|
||||
recipeId: string;
|
||||
recipeQuantity?: number;
|
||||
recipeScale?: number | null;
|
||||
recipeNote?: string | null;
|
||||
id: string;
|
||||
shoppingListItemId: string;
|
||||
}
|
||||
export interface ShoppingListItemUpdate {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string | null;
|
||||
labelId?: string | null;
|
||||
unitId?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
recipeReferences?: (ShoppingListItemRecipeRefCreate | ShoppingListItemRecipeRefUpdate)[];
|
||||
}
|
||||
/**
|
||||
* Only used for bulk update operations where the shopping list item id isn't already supplied
|
||||
*/
|
||||
export interface ShoppingListItemUpdateBulk {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
shoppingListId: string;
|
||||
checked?: boolean;
|
||||
position?: number;
|
||||
foodId?: string | null;
|
||||
labelId?: string | null;
|
||||
unitId?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
recipeReferences?: (ShoppingListItemRecipeRefCreate | ShoppingListItemRecipeRefUpdate)[];
|
||||
id: string;
|
||||
}
|
||||
/**
|
||||
* Container for bulk shopping list item changes
|
||||
*/
|
||||
export interface ShoppingListItemsCollectionOut {
|
||||
createdItems?: ShoppingListItemOut[];
|
||||
updatedItems?: ShoppingListItemOut[];
|
||||
deletedItems?: ShoppingListItemOut[];
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelCreate {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelOut {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
id: string;
|
||||
label: MultiPurposeLabelSummary;
|
||||
}
|
||||
export interface ShoppingListMultiPurposeLabelUpdate {
|
||||
shoppingListId: string;
|
||||
labelId: string;
|
||||
position?: number;
|
||||
id: string;
|
||||
}
|
||||
export interface ShoppingListOut {
|
||||
name?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
listItems?: ShoppingListItemOut[];
|
||||
householdId: string;
|
||||
recipeReferences?: ShoppingListRecipeRefOut[];
|
||||
labelSettings?: ShoppingListMultiPurposeLabelOut[];
|
||||
}
|
||||
export interface ShoppingListRecipeRefOut {
|
||||
id: string;
|
||||
shoppingListId: string;
|
||||
recipeId: string;
|
||||
recipeQuantity: number;
|
||||
recipe: RecipeSummary;
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTool {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
onHand?: boolean;
|
||||
}
|
||||
export interface ShoppingListRemoveRecipeParams {
|
||||
recipeDecrementQuantity?: number;
|
||||
}
|
||||
export interface ShoppingListSave {
|
||||
name?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
}
|
||||
export interface ShoppingListSummary {
|
||||
name?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
householdId: string;
|
||||
recipeReferences: ShoppingListRecipeRefOut[];
|
||||
labelSettings: ShoppingListMultiPurposeLabelOut[];
|
||||
}
|
||||
export interface ShoppingListUpdate {
|
||||
name?: string | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
groupId: string;
|
||||
userId: string;
|
||||
id: string;
|
||||
listItems?: ShoppingListItemOut[];
|
||||
}
|
||||
export interface UpdateHousehold {
|
||||
groupId: string;
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
}
|
||||
export interface UpdateHouseholdAdmin {
|
||||
groupId: string;
|
||||
name: string;
|
||||
id: string;
|
||||
preferences?: UpdateHouseholdPreferences | null;
|
||||
}
|
||||
export interface UpdateHouseholdPreferences {
|
||||
privateHousehold?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
}
|
||||
export interface RecipeIngredientBase {
|
||||
quantity?: number | null;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean | null;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
}
|
@ -19,46 +19,18 @@ export interface CreatePlanEntry {
|
||||
entryType?: PlanEntryType & string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
recipeId?: string;
|
||||
recipeId?: string | null;
|
||||
}
|
||||
export interface CreateRandomEntry {
|
||||
date: string;
|
||||
entryType?: PlanEntryType & string;
|
||||
}
|
||||
export interface ListItem {
|
||||
title?: string;
|
||||
title?: string | null;
|
||||
text?: string;
|
||||
quantity?: number;
|
||||
checked?: boolean;
|
||||
}
|
||||
export interface MealDayIn {
|
||||
date?: string;
|
||||
meals: MealIn[];
|
||||
}
|
||||
export interface MealIn {
|
||||
slug?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
export interface MealDayOut {
|
||||
date?: string;
|
||||
meals: MealIn[];
|
||||
id: number;
|
||||
}
|
||||
export interface MealPlanIn {
|
||||
group: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
planDays: MealDayIn[];
|
||||
}
|
||||
export interface MealPlanOut {
|
||||
group: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
planDays: MealDayIn[];
|
||||
id: number;
|
||||
shoppingList?: number;
|
||||
}
|
||||
export interface PlanRulesCreate {
|
||||
day?: PlanRulesDay & string;
|
||||
entryType?: PlanRulesType & string;
|
||||
@ -76,6 +48,7 @@ export interface PlanRulesOut {
|
||||
categories?: Category[];
|
||||
tags?: Tag[];
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface PlanRulesSave {
|
||||
@ -84,51 +57,56 @@ export interface PlanRulesSave {
|
||||
categories?: Category[];
|
||||
tags?: Tag[];
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
}
|
||||
export interface ReadPlanEntry {
|
||||
date: string;
|
||||
entryType?: PlanEntryType & string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
recipeId?: string;
|
||||
recipeId?: string | null;
|
||||
id: number;
|
||||
groupId: string;
|
||||
userId?: string;
|
||||
recipe?: RecipeSummary;
|
||||
userId?: string | null;
|
||||
householdId: string;
|
||||
recipe?: RecipeSummary | null;
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecipeTool {
|
||||
id: string;
|
||||
@ -141,18 +119,18 @@ export interface SavePlanEntry {
|
||||
entryType?: PlanEntryType & string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
recipeId?: string;
|
||||
recipeId?: string | null;
|
||||
groupId: string;
|
||||
userId?: string;
|
||||
userId?: string | null;
|
||||
}
|
||||
export interface ShoppingListIn {
|
||||
name: string;
|
||||
group?: string;
|
||||
group?: string | null;
|
||||
items: ListItem[];
|
||||
}
|
||||
export interface ShoppingListOut {
|
||||
name: string;
|
||||
group?: string;
|
||||
group?: string | null;
|
||||
items: ListItem[];
|
||||
id: number;
|
||||
}
|
||||
@ -161,8 +139,8 @@ export interface UpdatePlanEntry {
|
||||
entryType?: PlanEntryType & string;
|
||||
title?: string;
|
||||
text?: string;
|
||||
recipeId?: string;
|
||||
recipeId?: string | null;
|
||||
id: number;
|
||||
groupId: string;
|
||||
userId?: string;
|
||||
userId?: string | null;
|
||||
}
|
||||
|
65
frontend/lib/api/types/openai.ts
Normal file
65
frontend/lib/api/types/openai.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
export interface OpenAIIngredient {
|
||||
/**
|
||||
*
|
||||
* The input is simply the ingredient string you are processing as-is. It is forbidden to
|
||||
* modify this at all, you must provide the input exactly as you received it.
|
||||
*
|
||||
*/
|
||||
input: string;
|
||||
/**
|
||||
*
|
||||
* This value is a float between 0 - 100, where 100 is full confidence that the result is correct,
|
||||
* and 0 is no confidence that the result is correct. If you're unable to parse anything,
|
||||
* and you put the entire string in the notes, you should return 0 confidence. If you can easily
|
||||
* parse the string into each component, then you should return a confidence of 100. If you have to
|
||||
* guess which part is the unit and which part is the food, your confidence should be lower, such as 60.
|
||||
* Even if there is no unit or note, if you're able to determine the food, you may use a higher confidence.
|
||||
* If the entire ingredient consists of only a food, you can use a confidence of 100.
|
||||
*
|
||||
*/
|
||||
confidence?: number | null;
|
||||
/**
|
||||
*
|
||||
* The numerical representation of how much of this ingredient. For instance, if you receive
|
||||
* "3 1/2 grams of minced garlic", the quantity is "3 1/2". Quantity may be represented as a whole number
|
||||
* (integer), a float or decimal, or a fraction. You should output quantity in only whole numbers or
|
||||
* floats, converting fractions into floats. Floats longer than 10 decimal places should be
|
||||
* rounded to 10 decimal places.
|
||||
*
|
||||
*/
|
||||
quantity?: number | null;
|
||||
/**
|
||||
*
|
||||
* The unit of measurement for this ingredient. For instance, if you receive
|
||||
* "2 lbs chicken breast", the unit is "lbs" (short for "pounds").
|
||||
*
|
||||
*/
|
||||
unit?: string | null;
|
||||
/**
|
||||
*
|
||||
* The actual physical ingredient used in the recipe. For instance, if you receive
|
||||
* "3 cups of onions, chopped", the food is "onions".
|
||||
*
|
||||
*/
|
||||
food?: string | null;
|
||||
/**
|
||||
*
|
||||
* The rest of the text that represents more detail on how to prepare the ingredient.
|
||||
* Anything that is not one of the above should be the note. For instance, if you receive
|
||||
* "one can of butter beans, drained" the note would be "drained". If you receive
|
||||
* "3 cloves of garlic peeled and finely chopped", the note would be "peeled and finely chopped".
|
||||
*
|
||||
*/
|
||||
note?: string | null;
|
||||
}
|
||||
export interface OpenAIIngredients {
|
||||
ingredients?: OpenAIIngredient[];
|
||||
}
|
||||
export interface OpenAIBase {}
|
@ -55,29 +55,32 @@ export interface CategorySave {
|
||||
groupId: string;
|
||||
}
|
||||
export interface CreateIngredientFood {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
labelId?: string | null;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
}
|
||||
export interface CreateIngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
pluralAbbreviation?: string | null;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
}
|
||||
@ -89,16 +92,16 @@ export interface CreateRecipe {
|
||||
}
|
||||
export interface CreateRecipeBulk {
|
||||
url: string;
|
||||
categories?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
categories?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
}
|
||||
export interface RecipeCategory {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
export interface RecipeTag {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
@ -116,27 +119,27 @@ export interface ExportRecipes {
|
||||
exportType?: ExportTypes & string;
|
||||
}
|
||||
export interface IngredientConfidence {
|
||||
average?: number;
|
||||
comment?: number;
|
||||
name?: number;
|
||||
unit?: number;
|
||||
quantity?: number;
|
||||
food?: number;
|
||||
average?: number | null;
|
||||
comment?: number | null;
|
||||
name?: number | null;
|
||||
unit?: number | null;
|
||||
quantity?: number | null;
|
||||
food?: number | null;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
id: string;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
id: string;
|
||||
label?: MultiPurposeLabelSummary;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
labelId?: string | null;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
label?: MultiPurposeLabelSummary | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface IngredientFoodAlias {
|
||||
name: string;
|
||||
@ -151,27 +154,28 @@ export interface MultiPurposeLabelSummary {
|
||||
* A list of ingredient references.
|
||||
*/
|
||||
export interface IngredientReferences {
|
||||
referenceId?: string;
|
||||
referenceId?: string | null;
|
||||
}
|
||||
export interface IngredientRequest {
|
||||
parser?: RegisteredParser & string;
|
||||
ingredient: string;
|
||||
}
|
||||
export interface IngredientUnit {
|
||||
id: string;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
pluralAbbreviation?: string | null;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: IngredientUnitAlias[];
|
||||
id: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
export interface IngredientUnitAlias {
|
||||
name: string;
|
||||
@ -189,64 +193,65 @@ export interface MergeUnit {
|
||||
toUnit: string;
|
||||
}
|
||||
export interface Nutrition {
|
||||
calories?: string;
|
||||
fatContent?: string;
|
||||
proteinContent?: string;
|
||||
carbohydrateContent?: string;
|
||||
fiberContent?: string;
|
||||
sodiumContent?: string;
|
||||
sugarContent?: string;
|
||||
calories?: string | null;
|
||||
fatContent?: string | null;
|
||||
proteinContent?: string | null;
|
||||
carbohydrateContent?: string | null;
|
||||
fiberContent?: string | null;
|
||||
sodiumContent?: string | null;
|
||||
sugarContent?: string | null;
|
||||
}
|
||||
export interface ParsedIngredient {
|
||||
input?: string;
|
||||
input?: string | null;
|
||||
confidence?: IngredientConfidence;
|
||||
ingredient: RecipeIngredient;
|
||||
}
|
||||
export interface RecipeIngredient {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
quantity?: number | null;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean | null;
|
||||
disableAmount?: boolean;
|
||||
display?: string;
|
||||
title?: string;
|
||||
originalText?: string;
|
||||
title?: string | null;
|
||||
originalText?: string | null;
|
||||
referenceId?: string;
|
||||
}
|
||||
export interface Recipe {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
recipeIngredient?: RecipeIngredient[];
|
||||
recipeInstructions?: RecipeStep[];
|
||||
nutrition?: Nutrition;
|
||||
settings?: RecipeSettings;
|
||||
assets?: RecipeAsset[];
|
||||
notes?: RecipeNote[];
|
||||
recipeInstructions?: RecipeStep[] | null;
|
||||
nutrition?: Nutrition | null;
|
||||
settings?: RecipeSettings | null;
|
||||
assets?: RecipeAsset[] | null;
|
||||
notes?: RecipeNote[] | null;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
comments?: RecipeCommentOut[];
|
||||
} | null;
|
||||
comments?: RecipeCommentOut[] | null;
|
||||
}
|
||||
export interface RecipeTool {
|
||||
id: string;
|
||||
@ -255,15 +260,15 @@ export interface RecipeTool {
|
||||
onHand?: boolean;
|
||||
}
|
||||
export interface RecipeStep {
|
||||
id?: string;
|
||||
title?: string;
|
||||
id?: string | null;
|
||||
title?: string | null;
|
||||
text: string;
|
||||
ingredientReferences?: IngredientReferences[];
|
||||
}
|
||||
export interface RecipeAsset {
|
||||
name: string;
|
||||
icon: string;
|
||||
fileName?: string;
|
||||
fileName?: string | null;
|
||||
}
|
||||
export interface RecipeNote {
|
||||
title: string;
|
||||
@ -274,13 +279,13 @@ export interface RecipeCommentOut {
|
||||
text: string;
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updateAt: string;
|
||||
updatedAt: string;
|
||||
userId: string;
|
||||
user: UserBase;
|
||||
}
|
||||
export interface UserBase {
|
||||
id: string;
|
||||
username?: string;
|
||||
username?: string | null;
|
||||
admin: boolean;
|
||||
}
|
||||
export interface RecipeCategoryResponse {
|
||||
@ -290,28 +295,29 @@ export interface RecipeCategoryResponse {
|
||||
recipes?: RecipeSummary[];
|
||||
}
|
||||
export interface RecipeSummary {
|
||||
id?: string;
|
||||
id?: string | null;
|
||||
userId?: string;
|
||||
householdId?: string;
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
slug?: string;
|
||||
image?: unknown;
|
||||
recipeYield?: string;
|
||||
totalTime?: string;
|
||||
prepTime?: string;
|
||||
cookTime?: string;
|
||||
performTime?: string;
|
||||
description?: string;
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
recipeYield?: string | null;
|
||||
totalTime?: string | null;
|
||||
prepTime?: string | null;
|
||||
cookTime?: string | null;
|
||||
performTime?: string | null;
|
||||
description?: string | null;
|
||||
recipeCategory?: RecipeCategory[] | null;
|
||||
tags?: RecipeTag[] | null;
|
||||
tools?: RecipeTool[];
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
lastMade?: string;
|
||||
rating?: number | null;
|
||||
orgURL?: string | null;
|
||||
dateAdded?: string | null;
|
||||
dateUpdated?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
lastMade?: string | null;
|
||||
}
|
||||
export interface RecipeCommentCreate {
|
||||
recipeId: string;
|
||||
@ -327,15 +333,15 @@ export interface RecipeCommentUpdate {
|
||||
text: string;
|
||||
}
|
||||
export interface RecipeDuplicate {
|
||||
name?: string;
|
||||
name?: string | null;
|
||||
}
|
||||
export interface RecipeIngredientBase {
|
||||
quantity?: number;
|
||||
unit?: IngredientUnit | CreateIngredientUnit;
|
||||
food?: IngredientFood | CreateIngredientFood;
|
||||
note?: string;
|
||||
isFood?: boolean;
|
||||
disableAmount?: boolean;
|
||||
quantity?: number | null;
|
||||
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||
food?: IngredientFood | CreateIngredientFood | null;
|
||||
note?: string | null;
|
||||
isFood?: boolean | null;
|
||||
disableAmount?: boolean | null;
|
||||
display?: string;
|
||||
}
|
||||
export interface RecipeLastMade {
|
||||
@ -379,17 +385,17 @@ export interface RecipeTimelineEventCreate {
|
||||
userId: string;
|
||||
subject: string;
|
||||
eventType: TimelineEventType;
|
||||
eventMessage?: string;
|
||||
image?: TimelineEventImage & string;
|
||||
eventMessage?: string | null;
|
||||
image?: TimelineEventImage | null;
|
||||
timestamp?: string;
|
||||
}
|
||||
export interface RecipeTimelineEventIn {
|
||||
recipeId: string;
|
||||
userId?: string;
|
||||
userId?: string | null;
|
||||
subject: string;
|
||||
eventType: TimelineEventType;
|
||||
eventMessage?: string;
|
||||
image?: TimelineEventImage & string;
|
||||
eventMessage?: string | null;
|
||||
image?: TimelineEventImage | null;
|
||||
timestamp?: string;
|
||||
}
|
||||
export interface RecipeTimelineEventOut {
|
||||
@ -397,17 +403,19 @@ export interface RecipeTimelineEventOut {
|
||||
userId: string;
|
||||
subject: string;
|
||||
eventType: TimelineEventType;
|
||||
eventMessage?: string;
|
||||
image?: TimelineEventImage & string;
|
||||
eventMessage?: string | null;
|
||||
image?: TimelineEventImage | null;
|
||||
timestamp?: string;
|
||||
id: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
createdAt: string;
|
||||
updateAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
export interface RecipeTimelineEventUpdate {
|
||||
subject: string;
|
||||
eventMessage?: string;
|
||||
image?: TimelineEventImage;
|
||||
eventMessage?: string | null;
|
||||
image?: TimelineEventImage | null;
|
||||
}
|
||||
export interface RecipeToolCreate {
|
||||
name: string;
|
||||
@ -435,26 +443,30 @@ export interface RecipeZipTokenResponse {
|
||||
token: string;
|
||||
}
|
||||
export interface SaveIngredientFood {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
labelId?: string | null;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
groupId: string;
|
||||
}
|
||||
export interface SaveIngredientUnit {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
pluralAbbreviation?: string | null;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
groupId: string;
|
||||
@ -465,8 +477,9 @@ export interface ScrapeRecipe {
|
||||
}
|
||||
export interface ScrapeRecipeTest {
|
||||
url: string;
|
||||
useOpenAI?: boolean;
|
||||
}
|
||||
export interface SlugResponse { }
|
||||
export interface SlugResponse {}
|
||||
export interface TagIn {
|
||||
name: string;
|
||||
}
|
||||
@ -481,12 +494,14 @@ export interface TagSave {
|
||||
groupId: string;
|
||||
}
|
||||
export interface UnitFoodBase {
|
||||
id?: string | null;
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
pluralName?: string | null;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
} | null;
|
||||
onHand?: boolean;
|
||||
}
|
||||
export interface UpdateImageResponse {
|
||||
image: string;
|
||||
|
@ -11,7 +11,7 @@ export type OrderDirection = "asc" | "desc";
|
||||
export interface ErrorResponse {
|
||||
message: string;
|
||||
error?: boolean;
|
||||
exception?: string;
|
||||
exception?: string | null;
|
||||
}
|
||||
export interface FileTokenResponse {
|
||||
fileToken: string;
|
||||
@ -19,19 +19,19 @@ export interface FileTokenResponse {
|
||||
export interface PaginationQuery {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
orderBy?: string;
|
||||
orderByNullPosition?: OrderByNullPosition;
|
||||
orderBy?: string | null;
|
||||
orderByNullPosition?: OrderByNullPosition | null;
|
||||
orderDirection?: OrderDirection & string;
|
||||
queryFilter?: string;
|
||||
paginationSeed?: string;
|
||||
queryFilter?: string | null;
|
||||
paginationSeed?: string | null;
|
||||
}
|
||||
export interface RecipeSearchQuery {
|
||||
cookbook?: string;
|
||||
cookbook?: string | null;
|
||||
requireAllCategories?: boolean;
|
||||
requireAllTags?: boolean;
|
||||
requireAllTools?: boolean;
|
||||
requireAllFoods?: boolean;
|
||||
search?: string;
|
||||
search?: string | null;
|
||||
}
|
||||
export interface SuccessResponse {
|
||||
message: string;
|
||||
|
@ -19,8 +19,9 @@ export interface CreateToken {
|
||||
token: string;
|
||||
}
|
||||
export interface CreateUserRegistration {
|
||||
group?: string;
|
||||
groupToken?: string;
|
||||
group?: string | null;
|
||||
household?: string | null;
|
||||
groupToken?: string | null;
|
||||
email: string;
|
||||
username: string;
|
||||
fullName: string;
|
||||
@ -45,21 +46,19 @@ export interface ForgotPassword {
|
||||
export interface GroupBase {
|
||||
name: string;
|
||||
}
|
||||
export interface GroupHouseholdSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
export interface GroupInDB {
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
categories?: CategoryBase[];
|
||||
categories?: CategoryBase[] | null;
|
||||
webhooks?: ReadWebhook[];
|
||||
users?: UserOut[];
|
||||
preferences?: ReadGroupPreferences;
|
||||
}
|
||||
export interface GroupSummary {
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
preferences?: ReadGroupPreferences;
|
||||
|
||||
households?: GroupHouseholdSummary[] | null;
|
||||
users?: UserSummary[] | null;
|
||||
preferences?: ReadGroupPreferences | null;
|
||||
}
|
||||
export interface CategoryBase {
|
||||
name: string;
|
||||
@ -73,43 +72,24 @@ export interface ReadWebhook {
|
||||
webhookType?: WebhookType & string;
|
||||
scheduledTime: string;
|
||||
groupId: string;
|
||||
householdId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface UserOut {
|
||||
export interface UserSummary {
|
||||
id: string;
|
||||
username?: string;
|
||||
fullName?: string;
|
||||
email: string;
|
||||
authMethod?: AuthMethod & string;
|
||||
admin?: boolean;
|
||||
group: string;
|
||||
advanced?: boolean;
|
||||
canInvite?: boolean;
|
||||
canManage?: boolean;
|
||||
canOrganize?: boolean;
|
||||
groupId: string;
|
||||
groupSlug: string;
|
||||
tokens?: LongLiveTokenOut[];
|
||||
cacheKey: string;
|
||||
}
|
||||
export interface LongLiveTokenOut {
|
||||
token: string;
|
||||
name: string;
|
||||
id: number;
|
||||
createdAt?: string;
|
||||
fullName: string;
|
||||
}
|
||||
export interface ReadGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
firstDayOfWeek?: number;
|
||||
recipePublic?: boolean;
|
||||
recipeShowNutrition?: boolean;
|
||||
recipeShowAssets?: boolean;
|
||||
recipeLandscapeView?: boolean;
|
||||
recipeDisableComments?: boolean;
|
||||
recipeDisableAmount?: boolean;
|
||||
groupId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface GroupSummary {
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
preferences?: ReadGroupPreferences | null;
|
||||
}
|
||||
export interface LongLiveTokenIn {
|
||||
name: string;
|
||||
integrationId?: string;
|
||||
@ -124,23 +104,32 @@ export interface LongLiveTokenInDB {
|
||||
}
|
||||
export interface PrivateUser {
|
||||
id: string;
|
||||
username?: string;
|
||||
fullName?: string;
|
||||
username?: string | null;
|
||||
fullName?: string | null;
|
||||
email: string;
|
||||
authMethod?: AuthMethod & string;
|
||||
admin?: boolean;
|
||||
group: string;
|
||||
household: string;
|
||||
advanced?: boolean;
|
||||
canInvite?: boolean;
|
||||
canManage?: boolean;
|
||||
canOrganize?: boolean;
|
||||
groupId: string;
|
||||
groupSlug: string;
|
||||
tokens?: LongLiveTokenOut[];
|
||||
householdId: string;
|
||||
householdSlug: string;
|
||||
tokens?: LongLiveTokenOut[] | null;
|
||||
cacheKey: string;
|
||||
password: string;
|
||||
loginAttemps?: number;
|
||||
lockedAt?: string;
|
||||
lockedAt?: string | null;
|
||||
}
|
||||
export interface LongLiveTokenOut {
|
||||
token: string;
|
||||
name: string;
|
||||
id: number;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
export interface OIDCRequest {
|
||||
id_token: string;
|
||||
@ -168,8 +157,8 @@ export interface Token {
|
||||
token_type: string;
|
||||
}
|
||||
export interface TokenData {
|
||||
user_id?: string;
|
||||
username?: string;
|
||||
user_id?: string | null;
|
||||
username?: string | null;
|
||||
}
|
||||
export interface UnlockResults {
|
||||
unlocked?: number;
|
||||
@ -178,7 +167,7 @@ export interface UpdateGroup {
|
||||
name: string;
|
||||
id: string;
|
||||
slug: string;
|
||||
categories?: CategoryBase[];
|
||||
categories?: CategoryBase[] | null;
|
||||
webhooks?: CreateWebhook[];
|
||||
}
|
||||
export interface CreateWebhook {
|
||||
@ -189,53 +178,75 @@ export interface CreateWebhook {
|
||||
scheduledTime: string;
|
||||
}
|
||||
export interface UserBase {
|
||||
id?: string;
|
||||
username?: string;
|
||||
fullName?: string;
|
||||
id?: string | null;
|
||||
username?: string | null;
|
||||
fullName?: string | null;
|
||||
email: string;
|
||||
authMethod?: AuthMethod & string;
|
||||
admin?: boolean;
|
||||
group?: string;
|
||||
group?: string | null;
|
||||
household?: string | null;
|
||||
advanced?: boolean;
|
||||
canInvite?: boolean;
|
||||
canManage?: boolean;
|
||||
canOrganize?: boolean;
|
||||
}
|
||||
export interface UserIn {
|
||||
id?: string;
|
||||
username?: string;
|
||||
fullName?: string;
|
||||
id?: string | null;
|
||||
username?: string | null;
|
||||
fullName?: string | null;
|
||||
email: string;
|
||||
authMethod?: AuthMethod & string;
|
||||
admin?: boolean;
|
||||
group?: string;
|
||||
group?: string | null;
|
||||
household?: string | null;
|
||||
advanced?: boolean;
|
||||
canInvite?: boolean;
|
||||
canManage?: boolean;
|
||||
canOrganize?: boolean;
|
||||
password: string;
|
||||
}
|
||||
export interface UserOut {
|
||||
id: string;
|
||||
username?: string | null;
|
||||
fullName?: string | null;
|
||||
email: string;
|
||||
authMethod?: AuthMethod & string;
|
||||
admin?: boolean;
|
||||
group: string;
|
||||
household: string;
|
||||
advanced?: boolean;
|
||||
canInvite?: boolean;
|
||||
canManage?: boolean;
|
||||
canOrganize?: boolean;
|
||||
groupId: string;
|
||||
groupSlug: string;
|
||||
householdId: string;
|
||||
householdSlug: string;
|
||||
tokens?: LongLiveTokenOut[] | null;
|
||||
cacheKey: string;
|
||||
}
|
||||
export interface UserRatingCreate {
|
||||
recipeId: string;
|
||||
rating?: number;
|
||||
rating?: number | null;
|
||||
isFavorite?: boolean;
|
||||
userId: string;
|
||||
}
|
||||
export interface UserRatingOut {
|
||||
recipeId: string;
|
||||
rating?: number;
|
||||
rating?: number | null;
|
||||
isFavorite?: boolean;
|
||||
userId: string;
|
||||
id: string;
|
||||
}
|
||||
export interface UserRatingSummary {
|
||||
recipeId: string;
|
||||
rating?: number;
|
||||
rating?: number | null;
|
||||
isFavorite?: boolean;
|
||||
}
|
||||
export interface UserSummary {
|
||||
id: string;
|
||||
fullName: string;
|
||||
export interface UserRatingUpdate {
|
||||
rating?: number | null;
|
||||
isFavorite?: boolean | null;
|
||||
}
|
||||
export interface ValidateResetToken {
|
||||
token: string;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BaseAPI } from "../base/base-clients";
|
||||
import { EmailInitationResponse, EmailInvitation } from "~/lib/api/types/group";
|
||||
import { EmailInitationResponse, EmailInvitation } from "~/lib/api/types/household";
|
||||
import { ForgotPassword } from "~/lib/api/types/user";
|
||||
import { EmailTest } from "~/lib/api/types/admin";
|
||||
|
||||
@ -7,7 +7,7 @@ const routes = {
|
||||
base: "/api/admin/email",
|
||||
forgotPassword: "/api/users/forgot-password",
|
||||
|
||||
invitation: "/api/groups/invitations/email",
|
||||
invitation: "/api/households/invitations/email",
|
||||
};
|
||||
|
||||
export class EmailAPI extends BaseAPI {
|
||||
|
@ -4,8 +4,8 @@ import { CreateCookBook, RecipeCookBook, UpdateCookBook } from "~/lib/api/types/
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
cookbooks: `${prefix}/groups/cookbooks`,
|
||||
cookbooksId: (id: number) => `${prefix}/groups/cookbooks/${id}`,
|
||||
cookbooks: `${prefix}/households/cookbooks`,
|
||||
cookbooksId: (id: number) => `${prefix}/households/cookbooks/${id}`,
|
||||
};
|
||||
|
||||
export class CookbookAPI extends BaseCRUDAPI<CreateCookBook, RecipeCookBook, UpdateCookBook> {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { GroupEventNotifierCreate, GroupEventNotifierOut, GroupEventNotifierUpdate } from "~/lib/api/types/group";
|
||||
import { GroupEventNotifierCreate, GroupEventNotifierOut, GroupEventNotifierUpdate } from "~/lib/api/types/household";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
eventNotifier: `${prefix}/groups/events/notifications`,
|
||||
eventNotifierId: (id: string | number) => `${prefix}/groups/events/notifications/${id}`,
|
||||
eventNotifier: `${prefix}/households/events/notifications`,
|
||||
eventNotifierId: (id: string | number) => `${prefix}/households/events/notifications/${id}`,
|
||||
};
|
||||
|
||||
export class GroupEventNotifierApi extends BaseCRUDAPI<
|
||||
|
@ -4,8 +4,8 @@ import { PlanRulesCreate, PlanRulesOut } from "~/lib/api/types/meal-plan";
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
rule: `${prefix}/groups/mealplans/rules`,
|
||||
ruleId: (id: string | number) => `${prefix}/groups/mealplans/rules/${id}`,
|
||||
rule: `${prefix}/households/mealplans/rules`,
|
||||
ruleId: (id: string | number) => `${prefix}/households/mealplans/rules/${id}`,
|
||||
};
|
||||
|
||||
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesCreate, PlanRulesOut> {
|
||||
|
@ -4,9 +4,9 @@ import { CreatePlanEntry, CreateRandomEntry, ReadPlanEntry, UpdatePlanEntry } fr
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
mealplan: `${prefix}/groups/mealplans`,
|
||||
random: `${prefix}/groups/mealplans/random`,
|
||||
mealplanId: (id: string | number) => `${prefix}/groups/mealplans/${id}`,
|
||||
mealplan: `${prefix}/households/mealplans`,
|
||||
random: `${prefix}/households/mealplans/random`,
|
||||
mealplanId: (id: string | number) => `${prefix}/households/mealplans/${id}`,
|
||||
};
|
||||
|
||||
export class MealPlanAPI extends BaseCRUDAPI<CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry> {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { CreateGroupRecipeAction, GroupRecipeActionOut } from "~/lib/api/types/group";
|
||||
import { CreateGroupRecipeAction, GroupRecipeActionOut } from "~/lib/api/types/household";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
groupRecipeActions: `${prefix}/groups/recipe-actions`,
|
||||
groupRecipeActionsId: (id: string | number) => `${prefix}/groups/recipe-actions/${id}`,
|
||||
groupRecipeActions: `${prefix}/households/recipe-actions`,
|
||||
groupRecipeActionsId: (id: string | number) => `${prefix}/households/recipe-actions/${id}`,
|
||||
};
|
||||
|
||||
export class GroupRecipeActionsAPI extends BaseCRUDAPI<CreateGroupRecipeAction, GroupRecipeActionOut> {
|
||||
|
@ -9,20 +9,20 @@ import {
|
||||
ShoppingListMultiPurposeLabelUpdate,
|
||||
ShoppingListOut,
|
||||
ShoppingListUpdate,
|
||||
} from "~/lib/api/types/group";
|
||||
} from "~/lib/api/types/household";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
shoppingLists: `${prefix}/groups/shopping/lists`,
|
||||
shoppingListsId: (id: string) => `${prefix}/groups/shopping/lists/${id}`,
|
||||
shoppingListIdAddRecipe: (id: string, recipeId: string) => `${prefix}/groups/shopping/lists/${id}/recipe/${recipeId}`,
|
||||
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/groups/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
||||
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/groups/shopping/lists/${id}/label-settings`,
|
||||
shoppingLists: `${prefix}/households/shopping/lists`,
|
||||
shoppingListsId: (id: string) => `${prefix}/households/shopping/lists/${id}`,
|
||||
shoppingListIdAddRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}`,
|
||||
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
||||
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/households/shopping/lists/${id}/label-settings`,
|
||||
|
||||
shoppingListItems: `${prefix}/groups/shopping/items`,
|
||||
shoppingListItemsCreateBulk: `${prefix}/groups/shopping/items/create-bulk`,
|
||||
shoppingListItemsId: (id: string) => `${prefix}/groups/shopping/items/${id}`,
|
||||
shoppingListItems: `${prefix}/households/shopping/items`,
|
||||
shoppingListItemsCreateBulk: `${prefix}/households/shopping/items/create-bulk`,
|
||||
shoppingListItemsId: (id: string) => `${prefix}/households/shopping/items/${id}`,
|
||||
};
|
||||
|
||||
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingListOut, ShoppingListUpdate> {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { CreateWebhook, ReadWebhook } from "~/lib/api/types/group";
|
||||
import { CreateWebhook, ReadWebhook } from "~/lib/api/types/household";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
webhooks: `${prefix}/groups/webhooks`,
|
||||
webhooksId: (id: string | number) => `${prefix}/groups/webhooks/${id}`,
|
||||
webhooksIdTest: (id: string | number) => `${prefix}/groups/webhooks/${id}/test`,
|
||||
webhooks: `${prefix}/households/webhooks`,
|
||||
webhooksId: (id: string | number) => `${prefix}/households/webhooks/${id}`,
|
||||
webhooksIdTest: (id: string | number) => `${prefix}/households/webhooks/${id}/test`,
|
||||
};
|
||||
|
||||
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
|
||||
|
@ -1,13 +1,10 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { CategoryBase, GroupBase, GroupInDB, GroupSummary, UserOut } from "~/lib/api/types/user";
|
||||
import { GroupBase, GroupInDB, GroupSummary, UserSummary } from "~/lib/api/types/user";
|
||||
import { HouseholdSummary } from "~/lib/api/types/household";
|
||||
import {
|
||||
CreateInviteToken,
|
||||
GroupAdminUpdate,
|
||||
GroupStatistics,
|
||||
GroupStorage,
|
||||
ReadGroupPreferences,
|
||||
ReadInviteToken,
|
||||
SetPermissions,
|
||||
UpdateGroupPreferences,
|
||||
} from "~/lib/api/types/group";
|
||||
|
||||
@ -16,16 +13,14 @@ const prefix = "/api";
|
||||
const routes = {
|
||||
groups: `${prefix}/admin/groups`,
|
||||
groupsSelf: `${prefix}/groups/self`,
|
||||
categories: `${prefix}/groups/categories`,
|
||||
members: `${prefix}/groups/members`,
|
||||
permissions: `${prefix}/groups/permissions`,
|
||||
|
||||
preferences: `${prefix}/groups/preferences`,
|
||||
statistics: `${prefix}/groups/statistics`,
|
||||
storage: `${prefix}/groups/storage`,
|
||||
|
||||
invitation: `${prefix}/groups/invitations`,
|
||||
|
||||
households: `${prefix}/households`,
|
||||
membersHouseholdId: (householdId: string | number | null) => {
|
||||
return householdId ?
|
||||
`${prefix}/households/members?householdId=${householdId}` :
|
||||
`${prefix}/groups/members`;
|
||||
},
|
||||
groupsId: (id: string | number) => `${prefix}/admin/groups/${id}`,
|
||||
};
|
||||
|
||||
@ -38,14 +33,6 @@ export class GroupAPI extends BaseCRUDAPI<GroupBase, GroupInDB, GroupAdminUpdate
|
||||
return await this.requests.get<GroupSummary>(routes.groupsSelf);
|
||||
}
|
||||
|
||||
async getCategories() {
|
||||
return await this.requests.get<CategoryBase[]>(routes.categories);
|
||||
}
|
||||
|
||||
async setCategories(payload: CategoryBase[]) {
|
||||
return await this.requests.put<CategoryBase[]>(routes.categories, payload);
|
||||
}
|
||||
|
||||
async getPreferences() {
|
||||
return await this.requests.get<ReadGroupPreferences>(routes.preferences);
|
||||
}
|
||||
@ -55,21 +42,12 @@ export class GroupAPI extends BaseCRUDAPI<GroupBase, GroupInDB, GroupAdminUpdate
|
||||
return await this.requests.put<ReadGroupPreferences, UpdateGroupPreferences>(routes.preferences, payload);
|
||||
}
|
||||
|
||||
async createInvitation(payload: CreateInviteToken) {
|
||||
return await this.requests.post<ReadInviteToken>(routes.invitation, payload);
|
||||
async fetchMembers(householdId: string | number | null = null) {
|
||||
return await this.requests.get<UserSummary[]>(routes.membersHouseholdId(householdId));
|
||||
}
|
||||
|
||||
async fetchMembers() {
|
||||
return await this.requests.get<UserOut[]>(routes.members);
|
||||
}
|
||||
|
||||
async setMemberPermissions(payload: SetPermissions) {
|
||||
// TODO: This should probably be a patch request, which isn't offered by the API currently
|
||||
return await this.requests.put<UserOut, SetPermissions>(routes.permissions, payload);
|
||||
}
|
||||
|
||||
async statistics() {
|
||||
return await this.requests.get<GroupStatistics>(routes.statistics);
|
||||
async fetchHouseholds() {
|
||||
return await this.requests.get<HouseholdSummary[]>(routes.households);
|
||||
}
|
||||
|
||||
async storage() {
|
||||
|
64
frontend/lib/api/user/households.ts
Normal file
64
frontend/lib/api/user/households.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
import {
|
||||
HouseholdCreate,
|
||||
HouseholdInDB,
|
||||
UpdateHouseholdAdmin,
|
||||
HouseholdStatistics,
|
||||
ReadHouseholdPreferences,
|
||||
SetPermissions,
|
||||
UpdateHouseholdPreferences,
|
||||
CreateInviteToken,
|
||||
ReadInviteToken,
|
||||
} from "~/lib/api/types/household";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
households: `${prefix}/admin/households`,
|
||||
householdsSelf: `${prefix}/households/self`,
|
||||
members: `${prefix}/households/members`,
|
||||
permissions: `${prefix}/households/permissions`,
|
||||
|
||||
preferences: `${prefix}/households/preferences`,
|
||||
statistics: `${prefix}/households/statistics`,
|
||||
invitation: `${prefix}/households/invitations`,
|
||||
|
||||
householdsId: (id: string | number) => `${prefix}/admin/households/${id}`,
|
||||
};
|
||||
|
||||
export class HouseholdAPI extends BaseCRUDAPI<HouseholdCreate, HouseholdInDB, UpdateHouseholdAdmin> {
|
||||
baseRoute = routes.households;
|
||||
itemRoute = routes.householdsId;
|
||||
/** Returns the Group Data for the Current User
|
||||
*/
|
||||
async getCurrentUserHousehold() {
|
||||
return await this.requests.get<HouseholdInDB>(routes.householdsSelf);
|
||||
}
|
||||
|
||||
async getPreferences() {
|
||||
return await this.requests.get<ReadHouseholdPreferences>(routes.preferences);
|
||||
}
|
||||
|
||||
async setPreferences(payload: UpdateHouseholdPreferences) {
|
||||
// TODO: This should probably be a patch request, which isn't offered by the API currently
|
||||
return await this.requests.put<ReadHouseholdPreferences, UpdateHouseholdPreferences>(routes.preferences, payload);
|
||||
}
|
||||
|
||||
async createInvitation(payload: CreateInviteToken) {
|
||||
return await this.requests.post<ReadInviteToken>(routes.invitation, payload);
|
||||
}
|
||||
|
||||
async fetchMembers() {
|
||||
return await this.requests.get<UserOut[]>(routes.members);
|
||||
}
|
||||
|
||||
async setMemberPermissions(payload: SetPermissions) {
|
||||
// TODO: This should probably be a patch request, which isn't offered by the API currently
|
||||
return await this.requests.put<UserOut, SetPermissions>(routes.permissions, payload);
|
||||
}
|
||||
|
||||
async statistics() {
|
||||
return await this.requests.get<HouseholdStatistics>(routes.statistics);
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
import { BaseCRUDAPI } from "../base/base-clients";
|
||||
import { QueryValue, route } from "~/lib/api/base/route";
|
||||
import { PaginationData } from "~/lib/api/types/non-generated";
|
||||
import {
|
||||
ChangePassword,
|
||||
DeleteTokenResponse,
|
||||
@ -12,7 +10,6 @@ import {
|
||||
UserOut,
|
||||
UserRatingOut,
|
||||
UserRatingSummary,
|
||||
UserSummary,
|
||||
} from "~/lib/api/types/user";
|
||||
|
||||
export interface UserRatingsSummaries {
|
||||
@ -26,7 +23,6 @@ export interface UserRatingsOut {
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
groupUsers: `${prefix}/users/group-users`,
|
||||
usersSelf: `${prefix}/users/self`,
|
||||
ratingsSelf: `${prefix}/users/self/ratings`,
|
||||
passwordReset: `${prefix}/users/reset-password`,
|
||||
@ -51,10 +47,6 @@ export class UserApi extends BaseCRUDAPI<UserIn, UserOut, UserBase> {
|
||||
baseRoute: string = routes.users;
|
||||
itemRoute = (itemid: string) => routes.usersId(itemid);
|
||||
|
||||
async getGroupUsers(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
return await this.requests.get<PaginationData<UserSummary>>(route(routes.groupUsers, { page, perPage, ...params }));
|
||||
}
|
||||
|
||||
async addFavorite(id: string, slug: string) {
|
||||
return await this.requests.post(routes.usersIdFavoritesSlug(id, slug), {});
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
mdiAccountGroup,
|
||||
mdiSlotMachine,
|
||||
mdiHome,
|
||||
mdiHomeAccount,
|
||||
mdiMagnify,
|
||||
mdiPotSteamOutline,
|
||||
mdiTranslate,
|
||||
@ -226,6 +227,7 @@ export const icons = {
|
||||
heart: mdiHeart,
|
||||
heartOutline: mdiHeartOutline,
|
||||
home: mdiHome,
|
||||
household: mdiHomeAccount,
|
||||
import: mdiImport,
|
||||
information: mdiInformation,
|
||||
informationVariant: mdiInformationVariant,
|
||||
|
@ -477,7 +477,7 @@ export default {
|
||||
"name": "Meal Planner",
|
||||
"short_name": "Meal Planner",
|
||||
"description": "Open the meal planner",
|
||||
"url": "/group/mealplan/planner/view",
|
||||
"url": "/household/mealplan/planner/view",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/mdiCalendarMultiselect-192x192.png",
|
||||
|
@ -60,7 +60,7 @@
|
||||
<i18n path="settings.backup.experimental-description" />
|
||||
</v-card-text>
|
||||
</BaseCardSectionTitle>
|
||||
<v-toolbar color="background" flat class="justify-between">
|
||||
<v-toolbar color="transparent" flat class="justify-between">
|
||||
<BaseButton class="mr-2" @click="createBackup"> {{ $t("settings.backup.create-heading") }} </BaseButton>
|
||||
<AppButtonUpload
|
||||
:text-btn="false"
|
||||
|
@ -1,15 +1,14 @@
|
||||
// TODO: Edit Group
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<BaseDialog
|
||||
v-model="createDialog"
|
||||
:title="$t('group.create-group')"
|
||||
:icon="$globals.icons.group"
|
||||
@submit="createGroup(createUserForm.data)"
|
||||
@submit="createGroup(createGroupForm.data)"
|
||||
>
|
||||
<template #activator> </template>
|
||||
<v-card-text>
|
||||
<AutoForm v-model="createUserForm.data" :update-mode="updateMode" :items="createUserForm.items" />
|
||||
<AutoForm v-model="createGroupForm.data" :update-mode="updateMode" :items="createGroupForm.items" />
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
@ -27,7 +26,7 @@
|
||||
|
||||
<BaseCardSectionTitle :title="$tc('group.group-management')"> </BaseCardSectionTitle>
|
||||
<section>
|
||||
<v-toolbar flat color="background" class="justify-between">
|
||||
<v-toolbar flat color="transparent" class="justify-between">
|
||||
<BaseButton @click="openDialog"> {{ $t("general.create") }} </BaseButton>
|
||||
</v-toolbar>
|
||||
|
||||
@ -41,15 +40,15 @@
|
||||
:search="search"
|
||||
@click:row="handleRowClick"
|
||||
>
|
||||
<template #item.households="{ item }">
|
||||
{{ item.households.length }}
|
||||
</template>
|
||||
<template #item.users="{ item }">
|
||||
{{ item.users.length }}
|
||||
</template>
|
||||
<template #item.webhookEnable="{ item }">
|
||||
{{ item.webhooks.length > 0 ? $t("general.yes") : $t("general.no") }}
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn
|
||||
:disabled="item && item.users.length > 0"
|
||||
:disabled="item && (item.households.length > 0 || item.users.length > 0)"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@ -94,12 +93,12 @@ export default defineComponent({
|
||||
value: "id",
|
||||
},
|
||||
{ text: i18n.t("general.name"), value: "name" },
|
||||
{ text: i18n.t("group.total-households"), value: "households" },
|
||||
{ text: i18n.t("user.total-users"), value: "users" },
|
||||
{ text: i18n.t("user.webhooks-enabled"), value: "webhookEnable" },
|
||||
{ text: i18n.t("general.delete"), value: "actions" },
|
||||
],
|
||||
updateMode: false,
|
||||
createUserForm: {
|
||||
createGroupForm: {
|
||||
items: [
|
||||
{
|
||||
label: i18n.t("group.group-name"),
|
||||
@ -116,7 +115,7 @@ export default defineComponent({
|
||||
|
||||
function openDialog() {
|
||||
state.createDialog = true;
|
||||
state.createUserForm.data.name = "";
|
||||
state.createGroupForm.data.name = "";
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
117
frontend/pages/admin/manage/households/_id.vue
Normal file
117
frontend/pages/admin/manage/households/_id.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<v-container v-if="household" class="narrow-container">
|
||||
<BasePageTitle>
|
||||
<template #header>
|
||||
<v-img max-height="125" max-width="125" :src="require('~/static/svgs/manage-group-settings.svg')"></v-img>
|
||||
</template>
|
||||
<template #title> {{ $t('household.admin-household-management') }} </template>
|
||||
{{ $t('household.admin-household-management-text') }}
|
||||
</BasePageTitle>
|
||||
<AppToolbar back> </AppToolbar>
|
||||
<v-card-text> {{ $t('household.household-id-value', [household.id]) }} </v-card-text>
|
||||
<v-form v-if="!userError" ref="refHouseholdEditForm" @submit.prevent="handleSubmit">
|
||||
<v-card outlined>
|
||||
<v-card-text>
|
||||
<v-select
|
||||
v-if="groups"
|
||||
v-model="household.groupId"
|
||||
disabled
|
||||
:items="groups"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
item-text="name"
|
||||
item-value="id"
|
||||
:return-object="false"
|
||||
filled
|
||||
:label="$tc('group.user-group')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="household.name"
|
||||
:label="$t('household.household-name')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<HouseholdPreferencesEditor v-if="household.preferences" v-model="household.preferences" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<div class="d-flex pa-2">
|
||||
<BaseButton type="submit" edit class="ml-auto"> {{ $t("general.update") }}</BaseButton>
|
||||
</div>
|
||||
</v-form>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, useRoute, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import HouseholdPreferencesEditor from "~/components/Domain/Household/HouseholdPreferencesEditor.vue";
|
||||
import { useGroups } from "~/composables/use-groups";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { HouseholdInDB } from "~/lib/api/types/household";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
HouseholdPreferencesEditor,
|
||||
},
|
||||
layout: "admin",
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const { i18n } = useContext();
|
||||
|
||||
const { groups } = useGroups();
|
||||
const householdId = route.value.params.id;
|
||||
|
||||
// ==============================================
|
||||
// New User Form
|
||||
|
||||
const refHouseholdEditForm = ref<VForm | null>(null);
|
||||
|
||||
const userApi = useUserApi();
|
||||
|
||||
const household = ref<HouseholdInDB | null>(null);
|
||||
|
||||
const userError = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
const { data, error } = await userApi.households.getOne(householdId);
|
||||
|
||||
if (error?.response?.status === 404) {
|
||||
alert.error(i18n.tc("user.user-not-found"));
|
||||
userError.value = true;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
household.value = data;
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!refHouseholdEditForm.value?.validate() || household.value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { response, data } = await userApi.households.updateOne(household.value.id, household.value);
|
||||
if (response?.status === 200 && data) {
|
||||
if (household.value.slug !== data.slug) {
|
||||
// the slug updated, which invalidates the nav URLs
|
||||
window.location.reload();
|
||||
}
|
||||
household.value = data;
|
||||
} else {
|
||||
alert.error(i18n.tc("settings.settings-update-failed"));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
groups,
|
||||
household,
|
||||
validators,
|
||||
userError,
|
||||
refHouseholdEditForm,
|
||||
handleSubmit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
167
frontend/pages/admin/manage/households/index.vue
Normal file
167
frontend/pages/admin/manage/households/index.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<BaseDialog
|
||||
v-model="createDialog"
|
||||
:title="$t('household.create-household')"
|
||||
:icon="$globals.icons.household"
|
||||
@submit="createHousehold(createHouseholdForm.data)"
|
||||
>
|
||||
<template #activator> </template>
|
||||
<v-card-text>
|
||||
<v-select
|
||||
v-if="groups"
|
||||
v-model="createHouseholdForm.data.groupId"
|
||||
:items="groups"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
item-text="name"
|
||||
item-value="id"
|
||||
:return-object="false"
|
||||
filled
|
||||
:label="$tc('household.household-group')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<AutoForm v-model="createHouseholdForm.data" :update-mode="updateMode" :items="createHouseholdForm.items" />
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<BaseDialog
|
||||
v-model="confirmDialog"
|
||||
:title="$t('general.confirm')"
|
||||
color="error"
|
||||
@confirm="deleteHousehold(deleteTarget)"
|
||||
>
|
||||
<template #activator> </template>
|
||||
<v-card-text>
|
||||
{{ $t("general.confirm-delete-generic") }}
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<BaseCardSectionTitle :title="$tc('household.household-management')"> </BaseCardSectionTitle>
|
||||
<section>
|
||||
<v-toolbar flat color="transparent" class="justify-between">
|
||||
<BaseButton @click="openDialog"> {{ $t("general.create") }} </BaseButton>
|
||||
</v-toolbar>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="households || []"
|
||||
item-key="id"
|
||||
class="elevation-0"
|
||||
hide-default-footer
|
||||
disable-pagination
|
||||
:search="search"
|
||||
@click:row="handleRowClick"
|
||||
>
|
||||
<template #item.users="{ item }">
|
||||
{{ item.users.length }}
|
||||
</template>
|
||||
<template #item.group="{ item }">
|
||||
{{ item.group }}
|
||||
</template>
|
||||
<template #item.webhookEnable="{ item }">
|
||||
{{ item.webhooks.length > 0 ? $t("general.yes") : $t("general.no") }}
|
||||
</template>
|
||||
<template #item.actions="{ item }">
|
||||
<v-btn
|
||||
:disabled="item && item.users.length > 0"
|
||||
class="mr-1"
|
||||
icon
|
||||
color="error"
|
||||
@click.stop="
|
||||
confirmDialog = true;
|
||||
deleteTarget = item.id;
|
||||
"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.delete }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<v-divider></v-divider>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, useContext, useRouter } from "@nuxtjs/composition-api";
|
||||
import { fieldTypes } from "~/composables/forms";
|
||||
import { useGroups } from "~/composables/use-groups";
|
||||
import { useHouseholds } from "~/composables/use-households";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { HouseholdInDB } from "~/lib/api/types/household";
|
||||
|
||||
export default defineComponent({
|
||||
layout: "admin",
|
||||
setup() {
|
||||
const { i18n } = useContext();
|
||||
const { groups } = useGroups();
|
||||
const { households, refreshAllHouseholds, deleteHousehold, createHousehold } = useHouseholds();
|
||||
|
||||
const state = reactive({
|
||||
createDialog: false,
|
||||
confirmDialog: false,
|
||||
deleteTarget: 0,
|
||||
search: "",
|
||||
headers: [
|
||||
{
|
||||
text: i18n.t("household.household"),
|
||||
align: "start",
|
||||
sortable: false,
|
||||
value: "id",
|
||||
},
|
||||
{ text: i18n.t("general.name"), value: "name" },
|
||||
{ text: i18n.t("group.group"), value: "group" },
|
||||
{ text: i18n.t("user.total-users"), value: "users" },
|
||||
{ text: i18n.t("user.webhooks-enabled"), value: "webhookEnable" },
|
||||
{ text: i18n.t("general.delete"), value: "actions" },
|
||||
],
|
||||
updateMode: false,
|
||||
createHouseholdForm: {
|
||||
items: [
|
||||
{
|
||||
label: i18n.t("household.household-name"),
|
||||
varName: "name",
|
||||
type: fieldTypes.TEXT,
|
||||
rules: ["required"],
|
||||
},
|
||||
],
|
||||
data: {
|
||||
groupId: "",
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function openDialog() {
|
||||
state.createDialog = true;
|
||||
state.createHouseholdForm.data.name = "";
|
||||
state.createHouseholdForm.data.groupId = "";
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function handleRowClick(item: HouseholdInDB) {
|
||||
router.push(`/admin/manage/households/${item.id}`);
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
groups,
|
||||
households,
|
||||
validators,
|
||||
refreshAllHouseholds,
|
||||
deleteHousehold,
|
||||
createHousehold,
|
||||
openDialog,
|
||||
handleRowClick,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("household.manage-households") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -14,9 +14,11 @@
|
||||
<div class="d-flex">
|
||||
<p> {{ $t("user.user-id-with-value", {id: user.id} ) }}</p>
|
||||
</div>
|
||||
<!-- This is disabled since we can't properly handle changing the user's group in most scenarios -->
|
||||
<v-select
|
||||
v-if="groups"
|
||||
v-model="user.group"
|
||||
disabled
|
||||
:items="groups"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
@ -26,7 +28,20 @@
|
||||
filled
|
||||
:label="$tc('group.user-group')"
|
||||
:rules="[validators.required]"
|
||||
></v-select>
|
||||
/>
|
||||
<v-select
|
||||
v-if="households"
|
||||
v-model="user.household"
|
||||
:items="households"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
item-text="name"
|
||||
item-value="name"
|
||||
:return-object="false"
|
||||
filled
|
||||
:label="$tc('household.user-household')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<div class="d-flex py-2 pr-2">
|
||||
<BaseButton type="button" :loading="generatingToken" create @click.prevent="handlePasswordReset">
|
||||
{{ $t("user.generate-password-reset-link") }}
|
||||
@ -65,6 +80,7 @@
|
||||
import { computed, defineComponent, useRoute, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { useAdminApi, useUserApi } from "~/composables/api";
|
||||
import { useGroups } from "~/composables/use-groups";
|
||||
import { useHouseholds } from "~/composables/use-households";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { useUserForm } from "~/composables/use-users";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
@ -76,6 +92,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const { userForm } = useUserForm();
|
||||
const { groups } = useGroups();
|
||||
const { useHouseholdsInGroup } = useHouseholds();
|
||||
const { i18n } = useContext();
|
||||
const route = useRoute();
|
||||
|
||||
@ -89,6 +106,8 @@ export default defineComponent({
|
||||
const adminApi = useAdminApi();
|
||||
|
||||
const user = ref<UserOut | null>(null);
|
||||
const households = useHouseholdsInGroup(computed(() => user.value?.groupId || ""));
|
||||
|
||||
const disabledFields = computed(() => {
|
||||
return user.value?.authMethod !== "Mealie" ? ["admin"] : [];
|
||||
})
|
||||
@ -154,6 +173,7 @@ export default defineComponent({
|
||||
refNewUserForm,
|
||||
handleSubmit,
|
||||
groups,
|
||||
households,
|
||||
validators,
|
||||
handlePasswordReset,
|
||||
resetUrl,
|
||||
|
@ -12,17 +12,30 @@
|
||||
<v-card-text>
|
||||
<v-select
|
||||
v-if="groups"
|
||||
v-model="newUserData.group"
|
||||
v-model="selectedGroupId"
|
||||
:items="groups"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
item-text="name"
|
||||
item-value="id"
|
||||
:return-object="false"
|
||||
filled
|
||||
:label="$t('group.user-group')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-select
|
||||
v-if="households"
|
||||
v-model="newUserData.household"
|
||||
:items="households"
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
item-text="name"
|
||||
item-value="name"
|
||||
:return-object="false"
|
||||
filled
|
||||
:label="$t('group.user-group')"
|
||||
:label="$t('household.user-household')"
|
||||
:rules="[validators.required]"
|
||||
></v-select>
|
||||
/>
|
||||
<AutoForm v-model="newUserData" :items="userForm" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@ -34,9 +47,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, useRouter, reactive, ref, toRefs } from "@nuxtjs/composition-api";
|
||||
import { computed, defineComponent, useRouter, reactive, ref, toRefs, watch } from "@nuxtjs/composition-api";
|
||||
import { useAdminApi } from "~/composables/api";
|
||||
import { useGroups } from "~/composables/use-groups";
|
||||
import { useHouseholds } from "~/composables/use-households";
|
||||
import { useUserForm } from "~/composables/use-users";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
@ -46,6 +60,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const { userForm } = useUserForm();
|
||||
const { groups } = useGroups();
|
||||
const { useHouseholdsInGroup } = useHouseholds();
|
||||
const router = useRouter();
|
||||
|
||||
// ==============================================
|
||||
@ -55,13 +70,20 @@ export default defineComponent({
|
||||
|
||||
const adminApi = useAdminApi();
|
||||
|
||||
const selectedGroupId = ref<string>("");
|
||||
const households = useHouseholdsInGroup(selectedGroupId);
|
||||
|
||||
const selectedGroup = computed(() => {
|
||||
return groups.value?.find((group) => group.id === selectedGroupId.value);
|
||||
});
|
||||
const state = reactive({
|
||||
newUserData: {
|
||||
username: "",
|
||||
fullName: "",
|
||||
email: "",
|
||||
admin: false,
|
||||
group: "",
|
||||
group: selectedGroup.value?.name || "",
|
||||
household: "",
|
||||
advanced: false,
|
||||
canInvite: false,
|
||||
canManage: false,
|
||||
@ -70,6 +92,10 @@ export default defineComponent({
|
||||
authMethod: "Mealie",
|
||||
},
|
||||
});
|
||||
watch(selectedGroup, (newGroup) => {
|
||||
state.newUserData.group = newGroup?.name || "";
|
||||
state.newUserData.household = "";
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!refNewUserForm.value?.validate()) return;
|
||||
@ -87,6 +113,8 @@ export default defineComponent({
|
||||
refNewUserForm,
|
||||
handleSubmit,
|
||||
groups,
|
||||
selectedGroupId,
|
||||
households,
|
||||
validators,
|
||||
};
|
||||
},
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
<BaseCardSectionTitle :title="$tc('user.user-management')"> </BaseCardSectionTitle>
|
||||
<section>
|
||||
<v-toolbar color="background" flat class="justify-between">
|
||||
<v-toolbar color="transparent" flat class="justify-between">
|
||||
<BaseButton to="/admin/manage/users/create" class="mr-2">
|
||||
{{ $t("general.create") }}
|
||||
</BaseButton>
|
||||
@ -129,6 +129,7 @@ export default defineComponent({
|
||||
{ text: i18n.t("user.full-name"), value: "fullName" },
|
||||
{ text: i18n.t("user.email"), value: "email" },
|
||||
{ text: i18n.t("group.group"), value: "group" },
|
||||
{ text: i18n.t("household.household"), value: "household" },
|
||||
{ text: i18n.t("user.auth-method"), value: "authMethod" },
|
||||
{ text: i18n.t("user.admin"), value: "admin" },
|
||||
{ text: i18n.t("general.delete"), value: "actions", sortable: false, align: "center" },
|
||||
|
@ -312,7 +312,6 @@ export default defineComponent({
|
||||
const preferences = {
|
||||
...data.preferences,
|
||||
privateGroup: !commonSettings.value.makeGroupRecipesPublic,
|
||||
recipePublic: commonSettings.value.makeGroupRecipesPublic,
|
||||
}
|
||||
|
||||
const payload = {
|
||||
@ -327,6 +326,32 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
async function updateHousehold() {
|
||||
// @ts-ignore-next-line user will never be null here
|
||||
const { data } = await api.households.getOne($auth.user?.householdId);
|
||||
if (!data || !data.preferences) {
|
||||
alert.error(i18n.tc("events.something-went-wrong"));
|
||||
return;
|
||||
}
|
||||
|
||||
const preferences = {
|
||||
...data.preferences,
|
||||
privateHousehold: !commonSettings.value.makeGroupRecipesPublic,
|
||||
recipePublic: commonSettings.value.makeGroupRecipesPublic,
|
||||
}
|
||||
|
||||
const payload = {
|
||||
...data,
|
||||
preferences,
|
||||
}
|
||||
|
||||
// @ts-ignore-next-line user will never be null here
|
||||
const { response } = await api.households.updateOne($auth.user?.householdId, payload);
|
||||
if (!response || response.status !== 200) {
|
||||
alert.error(i18n.tc("events.something-went-wrong"));
|
||||
}
|
||||
}
|
||||
|
||||
async function seedFoods() {
|
||||
const { response } = await api.seeders.foods({ locale: locale.value })
|
||||
if (!response || response.status !== 200) {
|
||||
@ -365,6 +390,7 @@ export default defineComponent({
|
||||
async function submitCommonSettings() {
|
||||
const tasks = [
|
||||
updateGroup(),
|
||||
updateHousehold(),
|
||||
seedData(),
|
||||
]
|
||||
|
||||
|
@ -367,6 +367,11 @@ export default defineComponent({
|
||||
icon: $globals.icons.group,
|
||||
value: data.defaultGroup,
|
||||
},
|
||||
{
|
||||
name: i18n.t("about.default-household"),
|
||||
icon: $globals.icons.household,
|
||||
value: data.defaultHousehold,
|
||||
},
|
||||
{
|
||||
slot: "recipe-scraper",
|
||||
name: i18n.t("settings.recipe-scraper-version"),
|
||||
|
@ -24,17 +24,18 @@ export default defineComponent({
|
||||
|
||||
const groupName = ref<string>("");
|
||||
const queryFilter = ref<string>("");
|
||||
async function fetchGroup() {
|
||||
const { data } = await api.groups.getCurrentUserGroup();
|
||||
async function fetchHousehold() {
|
||||
const { data } = await api.households.getCurrentUserHousehold();
|
||||
if (data) {
|
||||
queryFilter.value = `recipe.group_id="${data.id}"`;
|
||||
groupName.value = data.name;
|
||||
// TODO: once users are able to fetch other households' recipes, remove the household filter
|
||||
queryFilter.value = `recipe.group_id="${data.groupId}" AND recipe.household_id="${data.id}"`;
|
||||
groupName.value = data.group;
|
||||
}
|
||||
|
||||
ready.value = true;
|
||||
}
|
||||
|
||||
fetchGroup();
|
||||
fetchHousehold();
|
||||
return {
|
||||
groupName,
|
||||
queryFilter,
|
||||
|
@ -121,7 +121,7 @@
|
||||
import { defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { useGroupRecipeActions, useGroupRecipeActionData } from "~/composables/use-group-recipe-actions";
|
||||
import { GroupRecipeActionOut } from "~/lib/api/types/group";
|
||||
import { GroupRecipeActionOut } from "~/lib/api/types/household";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
|
@ -25,139 +25,22 @@
|
||||
<DocLink class="mt-2" link="/documentation/getting-started/faq/#how-do-private-groups-and-recipes-work" />
|
||||
</div>
|
||||
</div>
|
||||
<v-select
|
||||
v-model="group.preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
@change="groupActions.updatePreferences()"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section v-if="group">
|
||||
<BaseCardSectionTitle class="mt-10" :title="$tc('group.default-recipe-preferences')">
|
||||
{{ $t("group.default-recipe-preferences-description") }}
|
||||
</BaseCardSectionTitle>
|
||||
|
||||
<div class="preference-container">
|
||||
<div v-for="p in preferencesEditor" :key="p.key">
|
||||
<v-checkbox
|
||||
v-model="group.preferences[p.key]"
|
||||
hide-details
|
||||
dense
|
||||
:label="p.label"
|
||||
@change="groupActions.updatePreferences()"
|
||||
/>
|
||||
<p class="ml-8 text-subtitle-2 my-0 py-0">
|
||||
{{ p.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { ReadGroupPreferences } from "~/lib/api/types/group";
|
||||
|
||||
export default defineComponent({
|
||||
middleware: ["auth", "can-manage-only"],
|
||||
setup() {
|
||||
const { group, actions: groupActions } = useGroupSelf();
|
||||
|
||||
const { i18n } = useContext();
|
||||
|
||||
type Preference = {
|
||||
key: keyof ReadGroupPreferences;
|
||||
value: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const preferencesEditor = computed<Preference[]>(() => {
|
||||
if (!group.value || !group.value.preferences) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: "recipePublic",
|
||||
value: group.value.preferences.recipePublic || false,
|
||||
label: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes"),
|
||||
description: i18n.t("group.allow-users-outside-of-your-group-to-see-your-recipes-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowNutrition",
|
||||
value: group.value.preferences.recipeShowNutrition || false,
|
||||
label: i18n.t("group.show-nutrition-information"),
|
||||
description: i18n.t("group.show-nutrition-information-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeShowAssets",
|
||||
value: group.value.preferences.recipeShowAssets || false,
|
||||
label: i18n.t("group.show-recipe-assets"),
|
||||
description: i18n.t("group.show-recipe-assets-description"),
|
||||
} as Preference,
|
||||
{
|
||||
key: "recipeLandscapeView",
|
||||
value: group.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: group.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,
|
||||
{
|
||||
key: "recipeDisableAmount",
|
||||
value: group.value.preferences.recipeDisableAmount || false,
|
||||
label: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
||||
description: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food-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,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
group,
|
||||
groupActions,
|
||||
allDays,
|
||||
preferencesEditor,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
|
178
frontend/pages/household/index.vue
Normal file
178
frontend/pages/household/index.vue
Normal file
@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<v-container class="narrow-container">
|
||||
<BasePageTitle class="mb-5">
|
||||
<template #header>
|
||||
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/manage-group-settings.svg')"></v-img>
|
||||
</template>
|
||||
<template #title> {{ $t("profile.household-settings") }} </template>
|
||||
{{ $t("profile.household-description") }}
|
||||
</BasePageTitle>
|
||||
|
||||
<section v-if="household">
|
||||
<BaseCardSectionTitle class="mt-10" :title="$tc('household.household-preferences')"></BaseCardSectionTitle>
|
||||
<div class="mb-6">
|
||||
<v-checkbox
|
||||
v-model="household.preferences.privateHousehold"
|
||||
hide-details
|
||||
dense
|
||||
:label="$t('household.private-household')"
|
||||
@change="householdActions.updatePreferences()"
|
||||
/>
|
||||
<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>
|
||||
<v-select
|
||||
v-model="household.preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
@change="householdActions.updatePreferences()"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section v-if="household">
|
||||
<BaseCardSectionTitle class="mt-10" :title="$tc('group.default-recipe-preferences')">
|
||||
{{ $t("household.default-recipe-preferences-description") }}
|
||||
</BaseCardSectionTitle>
|
||||
|
||||
<div class="preference-container">
|
||||
<div v-for="p in preferencesEditor" :key="p.key">
|
||||
<v-checkbox
|
||||
v-model="household.preferences[p.key]"
|
||||
hide-details
|
||||
dense
|
||||
:label="p.label"
|
||||
@change="householdActions.updatePreferences()"
|
||||
/>
|
||||
<p class="ml-8 text-subtitle-2 my-0 py-0">
|
||||
{{ p.description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { ReadHouseholdPreferences } from "~/lib/api/types/household";
|
||||
|
||||
export default defineComponent({
|
||||
middleware: ["auth", "can-manage-only"],
|
||||
setup() {
|
||||
const { household, actions: householdActions } = useHouseholdSelf();
|
||||
|
||||
const { i18n } = useContext();
|
||||
|
||||
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,
|
||||
{
|
||||
key: "recipeDisableAmount",
|
||||
value: household.value.preferences.recipeDisableAmount || false,
|
||||
label: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food"),
|
||||
description: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food-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,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
household,
|
||||
householdActions,
|
||||
allDays,
|
||||
preferencesEditor,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("household.household") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.preference-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
max-width: 600px;
|
||||
}
|
||||
</style>
|
@ -39,10 +39,10 @@
|
||||
|
||||
<div class="d-flex flex-wrap align-center justify-space-between mb-2">
|
||||
<v-tabs style="width: fit-content;">
|
||||
<v-tab :to="`/group/mealplan/planner/view`">{{ $t('meal-plan.meal-planner') }}</v-tab>
|
||||
<v-tab :to="`/group/mealplan/planner/edit`">{{ $t('general.edit') }}</v-tab>
|
||||
<v-tab :to="`/household/mealplan/planner/view`">{{ $t('meal-plan.meal-planner') }}</v-tab>
|
||||
<v-tab :to="`/household/mealplan/planner/edit`">{{ $t('general.edit') }}</v-tab>
|
||||
</v-tabs>
|
||||
<ButtonLink :icon="$globals.icons.calendar" :to="`/group/mealplan/settings`" :text="$tc('general.settings')" />
|
||||
<ButtonLink :icon="$globals.icons.calendar" :to="`/household/mealplan/settings`" :text="$tc('general.settings')" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -56,7 +56,7 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
|
||||
import { isSameDay, addDays, parseISO } from "date-fns";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { useMealplans } from "~/composables/use-group-mealplan";
|
||||
import { useUserMealPlanPreferences } from "~/composables/use-users/preferences";
|
||||
|
||||
@ -65,7 +65,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { group } = useGroupSelf();
|
||||
const { household } = useHouseholdSelf();
|
||||
|
||||
const mealPlanPreferences = useUserMealPlanPreferences();
|
||||
const numberOfDays = ref<number>(mealPlanPreferences.value.numberOfDays || 7);
|
||||
@ -74,8 +74,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
// Force to /view if current route is /planner
|
||||
if (route.value.path === "/group/mealplan/planner") {
|
||||
router.push("/group/mealplan/planner/view");
|
||||
if (route.value.path === "/household/mealplan/planner") {
|
||||
router.push("/household/mealplan/planner/view");
|
||||
}
|
||||
|
||||
function fmtYYYYMMDD(date: Date) {
|
||||
@ -95,7 +95,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
|
||||
const weekRange = computed(() => {
|
@ -229,7 +229,7 @@ import { useMealplans, usePlanTypeOptions, getEntryTypeText } from "~/composable
|
||||
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
|
||||
import { PlanEntryType, UpdatePlanEntry } from "~/lib/api/types/meal-plan";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useGroupSelf } from "~/composables/use-groups";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { useRecipeSearch } from "~/composables/recipes/use-recipe-search";
|
||||
|
||||
export default defineComponent({
|
||||
@ -249,7 +249,7 @@ export default defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const api = useUserApi();
|
||||
const { group } = useGroupSelf();
|
||||
const { household } = useHouseholdSelf();
|
||||
|
||||
const state = ref({
|
||||
dialog: false,
|
||||
@ -257,7 +257,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
|
||||
function onMoveCallback(evt: SortableEvent) {
|
||||
@ -308,7 +308,7 @@ export default defineComponent({
|
||||
entryType: "dinner" as PlanEntryType,
|
||||
existing: false,
|
||||
id: 0,
|
||||
groupId: ""
|
||||
groupId: "",
|
||||
});
|
||||
|
||||
function openDialog(date: Date) {
|
@ -54,7 +54,7 @@
|
||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
import { MealsByDate } from "./types";
|
||||
import { ReadPlanEntry } from "~/lib/api/types/meal-plan";
|
||||
import GroupMealPlanDayContextMenu from "~/components/Domain/Group/GroupMealPlanDayContextMenu.vue";
|
||||
import GroupMealPlanDayContextMenu from "~/components/Domain/Household/GroupMealPlanDayContextMenu.vue";
|
||||
import RecipeCardMobile from "~/components/Domain/Recipe/RecipeCardMobile.vue";
|
||||
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||
|
@ -89,7 +89,7 @@
|
||||
import { defineComponent, ref, useAsync } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { PlanRulesCreate, PlanRulesOut } from "~/lib/api/types/meal-plan";
|
||||
import GroupMealPlanRuleForm from "~/components/Domain/Group/GroupMealPlanRuleForm.vue";
|
||||
import GroupMealPlanRuleForm from "~/components/Domain/Household/GroupMealPlanRuleForm.vue";
|
||||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
import RecipeChips from "~/components/Domain/Recipe/RecipeChips.vue";
|
||||
|
@ -97,7 +97,7 @@ export default defineComponent({
|
||||
];
|
||||
|
||||
async function refreshMembers() {
|
||||
const { data } = await api.groups.fetchMembers();
|
||||
const { data } = await api.households.fetchMembers();
|
||||
if (data) {
|
||||
members.value = data;
|
||||
}
|
||||
@ -111,7 +111,7 @@ export default defineComponent({
|
||||
canOrganize: user.canOrganize,
|
||||
};
|
||||
|
||||
await api.groups.setMemberPermissions(payload);
|
||||
await api.households.setMemberPermissions(payload);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
@ -109,7 +109,7 @@
|
||||
import { defineComponent, useAsync, reactive, useContext, toRefs } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useAsyncKey } from "~/composables/use-utils";
|
||||
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/lib/api/types/group";
|
||||
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/lib/api/types/household";
|
||||
|
||||
interface OptionKey {
|
||||
text: string;
|
@ -45,7 +45,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import { useGroupWebhooks, timeUTC } from "~/composables/use-group-webhooks";
|
||||
import GroupWebhookEditor from "~/components/Domain/Group/GroupWebhookEditor.vue";
|
||||
import GroupWebhookEditor from "~/components/Domain/Household/GroupWebhookEditor.vue";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
|
||||
export default defineComponent({
|
@ -294,8 +294,8 @@ import { useCopyList } from "~/composables/use-copy";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
||||
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
||||
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/group";
|
||||
import { UserSummary } from "~/lib/api/types/user";
|
||||
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/household";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
||||
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
||||
@ -444,7 +444,7 @@ export default defineComponent({
|
||||
unchecked: shoppingList.value?.listItems?.filter((item) => !item.checked) ?? [],
|
||||
checked: shoppingList.value?.listItems
|
||||
?.filter((item) => item.checked)
|
||||
.sort((a, b) => (a.updateAt < b.updateAt ? 1 : -1))
|
||||
.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1))
|
||||
?? [],
|
||||
};
|
||||
});
|
||||
@ -863,7 +863,7 @@ export default defineComponent({
|
||||
item.position = shoppingList.value.listItems.length;
|
||||
|
||||
// set a temporary updatedAt timestamp prior to refresh so it appears at the top of the checked items
|
||||
item.updateAt = new Date().toISOString();
|
||||
item.updatedAt = new Date().toISOString();
|
||||
}
|
||||
|
||||
// make updates reflect immediately
|
||||
@ -934,7 +934,7 @@ export default defineComponent({
|
||||
: 0;
|
||||
|
||||
createListItemData.value.createdAt = new Date().toISOString();
|
||||
createListItemData.value.updateAt = createListItemData.value.createdAt;
|
||||
createListItemData.value.updatedAt = createListItemData.value.createdAt;
|
||||
|
||||
updateListItemOrder();
|
||||
|
||||
@ -1020,16 +1020,16 @@ export default defineComponent({
|
||||
// ===============================================================
|
||||
// Shopping List Settings
|
||||
|
||||
const allUsers = ref<UserSummary[]>([]);
|
||||
const allUsers = ref<UserOut[]>([]);
|
||||
const currentUserId = ref<string | undefined>();
|
||||
async function fetchAllUsers() {
|
||||
const { data } = await userApi.users.getGroupUsers(1, -1, { orderBy: "full_name", orderDirection: "asc" });
|
||||
const { data } = await userApi.households.fetchMembers();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update current user
|
||||
allUsers.value = data.items;
|
||||
allUsers.value = data.sort((a, b) => ((a.fullName || "") < (b.fullName || "") ? -1 : 1));
|
||||
currentUserId.value = shoppingList.value?.userId;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<p class="subtitle-1 mb-0 text-center">
|
||||
{{ $t('profile.description') }}
|
||||
</p>
|
||||
<v-card flat color="background" width="100%" max-width="600px">
|
||||
<v-card flat color="transparent" width="100%" max-width="600px">
|
||||
<v-card-actions class="d-flex justify-center my-4">
|
||||
<v-btn v-if="$auth.user.canInvite" outlined rounded @click="getSignupLink()">
|
||||
<v-icon left>
|
||||
@ -57,9 +57,9 @@
|
||||
<v-row tag="section">
|
||||
<v-col cols="12" sm="12" md="12">
|
||||
<v-card outlined>
|
||||
<v-card-title class="headline pb-0"> {{ $t('profile.group-statistics') }} </v-card-title>
|
||||
<v-card-title class="headline pb-0"> {{ $t('profile.household-statistics') }} </v-card-title>
|
||||
<v-card-text class="py-0">
|
||||
{{ $t('profile.group-statistics-description') }}
|
||||
{{ $t('profile.household-statistics-description') }}
|
||||
</v-card-text>
|
||||
<v-card-text class="d-flex flex-wrap justify-center align-center" style="gap: 0.8rem">
|
||||
<StatsCards
|
||||
@ -106,8 +106,66 @@
|
||||
</AdvancedOnly>
|
||||
</v-row>
|
||||
</section>
|
||||
<v-divider class="my-7"></v-divider>
|
||||
<v-divider class="my-7" />
|
||||
<section>
|
||||
<div>
|
||||
<h3 class="headline">{{ $t('household.household') }}</h3>
|
||||
<p>{{ $t('profile.household-description') }}</p>
|
||||
</div>
|
||||
<v-row tag="section">
|
||||
<v-col v-if="$auth.user.canManage" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.household-settings'), to: `/household` }"
|
||||
:image="require('~/static/svgs/manage-group-settings.svg')"
|
||||
>
|
||||
<template #title> {{ $t('profile.household-settings') }} </template>
|
||||
{{ $t('profile.household-settings-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-cookbooks'), to: `/g/${groupSlug}/cookbooks` }"
|
||||
:image="require('~/static/svgs/manage-cookbooks.svg')"
|
||||
>
|
||||
<template #title> {{ $t('sidebar.cookbooks') }} </template>
|
||||
{{ $t('profile.cookbooks-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<v-col v-if="user.canManage" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-members'), to: `/household/members` }"
|
||||
:image="require('~/static/svgs/manage-members.svg')"
|
||||
>
|
||||
<template #title> {{ $t('profile.members') }} </template>
|
||||
{{ $t('profile.members-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<AdvancedOnly>
|
||||
<v-col v-if="user.advanced" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-webhooks'), to: `/household/webhooks` }"
|
||||
:image="require('~/static/svgs/manage-webhooks.svg')"
|
||||
>
|
||||
<template #title> {{ $t('settings.webhooks.webhooks') }} </template>
|
||||
{{ $t('profile.webhooks-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
</AdvancedOnly>
|
||||
<AdvancedOnly>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-notifiers'), to: `/household/notifiers` }"
|
||||
:image="require('~/static/svgs/manage-notifiers.svg')"
|
||||
>
|
||||
<template #title> {{ $t('profile.notifiers') }} </template>
|
||||
{{ $t('profile.notifiers-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
</AdvancedOnly>
|
||||
</v-row>
|
||||
</section>
|
||||
<v-divider class="my-7" />
|
||||
<section v-if="$auth.user.canManage || $auth.user.canOrganize || $auth.user.advanced">
|
||||
<div>
|
||||
<h3 class="headline">{{ $t('group.group') }}</h3>
|
||||
<p>{{ $t('profile.group-description') }}</p>
|
||||
@ -122,46 +180,6 @@
|
||||
{{ $t('profile.group-settings-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-cookbooks'), to: `/g/${groupSlug}/cookbooks` }"
|
||||
:image="require('~/static/svgs/manage-cookbooks.svg')"
|
||||
>
|
||||
<template #title> {{ $t('sidebar.cookbooks') }} </template>
|
||||
{{ $t('profile.cookbooks-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<v-col v-if="user.canManage" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-members'), to: `/group/members` }"
|
||||
:image="require('~/static/svgs/manage-members.svg')"
|
||||
>
|
||||
<template #title> {{ $t('profile.members') }} </template>
|
||||
{{ $t('profile.members-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
<AdvancedOnly>
|
||||
<v-col v-if="user.advanced" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-webhooks'), to: `/group/webhooks` }"
|
||||
:image="require('~/static/svgs/manage-webhooks.svg')"
|
||||
>
|
||||
<template #title> {{ $t('settings.webhooks.webhooks') }} </template>
|
||||
{{ $t('profile.webhooks-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
</AdvancedOnly>
|
||||
<AdvancedOnly>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
:link="{ text: $tc('profile.manage-notifiers'), to: `/group/notifiers` }"
|
||||
:image="require('~/static/svgs/manage-notifiers.svg')"
|
||||
>
|
||||
<template #title> {{ $t('profile.notifiers') }} </template>
|
||||
{{ $t('profile.notifiers-description') }}
|
||||
</UserProfileLinkCard>
|
||||
</v-col>
|
||||
</AdvancedOnly>
|
||||
<!-- $auth.user.canOrganize should not be null because of the auth middleware -->
|
||||
<v-col v-if="$auth.user.canOrganize" cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
@ -224,7 +242,7 @@ export default defineComponent({
|
||||
const api = useUserApi();
|
||||
|
||||
async function getSignupLink() {
|
||||
const { data } = await api.groups.createInvitation({ uses: 1 });
|
||||
const { data } = await api.households.createInvitation({ uses: 1 });
|
||||
if (data) {
|
||||
token.value = data.token;
|
||||
generatedSignupLink.value = constructLink(data.token);
|
||||
@ -272,7 +290,7 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const stats = useAsync(async () => {
|
||||
const { data } = await api.groups.statistics();
|
||||
const { data } = await api.households.statistics();
|
||||
|
||||
if (data) {
|
||||
return data;
|
||||
@ -306,7 +324,7 @@ export default defineComponent({
|
||||
|
||||
const statsTo = computed<{ [key: string]: string }>(() => { return {
|
||||
totalRecipes: `/g/${groupSlug.value}/`,
|
||||
totalUsers: "/group/members",
|
||||
totalUsers: "/household/members",
|
||||
totalCategories: `/g/${groupSlug.value}/recipes/categories`,
|
||||
totalTags: `/g/${groupSlug.value}/recipes/tags`,
|
||||
totalTools: `/g/${groupSlug.value}/recipes/tools`,
|
||||
|
@ -68,7 +68,7 @@ async def get_public_group(group_slug: str = fastapi.Path(...), session=Depends(
|
||||
repos = get_repositories(session)
|
||||
group = repos.groups.get_by_slug_or_id(group_slug)
|
||||
|
||||
if not group or group.preferences.private_group or not group.preferences.recipe_public:
|
||||
if not group or group.preferences.private_group:
|
||||
raise HTTPException(404, "group not found")
|
||||
else:
|
||||
return group
|
||||
@ -111,7 +111,7 @@ async def get_current_user(
|
||||
except PyJWTError as e:
|
||||
raise credentials_exception from e
|
||||
|
||||
repos = get_repositories(session)
|
||||
repos = get_repositories(session, group_id=None, household_id=None)
|
||||
|
||||
user = repos.users.get_one(token_data.user_id, "id", any_case=False)
|
||||
|
||||
@ -139,7 +139,7 @@ async def get_admin_user(current_user: PrivateUser = Depends(get_current_user))
|
||||
|
||||
|
||||
def validate_long_live_token(session: Session, client_token: str, user_id: str) -> PrivateUser:
|
||||
repos = get_repositories(session)
|
||||
repos = get_repositories(session, group_id=None, household_id=None)
|
||||
|
||||
token = repos.api_tokens.multi_query({"token": client_token, "user_id": user_id})
|
||||
|
||||
|
@ -56,7 +56,7 @@ class AuthProvider(Generic[T], metaclass=abc.ABCMeta):
|
||||
if self.__has_tried_user:
|
||||
return self.user
|
||||
|
||||
db = get_repositories(self.session)
|
||||
db = get_repositories(self.session, group_id=None, household_id=None)
|
||||
|
||||
user = user = db.users.get_one(username, "username", any_case=True)
|
||||
if not user:
|
||||
|
@ -23,7 +23,7 @@ class CredentialsProvider(AuthProvider[CredentialsRequest]):
|
||||
async def authenticate(self) -> tuple[str, timedelta] | None:
|
||||
"""Attempt to authenticate a user given a username and password"""
|
||||
settings = get_app_settings()
|
||||
db = get_repositories(self.session)
|
||||
db = get_repositories(self.session, group_id=None, household_id=None)
|
||||
user = self.try_get_user(self.data.username)
|
||||
|
||||
if not user:
|
||||
|
@ -95,7 +95,7 @@ class LDAPProvider(CredentialsProvider):
|
||||
"""
|
||||
|
||||
settings = get_app_settings()
|
||||
db = get_repositories(self.session)
|
||||
db = get_repositories(self.session, group_id=None, household_id=None)
|
||||
if not self.data:
|
||||
return None
|
||||
data = self.data
|
||||
|
@ -33,7 +33,7 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
||||
if not claims:
|
||||
return None
|
||||
|
||||
repos = get_repositories(self.session)
|
||||
repos = get_repositories(self.session, group_id=None, household_id=None)
|
||||
|
||||
user = self.try_get_user(claims.get(settings.OIDC_USER_CLAIM))
|
||||
is_admin = False
|
||||
|
@ -179,6 +179,7 @@ class AppSettings(AppLoggingSettings):
|
||||
return self.DB_PROVIDER.db_url_public if self.DB_PROVIDER else None
|
||||
|
||||
DEFAULT_GROUP: str = "Home"
|
||||
DEFAULT_HOUSEHOLD: str = "Family"
|
||||
|
||||
_DEFAULT_EMAIL: str = "changeme@example.com"
|
||||
"""
|
||||
|
@ -26,7 +26,7 @@ SessionLocal, engine = sql_global_init(settings.DB_URL) # type: ignore
|
||||
|
||||
|
||||
@contextmanager
|
||||
def session_context() -> Session:
|
||||
def session_context() -> Generator[Session, None, None]:
|
||||
"""
|
||||
session_context() provides a managed session to the database that is automatically
|
||||
closed when the context is exited. This is the preferred method of accessing the
|
||||
|
@ -5,7 +5,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.db.models.group.group import Group
|
||||
from mealie.db.models.group.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel
|
||||
from mealie.db.models.household.shopping_list import ShoppingList, ShoppingListMultiPurposeLabel
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user