mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-05 14:18:51 -05:00
first draft of model list view and edit pages
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-divider></v-divider>
|
<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: '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-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-list-item><template #prepend><v-icon icon="fa-solid fa-question"></v-icon></template>Help</v-list-item>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import SpaceSettings from "@/components/settings/SpaceSettings.vue";
|
|||||||
import SpaceMemberSettings from "@/components/settings/SpaceMemberSettings.vue";
|
import SpaceMemberSettings from "@/components/settings/SpaceMemberSettings.vue";
|
||||||
import UserSpaceSettings from "@/components/settings/UserSpaceSettings.vue";
|
import UserSpaceSettings from "@/components/settings/UserSpaceSettings.vue";
|
||||||
import ApiSettings from "@/components/settings/ApiSettings.vue";
|
import ApiSettings from "@/components/settings/ApiSettings.vue";
|
||||||
|
import ModelListPage from "@/pages/ModelListPage.vue";
|
||||||
|
import ModelEditPage from "@/pages/ModelEditPage.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{path: '/', component: StartPage, name: 'view_home'},
|
{path: '/', component: StartPage, name: 'view_home'},
|
||||||
@@ -46,6 +48,10 @@ const routes = [
|
|||||||
{path: '/books', component: ShoppingListPage, name: 'view_books'},
|
{path: '/books', component: ShoppingListPage, name: 'view_books'},
|
||||||
{path: '/recipe/:id', component: RecipeViewPage, name: 'view_recipe', props: true},
|
{path: '/recipe/:id', component: RecipeViewPage, name: 'view_recipe', props: true},
|
||||||
{path: '/recipe/edit/:recipe_id', component: RecipeEditPage, name: 'edit_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({
|
const router = createRouter({
|
||||||
|
|||||||
114
vue3/src/components/model_editors/FoodEditor.vue
Normal file
114
vue3/src/components/model_editors/FoodEditor.vue
Normal 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>
|
||||||
27
vue3/src/pages/ModelEditPage.vue
Normal file
27
vue3/src/pages/ModelEditPage.vue
Normal 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>
|
||||||
80
vue3/src/pages/ModelListPage.vue
Normal file
80
vue3/src/pages/ModelListPage.vue
Normal 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>
|
||||||
Reference in New Issue
Block a user