Files
recipes/vue3/src/pages/ModelListPage.vue
2025-06-03 18:04:46 +02:00

205 lines
8.1 KiB
Vue

<template>
<v-container>
<v-row>
<v-col>
<v-card>
<v-card-text class="pt-2 pb-2">
<v-btn variant="flat" @click="router.go(-1)" prepend-icon="fa-solid fa-arrow-left">{{ $t('Back') }}</v-btn>
</v-card-text>
</v-card>
</v-col>
</v-row>
<v-row dense>
<v-col>
<v-card :prepend-icon="genericModel.model.icon" :title="$t(genericModel.model.localizationKey)">
<template #subtitle v-if="TFood.localizationKeyDescription">
<div class="text-wrap">
{{ $t(TFood.localizationKeyDescription) }}
</div>
</template>
<template #append>
<v-btn class="float-right" icon="$create" color="create">
<i class="fa-solid fa-plus"></i>
<model-edit-dialog :close-after-create="false" :model="model"
@create="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery})"></model-edit-dialog>
</v-btn>
</template>
</v-card>
</v-col>
</v-row>
<v-row>
<v-col>
<v-text-field prepend-inner-icon="$search" :label="$t('Search')" v-model="searchQuery"></v-text-field>
<v-data-table-server
@update:options="loadItems"
:items="items"
:items-length="itemCount"
:loading="loading"
:search="searchQuery"
:headers="genericModel.getTableHeaders()"
:items-per-page-options="itemsPerPageOptions"
:show-select="tableShowSelect"
:page="tablePage"
:items-per-page="useUserPreferenceStore().deviceSettings.general_tableItemsPerPage"
disable-sort
>
<template v-slot:item.action="{ item }">
<v-btn class="float-right" icon="$menu" variant="plain">
<v-icon icon="$menu"></v-icon>
<v-menu activator="parent" close-on-content-click>
<v-list density="compact">
<v-list-item prepend-icon="$edit" :to="{name: 'ModelEditPage', params: {model: model, id: item.id}}">
{{ $t('Edit') }}
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-arrows-to-dot" v-if="genericModel.model.isMerge" link>
{{ $t('Merge') }}
<model-merge-dialog :model="model" :source="item"
@change="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage})"></model-merge-dialog>
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-table-list" :to="{name: 'IngredientEditorPage', query: {food_id: item.id}}"
v-if="genericModel.model.name == 'Food'">
{{ $t('Ingredient Editor') }}
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-table-list" :to="{name: 'IngredientEditorPage', query: {unit_id: item.id}}"
v-if="genericModel.model.name == 'Unit'">
{{ $t('Ingredient Editor') }}
</v-list-item>
</v-list>
</v-menu>
</v-btn>
</template>
</v-data-table-server>
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import {onBeforeMount, onMounted, PropType, ref, watch} from "vue";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {useI18n} from "vue-i18n";
import {
EditorSupportedModels,
GenericModel,
getGenericModelFromString, getListModels,
Model, TFood,
} from "@/types/Models";
import {VDataTable} from "vuetify/components";
import {useUrlSearchParams} from "@vueuse/core";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import {useRoute, useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import ModelMergeDialog from "@/components/dialogs/ModelMergeDialog.vue";
import {VDataTableUpdateOptions} from "@/vuetify";
const {t} = useI18n()
const router = useRouter()
const route = useRoute()
const props = defineProps({
model: {
type: String as PropType<EditorSupportedModels>,
default: 'food'
},
})
// table config
const itemsPerPageOptions = [
{value: 10, title: '10'},
{value: 25, title: '25'},
{value: 50, title: '50'},
]
const tablePage = ref(1)
const tableShowSelect = ref(false) // TODO enable once mass edit functions are implemented
// data
const loading = ref(false);
const items = ref([] as Array<any>)
const itemCount = ref(0)
const searchQuery = ref('')
const genericModel = ref({} as GenericModel)
/**
* watch route changes (trough navigation) and set table page accordingly
*/
watch(() => route.query.page, () => {
if (!loading.value && typeof route.query.page == "string" && !isNaN(parseInt(route.query.page))) {
tablePage.value = parseInt(route.query.page)
}
})
// when navigating to ModelListPage from ModelListPage with a different model lifecycle hooks are not called so watch for change here
watch(() => props.model, (newValue, oldValue) => {
if (newValue != oldValue) {
genericModel.value = getGenericModelFromString(props.model, t)
tablePage.value = 1
loadItems({page: 1, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage, search: searchQuery.value})
}
})
/**
* select model class before mount because template renders (and requests item load) before onMounted is called
*/
onBeforeMount(() => {
try {
genericModel.value = getGenericModelFromString(props.model, t)
} catch (Error) {
console.error('Invalid model passed to ModelListPage, loading Food instead')
genericModel.value = getGenericModelFromString('Food', t)
}
if (typeof route.query.page == "string" && !isNaN(parseInt(route.query.page))) {
tablePage.value = parseInt(route.query.page)
}
})
/**
* load items from API whenever the table calls for it
* parameters defined by vuetify
* @param options
*/
function loadItems(options: VDataTableUpdateOptions) {
loading.value = true
window.scrollTo({top: 0, behavior: 'smooth'})
if (tablePage.value != options.page) {
tablePage.value = options.page
}
if (route.query.page == undefined) {
router.replace({name: 'ModelListPage', params: {model: props.model}, query: {page: options.page}})
} else {
router.push({name: 'ModelListPage', params: {model: props.model}, query: {page: options.page}})
}
useUserPreferenceStore().deviceSettings.general_tableItemsPerPage = options.itemsPerPage
genericModel.value.list({page: options.page, pageSize: options.itemsPerPage, query: options.search}).then((r: any) => {
items.value = r.results
itemCount.value = r.count
}).catch((err: any) => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
loading.value = false
})
}
/**
* change models and reset page/scroll
* @param m
*/
function changeModel(m: Model) {
tablePage.value = 1
router.push({name: 'ModelListPage', params: {model: m.name.toLowerCase()}, query: {page: 1}})
window.scrollTo({top: 0, behavior: 'smooth'})
}
</script>
<style scoped>
</style>