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

Merge branch 'mealie-next' into feat-frontend-access-controll

This commit is contained in:
Kuchenpirat 2024-02-05 11:47:14 +01:00 committed by GitHub
commit 813a124250
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 765 additions and 253 deletions

View file

@ -1,73 +1,81 @@
<template>
<v-container class="narrow-container">
<BasePageTitle divider>
<template #header>
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/manage-cookbooks.svg')"></v-img>
</template>
<template #title> {{ $t('cookbook.cookbooks') }} </template>
{{ $t('cookbook.description') }}
</BasePageTitle>
<div>
<!-- Create Dialog -->
<BaseDialog
v-if="createTarget"
v-model="dialogStates.create"
:width="650"
:icon="$globals.icons.pages"
:title="$t('cookbook.create-a-cookbook')"
:submit-icon="$globals.icons.save"
:submit-text="$tc('general.save')"
@submit="actions.updateOne(createTarget)"
@cancel="actions.deleteOne(createTarget.id)"
>
<v-card-text>
<CookbookEditor
:cookbook=createTarget
:actions="actions"
/>
</v-card-text>
</BaseDialog>
<BaseButton create @click="actions.createOne()" />
<v-expansion-panels class="mt-2">
<draggable v-model="cookbooks" handle=".handle" style="width: 100%" @change="actions.updateOrder()">
<v-expansion-panel v-for="(cookbook, index) in cookbooks" :key="index" class="my-2 left-border rounded">
<v-expansion-panel-header disable-icon-rotate class="headline">
<div class="d-flex align-center">
<v-icon large left>
{{ $globals.icons.pages }}
</v-icon>
{{ cookbook.name }}
</div>
<template #actions>
<v-icon class="handle">
{{ $globals.icons.arrowUpDown }}
</v-icon>
<v-btn icon small class="ml-2">
<v-icon>
{{ $globals.icons.edit }}
<!-- Delete Dialog -->
<BaseDialog
v-model="dialogStates.delete"
:title="$t('general.delete-with-name', { name: $t('cookbook.cookbook') })"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteCookbook()"
>
<v-card-text>
<p>{{ $t("general.confirm-delete-generic-with-name", { name: $t('cookbook.cookbook') }) }}</p>
<p v-if="deleteTarget" class="mt-4 ml-4">{{ deleteTarget.name }}</p>
</v-card-text>
</BaseDialog>
<!-- Cookbook Page -->
<!-- Page Title -->
<v-container class="narrow-container">
<BasePageTitle divider>
<template #header>
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/manage-cookbooks.svg')"></v-img>
</template>
<template #title> {{ $t('cookbook.cookbooks') }} </template>
{{ $t('cookbook.description') }}
</BasePageTitle>
<!-- Create New -->
<BaseButton create @click="createCookbook" />
<!-- Cookbook List -->
<v-expansion-panels class="mt-2">
<draggable v-model="cookbooks" handle=".handle" style="width: 100%" @change="actions.updateOrder()">
<v-expansion-panel v-for="(cookbook, index) in cookbooks" :key="index" class="my-2 left-border rounded">
<v-expansion-panel-header disable-icon-rotate class="headline">
<div class="d-flex align-center">
<v-icon large left>
{{ $globals.icons.pages }}
</v-icon>
</v-btn>
</template>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-card-text v-if="cookbooks">
<v-text-field v-model="cookbooks[index].name" :label="$t('cookbook.cookbook-name')"></v-text-field>
<v-textarea v-model="cookbooks[index].description" auto-grow :rows="2" :label="$t('recipe.description')"></v-textarea>
<RecipeOrganizerSelector v-model="cookbooks[index].categories" selector-type="categories" />
<RecipeOrganizerSelector v-model="cookbooks[index].tags" selector-type="tags" />
<RecipeOrganizerSelector v-model="cookbooks[index].tools" selector-type="tools" />
<v-switch v-model="cookbooks[index].public" hide-details single-line>
<template #label>
{{ $t('cookbook.public-cookbook') }}
<HelpIcon small right class="ml-2">
{{ $t('cookbook.public-cookbook-description') }}
</HelpIcon>
</template>
</v-switch>
<div class="mt-4">
<h3 class="text-subtitle-1 d-flex align-center mb-0 pb-0">
{{ $t('cookbook.filter-options') }}
<HelpIcon right small class="ml-2">
{{ $t('cookbook.filter-options-description') }}
</HelpIcon>
</h3>
<v-switch v-model="cookbooks[index].requireAllCategories" class="mt-0" hide-details single-line>
<template #label> {{ $t('cookbook.require-all-categories') }} </template>
</v-switch>
<v-switch v-model="cookbooks[index].requireAllTags" hide-details single-line>
<template #label> {{ $t('cookbook.require-all-tags') }} </template>
</v-switch>
<v-switch v-model="cookbooks[index].requireAllTools" hide-details single-line>
<template #label> {{ $t('cookbook.require-all-tools') }} </template>
</v-switch>
{{ cookbook.name }}
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<BaseButtonGroup
:buttons="[
{
<template #actions>
<v-icon class="handle">
{{ $globals.icons.arrowUpDown }}
</v-icon>
<v-btn icon small class="ml-2">
<v-icon>
{{ $globals.icons.edit }}
</v-icon>
</v-btn>
</template>
</v-expansion-panel-header>
<v-expansion-panel-content>
<CookbookEditor :cookbook="cookbook" :actions="actions" :collapsable="false" @delete="deleteEventHandler" />
<v-card-actions>
<v-spacer></v-spacer>
<BaseButtonGroup
:buttons="[{
icon: $globals.icons.delete,
text: $tc('general.delete'),
event: 'delete',
@ -78,31 +86,70 @@
event: 'save',
},
]"
@delete="actions.deleteOne(cookbook.id)"
@save="actions.updateOne(cookbook)"
/>
</v-card-actions>
</v-expansion-panel-content>
</v-expansion-panel>
</draggable>
</v-expansion-panels>
</v-container>
@delete="deleteEventHandler(cookbook)"
@save="actions.updateOne(cookbook)" />
</v-card-actions>
</v-expansion-panel-content>
</v-expansion-panel>
</draggable>
</v-expansion-panels>
</v-container>
</div>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import { defineComponent, reactive, ref } from "@nuxtjs/composition-api";
import draggable from "vuedraggable";
import { useCookbooks } from "@/composables/use-group-cookbooks";
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
import CookbookEditor from "~/components/Domain/Cookbook/CookbookEditor.vue";
import { ReadCookBook } from "~/lib/api/types/cookbook";
export default defineComponent({
components: { draggable, RecipeOrganizerSelector },
components: { CookbookEditor, draggable },
middleware: ["auth", "group-only"],
setup() {
const dialogStates = reactive({
create: false,
delete: false,
});
const { cookbooks, actions } = useCookbooks();
// create
const createTarget = ref<ReadCookBook | null>(null);
async function createCookbook() {
await actions.createOne().then((cookbook) => {
createTarget.value = cookbook as ReadCookBook;
});
dialogStates.create = true;
}
// delete
const deleteTarget = ref<ReadCookBook | null>(null);
function deleteEventHandler(item: ReadCookBook){
deleteTarget.value = item;
dialogStates.delete = true;
}
function deleteCookbook() {
if (!deleteTarget.value) {
return;
}
actions.deleteOne(deleteTarget.value.id);
dialogStates.delete = false;
deleteTarget.value = null;
}
return {
cookbooks,
actions,
dialogStates,
// create
createTarget,
createCookbook,
// delete
deleteTarget,
deleteEventHandler,
deleteCookbook,
};
},
head() {

View file

@ -49,15 +49,41 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="state.bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Data Table -->
<BaseCardSectionTitle :icon="$globals.icons.categories" section :title="$tc('data-pages.categories.category-data')"> </BaseCardSectionTitle>
<CrudTable
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="categories || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
@ -96,6 +122,7 @@ export default defineComponent({
createDialog: false,
editDialog: false,
deleteDialog: false,
bulkDeleteDialog: false,
});
const categoryData = useCategoryData();
const categoryStore = useCategoryStore();
@ -149,6 +176,24 @@ export default defineComponent({
state.deleteDialog = false;
}
// ============================================================
// Bulk Delete Category
const bulkDeleteTarget = ref<RecipeCategory[]>([]);
function bulkDeleteEventHandler(selection: RecipeCategory[]) {
bulkDeleteTarget.value = selection;
state.bulkDeleteDialog = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
if (!item.id) {
continue;
}
await categoryStore.actions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
return {
state,
tableConfig,
@ -168,7 +213,12 @@ export default defineComponent({
// delete
deleteTarget,
deleteEventHandler,
deleteCategory
deleteCategory,
// bulk delete
bulkDeleteTarget,
bulkDeleteEventHandler,
deleteSelected,
};
},
});

View file

@ -155,16 +155,42 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Data Table -->
<BaseCardSectionTitle :icon="$globals.icons.foods" section :title="$tc('data-pages.foods.food-data')"> </BaseCardSectionTitle>
<CrudTable
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="foods || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@create-one="createEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="createDialog = true" />
@ -305,6 +331,21 @@ export default defineComponent({
deleteDialog.value = false;
}
const bulkDeleteDialog = ref(false);
const bulkDeleteTarget = ref<IngredientFood[]>([]);
function bulkDeleteEventHandler(selection: IngredientFood[]) {
bulkDeleteTarget.value = selection;
bulkDeleteDialog.value = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
await foodStore.actions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
// ============================================================
// Alias Manager
@ -395,6 +436,10 @@ export default defineComponent({
deleteDialog,
deleteFood,
deleteTarget,
bulkDeleteDialog,
bulkDeleteTarget,
bulkDeleteEventHandler,
deleteSelected,
// Alias Manager
aliasManagerDialog,
aliasManagerEventHandler,

View file

@ -45,6 +45,31 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="state.bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Seed Dialog-->
<BaseDialog
v-model="seedDialog"
@ -88,9 +113,10 @@
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="labels || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
@ -146,6 +172,7 @@ export default defineComponent({
createDialog: false,
editDialog: false,
deleteDialog: false,
bulkDeleteDialog: false,
});
// ============================================================
@ -179,6 +206,21 @@ export default defineComponent({
state.deleteDialog = false;
}
// Bulk Delete
const bulkDeleteTarget = ref<MultiPurposeLabelSummary[]>([]);
function bulkDeleteEventHandler(selection: MultiPurposeLabelSummary[]) {
bulkDeleteTarget.value = selection;
state.bulkDeleteDialog = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
await labelStore.actions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
// Edit
const editLabel = ref<MultiPurposeLabelSummary | null>(null);
@ -244,6 +286,9 @@ export default defineComponent({
deleteEventHandler,
deleteLabel,
deleteTarget,
bulkDeleteEventHandler,
deleteSelected,
bulkDeleteTarget,
// Seed
seedDatabase,

View file

@ -49,15 +49,41 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="state.bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Data Table -->
<BaseCardSectionTitle :icon="$globals.icons.tags" section :title="$tc('data-pages.tags.tag-data')"> </BaseCardSectionTitle>
<CrudTable
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="tags || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
@ -96,6 +122,7 @@ export default defineComponent({
createDialog: false,
editDialog: false,
deleteDialog: false,
bulkDeleteDialog: false,
});
const tagData = useTagData();
@ -150,6 +177,24 @@ export default defineComponent({
state.deleteDialog = false;
}
// ============================================================
// Bulk Delete Tag
const bulkDeleteTarget = ref<RecipeTag[]>([]);
function bulkDeleteEventHandler(selection: RecipeTag[]) {
bulkDeleteTarget.value = selection;
state.bulkDeleteDialog = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
if (!item.id) {
continue;
}
await tagStore.actions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
return {
state,
tableConfig,
@ -169,7 +214,12 @@ export default defineComponent({
// delete
deleteTarget,
deleteEventHandler,
deleteTag
deleteTag,
// bulk delete
bulkDeleteTarget,
bulkDeleteEventHandler,
deleteSelected,
};
},
});

View file

@ -51,15 +51,41 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="state.bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Data Table -->
<BaseCardSectionTitle :icon="$globals.icons.potSteam" section :title="$tc('data-pages.tools.tool-data')"> </BaseCardSectionTitle>
<CrudTable
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="tools || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="state.createDialog = true">{{ $t("general.create") }}</BaseButton>
@ -108,6 +134,7 @@ export default defineComponent({
createDialog: false,
editDialog: false,
deleteDialog: false,
bulkDeleteDialog: false,
});
const toolData = useToolData();
@ -162,6 +189,22 @@ export default defineComponent({
state.deleteDialog = false;
}
// ============================================================
// Bulk Delete Tag
const bulkDeleteTarget = ref<RecipeTool[]>([]);
function bulkDeleteEventHandler(selection: RecipeTool[]) {
bulkDeleteTarget.value = selection;
state.bulkDeleteDialog = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
await toolStore.actions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
return {
state,
tableConfig,
@ -181,7 +224,12 @@ export default defineComponent({
// delete
deleteTarget,
deleteEventHandler,
deleteTool
deleteTool,
// bulk delete
bulkDeleteTarget,
bulkDeleteEventHandler,
deleteSelected,
};
},
});

View file

@ -129,6 +129,31 @@
</v-card-text>
</BaseDialog>
<!-- Bulk Delete Dialog -->
<BaseDialog
v-model="bulkDeleteDialog"
width="650px"
:title="$tc('general.confirm')"
:icon="$globals.icons.alertCircle"
color="error"
@confirm="deleteSelected"
>
<v-card-text>
<p class="h4">{{ $t('general.confirm-delete-generic-items') }}</p>
<v-card outlined>
<v-virtual-scroll height="400" item-height="25" :items="bulkDeleteTarget">
<template #default="{ item }">
<v-list-item class="pb-2">
<v-list-item-content>
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-virtual-scroll>
</v-card>
</v-card-text>
</BaseDialog>
<!-- Seed Dialog-->
<BaseDialog
v-model="seedDialog"
@ -172,10 +197,11 @@
:table-config="tableConfig"
:headers.sync="tableHeaders"
:data="units || []"
:bulk-actions="[]"
:bulk-actions="[{icon: $globals.icons.delete, text: $tc('general.delete'), event: 'delete-selected'}]"
@delete-one="deleteEventHandler"
@edit-one="editEventHandler"
@create-one="createEventHandler"
@delete-selected="bulkDeleteEventHandler"
>
<template #button-row>
<BaseButton create @click="createDialog = true" />
@ -339,6 +365,22 @@ export default defineComponent({
deleteDialog.value = false;
}
// ============================================================
// Bulk Delete Units
const bulkDeleteDialog = ref(false);
const bulkDeleteTarget = ref<IngredientUnit[]>([]);
function bulkDeleteEventHandler(selection: IngredientUnit[]) {
bulkDeleteTarget.value = selection;
bulkDeleteDialog.value = true;
}
async function deleteSelected() {
for (const item of bulkDeleteTarget.value) {
await unitActions.deleteOne(item.id);
}
bulkDeleteTarget.value = [];
}
// ============================================================
// Alias Manager
@ -423,6 +465,11 @@ export default defineComponent({
deleteDialog,
deleteUnit,
deleteTarget,
// Bulk Delete
bulkDeleteDialog,
bulkDeleteEventHandler,
bulkDeleteTarget,
deleteSelected,
// Alias Manager
aliasManagerDialog,
aliasManagerEventHandler,