1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-02 20:15:24 +02:00

feat: Add Households to Mealie (#3970)

This commit is contained in:
Michael Genson 2024-08-22 10:14:32 -05:00 committed by GitHub
parent 0c29cef17d
commit eb170cc7e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
315 changed files with 6975 additions and 3577 deletions

View file

@ -2,30 +2,11 @@
<div v-if="preferences">
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" :label="$t('group.private-group')"></v-checkbox>
<v-select
v-model="preferences.firstDayOfWeek"
:prepend-icon="$globals.icons.calendarWeekBegin"
:items="allDays"
item-text="name"
item-value="value"
:label="$t('settings.first-day-of-week')"
/>
<BaseCardSectionTitle class="mt-5" :title="$tc('group.group-recipe-preferences')"></BaseCardSectionTitle>
<template v-for="(_, key) in preferences">
<v-checkbox
v-if="labels[key]"
:key="key"
v-model="preferences[key]"
class="mt-n4"
:label="labels[key]"
></v-checkbox>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, useContext } from "@nuxtjs/composition-api";
import { defineComponent, computed } from "@nuxtjs/composition-api";
export default defineComponent({
props: {
@ -35,48 +16,6 @@ export default defineComponent({
},
},
setup(props, context) {
const { i18n } = useContext();
const labels = {
recipePublic: i18n.tc("group.allow-users-outside-of-your-group-to-see-your-recipes"),
recipeShowNutrition: i18n.tc("group.show-nutrition-information"),
recipeShowAssets: i18n.tc("group.show-recipe-assets"),
recipeLandscapeView: i18n.tc("group.default-to-landscape-view"),
recipeDisableComments: i18n.tc("group.disable-users-from-commenting-on-recipes"),
recipeDisableAmount: i18n.tc("group.disable-organizing-recipe-ingredients-by-units-and-food"),
};
const allDays = [
{
name: i18n.t("general.sunday"),
value: 0,
},
{
name: i18n.t("general.monday"),
value: 1,
},
{
name: i18n.t("general.tuesday"),
value: 2,
},
{
name: i18n.t("general.wednesday"),
value: 3,
},
{
name: i18n.t("general.thursday"),
value: 4,
},
{
name: i18n.t("general.friday"),
value: 5,
},
{
name: i18n.t("general.saturday"),
value: 6,
},
];
const preferences = computed({
get() {
return props.value;
@ -87,8 +26,6 @@ export default defineComponent({
});
return {
allDays,
labels,
preferences,
};
},

View file

@ -39,7 +39,7 @@
import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@nuxtjs/composition-api";
import { Recipe } from "~/lib/api/types/recipe";
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
import { ShoppingListSummary } from "~/lib/api/types/group";
import { ShoppingListSummary } from "~/lib/api/types/household";
import { useUserApi } from "~/composables/api";
export interface ContextMenuItem {

View file

@ -35,7 +35,7 @@
<script lang="ts">
import { defineComponent, computed, ref } from "@nuxtjs/composition-api";
import { ReadWebhook } from "~/lib/api/types/group";
import { ReadWebhook } from "~/lib/api/types/household";
import { timeLocalToUTC, timeUTCToLocal } from "~/composables/use-group-webhooks";
export default defineComponent({

View file

@ -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>

View file

@ -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(

View file

@ -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;
});
// ===========================================================================

View file

@ -18,7 +18,7 @@
</tr>
</template>
<template #item.name="{ item }">
<a :href="`/r/${item.slug}`" style="color: inherit; text-decoration: inherit; " @click="$emit('click')">{{ item.name }}</a>
<a :href="`/g/${groupSlug}/r/${item.slug}`" style="color: inherit; text-decoration: inherit; " @click="$emit('click')">{{ item.name }}</a>
</template>
<template #item.tags="{ item }">
<RecipeChip small :items="item.tags" :is-category="false" url-prefix="tags" />
@ -48,7 +48,7 @@ import UserAvatar from "../User/UserAvatar.vue";
import RecipeChip from "./RecipeChips.vue";
import { Recipe } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api";
import { UserOut } from "~/lib/api/types/user";
import { UserSummary } from "~/lib/api/types/user";
const INPUT_EVENT = "input";
@ -95,7 +95,8 @@ export default defineComponent({
},
},
setup(props, context) {
const { i18n } = useContext();
const { $auth, i18n } = useContext();
const groupSlug = $auth.user?.groupSlug;
function setValue(value: Recipe[]) {
context.emit(INPUT_EVENT, value);
@ -134,7 +135,7 @@ export default defineComponent({
// ============
// Group Members
const api = useUserApi();
const members = ref<UserOut[]>([]);
const members = ref<UserSummary[]>([]);
async function refreshMembers() {
const { data } = await api.groups.fetchMembers();
@ -149,13 +150,19 @@ export default defineComponent({
function getMember(id: string) {
if (members.value[0]) {
return members.value.find((m) => m.id === id)?.username;
return members.value.find((m) => m.id === id)?.fullName;
}
return i18n.t("general.none");
}
return { setValue, headers, members, getMember };
return {
groupSlug,
setValue,
headers,
members,
getMember,
};
},
data() {

View file

@ -127,14 +127,14 @@
</template>
<script lang="ts">
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api"
import { toRefs } from "@vueuse/core"
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue"
import { useUserApi } from "~/composables/api"
import { alert } from "~/composables/use-toast"
import { useShoppingListPreferences } from "~/composables/use-users/preferences"
import { ShoppingListSummary } from "~/lib/api/types/group"
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe"
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api";
import { toRefs } from "@vueuse/core";
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
import { ShoppingListSummary } from "~/lib/api/types/household";
import { Recipe, RecipeIngredient } from "~/lib/api/types/recipe";
export interface RecipeWithScale extends Recipe {
scale: number;
@ -209,7 +209,8 @@ export default defineComponent({
watchEffect(
() => {
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
openShoppingListIngredientDialog(shoppingListChoices.value[0]);
selectedShoppingList.value = shoppingListChoices.value[0];
openShoppingListIngredientDialog(selectedShoppingList.value);
} else {
ready.value = true;
}
@ -365,12 +366,8 @@ export default defineComponent({
}
})
const successMessage = promises.length === 1
? i18n.t("recipe.successfully-added-to-list") as string
: i18n.t("recipe.failed-to-add-to-list") as string;
success ? alert.success(successMessage)
: alert.error(i18n.t("failed-to-add-recipes-to-list") as string)
success ? alert.success(i18n.tc("recipe.successfully-added-to-list"))
: alert.error(i18n.tc("failed-to-add-recipes-to-list"))
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = false;

View file

@ -66,7 +66,7 @@ import { defineComponent, computed, toRefs, reactive, useContext, useRoute } fro
import { useClipboard, useShare, whenever } from "@vueuse/core";
import { RecipeShareToken } from "~/lib/api/types/recipe";
import { useUserApi } from "~/composables/api";
import { useGroupSelf } from "~/composables/use-groups";
import { useHouseholdSelf } from "~/composables/use-households";
import { alert } from "~/composables/use-toast";
export default defineComponent({
@ -113,12 +113,12 @@ export default defineComponent({
);
const { $auth, i18n } = useContext();
const { group } = useGroupSelf();
const { household } = useHouseholdSelf();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const firstDayOfWeek = computed(() => {
return group.value?.preferences?.firstDayOfWeek || 0;
return household.value?.preferences?.firstDayOfWeek || 0;
});
// ============================================================

View file

@ -349,7 +349,7 @@ export default defineComponent({
{
icon: $globals.icons.update,
name: i18n.tc("general.updated"),
value: "update_at",
value: "updated_at",
},
{
icon: $globals.icons.diceMultiple,

View file

@ -11,7 +11,7 @@
</template>
<script lang="ts">
import { computed, defineComponent } from "@nuxtjs/composition-api";
import { RecipeIngredient } from "~/lib/api/types/group";
import { RecipeIngredient } from "~/lib/api/types/household";
import { useParsedIngredientText } from "~/composables/recipes";
export default defineComponent({

View file

@ -114,7 +114,7 @@ import { computed, defineComponent, reactive, ref, toRefs, useContext } from "@n
import { whenever } from "@vueuse/core";
import { VForm } from "~/types/vuetify";
import { useUserApi } from "~/composables/api";
import { useGroupSelf } from "~/composables/use-groups";
import { useHouseholdSelf } from "~/composables/use-households";
import { Recipe, RecipeTimelineEventIn } from "~/lib/api/types/recipe";
export default defineComponent({
@ -131,7 +131,7 @@ export default defineComponent({
setup(props, context) {
const madeThisDialog = ref(false);
const userApi = useUserApi();
const { group } = useGroupSelf();
const { household } = useHouseholdSelf();
const { $auth, i18n } = useContext();
const domMadeThisForm = ref<VForm>();
const newTimelineEvent = ref<RecipeTimelineEventIn>({
@ -157,7 +157,7 @@ export default defineComponent({
);
const firstDayOfWeek = computed(() => {
return group.value?.preferences?.firstDayOfWeek || 0;
return household.value?.preferences?.firstDayOfWeek || 0;
});
function clearImage() {

View file

@ -31,7 +31,7 @@
import { computed, defineComponent, useContext, useRoute } from "@nuxtjs/composition-api";
import DOMPurify from "dompurify";
import { useFraction } from "~/composables/recipes/use-fraction";
import { ShoppingListItemOut } from "~/lib/api/types/group";
import { ShoppingListItemOut } from "~/lib/api/types/household";
import { RecipeSummary } from "~/lib/api/types/recipe";
export default defineComponent({

View file

@ -29,7 +29,7 @@
<script lang="ts">
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/group";
import { ShoppingListMultiPurposeLabelOut } from "~/lib/api/types/household";
interface actions {
text: string;

View file

@ -75,7 +75,7 @@
<v-row v-if="listItem.checked" no-gutters class="mb-2">
<v-col cols="auto">
<div class="text-caption font-weight-light font-italic">
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updateAt || "").toLocaleDateString($i18n.locale)}) }}
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updatedAt || "").toLocaleDateString($i18n.locale)}) }}
</div>
</v-col>
</v-row>
@ -99,7 +99,7 @@ import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
import { ShoppingListItemOut } from "~/lib/api/types/group";
import { ShoppingListItemOut } from "~/lib/api/types/household";
import { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";

View file

@ -111,7 +111,7 @@
<script lang="ts">
import { defineComponent, computed, watch } from "@nuxtjs/composition-api";
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/group";
import { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";

View file

@ -183,7 +183,7 @@ export default defineComponent({
{
icon: $globals.icons.calendarMultiselect,
title: i18n.tc("meal-plan.meal-planner"),
to: "/group/mealplan/planner/view",
to: "/household/mealplan/planner/view",
restricted: true,
},
{

View file

@ -1,5 +1,5 @@
<template>
<v-toolbar flat>
<v-toolbar color="transparent" flat>
<BaseButton color="null" rounded secondary @click="$router.go(-1)">
<template #icon> {{ $globals.icons.arrowLeftBold }}</template>
{{ $t('general.back') }}

View file

@ -85,6 +85,8 @@
:label="inputField.label"
:name="inputField.varName"
:items="inputField.options"
:item-text="inputField.itemText"
:item-value="inputField.itemValue"
:return-object="false"
:hint="inputField.hint"
persistent-hint