first draft of model list view and edit pages

This commit is contained in:
vabene1111
2024-09-21 19:02:42 +02:00
parent cad75408c3
commit a173e66a59
5 changed files with 228 additions and 0 deletions

View File

@@ -19,6 +19,7 @@
</v-list-item>
<v-divider></v-divider>
<v-list-item :to="{ name: 'view_settings', params: {} }"><template #prepend><v-icon icon="fa-solid fa-sliders"></v-icon></template>Settings</v-list-item>
<v-list-item :to="{ name: 'ModelListPage', params: {} }"><template #prepend><v-icon icon="fa-solid fa-folder-tree"></v-icon></template>Database</v-list-item>
<v-list-item><template #prepend><v-icon icon="fa-solid fa-user-shield"></v-icon></template>Admin</v-list-item>
<v-list-item><template #prepend><v-icon icon="fa-solid fa-question"></v-icon></template>Help</v-list-item>
<v-divider></v-divider>

View File

@@ -24,6 +24,8 @@ import SpaceSettings from "@/components/settings/SpaceSettings.vue";
import SpaceMemberSettings from "@/components/settings/SpaceMemberSettings.vue";
import UserSpaceSettings from "@/components/settings/UserSpaceSettings.vue";
import ApiSettings from "@/components/settings/ApiSettings.vue";
import ModelListPage from "@/pages/ModelListPage.vue";
import ModelEditPage from "@/pages/ModelEditPage.vue";
const routes = [
{path: '/', component: StartPage, name: 'view_home'},
@@ -46,6 +48,10 @@ const routes = [
{path: '/books', component: ShoppingListPage, name: 'view_books'},
{path: '/recipe/:id', component: RecipeViewPage, name: 'view_recipe', props: true},
{path: '/recipe/edit/:recipe_id', component: RecipeEditPage, name: 'edit_recipe', props: true},
{path: '/list/:model?', component: ModelListPage, props: true, name: 'ModelListPage'},
{path: '/edit/:model?/:id', component: ModelEditPage, props: true, name: 'ModelEditPage'},
]
const router = createRouter({

View File

@@ -0,0 +1,114 @@
<template>
<v-card>
<v-card-title>
{{ $t(OBJ_LOCALIZATION_KEY) }}
<v-btn class="float-right" icon="$close" variant="plain" @click="emit('close')" v-if="dialog"></v-btn>
</v-card-title>
<v-card-text>
<v-form>
<v-text-field v-model="editingObj.name"></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn color="delete" prepend-icon="$delete" v-if="isUpdate">{{ $t('Delete') }}
<delete-confirm-dialog :object-name="objectName" @delete="deleteObject"></delete-confirm-dialog>
</v-btn>
<v-btn color="save" prepend-icon="$save" @click="saveObject">{{ isUpdate ? $t('Save') : $t('Create') }}</v-btn>
</v-card-actions>
</v-card>
</template>
<script setup lang="ts">
import {computed, onMounted, ref} from "vue";
import {ApiApi, Food} from "@/openapi";
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
import {useI18n} from "vue-i18n";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
const {t} = useI18n()
const emit = defineEmits(['create', 'save', 'delete', 'close'])
const props = defineProps({
item: {type: {} as Food, required: false},
itemId: {type: String, required: false},
dialog: {type: Boolean, default: false}
})
const OBJ_LOCALIZATION_KEY = 'Food'
const editingObj = ref({} as Food)
const loading = ref(false)
/**
* checks if given object has ID property to determine if it needs to be updated or created
*/
const isUpdate = computed(() => {
return editingObj.value.id !== undefined
})
/**
* display name for object in headers/delete dialog/...
*/
const objectName = computed(() => {
return isUpdate ? `${t(OBJ_LOCALIZATION_KEY)} ${editingObj.value.token}` : `${t(OBJ_LOCALIZATION_KEY)} (${t('New')})`
})
onMounted(() => {
if (props.item != null) {
editingObj.value = props.item
} else if(props.itemId != null){
const api = new ApiApi()
api.apiFoodRetrieve({id: props.itemId}).then(r => {
editingObj.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
} else {
// functions to populate defaults for new item
}
})
/**
* saves the edited object in the database
*/
async function saveObject() {
let api = new ApiApi()
if (isUpdate.value) {
api.apiFoodUpdate({id: editingObj.value.id, accessToken: editingObj.value}).then(r => {
editingObj.value = r
emit('save', r)
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
})
} else {
api.apiFoodCreate({accessToken: editingObj.value}).then(r => {
editingObj.value = r
emit('create', r)
useMessageStore().addPreparedMessage(PreparedMessage.CREATE_SUCCESS)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
}
/**
* deletes the editing object from the database
*/
async function deleteObject() {
let api = new ApiApi()
api.apiFoodDestroy({id: editingObj.value.id}).then(r => {
editingObj.value = {} as Food
emit('delete', editingObj.value)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.DELETE_ERROR, err)
})
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,27 @@
<template>
<v-container>
<v-row>
<v-col>
<food-editor :item-id="id"></food-editor>
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import FoodEditor from "@/components/model_editors/FoodEditor.vue";
import {onMounted, ref} from "vue";
import {ApiApi, Food} from "@/openapi";
const props = defineProps({
model: {type: String, default: 'Food'},
id: {type: String, required: true},
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,80 @@
<template>
<v-container>
<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="tableHeaders"
:items-per-page-options="itemsPerPageOptions"
:show-select="tableShowSelect"
>
<template v-slot:item.action="{ item }">
<v-btn color="edit" :to="{name: 'ModelEditPage', params: {id: item.id}}"><v-icon icon="$edit"></v-icon></v-btn>
</template>
</v-data-table-server>
</v-col>
</v-row>
</v-container>
</template>
<script setup lang="ts">
import {ApiApi, Food} from "@/openapi";
import {ref} from "vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {useI18n} from "vue-i18n";
const {t} = useI18n()
const props = defineProps({
model: {type: String, default: 'Food'},
})
// table config
const itemsPerPageOptions = [
{value: 10, title: '10'},
{value: 25, title: '25'},
{value: 50, title: '50'},
]
const tableHeaders = [
{title: t('Name'), key: 'name'},
{title: t('Category'), key: 'supermarketCategory.name'},
{title: t('Edit'), key: 'action', align: 'end'},
]
const tableShowSelect = ref(true)
// data
const loading = ref(false);
const items = ref([] as Food[])
const itemCount = ref(0)
const searchQuery = ref('')
function loadItems({page, itemsPerPage, search, sortBy, groupBy}) {
const api = new ApiApi()
loading.value = true
api.apiFoodList({page: page, pageSize: itemsPerPage, query: search}).then(r => {
items.value = r.results
itemCount.value = r.count
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
loading.value = false
})
}
</script>
<style scoped>
</style>