mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -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
|
import mealie.db.migration_types
|
||||||
from alembic import op
|
from alembic import op
|
||||||
from mealie.db.models.group.shopping_list import ShoppingList
|
from mealie.db.models._model_utils.guid import GUID
|
||||||
from mealie.db.models.labels import MultiPurposeLabel
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "b04a08da2108"
|
revision = "b04a08da2108"
|
||||||
@ -23,6 +22,25 @@ branch_labels = None
|
|||||||
depends_on = 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(
|
def populate_shopping_lists_multi_purpose_labels(
|
||||||
shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session
|
shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session
|
||||||
):
|
):
|
||||||
|
@ -12,11 +12,11 @@ from typing import Any
|
|||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from pydantic import UUID4
|
from pydantic import UUID4
|
||||||
|
from sqlalchemy import orm
|
||||||
from sqlalchemy.orm import Session, load_only
|
from sqlalchemy.orm import Session, load_only
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
from mealie.db.models._model_base import SqlAlchemyBase
|
from mealie.db.models._model_utils.guid import GUID
|
||||||
from mealie.db.models.group.shopping_list import ShoppingListItem
|
|
||||||
from mealie.db.models.labels import MultiPurposeLabel
|
from mealie.db.models.labels import MultiPurposeLabel
|
||||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel
|
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel
|
||||||
|
|
||||||
@ -27,6 +27,27 @@ branch_labels = None
|
|||||||
depends_on = 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
|
@dataclass
|
||||||
class TableMeta:
|
class TableMeta:
|
||||||
tablename: str
|
tablename: str
|
||||||
@ -42,7 +63,7 @@ def _is_postgres():
|
|||||||
return op.get_context().dialect.name == "postgresql"
|
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)
|
duplicate_map: defaultdict[str, list] = defaultdict(list)
|
||||||
|
|
||||||
query = session.execute(sa.text(f"SELECT id, group_id, name FROM {model.__tablename__}"))
|
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.
|
kabab case.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ignore_files = ["DS_Store", ".gitkeep"]
|
ignore_files = ["DS_Store", ".gitkeep", "af-ZA.json", "en-US.json"]
|
||||||
|
|
||||||
ignore_extensions = [".pyc", ".pyo", ".py"]
|
ignore_extensions = [".pyc", ".pyo", ".py"]
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -23,6 +24,11 @@ def render_python_template(template_file: Path | str, dest: Path, data: dict):
|
|||||||
|
|
||||||
dest.write_text(text)
|
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
|
@dataclass
|
||||||
class CodeSlicer:
|
class CodeSlicer:
|
||||||
|
@ -173,7 +173,7 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic
|
|||||||
"dateAdded": "2022-09-03",
|
"dateAdded": "2022-09-03",
|
||||||
"dateUpdated": "2022-09-10T15:18:19.866085",
|
"dateUpdated": "2022-09-10T15:18:19.866085",
|
||||||
"createdAt": "2022-09-03T18:31:17.488118",
|
"createdAt": "2022-09-03T18:31:17.488118",
|
||||||
"updateAt": "2022-09-10T15:18:19.869630",
|
"updatedAt": "2022-09-10T15:18:19.869630",
|
||||||
"recipeInstructions": [
|
"recipeInstructions": [
|
||||||
{
|
{
|
||||||
"id": "60ae53a3-b3ff-40ee-bae3-89fea0b1c637",
|
"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
|
```yaml
|
||||||
- platform: rest
|
- platform: rest
|
||||||
resource: "http://mealie:9000/api/groups/mealplans/today"
|
resource: "http://mealie:9000/api/households/mealplans/today"
|
||||||
method: GET
|
method: GET
|
||||||
name: Mealie todays meal
|
name: Mealie todays meal
|
||||||
headers:
|
headers:
|
||||||
@ -36,7 +36,7 @@ Make sure the url and port (`http://mealie:9000` ) matches your installation's a
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- platform: rest
|
- platform: rest
|
||||||
resource: "http://mealie:9000/api/groups/mealplans/today"
|
resource: "http://mealie:9000/api/households/mealplans/today"
|
||||||
method: GET
|
method: GET
|
||||||
name: Mealie todays meal ID
|
name: Mealie todays meal ID
|
||||||
headers:
|
headers:
|
||||||
|
@ -73,13 +73,13 @@ Mealie uses a calendar like view to help you plan your meals. It shows you the p
|
|||||||
!!! tip
|
!!! 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.
|
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
|
### 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.**
|
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
|
## Shopping Lists
|
||||||
|
|
||||||
@ -105,13 +105,13 @@ Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), whi
|
|||||||
- `json` and `jsons`
|
- `json` and `jsons`
|
||||||
- `xml` and `xmls`
|
- `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
|
### 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.
|
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
|
### Recipe Actions
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -2,30 +2,11 @@
|
|||||||
<div v-if="preferences">
|
<div v-if="preferences">
|
||||||
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
|
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
|
||||||
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" :label="$t('group.private-group')"></v-checkbox>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, computed } from "@nuxtjs/composition-api";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -35,48 +16,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
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({
|
const preferences = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.value;
|
return props.value;
|
||||||
@ -87,8 +26,6 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
allDays,
|
|
||||||
labels,
|
|
||||||
preferences,
|
preferences,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
|
||||||
import { Recipe } from "~/lib/api/types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
|
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";
|
import { useUserApi } from "~/composables/api";
|
||||||
|
|
||||||
export interface ContextMenuItem {
|
export interface ContextMenuItem {
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
|
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";
|
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
|
||||||
|
|
||||||
export default defineComponent({
|
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;
|
break;
|
||||||
case EVENTS.updated:
|
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;
|
break;
|
||||||
case EVENTS.lastMade:
|
case EVENTS.lastMade:
|
||||||
setter(
|
setter(
|
||||||
|
@ -138,11 +138,11 @@ import RecipeDialogShare from "./RecipeDialogShare.vue";
|
|||||||
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
import { useLoggedInState } from "~/composables/use-logged-in-state";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useGroupRecipeActions } from "~/composables/use-group-recipe-actions";
|
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 { alert } from "~/composables/use-toast";
|
||||||
import { usePlanTypeOptions } from "~/composables/use-group-mealplan";
|
import { usePlanTypeOptions } from "~/composables/use-group-mealplan";
|
||||||
import { Recipe } from "~/lib/api/types/recipe";
|
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 { PlanEntryType } from "~/lib/api/types/meal-plan";
|
||||||
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
import { useAxiosDownloader } from "~/composables/api/use-axios-download";
|
||||||
|
|
||||||
@ -254,14 +254,14 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { i18n, $auth, $globals } = useContext();
|
const { i18n, $auth, $globals } = useContext();
|
||||||
const { group } = useGroupSelf();
|
const { household } = useHouseholdSelf();
|
||||||
const { isOwnGroup } = useLoggedInState();
|
const { isOwnGroup } = useLoggedInState();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||||
|
|
||||||
const firstDayOfWeek = computed(() => {
|
const firstDayOfWeek = computed(() => {
|
||||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<template #item.name="{ item }">
|
<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>
|
||||||
<template #item.tags="{ item }">
|
<template #item.tags="{ item }">
|
||||||
<RecipeChip small :items="item.tags" :is-category="false" url-prefix="tags" />
|
<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 RecipeChip from "./RecipeChips.vue";
|
||||||
import { Recipe } from "~/lib/api/types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { UserOut } from "~/lib/api/types/user";
|
import { UserSummary } from "~/lib/api/types/user";
|
||||||
|
|
||||||
const INPUT_EVENT = "input";
|
const INPUT_EVENT = "input";
|
||||||
|
|
||||||
@ -95,7 +95,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const { i18n } = useContext();
|
const { $auth, i18n } = useContext();
|
||||||
|
const groupSlug = $auth.user?.groupSlug;
|
||||||
|
|
||||||
function setValue(value: Recipe[]) {
|
function setValue(value: Recipe[]) {
|
||||||
context.emit(INPUT_EVENT, value);
|
context.emit(INPUT_EVENT, value);
|
||||||
@ -134,7 +135,7 @@ export default defineComponent({
|
|||||||
// ============
|
// ============
|
||||||
// Group Members
|
// Group Members
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
const members = ref<UserOut[]>([]);
|
const members = ref<UserSummary[]>([]);
|
||||||
|
|
||||||
async function refreshMembers() {
|
async function refreshMembers() {
|
||||||
const { data } = await api.groups.fetchMembers();
|
const { data } = await api.groups.fetchMembers();
|
||||||
@ -149,13 +150,19 @@ export default defineComponent({
|
|||||||
|
|
||||||
function getMember(id: string) {
|
function getMember(id: string) {
|
||||||
if (members.value[0]) {
|
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 i18n.t("general.none");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { setValue, headers, members, getMember };
|
return {
|
||||||
|
groupSlug,
|
||||||
|
setValue,
|
||||||
|
headers,
|
||||||
|
members,
|
||||||
|
getMember,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -127,14 +127,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api"
|
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api";
|
||||||
import { toRefs } from "@vueuse/core"
|
import { toRefs } from "@vueuse/core";
|
||||||
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue"
|
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
|
||||||
import { useUserApi } from "~/composables/api"
|
import { useUserApi } from "~/composables/api";
|
||||||
import { alert } from "~/composables/use-toast"
|
import { alert } from "~/composables/use-toast";
|
||||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences"
|
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
||||||
import { ShoppingListSummary } from "~/lib/api/types/group"
|
import { ShoppingListSummary } from "~/lib/api/types/household";
|
||||||
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe"
|
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export interface RecipeWithScale extends Recipe {
|
export interface RecipeWithScale extends Recipe {
|
||||||
scale: number;
|
scale: number;
|
||||||
@ -209,7 +209,8 @@ export default defineComponent({
|
|||||||
watchEffect(
|
watchEffect(
|
||||||
() => {
|
() => {
|
||||||
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
|
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
|
||||||
openShoppingListIngredientDialog(shoppingListChoices.value[0]);
|
selectedShoppingList.value = shoppingListChoices.value[0];
|
||||||
|
openShoppingListIngredientDialog(selectedShoppingList.value);
|
||||||
} else {
|
} else {
|
||||||
ready.value = true;
|
ready.value = true;
|
||||||
}
|
}
|
||||||
@ -365,12 +366,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const successMessage = promises.length === 1
|
success ? alert.success(i18n.tc("recipe.successfully-added-to-list"))
|
||||||
? i18n.t("recipe.successfully-added-to-list") as string
|
: alert.error(i18n.tc("failed-to-add-recipes-to-list"))
|
||||||
: 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)
|
|
||||||
|
|
||||||
state.shoppingListDialog = false;
|
state.shoppingListDialog = false;
|
||||||
state.shoppingListIngredientDialog = false;
|
state.shoppingListIngredientDialog = false;
|
||||||
|
@ -66,7 +66,7 @@ import { defineComponent, computed, toRefs, reactive, useContext, useRoute } fro
|
|||||||
import { useClipboard, useShare, whenever } from "@vueuse/core";
|
import { useClipboard, useShare, whenever } from "@vueuse/core";
|
||||||
import { RecipeShareToken } from "~/lib/api/types/recipe";
|
import { RecipeShareToken } from "~/lib/api/types/recipe";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useGroupSelf } from "~/composables/use-groups";
|
import { useHouseholdSelf } from "~/composables/use-households";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -113,12 +113,12 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { $auth, i18n } = useContext();
|
const { $auth, i18n } = useContext();
|
||||||
const { group } = useGroupSelf();
|
const { household } = useHouseholdSelf();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
|
||||||
|
|
||||||
const firstDayOfWeek = computed(() => {
|
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,
|
icon: $globals.icons.update,
|
||||||
name: i18n.tc("general.updated"),
|
name: i18n.tc("general.updated"),
|
||||||
value: "update_at",
|
value: "updated_at",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: $globals.icons.diceMultiple,
|
icon: $globals.icons.diceMultiple,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
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";
|
import { useParsedIngredientText } from "~/composables/recipes";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -114,7 +114,7 @@ import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@n
|
|||||||
import { whenever } from "@vueuse/core";
|
import { whenever } from "@vueuse/core";
|
||||||
import { VForm } from "~/types/vuetify";
|
import { VForm } from "~/types/vuetify";
|
||||||
import { useUserApi } from "~/composables/api";
|
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";
|
import { Recipe, RecipeTimelineEventIn } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -131,7 +131,7 @@ export default defineComponent({
|
|||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const madeThisDialog = ref(false);
|
const madeThisDialog = ref(false);
|
||||||
const userApi = useUserApi();
|
const userApi = useUserApi();
|
||||||
const { group } = useGroupSelf();
|
const { household } = useHouseholdSelf();
|
||||||
const { $auth, i18n } = useContext();
|
const { $auth, i18n } = useContext();
|
||||||
const domMadeThisForm = ref<VForm>();
|
const domMadeThisForm = ref<VForm>();
|
||||||
const newTimelineEvent = ref<RecipeTimelineEventIn>({
|
const newTimelineEvent = ref<RecipeTimelineEventIn>({
|
||||||
@ -157,7 +157,7 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const firstDayOfWeek = computed(() => {
|
const firstDayOfWeek = computed(() => {
|
||||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
function clearImage() {
|
function clearImage() {
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
import { computed, defineComponent, useContext, useRoute } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useContext, useRoute } from "@nuxtjs/composition-api";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import { useFraction } from "~/composables/recipes/use-fraction";
|
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";
|
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/group";
|
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/household";
|
||||||
|
|
||||||
interface actions {
|
interface actions {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
<v-row v-if="listItem.checked" no-gutters class="mb-2">
|
<v-row v-if="listItem.checked" no-gutters class="mb-2">
|
||||||
<v-col cols="auto">
|
<v-col cols="auto">
|
||||||
<div class="text-caption font-weight-light font-italic">
|
<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>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@ -99,7 +99,7 @@ import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-
|
|||||||
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
|
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
|
||||||
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
||||||
import MultiPurposeLabel from "./MultiPurposeLabel.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 { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
|
||||||
import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
|
import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
|
||||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, watch } from "@nuxtjs/composition-api";
|
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 { MultiPurposeLabelOut } from "~/lib/api/types/labels";
|
||||||
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||||
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
|
||||||
|
@ -183,7 +183,7 @@ export default defineComponent({
|
|||||||
{
|
{
|
||||||
icon: $globals.icons.calendarMultiselect,
|
icon: $globals.icons.calendarMultiselect,
|
||||||
title: i18n.tc("meal-plan.meal-planner"),
|
title: i18n.tc("meal-plan.meal-planner"),
|
||||||
to: "/group/mealplan/planner/view",
|
to: "/household/mealplan/planner/view",
|
||||||
restricted: true,
|
restricted: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-toolbar flat>
|
<v-toolbar color="transparent" flat>
|
||||||
<BaseButton color="null" rounded secondary @click="$router.go(-1)">
|
<BaseButton color="null" rounded secondary @click="$router.go(-1)">
|
||||||
<template #icon> {{ $globals.icons.arrowLeftBold }}</template>
|
<template #icon> {{ $globals.icons.arrowLeftBold }}</template>
|
||||||
{{ $t('general.back') }}
|
{{ $t('general.back') }}
|
||||||
|
@ -85,6 +85,8 @@
|
|||||||
:label="inputField.label"
|
:label="inputField.label"
|
||||||
:name="inputField.varName"
|
:name="inputField.varName"
|
||||||
:items="inputField.options"
|
:items="inputField.options"
|
||||||
|
:item-text="inputField.itemText"
|
||||||
|
:item-value="inputField.itemValue"
|
||||||
:return-object="false"
|
:return-object="false"
|
||||||
:hint="inputField.hint"
|
:hint="inputField.hint"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
|
@ -4,7 +4,7 @@ import { BaseCRUDAPI, BaseCRUDAPIReadOnly } from "~/lib/api/base/base-clients";
|
|||||||
import { QueryValue } from "~/lib/api/base/route";
|
import { QueryValue } from "~/lib/api/base/route";
|
||||||
|
|
||||||
type BoundT = {
|
type BoundT = {
|
||||||
id?: string | number;
|
id?: string | number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface PublicStoreActions<T extends BoundT> {
|
interface PublicStoreActions<T extends BoundT> {
|
||||||
|
@ -160,6 +160,9 @@ export function usePageUser(): { user: UserOut } {
|
|||||||
group: "",
|
group: "",
|
||||||
groupId: "",
|
groupId: "",
|
||||||
groupSlug: "",
|
groupSlug: "",
|
||||||
|
household: "",
|
||||||
|
householdId: "",
|
||||||
|
householdSlug: "",
|
||||||
cacheKey: "",
|
cacheKey: "",
|
||||||
email: "",
|
email: "",
|
||||||
},
|
},
|
||||||
|
@ -46,7 +46,7 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { quantity, food, unit, note } = ingredient;
|
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
|
const usePluralFood = (!quantity) || quantity * scale > 1
|
||||||
|
|
||||||
let returnQty = "";
|
let returnQty = "";
|
||||||
@ -69,8 +69,8 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unitName = useUnitName(unit, usePluralUnit);
|
const unitName = useUnitName(unit || undefined, usePluralUnit);
|
||||||
const foodName = useFoodName(food, usePluralFood);
|
const foodName = useFoodName(food || undefined, usePluralFood);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
quantity: returnQty ? sanitizeIngredientHTML(returnQty) : undefined,
|
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 { usePublicStoreActions, useStoreActions } from "../partials/use-actions-factory";
|
||||||
import { usePublicExploreApi } from "../api/api-client";
|
import { usePublicExploreApi } from "../api/api-client";
|
||||||
import { useUserApi } from "~/composables/api";
|
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 items: Ref<RecipeTag[]> = ref([]);
|
||||||
const publicStoreLoading = ref(false);
|
const publicStoreLoading = ref(false);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useAsync, ref, Ref, useContext } from "@nuxtjs/composition-api";
|
import { useAsync, ref, Ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { usePublicExploreApi } from "./api/api-client";
|
import { usePublicExploreApi } from "./api/api-client";
|
||||||
|
import { useHouseholdSelf } from "./use-households";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
|
import { ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ export const usePublicCookbooks = function (groupSlug: string) {
|
|||||||
|
|
||||||
export const useCookbooks = function () {
|
export const useCookbooks = function () {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
const { household } = useHouseholdSelf();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const { i18n } = useContext();
|
const { i18n } = useContext();
|
||||||
@ -100,7 +102,7 @@ export const useCookbooks = function () {
|
|||||||
async createOne() {
|
async createOne() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { data } = await api.cookbooks.createOne({
|
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) {
|
if (data && cookbookStore?.value) {
|
||||||
cookbookStore.value.push(data);
|
cookbookStore.value.push(data);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { computed, reactive, ref } from "@nuxtjs/composition-api";
|
import { computed, reactive, ref } from "@nuxtjs/composition-api";
|
||||||
import { useStoreActions } from "./partials/use-actions-factory";
|
import { useStoreActions } from "./partials/use-actions-factory";
|
||||||
import { useUserApi } from "~/composables/api";
|
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";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
|
|
||||||
const groupRecipeActions = ref<GroupRecipeActionOut[] | null>(null);
|
const groupRecipeActions = ref<GroupRecipeActionOut[] | null>(null);
|
||||||
@ -10,7 +10,7 @@ const loading = ref(false);
|
|||||||
export function useGroupRecipeActionData() {
|
export function useGroupRecipeActionData() {
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
actionType: "link" as RecipeActionType,
|
actionType: "link" as GroupRecipeActionType,
|
||||||
title: "",
|
title: "",
|
||||||
url: "",
|
url: "",
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||||
import { useAsyncKey } from "./use-utils";
|
import { useAsyncKey } from "./use-utils";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { ReadWebhook } from "~/lib/api/types/group";
|
import { ReadWebhook } from "~/lib/api/types/household";
|
||||||
|
|
||||||
export const useGroupWebhooks = function () {
|
export const useGroupWebhooks = function () {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
@ -51,7 +51,7 @@ export const useGroups = function () {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
const asyncKey = String(Date.now());
|
const asyncKey = String(Date.now());
|
||||||
const groups = useAsync(async () => {
|
const groups = useAsync(async () => {
|
||||||
const { data } = await api.groups.getAll();
|
const { data } = await api.groups.getAll(1, -1, {orderBy: "name", orderDirection: "asc"});;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
return data.items;
|
return data.items;
|
||||||
@ -66,7 +66,7 @@ export const useGroups = function () {
|
|||||||
|
|
||||||
async function refreshAllGroups() {
|
async function refreshAllGroups() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { data } = await api.groups.getAll();
|
const { data } = await api.groups.getAll(1, -1, {orderBy: "name", orderDirection: "asc"});;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
groups.value = data.items;
|
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 { computed, reactive, watch } from "@nuxtjs/composition-api";
|
||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { useUserApi } from "~/composables/api";
|
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";
|
import { RequestResponse } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
const localStorageKey = "shopping-list-queue";
|
const localStorageKey = "shopping-list-queue";
|
||||||
@ -144,7 +144,7 @@ export function useShoppingListItemActions(shoppingListId: string) {
|
|||||||
|
|
||||||
function checkUpdateState(list: ShoppingListOut) {
|
function checkUpdateState(list: ShoppingListOut) {
|
||||||
const cutoffDate = new Date(queue.lastUpdate + queueTimeout).toISOString();
|
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
|
// 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");
|
console.log("Out of sync with server; clearing queue");
|
||||||
clearQueueItems("all");
|
clearQueueItems("all");
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"database-type": "Database Type",
|
"database-type": "Database Type",
|
||||||
"database-url": "Database URL",
|
"database-url": "Database URL",
|
||||||
"default-group": "Default Group",
|
"default-group": "Default Group",
|
||||||
|
"default-household": "Default Household",
|
||||||
"demo": "Demo",
|
"demo": "Demo",
|
||||||
"demo-status": "Demo Status",
|
"demo-status": "Demo Status",
|
||||||
"development": "Development",
|
"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."
|
"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": "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",
|
"manage": "Manage",
|
||||||
"invite": "Invite",
|
"invite": "Invite",
|
||||||
"looking-to-update-your-profile": "Looking to Update Your Profile?",
|
"looking-to-update-your-profile": "Looking to Update Your Profile?",
|
||||||
@ -246,7 +247,7 @@
|
|||||||
"default-recipe-preferences": "Default Recipe Preferences",
|
"default-recipe-preferences": "Default Recipe Preferences",
|
||||||
"group-preferences": "Group Preferences",
|
"group-preferences": "Group Preferences",
|
||||||
"private-group": "Private Group",
|
"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": "Enable Public Access",
|
||||||
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
|
"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",
|
"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": "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-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": "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",
|
"general-preferences": "General Preferences",
|
||||||
"group-recipe-preferences": "Group Recipe Preferences",
|
"group-recipe-preferences": "Group Recipe Preferences",
|
||||||
"report": "Report",
|
"report": "Report",
|
||||||
@ -268,7 +269,28 @@
|
|||||||
"group-management": "Group Management",
|
"group-management": "Group Management",
|
||||||
"admin-group-management": "Admin Group Management",
|
"admin-group-management": "Admin Group Management",
|
||||||
"admin-group-management-text": "Changes to this group will be reflected immediately.",
|
"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": {
|
"meal-plan": {
|
||||||
"create-a-new-meal-plan": "Create a New 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.",
|
"account-summary-description": "Here's a summary of your group's information.",
|
||||||
"group-statistics": "Group Statistics",
|
"group-statistics": "Group Statistics",
|
||||||
"group-statistics-description": "Your Group Statistics provide some insight how you're using Mealie.",
|
"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": "Storage Capacity",
|
||||||
"storage-capacity-description": "Your storage capacity is a calculation of the images and assets you have uploaded.",
|
"storage-capacity-description": "Your storage capacity is a calculation of the images and assets you have uploaded.",
|
||||||
"personal": "Personal",
|
"personal": "Personal",
|
||||||
@ -1239,10 +1263,13 @@
|
|||||||
"api-tokens-description": "Manage your API Tokens for access from external applications.",
|
"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-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": "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.",
|
"cookbooks-description": "Manage a collection of recipe categories and generate pages for them.",
|
||||||
"members": "Members",
|
"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.",
|
"webhooks-description": "Set up webhooks that trigger on days that you have mealplans scheduled.",
|
||||||
"notifiers": "Notifiers",
|
"notifiers": "Notifiers",
|
||||||
"notifiers-description": "Set up email and push notifications that trigger on specific events.",
|
"notifiers-description": "Set up email and push notifications that trigger on specific events.",
|
||||||
@ -1277,6 +1304,7 @@
|
|||||||
"require-all-tools": "Require All Tools",
|
"require-all-tools": "Require All Tools",
|
||||||
"cookbook-name": "Cookbook Name",
|
"cookbook-name": "Cookbook Name",
|
||||||
"cookbook-with-name": "Cookbook {0}",
|
"cookbook-with-name": "Cookbook {0}",
|
||||||
|
"household-cookbook-name": "{0} Cookbook {1}",
|
||||||
"create-a-cookbook": "Create a Cookbook",
|
"create-a-cookbook": "Create a Cookbook",
|
||||||
"cookbook": "Cookbook"
|
"cookbook": "Cookbook"
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,12 @@ export default defineComponent({
|
|||||||
title: i18n.tc("user.users"),
|
title: i18n.tc("user.users"),
|
||||||
restricted: true,
|
restricted: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: $globals.icons.household,
|
||||||
|
to: "/admin/manage/households",
|
||||||
|
title: i18n.tc("household.households"),
|
||||||
|
restricted: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: $globals.icons.group,
|
icon: $globals.icons.group,
|
||||||
to: "/admin/manage/groups",
|
to: "/admin/manage/groups",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app dark>
|
<v-app v-if="ready" dark>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<slot>
|
<slot>
|
||||||
<h1 class="mx-auto">{{ $t("page.404-page-not-found") }}</h1>
|
<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) {
|
if (props.error.statusCode === 404) {
|
||||||
// see if adding the groupSlug fixes the error
|
handle404();
|
||||||
insertGroupSlugIntoRoute().then(() => { ready.value = true });
|
|
||||||
} else {
|
} else {
|
||||||
ready.value = true;
|
ready.value = true;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RecipeAPI } from "./user/recipes";
|
import { RecipeAPI } from "./user/recipes";
|
||||||
import { UserApi } from "./user/users";
|
import { UserApi } from "./user/users";
|
||||||
|
import { HouseholdAPI } from "./user/households";
|
||||||
import { GroupAPI } from "./user/groups";
|
import { GroupAPI } from "./user/groups";
|
||||||
import { BackupAPI } from "./user/backups";
|
import { BackupAPI } from "./user/backups";
|
||||||
import { UploadFile } from "./user/upload";
|
import { UploadFile } from "./user/upload";
|
||||||
@ -28,6 +29,7 @@ import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
|||||||
export class UserApiClient {
|
export class UserApiClient {
|
||||||
public recipes: RecipeAPI;
|
public recipes: RecipeAPI;
|
||||||
public users: UserApi;
|
public users: UserApi;
|
||||||
|
public households: HouseholdAPI;
|
||||||
public groups: GroupAPI;
|
public groups: GroupAPI;
|
||||||
public backups: BackupAPI;
|
public backups: BackupAPI;
|
||||||
public categories: CategoriesAPI;
|
public categories: CategoriesAPI;
|
||||||
@ -63,6 +65,7 @@ export class UserApiClient {
|
|||||||
|
|
||||||
// Users
|
// Users
|
||||||
this.users = new UserApi(requests);
|
this.users = new UserApi(requests);
|
||||||
|
this.households = new HouseholdAPI(requests);
|
||||||
this.groups = new GroupAPI(requests);
|
this.groups = new GroupAPI(requests);
|
||||||
this.cookbooks = new CookbookAPI(requests);
|
this.cookbooks = new CookbookAPI(requests);
|
||||||
this.groupRecipeActions = new GroupRecipeActionsAPI(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";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
cookbooksGroupSlug: (groupSlug: string | number) => `${prefix}/explore/cookbooks/${groupSlug}`,
|
cookbooksGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/cookbooks`,
|
||||||
cookbooksGroupSlugCookbookId: (groupSlug: string | number, cookbookId: string | number) => `${prefix}/explore/cookbooks/${groupSlug}/${cookbookId}`,
|
cookbooksGroupSlugCookbookId: (groupSlug: string | number, cookbookId: string | number) => `${exploreGroupSlug(groupSlug)}/cookbooks/${cookbookId}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PublicCookbooksApi extends BaseCRUDAPIReadOnly<RecipeCookBook> {
|
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";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
foodsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/foods/${groupSlug}`,
|
foodsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/foods`,
|
||||||
foodsGroupSlugFoodId: (groupSlug: string | number, foodId: string | number) => `${prefix}/explore/foods/${groupSlug}/${foodId}`,
|
foodsGroupSlugFoodId: (groupSlug: string | number, foodId: string | number) => `${exploreGroupSlug(groupSlug)}/foods/${foodId}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PublicFoodsApi extends BaseCRUDAPIReadOnly<IngredientFood> {
|
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";
|
import { ApiRequestInstance } from "~/lib/api/types/non-generated";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
categoriesGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/categories`,
|
categoriesGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/categories`,
|
||||||
categoriesGroupSlugCategoryId: (groupSlug: string | number, categoryId: string | number) => `${prefix}/explore/organizers/${groupSlug}/categories/${categoryId}`,
|
categoriesGroupSlugCategoryId: (groupSlug: string | number, categoryId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/categories/${categoryId}`,
|
||||||
tagsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/tags`,
|
tagsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tags`,
|
||||||
tagsGroupSlugTagId: (groupSlug: string | number, tagId: string | number) => `${prefix}/explore/organizers/${groupSlug}/tags/${tagId}`,
|
tagsGroupSlugTagId: (groupSlug: string | number, tagId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tags/${tagId}`,
|
||||||
toolsGroupSlug: (groupSlug: string | number) => `${prefix}/explore/organizers/${groupSlug}/tools`,
|
toolsGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tools`,
|
||||||
toolsGroupSlugToolId: (groupSlug: string | number, toolId: string | number) => `${prefix}/explore/organizers/${groupSlug}/tools/${toolId}`,
|
toolsGroupSlugToolId: (groupSlug: string | number, toolId: string | number) => `${exploreGroupSlug(groupSlug)}/organizers/tools`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PublicCategoriesApi extends BaseCRUDAPIReadOnly<RecipeCategory> {
|
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";
|
import { RecipeSearchQuery } from "../../user/recipes/recipe";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
const exploreGroupSlug = (groupSlug: string | number) => `${prefix}/explore/groups/${groupSlug}`
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
recipesGroupSlug: (groupSlug: string | number) => `${prefix}/explore/recipes/${groupSlug}`,
|
recipesGroupSlug: (groupSlug: string | number) => `${exploreGroupSlug(groupSlug)}/recipes`,
|
||||||
recipesGroupSlugRecipeSlug: (groupSlug: string | number, recipeSlug: string | number) => `${prefix}/explore/recipes/${groupSlug}/${recipeSlug}`,
|
recipesGroupSlugRecipeSlug: (groupSlug: string | number, recipeSlug: string | number) => `${exploreGroupSlug(groupSlug)}/recipes/${recipeSlug}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PublicRecipeApi extends BaseCRUDAPIReadOnly<Recipe> {
|
export class PublicRecipeApi extends BaseCRUDAPIReadOnly<Recipe> {
|
||||||
|
@ -10,6 +10,8 @@ export interface AdminAboutInfo {
|
|||||||
version: string;
|
version: string;
|
||||||
demoStatus: boolean;
|
demoStatus: boolean;
|
||||||
allowSignup: boolean;
|
allowSignup: boolean;
|
||||||
|
defaultGroupSlug?: string | null;
|
||||||
|
defaultHouseholdSlug?: string | null;
|
||||||
enableOidc: boolean;
|
enableOidc: boolean;
|
||||||
oidcRedirect: boolean;
|
oidcRedirect: boolean;
|
||||||
oidcProviderName: string;
|
oidcProviderName: string;
|
||||||
@ -18,8 +20,9 @@ export interface AdminAboutInfo {
|
|||||||
apiPort: number;
|
apiPort: number;
|
||||||
apiDocs: boolean;
|
apiDocs: boolean;
|
||||||
dbType: string;
|
dbType: string;
|
||||||
dbUrl?: string;
|
dbUrl?: string | null;
|
||||||
defaultGroup: string;
|
defaultGroup: string;
|
||||||
|
defaultHousehold: string;
|
||||||
buildId: string;
|
buildId: string;
|
||||||
recipeScraperVersion: string;
|
recipeScraperVersion: string;
|
||||||
}
|
}
|
||||||
@ -37,7 +40,8 @@ export interface AppInfo {
|
|||||||
version: string;
|
version: string;
|
||||||
demoStatus: boolean;
|
demoStatus: boolean;
|
||||||
allowSignup: boolean;
|
allowSignup: boolean;
|
||||||
defaultGroupSlug?: string;
|
defaultGroupSlug?: string | null;
|
||||||
|
defaultHouseholdSlug?: string | null;
|
||||||
enableOidc: boolean;
|
enableOidc: boolean;
|
||||||
oidcRedirect: boolean;
|
oidcRedirect: boolean;
|
||||||
oidcProviderName: string;
|
oidcProviderName: string;
|
||||||
@ -51,6 +55,7 @@ export interface AppStartupInfo {
|
|||||||
export interface AppStatistics {
|
export interface AppStatistics {
|
||||||
totalRecipes: number;
|
totalRecipes: number;
|
||||||
totalUsers: number;
|
totalUsers: number;
|
||||||
|
totalHouseholds: number;
|
||||||
totalGroups: number;
|
totalGroups: number;
|
||||||
uncategorizedRecipes: number;
|
uncategorizedRecipes: number;
|
||||||
untaggedRecipes: number;
|
untaggedRecipes: number;
|
||||||
@ -93,16 +98,16 @@ export interface ChowdownURL {
|
|||||||
export interface CommentImport {
|
export interface CommentImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface CreateBackup {
|
export interface CreateBackup {
|
||||||
tag?: string;
|
tag?: string | null;
|
||||||
options: BackupOptions;
|
options: BackupOptions;
|
||||||
templates?: string[];
|
templates?: string[] | null;
|
||||||
}
|
}
|
||||||
export interface CustomPageBase {
|
export interface CustomPageBase {
|
||||||
name: string;
|
name: string;
|
||||||
slug?: string;
|
slug: string | null;
|
||||||
position: number;
|
position: number;
|
||||||
categories?: RecipeCategoryResponse[];
|
categories?: RecipeCategoryResponse[];
|
||||||
}
|
}
|
||||||
@ -113,38 +118,41 @@ export interface RecipeCategoryResponse {
|
|||||||
recipes?: RecipeSummary[];
|
recipes?: RecipeSummary[];
|
||||||
}
|
}
|
||||||
export interface RecipeSummary {
|
export interface RecipeSummary {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
householdId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string | null;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
image?: unknown;
|
image?: unknown;
|
||||||
recipeYield?: string;
|
recipeYield?: string | null;
|
||||||
totalTime?: string;
|
totalTime?: string | null;
|
||||||
prepTime?: string;
|
prepTime?: string | null;
|
||||||
cookTime?: string;
|
cookTime?: string | null;
|
||||||
performTime?: string;
|
performTime?: string | null;
|
||||||
description?: string;
|
description?: string | null;
|
||||||
recipeCategory?: RecipeCategory[];
|
recipeCategory?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
tools?: RecipeTool[];
|
tools?: RecipeTool[];
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
orgURL?: string;
|
orgURL?: string | null;
|
||||||
dateAdded?: string;
|
dateAdded?: string | null;
|
||||||
dateUpdated?: string;
|
dateUpdated?: string | null;
|
||||||
createdAt?: string;
|
createdAt?: string | null;
|
||||||
updateAt?: string;
|
updatedAt?: string | null;
|
||||||
lastMade?: string;
|
lastMade?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCategory {
|
export interface RecipeCategory {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface RecipeTag {
|
export interface RecipeTag {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface RecipeTool {
|
export interface RecipeTool {
|
||||||
id: string;
|
id: string;
|
||||||
@ -155,11 +163,11 @@ export interface RecipeTool {
|
|||||||
export interface CustomPageImport {
|
export interface CustomPageImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface CustomPageOut {
|
export interface CustomPageOut {
|
||||||
name: string;
|
name: string;
|
||||||
slug?: string;
|
slug: string | null;
|
||||||
position: number;
|
position: number;
|
||||||
categories?: RecipeCategoryResponse[];
|
categories?: RecipeCategoryResponse[];
|
||||||
id: number;
|
id: number;
|
||||||
@ -169,7 +177,7 @@ export interface EmailReady {
|
|||||||
}
|
}
|
||||||
export interface EmailSuccess {
|
export interface EmailSuccess {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error?: string;
|
error?: string | null;
|
||||||
}
|
}
|
||||||
export interface EmailTest {
|
export interface EmailTest {
|
||||||
email: string;
|
email: string;
|
||||||
@ -177,12 +185,12 @@ export interface EmailTest {
|
|||||||
export interface GroupImport {
|
export interface GroupImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface ImportBase {
|
export interface ImportBase {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface ImportJob {
|
export interface ImportJob {
|
||||||
recipes?: boolean;
|
recipes?: boolean;
|
||||||
@ -217,8 +225,8 @@ export interface MigrationFile {
|
|||||||
export interface MigrationImport {
|
export interface MigrationImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
}
|
}
|
||||||
export interface Migrations {
|
export interface Migrations {
|
||||||
type: string;
|
type: string;
|
||||||
@ -227,25 +235,26 @@ export interface Migrations {
|
|||||||
export interface NotificationImport {
|
export interface NotificationImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface OIDCInfo {
|
export interface OIDCInfo {
|
||||||
configurationUrl?: string;
|
configurationUrl: string | null;
|
||||||
clientId?: string;
|
clientId: string | null;
|
||||||
|
groupsClaim: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeImport {
|
export interface RecipeImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
}
|
}
|
||||||
export interface SettingsImport {
|
export interface SettingsImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface UserImport {
|
export interface UserImport {
|
||||||
name: string;
|
name: string;
|
||||||
status: boolean;
|
status: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
export interface CreateCookBook {
|
export interface CreateCookBook {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
position?: number;
|
position?: number;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[];
|
||||||
@ -37,7 +37,7 @@ export interface RecipeTool {
|
|||||||
export interface ReadCookBook {
|
export interface ReadCookBook {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
position?: number;
|
position?: number;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[];
|
||||||
@ -47,12 +47,13 @@ export interface ReadCookBook {
|
|||||||
requireAllTags?: boolean;
|
requireAllTags?: boolean;
|
||||||
requireAllTools?: boolean;
|
requireAllTools?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
export interface RecipeCookBook {
|
export interface RecipeCookBook {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
position?: number;
|
position?: number;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[];
|
||||||
@ -62,47 +63,51 @@ export interface RecipeCookBook {
|
|||||||
requireAllTags?: boolean;
|
requireAllTags?: boolean;
|
||||||
requireAllTools?: boolean;
|
requireAllTools?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
id: string;
|
id: string;
|
||||||
recipes: RecipeSummary[];
|
recipes: RecipeSummary[];
|
||||||
}
|
}
|
||||||
export interface RecipeSummary {
|
export interface RecipeSummary {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
householdId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string | null;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
image?: unknown;
|
image?: unknown;
|
||||||
recipeYield?: string;
|
recipeYield?: string | null;
|
||||||
totalTime?: string;
|
totalTime?: string | null;
|
||||||
prepTime?: string;
|
prepTime?: string | null;
|
||||||
cookTime?: string;
|
cookTime?: string | null;
|
||||||
performTime?: string;
|
performTime?: string | null;
|
||||||
description?: string;
|
description?: string | null;
|
||||||
recipeCategory?: RecipeCategory[];
|
recipeCategory?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
tools?: RecipeTool[];
|
tools?: RecipeTool[];
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
orgURL?: string;
|
orgURL?: string | null;
|
||||||
dateAdded?: string;
|
dateAdded?: string | null;
|
||||||
dateUpdated?: string;
|
dateUpdated?: string | null;
|
||||||
createdAt?: string;
|
createdAt?: string | null;
|
||||||
updateAt?: string;
|
updatedAt?: string | null;
|
||||||
lastMade?: string;
|
lastMade?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCategory {
|
export interface RecipeCategory {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface RecipeTag {
|
export interface RecipeTag {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface SaveCookBook {
|
export interface SaveCookBook {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
position?: number;
|
position?: number;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[];
|
||||||
@ -112,11 +117,12 @@ export interface SaveCookBook {
|
|||||||
requireAllTags?: boolean;
|
requireAllTags?: boolean;
|
||||||
requireAllTools?: boolean;
|
requireAllTools?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
}
|
}
|
||||||
export interface UpdateCookBook {
|
export interface UpdateCookBook {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
slug?: string;
|
slug?: string | null;
|
||||||
position?: number;
|
position?: number;
|
||||||
public?: boolean;
|
public?: boolean;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[];
|
||||||
@ -126,5 +132,6 @@ export interface UpdateCookBook {
|
|||||||
requireAllTags?: boolean;
|
requireAllTags?: boolean;
|
||||||
requireAllTools?: boolean;
|
requireAllTools?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
/* 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 =
|
export type SupportedMigrations =
|
||||||
| "nextcloud"
|
| "nextcloud"
|
||||||
| "chowdown"
|
| "chowdown"
|
||||||
@ -17,59 +13,23 @@ export type SupportedMigrations =
|
|||||||
| "mealie_alpha"
|
| "mealie_alpha"
|
||||||
| "tandoor"
|
| "tandoor"
|
||||||
| "plantoeat"
|
| "plantoeat"
|
||||||
|
| "myrecipebox"
|
||||||
| "recipekeeper";
|
| "recipekeeper";
|
||||||
|
|
||||||
export interface CreateGroupPreferences {
|
export interface CreateGroupPreferences {
|
||||||
privateGroup?: boolean;
|
privateGroup?: boolean;
|
||||||
firstDayOfWeek?: number;
|
|
||||||
recipePublic?: boolean;
|
|
||||||
recipeShowNutrition?: boolean;
|
|
||||||
recipeShowAssets?: boolean;
|
|
||||||
recipeLandscapeView?: boolean;
|
|
||||||
recipeDisableComments?: boolean;
|
|
||||||
recipeDisableAmount?: boolean;
|
|
||||||
groupId: string;
|
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 {
|
export interface DataMigrationCreate {
|
||||||
sourceType: SupportedMigrations;
|
sourceType: SupportedMigrations;
|
||||||
}
|
}
|
||||||
export interface EmailInitationResponse {
|
|
||||||
success: boolean;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
export interface EmailInvitation {
|
|
||||||
email: string;
|
|
||||||
token: string;
|
|
||||||
}
|
|
||||||
export interface GroupAdminUpdate {
|
export interface GroupAdminUpdate {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
preferences?: UpdateGroupPreferences;
|
preferences?: UpdateGroupPreferences | null;
|
||||||
}
|
}
|
||||||
export interface UpdateGroupPreferences {
|
export interface UpdateGroupPreferences {
|
||||||
privateGroup?: boolean;
|
privateGroup?: boolean;
|
||||||
firstDayOfWeek?: number;
|
|
||||||
recipePublic?: boolean;
|
|
||||||
recipeShowNutrition?: boolean;
|
|
||||||
recipeShowAssets?: boolean;
|
|
||||||
recipeLandscapeView?: boolean;
|
|
||||||
recipeDisableComments?: boolean;
|
|
||||||
recipeDisableAmount?: boolean;
|
|
||||||
}
|
}
|
||||||
export interface GroupDataExport {
|
export interface GroupDataExport {
|
||||||
id: string;
|
id: string;
|
||||||
@ -80,140 +40,6 @@ export interface GroupDataExport {
|
|||||||
size: string;
|
size: string;
|
||||||
expires: 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 {
|
export interface GroupStorage {
|
||||||
usedStorageBytes: number;
|
usedStorageBytes: number;
|
||||||
usedStorageStr: string;
|
usedStorageStr: string;
|
||||||
@ -222,408 +48,9 @@ export interface GroupStorage {
|
|||||||
}
|
}
|
||||||
export interface ReadGroupPreferences {
|
export interface ReadGroupPreferences {
|
||||||
privateGroup?: boolean;
|
privateGroup?: boolean;
|
||||||
firstDayOfWeek?: number;
|
|
||||||
recipePublic?: boolean;
|
|
||||||
recipeShowNutrition?: boolean;
|
|
||||||
recipeShowAssets?: boolean;
|
|
||||||
recipeLandscapeView?: boolean;
|
|
||||||
recipeDisableComments?: boolean;
|
|
||||||
recipeDisableAmount?: boolean;
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
id: 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 {
|
export interface SeederConfig {
|
||||||
locale: string;
|
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;
|
entryType?: PlanEntryType & string;
|
||||||
title?: string;
|
title?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
recipeId?: string;
|
recipeId?: string | null;
|
||||||
}
|
}
|
||||||
export interface CreateRandomEntry {
|
export interface CreateRandomEntry {
|
||||||
date: string;
|
date: string;
|
||||||
entryType?: PlanEntryType & string;
|
entryType?: PlanEntryType & string;
|
||||||
}
|
}
|
||||||
export interface ListItem {
|
export interface ListItem {
|
||||||
title?: string;
|
title?: string | null;
|
||||||
text?: string;
|
text?: string;
|
||||||
quantity?: number;
|
quantity?: number;
|
||||||
checked?: boolean;
|
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 {
|
export interface PlanRulesCreate {
|
||||||
day?: PlanRulesDay & string;
|
day?: PlanRulesDay & string;
|
||||||
entryType?: PlanRulesType & string;
|
entryType?: PlanRulesType & string;
|
||||||
@ -76,6 +48,7 @@ export interface PlanRulesOut {
|
|||||||
categories?: Category[];
|
categories?: Category[];
|
||||||
tags?: Tag[];
|
tags?: Tag[];
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
export interface PlanRulesSave {
|
export interface PlanRulesSave {
|
||||||
@ -84,51 +57,56 @@ export interface PlanRulesSave {
|
|||||||
categories?: Category[];
|
categories?: Category[];
|
||||||
tags?: Tag[];
|
tags?: Tag[];
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
}
|
}
|
||||||
export interface ReadPlanEntry {
|
export interface ReadPlanEntry {
|
||||||
date: string;
|
date: string;
|
||||||
entryType?: PlanEntryType & string;
|
entryType?: PlanEntryType & string;
|
||||||
title?: string;
|
title?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
recipeId?: string;
|
recipeId?: string | null;
|
||||||
id: number;
|
id: number;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
userId?: string;
|
userId?: string | null;
|
||||||
recipe?: RecipeSummary;
|
householdId: string;
|
||||||
|
recipe?: RecipeSummary | null;
|
||||||
}
|
}
|
||||||
export interface RecipeSummary {
|
export interface RecipeSummary {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
householdId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string | null;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
image?: unknown;
|
image?: unknown;
|
||||||
recipeYield?: string;
|
recipeYield?: string | null;
|
||||||
totalTime?: string;
|
totalTime?: string | null;
|
||||||
prepTime?: string;
|
prepTime?: string | null;
|
||||||
cookTime?: string;
|
cookTime?: string | null;
|
||||||
performTime?: string;
|
performTime?: string | null;
|
||||||
description?: string;
|
description?: string | null;
|
||||||
recipeCategory?: RecipeCategory[];
|
recipeCategory?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
tools?: RecipeTool[];
|
tools?: RecipeTool[];
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
orgURL?: string;
|
orgURL?: string | null;
|
||||||
dateAdded?: string;
|
dateAdded?: string | null;
|
||||||
dateUpdated?: string;
|
dateUpdated?: string | null;
|
||||||
createdAt?: string;
|
createdAt?: string | null;
|
||||||
updateAt?: string;
|
updatedAt?: string | null;
|
||||||
lastMade?: string;
|
lastMade?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCategory {
|
export interface RecipeCategory {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface RecipeTag {
|
export interface RecipeTag {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
export interface RecipeTool {
|
export interface RecipeTool {
|
||||||
id: string;
|
id: string;
|
||||||
@ -141,18 +119,18 @@ export interface SavePlanEntry {
|
|||||||
entryType?: PlanEntryType & string;
|
entryType?: PlanEntryType & string;
|
||||||
title?: string;
|
title?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
recipeId?: string;
|
recipeId?: string | null;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
userId?: string;
|
userId?: string | null;
|
||||||
}
|
}
|
||||||
export interface ShoppingListIn {
|
export interface ShoppingListIn {
|
||||||
name: string;
|
name: string;
|
||||||
group?: string;
|
group?: string | null;
|
||||||
items: ListItem[];
|
items: ListItem[];
|
||||||
}
|
}
|
||||||
export interface ShoppingListOut {
|
export interface ShoppingListOut {
|
||||||
name: string;
|
name: string;
|
||||||
group?: string;
|
group?: string | null;
|
||||||
items: ListItem[];
|
items: ListItem[];
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
@ -161,8 +139,8 @@ export interface UpdatePlanEntry {
|
|||||||
entryType?: PlanEntryType & string;
|
entryType?: PlanEntryType & string;
|
||||||
title?: string;
|
title?: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
recipeId?: string;
|
recipeId?: string | null;
|
||||||
id: number;
|
id: number;
|
||||||
groupId: string;
|
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;
|
groupId: string;
|
||||||
}
|
}
|
||||||
export interface CreateIngredientFood {
|
export interface CreateIngredientFood {
|
||||||
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
labelId?: string;
|
|
||||||
aliases?: CreateIngredientFoodAlias[];
|
|
||||||
onHand?: boolean;
|
onHand?: boolean;
|
||||||
|
labelId?: string | null;
|
||||||
|
aliases?: CreateIngredientFoodAlias[];
|
||||||
}
|
}
|
||||||
export interface CreateIngredientFoodAlias {
|
export interface CreateIngredientFoodAlias {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
export interface CreateIngredientUnit {
|
export interface CreateIngredientUnit {
|
||||||
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
|
onHand?: boolean;
|
||||||
fraction?: boolean;
|
fraction?: boolean;
|
||||||
abbreviation?: string;
|
abbreviation?: string;
|
||||||
pluralAbbreviation?: string;
|
pluralAbbreviation?: string | null;
|
||||||
useAbbreviation?: boolean;
|
useAbbreviation?: boolean;
|
||||||
aliases?: CreateIngredientUnitAlias[];
|
aliases?: CreateIngredientUnitAlias[];
|
||||||
}
|
}
|
||||||
@ -89,16 +92,16 @@ export interface CreateRecipe {
|
|||||||
}
|
}
|
||||||
export interface CreateRecipeBulk {
|
export interface CreateRecipeBulk {
|
||||||
url: string;
|
url: string;
|
||||||
categories?: RecipeCategory[];
|
categories?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCategory {
|
export interface RecipeCategory {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
}
|
||||||
export interface RecipeTag {
|
export interface RecipeTag {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
}
|
||||||
@ -116,27 +119,27 @@ export interface ExportRecipes {
|
|||||||
exportType?: ExportTypes & string;
|
exportType?: ExportTypes & string;
|
||||||
}
|
}
|
||||||
export interface IngredientConfidence {
|
export interface IngredientConfidence {
|
||||||
average?: number;
|
average?: number | null;
|
||||||
comment?: number;
|
comment?: number | null;
|
||||||
name?: number;
|
name?: number | null;
|
||||||
unit?: number;
|
unit?: number | null;
|
||||||
quantity?: number;
|
quantity?: number | null;
|
||||||
food?: number;
|
food?: number | null;
|
||||||
}
|
}
|
||||||
export interface IngredientFood {
|
export interface IngredientFood {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
labelId?: string;
|
|
||||||
aliases?: IngredientFoodAlias[];
|
|
||||||
id: string;
|
|
||||||
label?: MultiPurposeLabelSummary;
|
|
||||||
createdAt?: string;
|
|
||||||
updateAt?: string;
|
|
||||||
onHand?: boolean;
|
onHand?: boolean;
|
||||||
|
labelId?: string | null;
|
||||||
|
aliases?: IngredientFoodAlias[];
|
||||||
|
label?: MultiPurposeLabelSummary | null;
|
||||||
|
createdAt?: string | null;
|
||||||
|
updatedAt?: string | null;
|
||||||
}
|
}
|
||||||
export interface IngredientFoodAlias {
|
export interface IngredientFoodAlias {
|
||||||
name: string;
|
name: string;
|
||||||
@ -151,27 +154,28 @@ export interface MultiPurposeLabelSummary {
|
|||||||
* A list of ingredient references.
|
* A list of ingredient references.
|
||||||
*/
|
*/
|
||||||
export interface IngredientReferences {
|
export interface IngredientReferences {
|
||||||
referenceId?: string;
|
referenceId?: string | null;
|
||||||
}
|
}
|
||||||
export interface IngredientRequest {
|
export interface IngredientRequest {
|
||||||
parser?: RegisteredParser & string;
|
parser?: RegisteredParser & string;
|
||||||
ingredient: string;
|
ingredient: string;
|
||||||
}
|
}
|
||||||
export interface IngredientUnit {
|
export interface IngredientUnit {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
|
onHand?: boolean;
|
||||||
fraction?: boolean;
|
fraction?: boolean;
|
||||||
abbreviation?: string;
|
abbreviation?: string;
|
||||||
pluralAbbreviation?: string;
|
pluralAbbreviation?: string | null;
|
||||||
useAbbreviation?: boolean;
|
useAbbreviation?: boolean;
|
||||||
aliases?: IngredientUnitAlias[];
|
aliases?: IngredientUnitAlias[];
|
||||||
id: string;
|
createdAt?: string | null;
|
||||||
createdAt?: string;
|
updatedAt?: string | null;
|
||||||
updateAt?: string;
|
|
||||||
}
|
}
|
||||||
export interface IngredientUnitAlias {
|
export interface IngredientUnitAlias {
|
||||||
name: string;
|
name: string;
|
||||||
@ -189,64 +193,65 @@ export interface MergeUnit {
|
|||||||
toUnit: string;
|
toUnit: string;
|
||||||
}
|
}
|
||||||
export interface Nutrition {
|
export interface Nutrition {
|
||||||
calories?: string;
|
calories?: string | null;
|
||||||
fatContent?: string;
|
fatContent?: string | null;
|
||||||
proteinContent?: string;
|
proteinContent?: string | null;
|
||||||
carbohydrateContent?: string;
|
carbohydrateContent?: string | null;
|
||||||
fiberContent?: string;
|
fiberContent?: string | null;
|
||||||
sodiumContent?: string;
|
sodiumContent?: string | null;
|
||||||
sugarContent?: string;
|
sugarContent?: string | null;
|
||||||
}
|
}
|
||||||
export interface ParsedIngredient {
|
export interface ParsedIngredient {
|
||||||
input?: string;
|
input?: string | null;
|
||||||
confidence?: IngredientConfidence;
|
confidence?: IngredientConfidence;
|
||||||
ingredient: RecipeIngredient;
|
ingredient: RecipeIngredient;
|
||||||
}
|
}
|
||||||
export interface RecipeIngredient {
|
export interface RecipeIngredient {
|
||||||
quantity?: number;
|
quantity?: number | null;
|
||||||
unit?: IngredientUnit | CreateIngredientUnit;
|
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||||
food?: IngredientFood | CreateIngredientFood;
|
food?: IngredientFood | CreateIngredientFood | null;
|
||||||
note?: string;
|
note?: string | null;
|
||||||
isFood?: boolean;
|
isFood?: boolean | null;
|
||||||
disableAmount?: boolean;
|
disableAmount?: boolean;
|
||||||
display?: string;
|
display?: string;
|
||||||
title?: string;
|
title?: string | null;
|
||||||
originalText?: string;
|
originalText?: string | null;
|
||||||
referenceId?: string;
|
referenceId?: string;
|
||||||
}
|
}
|
||||||
export interface Recipe {
|
export interface Recipe {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
householdId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string | null;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
image?: unknown;
|
image?: unknown;
|
||||||
recipeYield?: string;
|
recipeYield?: string | null;
|
||||||
totalTime?: string;
|
totalTime?: string | null;
|
||||||
prepTime?: string;
|
prepTime?: string | null;
|
||||||
cookTime?: string;
|
cookTime?: string | null;
|
||||||
performTime?: string;
|
performTime?: string | null;
|
||||||
description?: string;
|
description?: string | null;
|
||||||
recipeCategory?: RecipeCategory[];
|
recipeCategory?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
tools?: RecipeTool[];
|
tools?: RecipeTool[];
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
orgURL?: string;
|
orgURL?: string | null;
|
||||||
dateAdded?: string;
|
dateAdded?: string | null;
|
||||||
dateUpdated?: string;
|
dateUpdated?: string | null;
|
||||||
createdAt?: string;
|
createdAt?: string | null;
|
||||||
updateAt?: string;
|
updatedAt?: string | null;
|
||||||
lastMade?: string;
|
lastMade?: string | null;
|
||||||
recipeIngredient?: RecipeIngredient[];
|
recipeIngredient?: RecipeIngredient[];
|
||||||
recipeInstructions?: RecipeStep[];
|
recipeInstructions?: RecipeStep[] | null;
|
||||||
nutrition?: Nutrition;
|
nutrition?: Nutrition | null;
|
||||||
settings?: RecipeSettings;
|
settings?: RecipeSettings | null;
|
||||||
assets?: RecipeAsset[];
|
assets?: RecipeAsset[] | null;
|
||||||
notes?: RecipeNote[];
|
notes?: RecipeNote[] | null;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
comments?: RecipeCommentOut[];
|
comments?: RecipeCommentOut[] | null;
|
||||||
}
|
}
|
||||||
export interface RecipeTool {
|
export interface RecipeTool {
|
||||||
id: string;
|
id: string;
|
||||||
@ -255,15 +260,15 @@ export interface RecipeTool {
|
|||||||
onHand?: boolean;
|
onHand?: boolean;
|
||||||
}
|
}
|
||||||
export interface RecipeStep {
|
export interface RecipeStep {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
title?: string;
|
title?: string | null;
|
||||||
text: string;
|
text: string;
|
||||||
ingredientReferences?: IngredientReferences[];
|
ingredientReferences?: IngredientReferences[];
|
||||||
}
|
}
|
||||||
export interface RecipeAsset {
|
export interface RecipeAsset {
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
fileName?: string;
|
fileName?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeNote {
|
export interface RecipeNote {
|
||||||
title: string;
|
title: string;
|
||||||
@ -274,13 +279,13 @@ export interface RecipeCommentOut {
|
|||||||
text: string;
|
text: string;
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updateAt: string;
|
updatedAt: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
user: UserBase;
|
user: UserBase;
|
||||||
}
|
}
|
||||||
export interface UserBase {
|
export interface UserBase {
|
||||||
id: string;
|
id: string;
|
||||||
username?: string;
|
username?: string | null;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
}
|
}
|
||||||
export interface RecipeCategoryResponse {
|
export interface RecipeCategoryResponse {
|
||||||
@ -290,28 +295,29 @@ export interface RecipeCategoryResponse {
|
|||||||
recipes?: RecipeSummary[];
|
recipes?: RecipeSummary[];
|
||||||
}
|
}
|
||||||
export interface RecipeSummary {
|
export interface RecipeSummary {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
householdId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
name?: string;
|
name?: string | null;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
image?: unknown;
|
image?: unknown;
|
||||||
recipeYield?: string;
|
recipeYield?: string | null;
|
||||||
totalTime?: string;
|
totalTime?: string | null;
|
||||||
prepTime?: string;
|
prepTime?: string | null;
|
||||||
cookTime?: string;
|
cookTime?: string | null;
|
||||||
performTime?: string;
|
performTime?: string | null;
|
||||||
description?: string;
|
description?: string | null;
|
||||||
recipeCategory?: RecipeCategory[];
|
recipeCategory?: RecipeCategory[] | null;
|
||||||
tags?: RecipeTag[];
|
tags?: RecipeTag[] | null;
|
||||||
tools?: RecipeTool[];
|
tools?: RecipeTool[];
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
orgURL?: string;
|
orgURL?: string | null;
|
||||||
dateAdded?: string;
|
dateAdded?: string | null;
|
||||||
dateUpdated?: string;
|
dateUpdated?: string | null;
|
||||||
createdAt?: string;
|
createdAt?: string | null;
|
||||||
updateAt?: string;
|
updatedAt?: string | null;
|
||||||
lastMade?: string;
|
lastMade?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeCommentCreate {
|
export interface RecipeCommentCreate {
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
@ -327,15 +333,15 @@ export interface RecipeCommentUpdate {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
export interface RecipeDuplicate {
|
export interface RecipeDuplicate {
|
||||||
name?: string;
|
name?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeIngredientBase {
|
export interface RecipeIngredientBase {
|
||||||
quantity?: number;
|
quantity?: number | null;
|
||||||
unit?: IngredientUnit | CreateIngredientUnit;
|
unit?: IngredientUnit | CreateIngredientUnit | null;
|
||||||
food?: IngredientFood | CreateIngredientFood;
|
food?: IngredientFood | CreateIngredientFood | null;
|
||||||
note?: string;
|
note?: string | null;
|
||||||
isFood?: boolean;
|
isFood?: boolean | null;
|
||||||
disableAmount?: boolean;
|
disableAmount?: boolean | null;
|
||||||
display?: string;
|
display?: string;
|
||||||
}
|
}
|
||||||
export interface RecipeLastMade {
|
export interface RecipeLastMade {
|
||||||
@ -379,17 +385,17 @@ export interface RecipeTimelineEventCreate {
|
|||||||
userId: string;
|
userId: string;
|
||||||
subject: string;
|
subject: string;
|
||||||
eventType: TimelineEventType;
|
eventType: TimelineEventType;
|
||||||
eventMessage?: string;
|
eventMessage?: string | null;
|
||||||
image?: TimelineEventImage & string;
|
image?: TimelineEventImage | null;
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
}
|
}
|
||||||
export interface RecipeTimelineEventIn {
|
export interface RecipeTimelineEventIn {
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
userId?: string;
|
userId?: string | null;
|
||||||
subject: string;
|
subject: string;
|
||||||
eventType: TimelineEventType;
|
eventType: TimelineEventType;
|
||||||
eventMessage?: string;
|
eventMessage?: string | null;
|
||||||
image?: TimelineEventImage & string;
|
image?: TimelineEventImage | null;
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
}
|
}
|
||||||
export interface RecipeTimelineEventOut {
|
export interface RecipeTimelineEventOut {
|
||||||
@ -397,17 +403,19 @@ export interface RecipeTimelineEventOut {
|
|||||||
userId: string;
|
userId: string;
|
||||||
subject: string;
|
subject: string;
|
||||||
eventType: TimelineEventType;
|
eventType: TimelineEventType;
|
||||||
eventMessage?: string;
|
eventMessage?: string | null;
|
||||||
image?: TimelineEventImage & string;
|
image?: TimelineEventImage | null;
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updateAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
export interface RecipeTimelineEventUpdate {
|
export interface RecipeTimelineEventUpdate {
|
||||||
subject: string;
|
subject: string;
|
||||||
eventMessage?: string;
|
eventMessage?: string | null;
|
||||||
image?: TimelineEventImage;
|
image?: TimelineEventImage | null;
|
||||||
}
|
}
|
||||||
export interface RecipeToolCreate {
|
export interface RecipeToolCreate {
|
||||||
name: string;
|
name: string;
|
||||||
@ -435,26 +443,30 @@ export interface RecipeZipTokenResponse {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
export interface SaveIngredientFood {
|
export interface SaveIngredientFood {
|
||||||
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
labelId?: string;
|
onHand?: boolean;
|
||||||
|
labelId?: string | null;
|
||||||
aliases?: CreateIngredientFoodAlias[];
|
aliases?: CreateIngredientFoodAlias[];
|
||||||
groupId: string;
|
groupId: string;
|
||||||
}
|
}
|
||||||
export interface SaveIngredientUnit {
|
export interface SaveIngredientUnit {
|
||||||
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
|
onHand?: boolean;
|
||||||
fraction?: boolean;
|
fraction?: boolean;
|
||||||
abbreviation?: string;
|
abbreviation?: string;
|
||||||
pluralAbbreviation?: string;
|
pluralAbbreviation?: string | null;
|
||||||
useAbbreviation?: boolean;
|
useAbbreviation?: boolean;
|
||||||
aliases?: CreateIngredientUnitAlias[];
|
aliases?: CreateIngredientUnitAlias[];
|
||||||
groupId: string;
|
groupId: string;
|
||||||
@ -465,8 +477,9 @@ export interface ScrapeRecipe {
|
|||||||
}
|
}
|
||||||
export interface ScrapeRecipeTest {
|
export interface ScrapeRecipeTest {
|
||||||
url: string;
|
url: string;
|
||||||
|
useOpenAI?: boolean;
|
||||||
}
|
}
|
||||||
export interface SlugResponse { }
|
export interface SlugResponse {}
|
||||||
export interface TagIn {
|
export interface TagIn {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
@ -481,12 +494,14 @@ export interface TagSave {
|
|||||||
groupId: string;
|
groupId: string;
|
||||||
}
|
}
|
||||||
export interface UnitFoodBase {
|
export interface UnitFoodBase {
|
||||||
|
id?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
pluralName?: string;
|
pluralName?: string | null;
|
||||||
description?: string;
|
description?: string;
|
||||||
extras?: {
|
extras?: {
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
} | null;
|
||||||
|
onHand?: boolean;
|
||||||
}
|
}
|
||||||
export interface UpdateImageResponse {
|
export interface UpdateImageResponse {
|
||||||
image: string;
|
image: string;
|
||||||
|
@ -11,7 +11,7 @@ export type OrderDirection = "asc" | "desc";
|
|||||||
export interface ErrorResponse {
|
export interface ErrorResponse {
|
||||||
message: string;
|
message: string;
|
||||||
error?: boolean;
|
error?: boolean;
|
||||||
exception?: string;
|
exception?: string | null;
|
||||||
}
|
}
|
||||||
export interface FileTokenResponse {
|
export interface FileTokenResponse {
|
||||||
fileToken: string;
|
fileToken: string;
|
||||||
@ -19,19 +19,19 @@ export interface FileTokenResponse {
|
|||||||
export interface PaginationQuery {
|
export interface PaginationQuery {
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
orderBy?: string;
|
orderBy?: string | null;
|
||||||
orderByNullPosition?: OrderByNullPosition;
|
orderByNullPosition?: OrderByNullPosition | null;
|
||||||
orderDirection?: OrderDirection & string;
|
orderDirection?: OrderDirection & string;
|
||||||
queryFilter?: string;
|
queryFilter?: string | null;
|
||||||
paginationSeed?: string;
|
paginationSeed?: string | null;
|
||||||
}
|
}
|
||||||
export interface RecipeSearchQuery {
|
export interface RecipeSearchQuery {
|
||||||
cookbook?: string;
|
cookbook?: string | null;
|
||||||
requireAllCategories?: boolean;
|
requireAllCategories?: boolean;
|
||||||
requireAllTags?: boolean;
|
requireAllTags?: boolean;
|
||||||
requireAllTools?: boolean;
|
requireAllTools?: boolean;
|
||||||
requireAllFoods?: boolean;
|
requireAllFoods?: boolean;
|
||||||
search?: string;
|
search?: string | null;
|
||||||
}
|
}
|
||||||
export interface SuccessResponse {
|
export interface SuccessResponse {
|
||||||
message: string;
|
message: string;
|
||||||
|
@ -19,8 +19,9 @@ export interface CreateToken {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
export interface CreateUserRegistration {
|
export interface CreateUserRegistration {
|
||||||
group?: string;
|
group?: string | null;
|
||||||
groupToken?: string;
|
household?: string | null;
|
||||||
|
groupToken?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
username: string;
|
username: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
@ -45,21 +46,19 @@ export interface ForgotPassword {
|
|||||||
export interface GroupBase {
|
export interface GroupBase {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
export interface GroupHouseholdSummary {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
export interface GroupInDB {
|
export interface GroupInDB {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[] | null;
|
||||||
webhooks?: ReadWebhook[];
|
webhooks?: ReadWebhook[];
|
||||||
users?: UserOut[];
|
households?: GroupHouseholdSummary[] | null;
|
||||||
preferences?: ReadGroupPreferences;
|
users?: UserSummary[] | null;
|
||||||
}
|
preferences?: ReadGroupPreferences | null;
|
||||||
export interface GroupSummary {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
slug: string;
|
|
||||||
preferences?: ReadGroupPreferences;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
export interface CategoryBase {
|
export interface CategoryBase {
|
||||||
name: string;
|
name: string;
|
||||||
@ -73,43 +72,24 @@ export interface ReadWebhook {
|
|||||||
webhookType?: WebhookType & string;
|
webhookType?: WebhookType & string;
|
||||||
scheduledTime: string;
|
scheduledTime: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
|
householdId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
export interface UserOut {
|
export interface UserSummary {
|
||||||
id: string;
|
id: string;
|
||||||
username?: string;
|
fullName: 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;
|
|
||||||
}
|
}
|
||||||
export interface ReadGroupPreferences {
|
export interface ReadGroupPreferences {
|
||||||
privateGroup?: boolean;
|
privateGroup?: boolean;
|
||||||
firstDayOfWeek?: number;
|
|
||||||
recipePublic?: boolean;
|
|
||||||
recipeShowNutrition?: boolean;
|
|
||||||
recipeShowAssets?: boolean;
|
|
||||||
recipeLandscapeView?: boolean;
|
|
||||||
recipeDisableComments?: boolean;
|
|
||||||
recipeDisableAmount?: boolean;
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
export interface GroupSummary {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
preferences?: ReadGroupPreferences | null;
|
||||||
|
}
|
||||||
export interface LongLiveTokenIn {
|
export interface LongLiveTokenIn {
|
||||||
name: string;
|
name: string;
|
||||||
integrationId?: string;
|
integrationId?: string;
|
||||||
@ -124,23 +104,32 @@ export interface LongLiveTokenInDB {
|
|||||||
}
|
}
|
||||||
export interface PrivateUser {
|
export interface PrivateUser {
|
||||||
id: string;
|
id: string;
|
||||||
username?: string;
|
username?: string | null;
|
||||||
fullName?: string;
|
fullName?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
authMethod?: AuthMethod & string;
|
authMethod?: AuthMethod & string;
|
||||||
admin?: boolean;
|
admin?: boolean;
|
||||||
group: string;
|
group: string;
|
||||||
|
household: string;
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
canInvite?: boolean;
|
canInvite?: boolean;
|
||||||
canManage?: boolean;
|
canManage?: boolean;
|
||||||
canOrganize?: boolean;
|
canOrganize?: boolean;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupSlug: string;
|
groupSlug: string;
|
||||||
tokens?: LongLiveTokenOut[];
|
householdId: string;
|
||||||
|
householdSlug: string;
|
||||||
|
tokens?: LongLiveTokenOut[] | null;
|
||||||
cacheKey: string;
|
cacheKey: string;
|
||||||
password: string;
|
password: string;
|
||||||
loginAttemps?: number;
|
loginAttemps?: number;
|
||||||
lockedAt?: string;
|
lockedAt?: string | null;
|
||||||
|
}
|
||||||
|
export interface LongLiveTokenOut {
|
||||||
|
token: string;
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
export interface OIDCRequest {
|
export interface OIDCRequest {
|
||||||
id_token: string;
|
id_token: string;
|
||||||
@ -168,8 +157,8 @@ export interface Token {
|
|||||||
token_type: string;
|
token_type: string;
|
||||||
}
|
}
|
||||||
export interface TokenData {
|
export interface TokenData {
|
||||||
user_id?: string;
|
user_id?: string | null;
|
||||||
username?: string;
|
username?: string | null;
|
||||||
}
|
}
|
||||||
export interface UnlockResults {
|
export interface UnlockResults {
|
||||||
unlocked?: number;
|
unlocked?: number;
|
||||||
@ -178,7 +167,7 @@ export interface UpdateGroup {
|
|||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
categories?: CategoryBase[];
|
categories?: CategoryBase[] | null;
|
||||||
webhooks?: CreateWebhook[];
|
webhooks?: CreateWebhook[];
|
||||||
}
|
}
|
||||||
export interface CreateWebhook {
|
export interface CreateWebhook {
|
||||||
@ -189,53 +178,75 @@ export interface CreateWebhook {
|
|||||||
scheduledTime: string;
|
scheduledTime: string;
|
||||||
}
|
}
|
||||||
export interface UserBase {
|
export interface UserBase {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
username?: string;
|
username?: string | null;
|
||||||
fullName?: string;
|
fullName?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
authMethod?: AuthMethod & string;
|
authMethod?: AuthMethod & string;
|
||||||
admin?: boolean;
|
admin?: boolean;
|
||||||
group?: string;
|
group?: string | null;
|
||||||
|
household?: string | null;
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
canInvite?: boolean;
|
canInvite?: boolean;
|
||||||
canManage?: boolean;
|
canManage?: boolean;
|
||||||
canOrganize?: boolean;
|
canOrganize?: boolean;
|
||||||
}
|
}
|
||||||
export interface UserIn {
|
export interface UserIn {
|
||||||
id?: string;
|
id?: string | null;
|
||||||
username?: string;
|
username?: string | null;
|
||||||
fullName?: string;
|
fullName?: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
authMethod?: AuthMethod & string;
|
authMethod?: AuthMethod & string;
|
||||||
admin?: boolean;
|
admin?: boolean;
|
||||||
group?: string;
|
group?: string | null;
|
||||||
|
household?: string | null;
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
canInvite?: boolean;
|
canInvite?: boolean;
|
||||||
canManage?: boolean;
|
canManage?: boolean;
|
||||||
canOrganize?: boolean;
|
canOrganize?: boolean;
|
||||||
password: string;
|
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 {
|
export interface UserRatingCreate {
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
export interface UserRatingOut {
|
export interface UserRatingOut {
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
export interface UserRatingSummary {
|
export interface UserRatingSummary {
|
||||||
recipeId: string;
|
recipeId: string;
|
||||||
rating?: number;
|
rating?: number | null;
|
||||||
isFavorite?: boolean;
|
isFavorite?: boolean;
|
||||||
}
|
}
|
||||||
export interface UserSummary {
|
export interface UserRatingUpdate {
|
||||||
id: string;
|
rating?: number | null;
|
||||||
fullName: string;
|
isFavorite?: boolean | null;
|
||||||
}
|
}
|
||||||
export interface ValidateResetToken {
|
export interface ValidateResetToken {
|
||||||
token: string;
|
token: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BaseAPI } from "../base/base-clients";
|
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 { ForgotPassword } from "~/lib/api/types/user";
|
||||||
import { EmailTest } from "~/lib/api/types/admin";
|
import { EmailTest } from "~/lib/api/types/admin";
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ const routes = {
|
|||||||
base: "/api/admin/email",
|
base: "/api/admin/email",
|
||||||
forgotPassword: "/api/users/forgot-password",
|
forgotPassword: "/api/users/forgot-password",
|
||||||
|
|
||||||
invitation: "/api/groups/invitations/email",
|
invitation: "/api/households/invitations/email",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class EmailAPI extends BaseAPI {
|
export class EmailAPI extends BaseAPI {
|
||||||
|
@ -4,8 +4,8 @@ import { CreateCookBook, RecipeCookBook, UpdateCookBook } from "~/lib/api/types/
|
|||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
cookbooks: `${prefix}/groups/cookbooks`,
|
cookbooks: `${prefix}/households/cookbooks`,
|
||||||
cookbooksId: (id: number) => `${prefix}/groups/cookbooks/${id}`,
|
cookbooksId: (id: number) => `${prefix}/households/cookbooks/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CookbookAPI extends BaseCRUDAPI<CreateCookBook, RecipeCookBook, UpdateCookBook> {
|
export class CookbookAPI extends BaseCRUDAPI<CreateCookBook, RecipeCookBook, UpdateCookBook> {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { BaseCRUDAPI } from "../base/base-clients";
|
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 prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
eventNotifier: `${prefix}/groups/events/notifications`,
|
eventNotifier: `${prefix}/households/events/notifications`,
|
||||||
eventNotifierId: (id: string | number) => `${prefix}/groups/events/notifications/${id}`,
|
eventNotifierId: (id: string | number) => `${prefix}/households/events/notifications/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GroupEventNotifierApi extends BaseCRUDAPI<
|
export class GroupEventNotifierApi extends BaseCRUDAPI<
|
||||||
|
@ -4,8 +4,8 @@ import { PlanRulesCreate, PlanRulesOut } from "~/lib/api/types/meal-plan";
|
|||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
rule: `${prefix}/groups/mealplans/rules`,
|
rule: `${prefix}/households/mealplans/rules`,
|
||||||
ruleId: (id: string | number) => `${prefix}/groups/mealplans/rules/${id}`,
|
ruleId: (id: string | number) => `${prefix}/households/mealplans/rules/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesCreate, PlanRulesOut> {
|
export class MealPlanRulesApi extends BaseCRUDAPI<PlanRulesCreate, PlanRulesOut> {
|
||||||
|
@ -4,9 +4,9 @@ import { CreatePlanEntry, CreateRandomEntry, ReadPlanEntry, UpdatePlanEntry } fr
|
|||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
mealplan: `${prefix}/groups/mealplans`,
|
mealplan: `${prefix}/households/mealplans`,
|
||||||
random: `${prefix}/groups/mealplans/random`,
|
random: `${prefix}/households/mealplans/random`,
|
||||||
mealplanId: (id: string | number) => `${prefix}/groups/mealplans/${id}`,
|
mealplanId: (id: string | number) => `${prefix}/households/mealplans/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class MealPlanAPI extends BaseCRUDAPI<CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry> {
|
export class MealPlanAPI extends BaseCRUDAPI<CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry> {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { BaseCRUDAPI } from "../base/base-clients";
|
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 prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
groupRecipeActions: `${prefix}/groups/recipe-actions`,
|
groupRecipeActions: `${prefix}/households/recipe-actions`,
|
||||||
groupRecipeActionsId: (id: string | number) => `${prefix}/groups/recipe-actions/${id}`,
|
groupRecipeActionsId: (id: string | number) => `${prefix}/households/recipe-actions/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GroupRecipeActionsAPI extends BaseCRUDAPI<CreateGroupRecipeAction, GroupRecipeActionOut> {
|
export class GroupRecipeActionsAPI extends BaseCRUDAPI<CreateGroupRecipeAction, GroupRecipeActionOut> {
|
||||||
|
@ -9,20 +9,20 @@ import {
|
|||||||
ShoppingListMultiPurposeLabelUpdate,
|
ShoppingListMultiPurposeLabelUpdate,
|
||||||
ShoppingListOut,
|
ShoppingListOut,
|
||||||
ShoppingListUpdate,
|
ShoppingListUpdate,
|
||||||
} from "~/lib/api/types/group";
|
} from "~/lib/api/types/household";
|
||||||
|
|
||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
shoppingLists: `${prefix}/groups/shopping/lists`,
|
shoppingLists: `${prefix}/households/shopping/lists`,
|
||||||
shoppingListsId: (id: string) => `${prefix}/groups/shopping/lists/${id}`,
|
shoppingListsId: (id: string) => `${prefix}/households/shopping/lists/${id}`,
|
||||||
shoppingListIdAddRecipe: (id: string, recipeId: string) => `${prefix}/groups/shopping/lists/${id}/recipe/${recipeId}`,
|
shoppingListIdAddRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}`,
|
||||||
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/groups/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
shoppingListIdRemoveRecipe: (id: string, recipeId: string) => `${prefix}/households/shopping/lists/${id}/recipe/${recipeId}/delete`,
|
||||||
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/groups/shopping/lists/${id}/label-settings`,
|
shoppingListIdUpdateLabelSettings: (id: string) => `${prefix}/households/shopping/lists/${id}/label-settings`,
|
||||||
|
|
||||||
shoppingListItems: `${prefix}/groups/shopping/items`,
|
shoppingListItems: `${prefix}/households/shopping/items`,
|
||||||
shoppingListItemsCreateBulk: `${prefix}/groups/shopping/items/create-bulk`,
|
shoppingListItemsCreateBulk: `${prefix}/households/shopping/items/create-bulk`,
|
||||||
shoppingListItemsId: (id: string) => `${prefix}/groups/shopping/items/${id}`,
|
shoppingListItemsId: (id: string) => `${prefix}/households/shopping/items/${id}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingListOut, ShoppingListUpdate> {
|
export class ShoppingListsApi extends BaseCRUDAPI<ShoppingListCreate, ShoppingListOut, ShoppingListUpdate> {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { BaseCRUDAPI } from "../base/base-clients";
|
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 prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
webhooks: `${prefix}/groups/webhooks`,
|
webhooks: `${prefix}/households/webhooks`,
|
||||||
webhooksId: (id: string | number) => `${prefix}/groups/webhooks/${id}`,
|
webhooksId: (id: string | number) => `${prefix}/households/webhooks/${id}`,
|
||||||
webhooksIdTest: (id: string | number) => `${prefix}/groups/webhooks/${id}/test`,
|
webhooksIdTest: (id: string | number) => `${prefix}/households/webhooks/${id}/test`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
|
export class WebhooksAPI extends BaseCRUDAPI<CreateWebhook, ReadWebhook> {
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import { BaseCRUDAPI } from "../base/base-clients";
|
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 {
|
import {
|
||||||
CreateInviteToken,
|
|
||||||
GroupAdminUpdate,
|
GroupAdminUpdate,
|
||||||
GroupStatistics,
|
|
||||||
GroupStorage,
|
GroupStorage,
|
||||||
ReadGroupPreferences,
|
ReadGroupPreferences,
|
||||||
ReadInviteToken,
|
|
||||||
SetPermissions,
|
|
||||||
UpdateGroupPreferences,
|
UpdateGroupPreferences,
|
||||||
} from "~/lib/api/types/group";
|
} from "~/lib/api/types/group";
|
||||||
|
|
||||||
@ -16,16 +13,14 @@ const prefix = "/api";
|
|||||||
const routes = {
|
const routes = {
|
||||||
groups: `${prefix}/admin/groups`,
|
groups: `${prefix}/admin/groups`,
|
||||||
groupsSelf: `${prefix}/groups/self`,
|
groupsSelf: `${prefix}/groups/self`,
|
||||||
categories: `${prefix}/groups/categories`,
|
|
||||||
members: `${prefix}/groups/members`,
|
|
||||||
permissions: `${prefix}/groups/permissions`,
|
|
||||||
|
|
||||||
preferences: `${prefix}/groups/preferences`,
|
preferences: `${prefix}/groups/preferences`,
|
||||||
statistics: `${prefix}/groups/statistics`,
|
|
||||||
storage: `${prefix}/groups/storage`,
|
storage: `${prefix}/groups/storage`,
|
||||||
|
households: `${prefix}/households`,
|
||||||
invitation: `${prefix}/groups/invitations`,
|
membersHouseholdId: (householdId: string | number | null) => {
|
||||||
|
return householdId ?
|
||||||
|
`${prefix}/households/members?householdId=${householdId}` :
|
||||||
|
`${prefix}/groups/members`;
|
||||||
|
},
|
||||||
groupsId: (id: string | number) => `${prefix}/admin/groups/${id}`,
|
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);
|
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() {
|
async getPreferences() {
|
||||||
return await this.requests.get<ReadGroupPreferences>(routes.preferences);
|
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);
|
return await this.requests.put<ReadGroupPreferences, UpdateGroupPreferences>(routes.preferences, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createInvitation(payload: CreateInviteToken) {
|
async fetchMembers(householdId: string | number | null = null) {
|
||||||
return await this.requests.post<ReadInviteToken>(routes.invitation, payload);
|
return await this.requests.get<UserSummary[]>(routes.membersHouseholdId(householdId));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchMembers() {
|
async fetchHouseholds() {
|
||||||
return await this.requests.get<UserOut[]>(routes.members);
|
return await this.requests.get<HouseholdSummary[]>(routes.households);
|
||||||
}
|
|
||||||
|
|
||||||
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 storage() {
|
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 { BaseCRUDAPI } from "../base/base-clients";
|
||||||
import { QueryValue, route } from "~/lib/api/base/route";
|
|
||||||
import { PaginationData } from "~/lib/api/types/non-generated";
|
|
||||||
import {
|
import {
|
||||||
ChangePassword,
|
ChangePassword,
|
||||||
DeleteTokenResponse,
|
DeleteTokenResponse,
|
||||||
@ -12,7 +10,6 @@ import {
|
|||||||
UserOut,
|
UserOut,
|
||||||
UserRatingOut,
|
UserRatingOut,
|
||||||
UserRatingSummary,
|
UserRatingSummary,
|
||||||
UserSummary,
|
|
||||||
} from "~/lib/api/types/user";
|
} from "~/lib/api/types/user";
|
||||||
|
|
||||||
export interface UserRatingsSummaries {
|
export interface UserRatingsSummaries {
|
||||||
@ -26,7 +23,6 @@ export interface UserRatingsOut {
|
|||||||
const prefix = "/api";
|
const prefix = "/api";
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
groupUsers: `${prefix}/users/group-users`,
|
|
||||||
usersSelf: `${prefix}/users/self`,
|
usersSelf: `${prefix}/users/self`,
|
||||||
ratingsSelf: `${prefix}/users/self/ratings`,
|
ratingsSelf: `${prefix}/users/self/ratings`,
|
||||||
passwordReset: `${prefix}/users/reset-password`,
|
passwordReset: `${prefix}/users/reset-password`,
|
||||||
@ -51,10 +47,6 @@ export class UserApi extends BaseCRUDAPI<UserIn, UserOut, UserBase> {
|
|||||||
baseRoute: string = routes.users;
|
baseRoute: string = routes.users;
|
||||||
itemRoute = (itemid: string) => routes.usersId(itemid);
|
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) {
|
async addFavorite(id: string, slug: string) {
|
||||||
return await this.requests.post(routes.usersIdFavoritesSlug(id, slug), {});
|
return await this.requests.post(routes.usersIdFavoritesSlug(id, slug), {});
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
mdiAccountGroup,
|
mdiAccountGroup,
|
||||||
mdiSlotMachine,
|
mdiSlotMachine,
|
||||||
mdiHome,
|
mdiHome,
|
||||||
|
mdiHomeAccount,
|
||||||
mdiMagnify,
|
mdiMagnify,
|
||||||
mdiPotSteamOutline,
|
mdiPotSteamOutline,
|
||||||
mdiTranslate,
|
mdiTranslate,
|
||||||
@ -226,6 +227,7 @@ export const icons = {
|
|||||||
heart: mdiHeart,
|
heart: mdiHeart,
|
||||||
heartOutline: mdiHeartOutline,
|
heartOutline: mdiHeartOutline,
|
||||||
home: mdiHome,
|
home: mdiHome,
|
||||||
|
household: mdiHomeAccount,
|
||||||
import: mdiImport,
|
import: mdiImport,
|
||||||
information: mdiInformation,
|
information: mdiInformation,
|
||||||
informationVariant: mdiInformationVariant,
|
informationVariant: mdiInformationVariant,
|
||||||
|
@ -477,7 +477,7 @@ export default {
|
|||||||
"name": "Meal Planner",
|
"name": "Meal Planner",
|
||||||
"short_name": "Meal Planner",
|
"short_name": "Meal Planner",
|
||||||
"description": "Open the meal planner",
|
"description": "Open the meal planner",
|
||||||
"url": "/group/mealplan/planner/view",
|
"url": "/household/mealplan/planner/view",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/icons/mdiCalendarMultiselect-192x192.png",
|
"src": "/icons/mdiCalendarMultiselect-192x192.png",
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
<i18n path="settings.backup.experimental-description" />
|
<i18n path="settings.backup.experimental-description" />
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</BaseCardSectionTitle>
|
</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>
|
<BaseButton class="mr-2" @click="createBackup"> {{ $t("settings.backup.create-heading") }} </BaseButton>
|
||||||
<AppButtonUpload
|
<AppButtonUpload
|
||||||
:text-btn="false"
|
:text-btn="false"
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
// TODO: Edit Group
|
|
||||||
<template>
|
<template>
|
||||||
<v-container fluid>
|
<v-container fluid>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
v-model="createDialog"
|
v-model="createDialog"
|
||||||
:title="$t('group.create-group')"
|
:title="$t('group.create-group')"
|
||||||
:icon="$globals.icons.group"
|
:icon="$globals.icons.group"
|
||||||
@submit="createGroup(createUserForm.data)"
|
@submit="createGroup(createGroupForm.data)"
|
||||||
>
|
>
|
||||||
<template #activator> </template>
|
<template #activator> </template>
|
||||||
<v-card-text>
|
<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>
|
</v-card-text>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
||||||
@ -27,7 +26,7 @@
|
|||||||
|
|
||||||
<BaseCardSectionTitle :title="$tc('group.group-management')"> </BaseCardSectionTitle>
|
<BaseCardSectionTitle :title="$tc('group.group-management')"> </BaseCardSectionTitle>
|
||||||
<section>
|
<section>
|
||||||
<v-toolbar flat color="background" class="justify-between">
|
<v-toolbar flat color="transparent" class="justify-between">
|
||||||
<BaseButton @click="openDialog"> {{ $t("general.create") }} </BaseButton>
|
<BaseButton @click="openDialog"> {{ $t("general.create") }} </BaseButton>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
|
|
||||||
@ -41,15 +40,15 @@
|
|||||||
:search="search"
|
:search="search"
|
||||||
@click:row="handleRowClick"
|
@click:row="handleRowClick"
|
||||||
>
|
>
|
||||||
|
<template #item.households="{ item }">
|
||||||
|
{{ item.households.length }}
|
||||||
|
</template>
|
||||||
<template #item.users="{ item }">
|
<template #item.users="{ item }">
|
||||||
{{ item.users.length }}
|
{{ item.users.length }}
|
||||||
</template>
|
</template>
|
||||||
<template #item.webhookEnable="{ item }">
|
|
||||||
{{ item.webhooks.length > 0 ? $t("general.yes") : $t("general.no") }}
|
|
||||||
</template>
|
|
||||||
<template #item.actions="{ item }">
|
<template #item.actions="{ item }">
|
||||||
<v-btn
|
<v-btn
|
||||||
:disabled="item && item.users.length > 0"
|
:disabled="item && (item.households.length > 0 || item.users.length > 0)"
|
||||||
class="mr-1"
|
class="mr-1"
|
||||||
icon
|
icon
|
||||||
color="error"
|
color="error"
|
||||||
@ -94,12 +93,12 @@ export default defineComponent({
|
|||||||
value: "id",
|
value: "id",
|
||||||
},
|
},
|
||||||
{ text: i18n.t("general.name"), value: "name" },
|
{ 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.total-users"), value: "users" },
|
||||||
{ text: i18n.t("user.webhooks-enabled"), value: "webhookEnable" },
|
|
||||||
{ text: i18n.t("general.delete"), value: "actions" },
|
{ text: i18n.t("general.delete"), value: "actions" },
|
||||||
],
|
],
|
||||||
updateMode: false,
|
updateMode: false,
|
||||||
createUserForm: {
|
createGroupForm: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: i18n.t("group.group-name"),
|
label: i18n.t("group.group-name"),
|
||||||
@ -116,7 +115,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
function openDialog() {
|
function openDialog() {
|
||||||
state.createDialog = true;
|
state.createDialog = true;
|
||||||
state.createUserForm.data.name = "";
|
state.createGroupForm.data.name = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = useRouter();
|
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">
|
<div class="d-flex">
|
||||||
<p> {{ $t("user.user-id-with-value", {id: user.id} ) }}</p>
|
<p> {{ $t("user.user-id-with-value", {id: user.id} ) }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- This is disabled since we can't properly handle changing the user's group in most scenarios -->
|
||||||
<v-select
|
<v-select
|
||||||
v-if="groups"
|
v-if="groups"
|
||||||
v-model="user.group"
|
v-model="user.group"
|
||||||
|
disabled
|
||||||
:items="groups"
|
:items="groups"
|
||||||
rounded
|
rounded
|
||||||
class="rounded-lg"
|
class="rounded-lg"
|
||||||
@ -26,7 +28,20 @@
|
|||||||
filled
|
filled
|
||||||
:label="$tc('group.user-group')"
|
:label="$tc('group.user-group')"
|
||||||
:rules="[validators.required]"
|
: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">
|
<div class="d-flex py-2 pr-2">
|
||||||
<BaseButton type="button" :loading="generatingToken" create @click.prevent="handlePasswordReset">
|
<BaseButton type="button" :loading="generatingToken" create @click.prevent="handlePasswordReset">
|
||||||
{{ $t("user.generate-password-reset-link") }}
|
{{ $t("user.generate-password-reset-link") }}
|
||||||
@ -65,6 +80,7 @@
|
|||||||
import { computed, defineComponent, useRoute, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useRoute, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { useAdminApi, useUserApi } from "~/composables/api";
|
import { useAdminApi, useUserApi } from "~/composables/api";
|
||||||
import { useGroups } from "~/composables/use-groups";
|
import { useGroups } from "~/composables/use-groups";
|
||||||
|
import { useHouseholds } from "~/composables/use-households";
|
||||||
import { alert } from "~/composables/use-toast";
|
import { alert } from "~/composables/use-toast";
|
||||||
import { useUserForm } from "~/composables/use-users";
|
import { useUserForm } from "~/composables/use-users";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
@ -76,6 +92,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const { userForm } = useUserForm();
|
const { userForm } = useUserForm();
|
||||||
const { groups } = useGroups();
|
const { groups } = useGroups();
|
||||||
|
const { useHouseholdsInGroup } = useHouseholds();
|
||||||
const { i18n } = useContext();
|
const { i18n } = useContext();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
@ -89,6 +106,8 @@ export default defineComponent({
|
|||||||
const adminApi = useAdminApi();
|
const adminApi = useAdminApi();
|
||||||
|
|
||||||
const user = ref<UserOut | null>(null);
|
const user = ref<UserOut | null>(null);
|
||||||
|
const households = useHouseholdsInGroup(computed(() => user.value?.groupId || ""));
|
||||||
|
|
||||||
const disabledFields = computed(() => {
|
const disabledFields = computed(() => {
|
||||||
return user.value?.authMethod !== "Mealie" ? ["admin"] : [];
|
return user.value?.authMethod !== "Mealie" ? ["admin"] : [];
|
||||||
})
|
})
|
||||||
@ -154,6 +173,7 @@ export default defineComponent({
|
|||||||
refNewUserForm,
|
refNewUserForm,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
groups,
|
groups,
|
||||||
|
households,
|
||||||
validators,
|
validators,
|
||||||
handlePasswordReset,
|
handlePasswordReset,
|
||||||
resetUrl,
|
resetUrl,
|
||||||
|
@ -12,17 +12,30 @@
|
|||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-select
|
<v-select
|
||||||
v-if="groups"
|
v-if="groups"
|
||||||
v-model="newUserData.group"
|
v-model="selectedGroupId"
|
||||||
:items="groups"
|
:items="groups"
|
||||||
rounded
|
rounded
|
||||||
class="rounded-lg"
|
class="rounded-lg"
|
||||||
item-text="name"
|
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"
|
item-value="name"
|
||||||
:return-object="false"
|
:return-object="false"
|
||||||
filled
|
filled
|
||||||
:label="$t('group.user-group')"
|
:label="$t('household.user-household')"
|
||||||
:rules="[validators.required]"
|
:rules="[validators.required]"
|
||||||
></v-select>
|
/>
|
||||||
<AutoForm v-model="newUserData" :items="userForm" />
|
<AutoForm v-model="newUserData" :items="userForm" />
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -34,9 +47,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { useAdminApi } from "~/composables/api";
|
||||||
import { useGroups } from "~/composables/use-groups";
|
import { useGroups } from "~/composables/use-groups";
|
||||||
|
import { useHouseholds } from "~/composables/use-households";
|
||||||
import { useUserForm } from "~/composables/use-users";
|
import { useUserForm } from "~/composables/use-users";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
import { VForm } from "~/types/vuetify";
|
import { VForm } from "~/types/vuetify";
|
||||||
@ -46,6 +60,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const { userForm } = useUserForm();
|
const { userForm } = useUserForm();
|
||||||
const { groups } = useGroups();
|
const { groups } = useGroups();
|
||||||
|
const { useHouseholdsInGroup } = useHouseholds();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// ==============================================
|
// ==============================================
|
||||||
@ -55,13 +70,20 @@ export default defineComponent({
|
|||||||
|
|
||||||
const adminApi = useAdminApi();
|
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({
|
const state = reactive({
|
||||||
newUserData: {
|
newUserData: {
|
||||||
username: "",
|
username: "",
|
||||||
fullName: "",
|
fullName: "",
|
||||||
email: "",
|
email: "",
|
||||||
admin: false,
|
admin: false,
|
||||||
group: "",
|
group: selectedGroup.value?.name || "",
|
||||||
|
household: "",
|
||||||
advanced: false,
|
advanced: false,
|
||||||
canInvite: false,
|
canInvite: false,
|
||||||
canManage: false,
|
canManage: false,
|
||||||
@ -70,6 +92,10 @@ export default defineComponent({
|
|||||||
authMethod: "Mealie",
|
authMethod: "Mealie",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
watch(selectedGroup, (newGroup) => {
|
||||||
|
state.newUserData.group = newGroup?.name || "";
|
||||||
|
state.newUserData.household = "";
|
||||||
|
});
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (!refNewUserForm.value?.validate()) return;
|
if (!refNewUserForm.value?.validate()) return;
|
||||||
@ -87,6 +113,8 @@ export default defineComponent({
|
|||||||
refNewUserForm,
|
refNewUserForm,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
groups,
|
groups,
|
||||||
|
selectedGroupId,
|
||||||
|
households,
|
||||||
validators,
|
validators,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<BaseCardSectionTitle :title="$tc('user.user-management')"> </BaseCardSectionTitle>
|
<BaseCardSectionTitle :title="$tc('user.user-management')"> </BaseCardSectionTitle>
|
||||||
<section>
|
<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">
|
<BaseButton to="/admin/manage/users/create" class="mr-2">
|
||||||
{{ $t("general.create") }}
|
{{ $t("general.create") }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -129,6 +129,7 @@ export default defineComponent({
|
|||||||
{ text: i18n.t("user.full-name"), value: "fullName" },
|
{ text: i18n.t("user.full-name"), value: "fullName" },
|
||||||
{ text: i18n.t("user.email"), value: "email" },
|
{ text: i18n.t("user.email"), value: "email" },
|
||||||
{ text: i18n.t("group.group"), value: "group" },
|
{ 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.auth-method"), value: "authMethod" },
|
||||||
{ text: i18n.t("user.admin"), value: "admin" },
|
{ text: i18n.t("user.admin"), value: "admin" },
|
||||||
{ text: i18n.t("general.delete"), value: "actions", sortable: false, align: "center" },
|
{ text: i18n.t("general.delete"), value: "actions", sortable: false, align: "center" },
|
||||||
|
@ -312,7 +312,6 @@ export default defineComponent({
|
|||||||
const preferences = {
|
const preferences = {
|
||||||
...data.preferences,
|
...data.preferences,
|
||||||
privateGroup: !commonSettings.value.makeGroupRecipesPublic,
|
privateGroup: !commonSettings.value.makeGroupRecipesPublic,
|
||||||
recipePublic: commonSettings.value.makeGroupRecipesPublic,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = {
|
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() {
|
async function seedFoods() {
|
||||||
const { response } = await api.seeders.foods({ locale: locale.value })
|
const { response } = await api.seeders.foods({ locale: locale.value })
|
||||||
if (!response || response.status !== 200) {
|
if (!response || response.status !== 200) {
|
||||||
@ -365,6 +390,7 @@ export default defineComponent({
|
|||||||
async function submitCommonSettings() {
|
async function submitCommonSettings() {
|
||||||
const tasks = [
|
const tasks = [
|
||||||
updateGroup(),
|
updateGroup(),
|
||||||
|
updateHousehold(),
|
||||||
seedData(),
|
seedData(),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -367,6 +367,11 @@ export default defineComponent({
|
|||||||
icon: $globals.icons.group,
|
icon: $globals.icons.group,
|
||||||
value: data.defaultGroup,
|
value: data.defaultGroup,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: i18n.t("about.default-household"),
|
||||||
|
icon: $globals.icons.household,
|
||||||
|
value: data.defaultHousehold,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
slot: "recipe-scraper",
|
slot: "recipe-scraper",
|
||||||
name: i18n.t("settings.recipe-scraper-version"),
|
name: i18n.t("settings.recipe-scraper-version"),
|
||||||
|
@ -24,17 +24,18 @@ export default defineComponent({
|
|||||||
|
|
||||||
const groupName = ref<string>("");
|
const groupName = ref<string>("");
|
||||||
const queryFilter = ref<string>("");
|
const queryFilter = ref<string>("");
|
||||||
async function fetchGroup() {
|
async function fetchHousehold() {
|
||||||
const { data } = await api.groups.getCurrentUserGroup();
|
const { data } = await api.households.getCurrentUserHousehold();
|
||||||
if (data) {
|
if (data) {
|
||||||
queryFilter.value = `recipe.group_id="${data.id}"`;
|
// TODO: once users are able to fetch other households' recipes, remove the household filter
|
||||||
groupName.value = data.name;
|
queryFilter.value = `recipe.group_id="${data.groupId}" AND recipe.household_id="${data.id}"`;
|
||||||
|
groupName.value = data.group;
|
||||||
}
|
}
|
||||||
|
|
||||||
ready.value = true;
|
ready.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchGroup();
|
fetchHousehold();
|
||||||
return {
|
return {
|
||||||
groupName,
|
groupName,
|
||||||
queryFilter,
|
queryFilter,
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
import { defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
|
||||||
import { validators } from "~/composables/use-validators";
|
import { validators } from "~/composables/use-validators";
|
||||||
import { useGroupRecipeActions, useGroupRecipeActionData } from "~/composables/use-group-recipe-actions";
|
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({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -25,139 +25,22 @@
|
|||||||
<DocLink class="mt-2" link="/documentation/getting-started/faq/#how-do-private-groups-and-recipes-work" />
|
<DocLink class="mt-2" link="/documentation/getting-started/faq/#how-do-private-groups-and-recipes-work" />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</section>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { useGroupSelf } from "~/composables/use-groups";
|
import { useGroupSelf } from "~/composables/use-groups";
|
||||||
import { ReadGroupPreferences } from "~/lib/api/types/group";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
middleware: ["auth", "can-manage-only"],
|
middleware: ["auth", "can-manage-only"],
|
||||||
setup() {
|
setup() {
|
||||||
const { group, actions: groupActions } = useGroupSelf();
|
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 {
|
return {
|
||||||
group,
|
group,
|
||||||
groupActions,
|
groupActions,
|
||||||
allDays,
|
|
||||||
preferencesEditor,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
head() {
|
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">
|
<div class="d-flex flex-wrap align-center justify-space-between mb-2">
|
||||||
<v-tabs style="width: fit-content;">
|
<v-tabs style="width: fit-content;">
|
||||||
<v-tab :to="`/group/mealplan/planner/view`">{{ $t('meal-plan.meal-planner') }}</v-tab>
|
<v-tab :to="`/household/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/edit`">{{ $t('general.edit') }}</v-tab>
|
||||||
</v-tabs>
|
</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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, ref, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, ref, useRoute, useRouter, watch } from "@nuxtjs/composition-api";
|
||||||
import { isSameDay, addDays, parseISO } from "date-fns";
|
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 { useMealplans } from "~/composables/use-group-mealplan";
|
||||||
import { useUserMealPlanPreferences } from "~/composables/use-users/preferences";
|
import { useUserMealPlanPreferences } from "~/composables/use-users/preferences";
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { group } = useGroupSelf();
|
const { household } = useHouseholdSelf();
|
||||||
|
|
||||||
const mealPlanPreferences = useUserMealPlanPreferences();
|
const mealPlanPreferences = useUserMealPlanPreferences();
|
||||||
const numberOfDays = ref<number>(mealPlanPreferences.value.numberOfDays || 7);
|
const numberOfDays = ref<number>(mealPlanPreferences.value.numberOfDays || 7);
|
||||||
@ -74,8 +74,8 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Force to /view if current route is /planner
|
// Force to /view if current route is /planner
|
||||||
if (route.value.path === "/group/mealplan/planner") {
|
if (route.value.path === "/household/mealplan/planner") {
|
||||||
router.push("/group/mealplan/planner/view");
|
router.push("/household/mealplan/planner/view");
|
||||||
}
|
}
|
||||||
|
|
||||||
function fmtYYYYMMDD(date: Date) {
|
function fmtYYYYMMDD(date: Date) {
|
||||||
@ -95,7 +95,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const firstDayOfWeek = computed(() => {
|
const firstDayOfWeek = computed(() => {
|
||||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const weekRange = computed(() => {
|
const weekRange = computed(() => {
|
@ -229,7 +229,7 @@ import { useMealplans, usePlanTypeOptions, getEntryTypeText } from "~/composable
|
|||||||
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
|
import RecipeCardImage from "~/components/Domain/Recipe/RecipeCardImage.vue";
|
||||||
import { PlanEntryType, UpdatePlanEntry } from "~/lib/api/types/meal-plan";
|
import { PlanEntryType, UpdatePlanEntry } from "~/lib/api/types/meal-plan";
|
||||||
import { useUserApi } from "~/composables/api";
|
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";
|
import { useRecipeSearch } from "~/composables/recipes/use-recipe-search";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -249,7 +249,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
const { group } = useGroupSelf();
|
const { household } = useHouseholdSelf();
|
||||||
|
|
||||||
const state = ref({
|
const state = ref({
|
||||||
dialog: false,
|
dialog: false,
|
||||||
@ -257,7 +257,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const firstDayOfWeek = computed(() => {
|
const firstDayOfWeek = computed(() => {
|
||||||
return group.value?.preferences?.firstDayOfWeek || 0;
|
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMoveCallback(evt: SortableEvent) {
|
function onMoveCallback(evt: SortableEvent) {
|
||||||
@ -308,7 +308,7 @@ export default defineComponent({
|
|||||||
entryType: "dinner" as PlanEntryType,
|
entryType: "dinner" as PlanEntryType,
|
||||||
existing: false,
|
existing: false,
|
||||||
id: 0,
|
id: 0,
|
||||||
groupId: ""
|
groupId: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
function openDialog(date: Date) {
|
function openDialog(date: Date) {
|
@ -54,7 +54,7 @@
|
|||||||
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||||
import { MealsByDate } from "./types";
|
import { MealsByDate } from "./types";
|
||||||
import { ReadPlanEntry } from "~/lib/api/types/meal-plan";
|
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 RecipeCardMobile from "~/components/Domain/Recipe/RecipeCardMobile.vue";
|
||||||
import { RecipeSummary } from "~/lib/api/types/recipe";
|
import { RecipeSummary } from "~/lib/api/types/recipe";
|
||||||
|
|
@ -89,7 +89,7 @@
|
|||||||
import { defineComponent, ref, useAsync } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, useAsync } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { PlanRulesCreate, PlanRulesOut } from "~/lib/api/types/meal-plan";
|
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 { useAsyncKey } from "~/composables/use-utils";
|
||||||
import RecipeChips from "~/components/Domain/Recipe/RecipeChips.vue";
|
import RecipeChips from "~/components/Domain/Recipe/RecipeChips.vue";
|
||||||
|
|
@ -97,7 +97,7 @@ export default defineComponent({
|
|||||||
];
|
];
|
||||||
|
|
||||||
async function refreshMembers() {
|
async function refreshMembers() {
|
||||||
const { data } = await api.groups.fetchMembers();
|
const { data } = await api.households.fetchMembers();
|
||||||
if (data) {
|
if (data) {
|
||||||
members.value = data;
|
members.value = data;
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ export default defineComponent({
|
|||||||
canOrganize: user.canOrganize,
|
canOrganize: user.canOrganize,
|
||||||
};
|
};
|
||||||
|
|
||||||
await api.groups.setMemberPermissions(payload);
|
await api.households.setMemberPermissions(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
@ -109,7 +109,7 @@
|
|||||||
import { defineComponent, useAsync, reactive, useContext, toRefs } from "@nuxtjs/composition-api";
|
import { defineComponent, useAsync, reactive, useContext, toRefs } from "@nuxtjs/composition-api";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import { useAsyncKey } from "~/composables/use-utils";
|
import { useAsyncKey } from "~/composables/use-utils";
|
||||||
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/lib/api/types/group";
|
import { GroupEventNotifierCreate, GroupEventNotifierOut } from "~/lib/api/types/household";
|
||||||
|
|
||||||
interface OptionKey {
|
interface OptionKey {
|
||||||
text: string;
|
text: string;
|
@ -45,7 +45,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "@nuxtjs/composition-api";
|
import { defineComponent } from "@nuxtjs/composition-api";
|
||||||
import { useGroupWebhooks, timeUTC } from "~/composables/use-group-webhooks";
|
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";
|
import { alert } from "~/composables/use-toast";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
@ -294,8 +294,8 @@ import { useCopyList } from "~/composables/use-copy";
|
|||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue"
|
||||||
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
|
||||||
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/group";
|
import { ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/household";
|
||||||
import { UserSummary } from "~/lib/api/types/user";
|
import { UserOut } from "~/lib/api/types/user";
|
||||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||||
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
|
||||||
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
|
||||||
@ -444,7 +444,7 @@ export default defineComponent({
|
|||||||
unchecked: shoppingList.value?.listItems?.filter((item) => !item.checked) ?? [],
|
unchecked: shoppingList.value?.listItems?.filter((item) => !item.checked) ?? [],
|
||||||
checked: shoppingList.value?.listItems
|
checked: shoppingList.value?.listItems
|
||||||
?.filter((item) => item.checked)
|
?.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;
|
item.position = shoppingList.value.listItems.length;
|
||||||
|
|
||||||
// set a temporary updatedAt timestamp prior to refresh so it appears at the top of the checked items
|
// 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
|
// make updates reflect immediately
|
||||||
@ -934,7 +934,7 @@ export default defineComponent({
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
createListItemData.value.createdAt = new Date().toISOString();
|
createListItemData.value.createdAt = new Date().toISOString();
|
||||||
createListItemData.value.updateAt = createListItemData.value.createdAt;
|
createListItemData.value.updatedAt = createListItemData.value.createdAt;
|
||||||
|
|
||||||
updateListItemOrder();
|
updateListItemOrder();
|
||||||
|
|
||||||
@ -1020,16 +1020,16 @@ export default defineComponent({
|
|||||||
// ===============================================================
|
// ===============================================================
|
||||||
// Shopping List Settings
|
// Shopping List Settings
|
||||||
|
|
||||||
const allUsers = ref<UserSummary[]>([]);
|
const allUsers = ref<UserOut[]>([]);
|
||||||
const currentUserId = ref<string | undefined>();
|
const currentUserId = ref<string | undefined>();
|
||||||
async function fetchAllUsers() {
|
async function fetchAllUsers() {
|
||||||
const { data } = await userApi.users.getGroupUsers(1, -1, { orderBy: "full_name", orderDirection: "asc" });
|
const { data } = await userApi.households.fetchMembers();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update current user
|
// update current user
|
||||||
allUsers.value = data.items;
|
allUsers.value = data.sort((a, b) => ((a.fullName || "") < (b.fullName || "") ? -1 : 1));
|
||||||
currentUserId.value = shoppingList.value?.userId;
|
currentUserId.value = shoppingList.value?.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<p class="subtitle-1 mb-0 text-center">
|
<p class="subtitle-1 mb-0 text-center">
|
||||||
{{ $t('profile.description') }}
|
{{ $t('profile.description') }}
|
||||||
</p>
|
</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-card-actions class="d-flex justify-center my-4">
|
||||||
<v-btn v-if="$auth.user.canInvite" outlined rounded @click="getSignupLink()">
|
<v-btn v-if="$auth.user.canInvite" outlined rounded @click="getSignupLink()">
|
||||||
<v-icon left>
|
<v-icon left>
|
||||||
@ -57,9 +57,9 @@
|
|||||||
<v-row tag="section">
|
<v-row tag="section">
|
||||||
<v-col cols="12" sm="12" md="12">
|
<v-col cols="12" sm="12" md="12">
|
||||||
<v-card outlined>
|
<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">
|
<v-card-text class="py-0">
|
||||||
{{ $t('profile.group-statistics-description') }}
|
{{ $t('profile.household-statistics-description') }}
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="d-flex flex-wrap justify-center align-center" style="gap: 0.8rem">
|
<v-card-text class="d-flex flex-wrap justify-center align-center" style="gap: 0.8rem">
|
||||||
<StatsCards
|
<StatsCards
|
||||||
@ -106,8 +106,66 @@
|
|||||||
</AdvancedOnly>
|
</AdvancedOnly>
|
||||||
</v-row>
|
</v-row>
|
||||||
</section>
|
</section>
|
||||||
<v-divider class="my-7"></v-divider>
|
<v-divider class="my-7" />
|
||||||
<section>
|
<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>
|
<div>
|
||||||
<h3 class="headline">{{ $t('group.group') }}</h3>
|
<h3 class="headline">{{ $t('group.group') }}</h3>
|
||||||
<p>{{ $t('profile.group-description') }}</p>
|
<p>{{ $t('profile.group-description') }}</p>
|
||||||
@ -122,46 +180,6 @@
|
|||||||
{{ $t('profile.group-settings-description') }}
|
{{ $t('profile.group-settings-description') }}
|
||||||
</UserProfileLinkCard>
|
</UserProfileLinkCard>
|
||||||
</v-col>
|
</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 -->
|
<!-- $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">
|
<v-col v-if="$auth.user.canOrganize" cols="12" sm="12" md="6">
|
||||||
<UserProfileLinkCard
|
<UserProfileLinkCard
|
||||||
@ -224,7 +242,7 @@ export default defineComponent({
|
|||||||
const api = useUserApi();
|
const api = useUserApi();
|
||||||
|
|
||||||
async function getSignupLink() {
|
async function getSignupLink() {
|
||||||
const { data } = await api.groups.createInvitation({ uses: 1 });
|
const { data } = await api.households.createInvitation({ uses: 1 });
|
||||||
if (data) {
|
if (data) {
|
||||||
token.value = data.token;
|
token.value = data.token;
|
||||||
generatedSignupLink.value = constructLink(data.token);
|
generatedSignupLink.value = constructLink(data.token);
|
||||||
@ -272,7 +290,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const stats = useAsync(async () => {
|
const stats = useAsync(async () => {
|
||||||
const { data } = await api.groups.statistics();
|
const { data } = await api.households.statistics();
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
return data;
|
return data;
|
||||||
@ -306,7 +324,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const statsTo = computed<{ [key: string]: string }>(() => { return {
|
const statsTo = computed<{ [key: string]: string }>(() => { return {
|
||||||
totalRecipes: `/g/${groupSlug.value}/`,
|
totalRecipes: `/g/${groupSlug.value}/`,
|
||||||
totalUsers: "/group/members",
|
totalUsers: "/household/members",
|
||||||
totalCategories: `/g/${groupSlug.value}/recipes/categories`,
|
totalCategories: `/g/${groupSlug.value}/recipes/categories`,
|
||||||
totalTags: `/g/${groupSlug.value}/recipes/tags`,
|
totalTags: `/g/${groupSlug.value}/recipes/tags`,
|
||||||
totalTools: `/g/${groupSlug.value}/recipes/tools`,
|
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)
|
repos = get_repositories(session)
|
||||||
group = repos.groups.get_by_slug_or_id(group_slug)
|
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")
|
raise HTTPException(404, "group not found")
|
||||||
else:
|
else:
|
||||||
return group
|
return group
|
||||||
@ -111,7 +111,7 @@ async def get_current_user(
|
|||||||
except PyJWTError as e:
|
except PyJWTError as e:
|
||||||
raise credentials_exception from 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)
|
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:
|
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})
|
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:
|
if self.__has_tried_user:
|
||||||
return self.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)
|
user = user = db.users.get_one(username, "username", any_case=True)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -23,7 +23,7 @@ class CredentialsProvider(AuthProvider[CredentialsRequest]):
|
|||||||
async def authenticate(self) -> tuple[str, timedelta] | None:
|
async def authenticate(self) -> tuple[str, timedelta] | None:
|
||||||
"""Attempt to authenticate a user given a username and password"""
|
"""Attempt to authenticate a user given a username and password"""
|
||||||
settings = get_app_settings()
|
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)
|
user = self.try_get_user(self.data.username)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -95,7 +95,7 @@ class LDAPProvider(CredentialsProvider):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
settings = get_app_settings()
|
settings = get_app_settings()
|
||||||
db = get_repositories(self.session)
|
db = get_repositories(self.session, group_id=None, household_id=None)
|
||||||
if not self.data:
|
if not self.data:
|
||||||
return None
|
return None
|
||||||
data = self.data
|
data = self.data
|
||||||
|
@ -33,7 +33,7 @@ class OpenIDProvider(AuthProvider[OIDCRequest]):
|
|||||||
if not claims:
|
if not claims:
|
||||||
return None
|
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))
|
user = self.try_get_user(claims.get(settings.OIDC_USER_CLAIM))
|
||||||
is_admin = False
|
is_admin = False
|
||||||
|
@ -179,6 +179,7 @@ class AppSettings(AppLoggingSettings):
|
|||||||
return self.DB_PROVIDER.db_url_public if self.DB_PROVIDER else None
|
return self.DB_PROVIDER.db_url_public if self.DB_PROVIDER else None
|
||||||
|
|
||||||
DEFAULT_GROUP: str = "Home"
|
DEFAULT_GROUP: str = "Home"
|
||||||
|
DEFAULT_HOUSEHOLD: str = "Family"
|
||||||
|
|
||||||
_DEFAULT_EMAIL: str = "changeme@example.com"
|
_DEFAULT_EMAIL: str = "changeme@example.com"
|
||||||
"""
|
"""
|
||||||
|
@ -26,7 +26,7 @@ SessionLocal, engine = sql_global_init(settings.DB_URL) # type: ignore
|
|||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def session_context() -> Session:
|
def session_context() -> Generator[Session, None, None]:
|
||||||
"""
|
"""
|
||||||
session_context() provides a managed session to the database that is automatically
|
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
|
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.core import root_logger
|
||||||
from mealie.db.models.group.group import Group
|
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.labels import MultiPurposeLabel
|
||||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||||
from mealie.db.models.recipe.recipe import RecipeModel
|
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