diff --git a/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py b/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py index fdc7046ca396..cdb5ca86a650 100644 --- a/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py +++ b/alembic/versions/2023-02-21-22.03.19_b04a08da2108_added_shopping_list_label_settings.py @@ -13,8 +13,7 @@ from sqlalchemy import orm import mealie.db.migration_types from alembic import op -from mealie.db.models.group.shopping_list import ShoppingList -from mealie.db.models.labels import MultiPurposeLabel +from mealie.db.models._model_utils.guid import GUID # revision identifiers, used by Alembic. revision = "b04a08da2108" @@ -23,6 +22,25 @@ branch_labels = None depends_on = None +# Intermediate table definitions +class SqlAlchemyBase(orm.DeclarativeBase): + pass + + +class ShoppingList(SqlAlchemyBase): + __tablename__ = "shopping_lists" + + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True) + + +class MultiPurposeLabel(SqlAlchemyBase): + __tablename__ = "multi_purpose_labels" + + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True) + + def populate_shopping_lists_multi_purpose_labels( shopping_lists_multi_purpose_labels_table: sa.Table, session: orm.Session ): diff --git a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py index 57a59a077125..5fc44eee66ce 100644 --- a/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py +++ b/alembic/versions/2023-10-04-14.29.26_dded3119c1fe_added_unique_constraints.py @@ -12,11 +12,11 @@ from typing import Any import sqlalchemy as sa from pydantic import UUID4 +from sqlalchemy import orm from sqlalchemy.orm import Session, load_only from alembic import op -from mealie.db.models._model_base import SqlAlchemyBase -from mealie.db.models.group.shopping_list import ShoppingListItem +from mealie.db.models._model_utils.guid import GUID from mealie.db.models.labels import MultiPurposeLabel from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel @@ -27,6 +27,27 @@ branch_labels = None depends_on = None +# Intermediate table definitions +class SqlAlchemyBase(orm.DeclarativeBase): + pass + + +class ShoppingList(SqlAlchemyBase): + __tablename__ = "shopping_lists" + + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + group_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("groups.id"), nullable=False, index=True) + + +class ShoppingListItem(SqlAlchemyBase): + __tablename__ = "shopping_list_items" + + id: orm.Mapped[GUID] = orm.mapped_column(GUID, primary_key=True, default=GUID.generate) + food_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_foods.id")) + unit_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("ingredient_units.id")) + label_id: orm.Mapped[GUID] = orm.mapped_column(GUID, sa.ForeignKey("multi_purpose_labels.id")) + + @dataclass class TableMeta: tablename: str @@ -42,7 +63,7 @@ def _is_postgres(): return op.get_context().dialect.name == "postgresql" -def _get_duplicates(session: Session, model: SqlAlchemyBase) -> defaultdict[str, list]: +def _get_duplicates(session: Session, model: orm.DeclarativeBase) -> defaultdict[str, list]: duplicate_map: defaultdict[str, list] = defaultdict(list) query = session.execute(sa.text(f"SELECT id, group_id, name FROM {model.__tablename__}")) diff --git a/alembic/versions/2024-07-12-16.16.29_feecc8ffb956_add_households.py b/alembic/versions/2024-07-12-16.16.29_feecc8ffb956_add_households.py new file mode 100644 index 000000000000..9436f7301721 --- /dev/null +++ b/alembic/versions/2024-07-12-16.16.29_feecc8ffb956_add_households.py @@ -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 ### diff --git a/dev/code-generation/gen_py_pytest_data_paths.py b/dev/code-generation/gen_py_pytest_data_paths.py index 51cdd854ef28..02313d819dff 100644 --- a/dev/code-generation/gen_py_pytest_data_paths.py +++ b/dev/code-generation/gen_py_pytest_data_paths.py @@ -67,7 +67,7 @@ def rename_non_compliant_paths(): kabab case. """ - ignore_files = ["DS_Store", ".gitkeep"] + ignore_files = ["DS_Store", ".gitkeep", "af-ZA.json", "en-US.json"] ignore_extensions = [".pyc", ".pyo", ".py"] diff --git a/dev/code-generation/utils/template.py b/dev/code-generation/utils/template.py index eb4962a639e0..6312426e296a 100644 --- a/dev/code-generation/utils/template.py +++ b/dev/code-generation/utils/template.py @@ -1,5 +1,6 @@ import logging import re +import subprocess from dataclasses import dataclass from pathlib import Path @@ -23,6 +24,11 @@ def render_python_template(template_file: Path | str, dest: Path, data: dict): dest.write_text(text) + # lint/format file with Ruff + log.info(f"Formatting {dest}") + subprocess.run(["poetry", "run", "ruff", "check", str(dest), "--fix"]) + subprocess.run(["poetry", "run", "ruff", "format", str(dest)]) + @dataclass class CodeSlicer: diff --git a/dev/scripts/all_recipes_stress_test.py b/dev/scripts/all_recipes_stress_test.py index 510e7b231290..0ce27cb13726 100644 --- a/dev/scripts/all_recipes_stress_test.py +++ b/dev/scripts/all_recipes_stress_test.py @@ -173,7 +173,7 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "dateAdded": "2022-09-03", "dateUpdated": "2022-09-10T15:18:19.866085", "createdAt": "2022-09-03T18:31:17.488118", - "updateAt": "2022-09-10T15:18:19.869630", + "updatedAt": "2022-09-10T15:18:19.869630", "recipeInstructions": [ { "id": "60ae53a3-b3ff-40ee-bae3-89fea0b1c637", diff --git a/docs/docs/documentation/community-guide/home-assistant.md b/docs/docs/documentation/community-guide/home-assistant.md index 79a926555cf6..5f35d80afe5e 100644 --- a/docs/docs/documentation/community-guide/home-assistant.md +++ b/docs/docs/documentation/community-guide/home-assistant.md @@ -24,7 +24,7 @@ Make sure the url and port (`http://mealie:9000` ) matches your installation's a ```yaml - platform: rest - resource: "http://mealie:9000/api/groups/mealplans/today" + resource: "http://mealie:9000/api/households/mealplans/today" method: GET name: Mealie todays meal headers: @@ -36,7 +36,7 @@ Make sure the url and port (`http://mealie:9000` ) matches your installation's a ```yaml - platform: rest - resource: "http://mealie:9000/api/groups/mealplans/today" + resource: "http://mealie:9000/api/households/mealplans/today" method: GET name: Mealie todays meal ID headers: diff --git a/docs/docs/documentation/getting-started/features.md b/docs/docs/documentation/getting-started/features.md index 9d0d58533d71..b9ee0cebd675 100644 --- a/docs/docs/documentation/getting-started/features.md +++ b/docs/docs/documentation/getting-started/features.md @@ -73,13 +73,13 @@ Mealie uses a calendar like view to help you plan your meals. It shows you the p !!! tip You can also add a "Note" type entry to your meal-plan when you want to include something that might not have a specific recipes. This is great for leftovers, or for ordering out. -[Mealplanner Demo](https://demo.mealie.io/group/mealplan/planner/view){ .md-button .md-button--primary } +[Mealplanner Demo](https://demo.mealie.io/household/mealplan/planner/view){ .md-button .md-button--primary } ### Planner Rules The meal planner has the concept of plan rules. These offer a flexible way to use your organizers to customize how a random recipe is inserted into your meal plan. You can set rules to restrict the pool of recipes based on the Tags and/or Categories of a recipe. Additionally, since meal plans have a Breakfast, Lunch, Dinner, and Snack labels, you can specifically set a rule to be active for a **specific meal type** or even a **specific day of the week.** -[Planner Settings Demo](https://demo.mealie.io/group/mealplan/settings){ .md-button .md-button--primary } +[Planner Settings Demo](https://demo.mealie.io/household/mealplan/settings){ .md-button .md-button--primary } ## Shopping Lists @@ -105,13 +105,13 @@ Notifiers use the [Apprise library](https://github.com/caronc/apprise/wiki), whi - `json` and `jsons` - `xml` and `xmls` -[Notifiers Demo](https://demo.mealie.io/group/notifiers){ .md-button .md-button--primary } +[Notifiers Demo](https://demo.mealie.io/household/notifiers){ .md-button .md-button--primary } ### Webhooks Unlike notifiers, which are event-driven notifications, Webhooks allow you to send scheduled notifications to your desired endpoint. Webhooks are sent on the day of a scheduled mealplan, at the specified time, and contain the mealplan data in the request. -[Webhooks Demo](https://demo.mealie.io/group/webhooks){ .md-button .md-button--primary } +[Webhooks Demo](https://demo.mealie.io/household/webhooks){ .md-button .md-button--primary } ### Recipe Actions diff --git a/docs/docs/overrides/api.html b/docs/docs/overrides/api.html index 334025e5eea6..c1b7fc8ada20 100644 --- a/docs/docs/overrides/api.html +++ b/docs/docs/overrides/api.html @@ -14,7 +14,7 @@
diff --git a/frontend/components/Domain/Group/GroupPreferencesEditor.vue b/frontend/components/Domain/Group/GroupPreferencesEditor.vue index 557f67ef273f..4fab77b6796d 100644 --- a/frontend/components/Domain/Group/GroupPreferencesEditor.vue +++ b/frontend/components/Domain/Group/GroupPreferencesEditor.vue @@ -2,30 +2,11 @@
- - - -
+ + diff --git a/frontend/components/Domain/Recipe/RecipeCardSection.vue b/frontend/components/Domain/Recipe/RecipeCardSection.vue index ecdfe9e7a33a..46d570c6acdc 100644 --- a/frontend/components/Domain/Recipe/RecipeCardSection.vue +++ b/frontend/components/Domain/Recipe/RecipeCardSection.vue @@ -337,7 +337,7 @@ export default defineComponent({ ); break; case EVENTS.updated: - setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false); + setter("updated_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false); break; case EVENTS.lastMade: setter( diff --git a/frontend/components/Domain/Recipe/RecipeContextMenu.vue b/frontend/components/Domain/Recipe/RecipeContextMenu.vue index 3e1e27edcf10..ebebeec647e2 100644 --- a/frontend/components/Domain/Recipe/RecipeContextMenu.vue +++ b/frontend/components/Domain/Recipe/RecipeContextMenu.vue @@ -138,11 +138,11 @@ import RecipeDialogShare from "./RecipeDialogShare.vue"; import { useLoggedInState } from "~/composables/use-logged-in-state"; import { useUserApi } from "~/composables/api"; import { useGroupRecipeActions } from "~/composables/use-group-recipe-actions"; -import { useGroupSelf } from "~/composables/use-groups"; +import { useHouseholdSelf } from "~/composables/use-households"; import { alert } from "~/composables/use-toast"; import { usePlanTypeOptions } from "~/composables/use-group-mealplan"; import { Recipe } from "~/lib/api/types/recipe"; -import { GroupRecipeActionOut, ShoppingListSummary } from "~/lib/api/types/group"; +import { GroupRecipeActionOut, ShoppingListSummary } from "~/lib/api/types/household"; import { PlanEntryType } from "~/lib/api/types/meal-plan"; import { useAxiosDownloader } from "~/composables/api/use-axios-download"; @@ -254,14 +254,14 @@ export default defineComponent({ }); const { i18n, $auth, $globals } = useContext(); - const { group } = useGroupSelf(); + const { household } = useHouseholdSelf(); const { isOwnGroup } = useLoggedInState(); const route = useRoute(); const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || ""); const firstDayOfWeek = computed(() => { - return group.value?.preferences?.firstDayOfWeek || 0; + return household.value?.preferences?.firstDayOfWeek || 0; }); // =========================================================================== diff --git a/frontend/components/Domain/Recipe/RecipeDataTable.vue b/frontend/components/Domain/Recipe/RecipeDataTable.vue index 755ae7ec5d8c..fd8900004ced 100644 --- a/frontend/components/Domain/Recipe/RecipeDataTable.vue +++ b/frontend/components/Domain/Recipe/RecipeDataTable.vue @@ -18,7 +18,7 @@ diff --git a/frontend/pages/admin/manage/households/index.vue b/frontend/pages/admin/manage/households/index.vue new file mode 100644 index 000000000000..02c36914c803 --- /dev/null +++ b/frontend/pages/admin/manage/households/index.vue @@ -0,0 +1,167 @@ + + + diff --git a/frontend/pages/admin/manage/users/_id.vue b/frontend/pages/admin/manage/users/_id.vue index 2c57d54bfab2..6982658a6689 100644 --- a/frontend/pages/admin/manage/users/_id.vue +++ b/frontend/pages/admin/manage/users/_id.vue @@ -14,9 +14,11 @@

{{ $t("user.user-id-with-value", {id: user.id} ) }}

+ + /> +
{{ $t("user.generate-password-reset-link") }} @@ -65,6 +80,7 @@ import { computed, defineComponent, useRoute, onMounted, ref, useContext } from "@nuxtjs/composition-api"; import { useAdminApi, useUserApi } from "~/composables/api"; import { useGroups } from "~/composables/use-groups"; +import { useHouseholds } from "~/composables/use-households"; import { alert } from "~/composables/use-toast"; import { useUserForm } from "~/composables/use-users"; import { validators } from "~/composables/use-validators"; @@ -76,6 +92,7 @@ export default defineComponent({ setup() { const { userForm } = useUserForm(); const { groups } = useGroups(); + const { useHouseholdsInGroup } = useHouseholds(); const { i18n } = useContext(); const route = useRoute(); @@ -89,6 +106,8 @@ export default defineComponent({ const adminApi = useAdminApi(); const user = ref(null); + const households = useHouseholdsInGroup(computed(() => user.value?.groupId || "")); + const disabledFields = computed(() => { return user.value?.authMethod !== "Mealie" ? ["admin"] : []; }) @@ -154,6 +173,7 @@ export default defineComponent({ refNewUserForm, handleSubmit, groups, + households, validators, handlePasswordReset, resetUrl, diff --git a/frontend/pages/admin/manage/users/create.vue b/frontend/pages/admin/manage/users/create.vue index 26a4ff7680fe..0d0e380e2394 100644 --- a/frontend/pages/admin/manage/users/create.vue +++ b/frontend/pages/admin/manage/users/create.vue @@ -12,17 +12,30 @@ + + /> @@ -34,9 +47,10 @@ + + diff --git a/frontend/pages/group/mealplan/planner.vue b/frontend/pages/household/mealplan/planner.vue similarity index 88% rename from frontend/pages/group/mealplan/planner.vue rename to frontend/pages/household/mealplan/planner.vue index 64b039a52fcf..09703feef101 100644 --- a/frontend/pages/group/mealplan/planner.vue +++ b/frontend/pages/household/mealplan/planner.vue @@ -39,10 +39,10 @@
- {{ $t('meal-plan.meal-planner') }} - {{ $t('general.edit') }} + {{ $t('meal-plan.meal-planner') }} + {{ $t('general.edit') }} - +
@@ -56,7 +56,7 @@