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

feat: Move "on hand" and "last made" to household (#4616)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2025-01-13 10:19:49 -06:00 committed by GitHub
parent e565b919df
commit e9892aba89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1618 additions and 400 deletions

View file

@ -1,7 +1,7 @@
<template>
<div>
<!-- Merge Dialog -->
<BaseDialog v-model="mergeDialog" :icon="$globals.icons.foods" :title="$t('data-pages.foods.combine-food')" @confirm="mergeFoods">
<BaseDialog v-model="mergeDialog" :icon="$globals.icons.foods" :title="$tc('data-pages.foods.combine-food')" @confirm="mergeFoods">
<v-card-text>
<div>
{{ $t("data-pages.foods.merge-dialog-text") }}
@ -58,7 +58,7 @@
<BaseDialog
v-model="createDialog"
:icon="$globals.icons.foods"
:title="$t('data-pages.foods.create-food')"
:title="$tc('data-pages.foods.create-food')"
:submit-icon="$globals.icons.save"
:submit-text="$tc('general.save')"
@submit="createFood"
@ -111,7 +111,7 @@
<BaseDialog
v-model="editDialog"
:icon="$globals.icons.foods"
:title="$t('data-pages.foods.edit-food')"
:title="$tc('data-pages.foods.edit-food')"
:submit-icon="$globals.icons.save"
:submit-text="$tc('general.save')"
@submit="editSaveFood"
@ -196,7 +196,7 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Asign Labels Dialog -->
<!-- Bulk Assign Labels Dialog -->
<BaseDialog
v-model="bulkAssignLabelDialog"
:title="$tc('data-pages.labels.assign-label')"
@ -292,11 +292,20 @@ import { useFoodStore, useLabelStore } from "~/composables/store";
import { VForm } from "~/types/vuetify";
import { MultiPurposeLabelOut } from "~/lib/api/types/labels";
interface CreateIngredientFoodWithOnHand extends CreateIngredientFood {
onHand: boolean;
householdsWithIngredientFood: string[];
}
interface IngredientFoodWithOnHand extends IngredientFood {
onHand: boolean;
}
export default defineComponent({
components: { MultiPurposeLabel, RecipeDataAliasManagerDialog },
setup() {
const userApi = useUserApi();
const { i18n } = useContext();
const { $auth, i18n } = useContext();
const tableConfig = {
hideColumns: true,
canExport: true,
@ -352,15 +361,22 @@ export default defineComponent({
}
}
const userHousehold = computed(() => $auth.user?.householdSlug || "");
const foodStore = useFoodStore();
const foods = computed(() => foodStore.store.value.map((food) => {
const onHand = food.householdsWithIngredientFood?.includes(userHousehold.value) || false;
return { ...food, onHand } as IngredientFoodWithOnHand;
}));
// ===============================================================
// Food Creator
const domNewFoodForm = ref<VForm>();
const createDialog = ref(false);
const createTarget = ref<CreateIngredientFood>({
const createTarget = ref<CreateIngredientFoodWithOnHand>({
name: "",
onHand: false,
householdsWithIngredientFood: [],
});
function createEventHandler() {
@ -372,6 +388,10 @@ export default defineComponent({
return;
}
if (createTarget.value.onHand) {
createTarget.value.householdsWithIngredientFood = [userHousehold.value];
}
// @ts-expect-error the createOne function erroneously expects an id because it uses the IngredientFood type
await foodStore.actions.createOne(createTarget.value);
createDialog.value = false;
@ -379,6 +399,8 @@ export default defineComponent({
domNewFoodForm.value?.reset();
createTarget.value = {
name: "",
onHand: false,
householdsWithIngredientFood: [],
};
}
@ -386,10 +408,11 @@ export default defineComponent({
// Food Editor
const editDialog = ref(false);
const editTarget = ref<IngredientFood | null>(null);
const editTarget = ref<IngredientFoodWithOnHand | null>(null);
function editEventHandler(item: IngredientFood) {
function editEventHandler(item: IngredientFoodWithOnHand) {
editTarget.value = item;
editTarget.value.onHand = item.householdsWithIngredientFood?.includes(userHousehold.value) || false;
editDialog.value = true;
}
@ -397,6 +420,17 @@ export default defineComponent({
if (!editTarget.value) {
return;
}
if (editTarget.value.onHand && !editTarget.value.householdsWithIngredientFood?.includes(userHousehold.value)) {
if (!editTarget.value.householdsWithIngredientFood) {
editTarget.value.householdsWithIngredientFood = [userHousehold.value];
} else {
editTarget.value.householdsWithIngredientFood.push(userHousehold.value);
}
} else if (!editTarget.value.onHand && editTarget.value.householdsWithIngredientFood?.includes(userHousehold.value)) {
editTarget.value.householdsWithIngredientFood = editTarget.value.householdsWithIngredientFood.filter(
(household) => household !== userHousehold.value
);
}
await foodStore.actions.updateOne(editTarget.value);
editDialog.value = false;
@ -406,8 +440,8 @@ export default defineComponent({
// Food Delete
const deleteDialog = ref(false);
const deleteTarget = ref<IngredientFood | null>(null);
function deleteEventHandler(item: IngredientFood) {
const deleteTarget = ref<IngredientFoodWithOnHand | null>(null);
function deleteEventHandler(item: IngredientFoodWithOnHand) {
deleteTarget.value = item;
deleteDialog.value = true;
}
@ -421,9 +455,9 @@ export default defineComponent({
}
const bulkDeleteDialog = ref(false);
const bulkDeleteTarget = ref<IngredientFood[]>([]);
const bulkDeleteTarget = ref<IngredientFoodWithOnHand[]>([]);
function bulkDeleteEventHandler(selection: IngredientFood[]) {
function bulkDeleteEventHandler(selection: IngredientFoodWithOnHand[]) {
bulkDeleteTarget.value = selection;
bulkDeleteDialog.value = true;
}
@ -455,8 +489,8 @@ export default defineComponent({
// Merge Foods
const mergeDialog = ref(false);
const fromFood = ref<IngredientFood | null>(null);
const toFood = ref<IngredientFood | null>(null);
const fromFood = ref<IngredientFoodWithOnHand | null>(null);
const toFood = ref<IngredientFoodWithOnHand | null>(null);
const canMerge = computed(() => {
return fromFood.value && toFood.value && fromFood.value.id !== toFood.value.id;
@ -506,10 +540,10 @@ export default defineComponent({
// ============================================================
// Bulk Assign Labels
const bulkAssignLabelDialog = ref(false);
const bulkAssignTarget = ref<IngredientFood[]>([]);
const bulkAssignTarget = ref<IngredientFoodWithOnHand[]>([]);
const bulkAssignLabelId = ref<string | undefined>();
function bulkAssignEventHandler(selection: IngredientFood[]) {
function bulkAssignEventHandler(selection: IngredientFoodWithOnHand[]) {
bulkAssignTarget.value = selection;
bulkAssignLabelDialog.value = true;
}
@ -530,7 +564,7 @@ export default defineComponent({
return {
tableConfig,
tableHeaders,
foods: foodStore.store,
foods,
allLabels,
validators,
formatDate,

View file

@ -109,11 +109,6 @@
<template #button-row>
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
</template>
<template #item.onHand="{ item }">
<v-icon :color="item.onHand ? 'success' : undefined">
{{ item.onHand ? $globals.icons.check : $globals.icons.close }}
</v-icon>
</template>
</CrudTable>
</div>
</template>

View file

@ -101,14 +101,18 @@
</template>
<script lang="ts">
import { defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
import { computed, defineComponent, reactive, ref, useContext } from "@nuxtjs/composition-api";
import { validators } from "~/composables/use-validators";
import { useToolStore, useToolData } from "~/composables/store";
import { RecipeTool } from "~/lib/api/types/admin";
import { RecipeTool } from "~/lib/api/types/recipe";
interface RecipeToolWithOnHand extends RecipeTool {
onHand: boolean;
}
export default defineComponent({
setup() {
const { i18n } = useContext();
const { $auth, i18n } = useContext();
const tableConfig = {
hideColumns: true,
canExport: true,
@ -138,27 +142,38 @@ export default defineComponent({
bulkDeleteDialog: false,
});
const userHousehold = computed(() => $auth.user?.householdSlug || "");
const toolData = useToolData();
const toolStore = useToolStore();
const tools = computed(() => toolStore.store.value.map((tools) => {
const onHand = tools.householdsWithTool?.includes(userHousehold.value) || false;
return { ...tools, onHand } as RecipeToolWithOnHand;
}));
// ============================================================
// Create Tag
// Create Tool
async function createTool() {
if (toolData.data.onHand) {
toolData.data.householdsWithTool = [userHousehold.value];
} else {
toolData.data.householdsWithTool = [];
}
// @ts-ignore - only property really required is the name and onHand (RecipeOrganizerPage)
await toolStore.actions.createOne({ name: toolData.data.name, onHand: toolData.data.onHand });
await toolStore.actions.createOne({ name: toolData.data.name, householdsWithTool: toolData.data.householdsWithTool });
toolData.reset();
state.createDialog = false;
}
// ============================================================
// Edit Tag
// Edit Tool
const editTarget = ref<RecipeTool | null>(null);
const editTarget = ref<RecipeToolWithOnHand | null>(null);
function editEventHandler(item: RecipeTool) {
function editEventHandler(item: RecipeToolWithOnHand) {
state.editDialog = true;
editTarget.value = item;
}
@ -167,17 +182,29 @@ export default defineComponent({
if (!editTarget.value) {
return;
}
if (editTarget.value.onHand && !editTarget.value.householdsWithTool?.includes(userHousehold.value)) {
if (!editTarget.value.householdsWithTool) {
editTarget.value.householdsWithTool = [userHousehold.value];
} else {
editTarget.value.householdsWithTool.push(userHousehold.value);
}
} else if (!editTarget.value.onHand && editTarget.value.householdsWithTool?.includes(userHousehold.value)) {
editTarget.value.householdsWithTool = editTarget.value.householdsWithTool.filter(
(household) => household !== userHousehold.value
);
}
await toolStore.actions.updateOne(editTarget.value);
state.editDialog = false;
}
// ============================================================
// Delete Tag
// Delete Tool
const deleteTarget = ref<RecipeTool | null>(null);
const deleteTarget = ref<RecipeToolWithOnHand | null>(null);
function deleteEventHandler(item: RecipeTool) {
function deleteEventHandler(item: RecipeToolWithOnHand) {
state.deleteDialog = true;
deleteTarget.value = item;
}
@ -191,10 +218,10 @@ export default defineComponent({
}
// ============================================================
// Bulk Delete Tag
// Bulk Delete Tool
const bulkDeleteTarget = ref<RecipeTool[]>([]);
function bulkDeleteEventHandler(selection: RecipeTool[]) {
const bulkDeleteTarget = ref<RecipeToolWithOnHand[]>([]);
function bulkDeleteEventHandler(selection: RecipeToolWithOnHand[]) {
bulkDeleteTarget.value = selection;
state.bulkDeleteDialog = true;
}
@ -210,7 +237,7 @@ export default defineComponent({
state,
tableConfig,
tableHeaders,
tools: toolStore.store,
tools,
validators,
// create