model edit improvements

This commit is contained in:
vabene1111
2024-10-08 18:45:41 +02:00
parent 3494bce2b8
commit a8256b461a
35 changed files with 122 additions and 33 deletions

View File

@@ -2,6 +2,7 @@ import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/Messa
import {onBeforeMount, PropType, ref} from "vue";
import {GenericModel, getGenericModelFromString} from "@/types/Models";
import {useI18n} from "vue-i18n";
import {ResponseError} from "@/openapi";
// TODO type emit parameter (https://mokkapps.de/vue-tips/emit-event-from-composable)
// TODO alternatively there seems to be a getContext method to get the calling context (good practice?)
@@ -33,8 +34,10 @@ export function useModelEditorFunctions<T>(modelName: string, emit: any) {
* @return promise resolving to either the editingObj or undefined if errored
*/
function setupState(item: T | null, itemId: number | string | undefined,
newItemFunction: () => void = () => {},
existingItemFunction: () => void = () => {}): Promise<T | undefined> {
newItemFunction: () => void = () => {
},
existingItemFunction: () => void = () => {
}): Promise<T | undefined> {
if (item === null && (itemId === undefined || itemId == '')) {
// neither item nor itemId given => new item
@@ -64,7 +67,11 @@ export function useModelEditorFunctions<T>(modelName: string, emit: any) {
existingItemFunction()
return editingObj.value
}).catch((err: any) => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
if (err instanceof ResponseError && err.response.status == 404) {
useMessageStore().addPreparedMessage(PreparedMessage.NOT_FOUND)
} else {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}
return undefined
}).finally(() => {
loading.value = false

View File

@@ -187,6 +187,8 @@
"NoCategory": "",
"No_ID": "",
"No_Results": "",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "",
"Note": "",
"Nutrition": "",

View File

@@ -180,6 +180,8 @@
"NoCategory": "Няма избрана категория.",
"No_ID": "Идентификатора не е намерен, не може да се изтрие.",
"No_Results": "Няма резултати",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} не е в списъка ви за пазаруване.",
"Note": "Бележка",
"Nutrition": "Хранителни стойности",

View File

@@ -244,6 +244,8 @@
"NoMoreUndo": "",
"No_ID": "",
"No_Results": "",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "",
"Note": "",
"Number of Objects": "",

View File

@@ -242,6 +242,8 @@
"NoCategory": "Není vybrána žádná kategorie.",
"No_ID": "ID nenalezeno, odstranění není možné.",
"No_Results": "Žádné výsledky",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} není na vašem nákupním seznamu.",
"Note": "Poznámka",
"Number of Objects": "Počet Objektů",

View File

@@ -227,6 +227,8 @@
"NoCategory": "Ingen kategori valgt.",
"No_ID": "ID findes ikke, kan ikke slette.",
"No_Results": "Ingen resultater",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} er ikke i din indkøbsliste.",
"Note": "Note",
"Number of Objects": "Antal objekter",

View File

@@ -246,6 +246,8 @@
"NoMoreUndo": "Rückgängig: Keine Änderungen",
"No_ID": "ID nicht gefunden und kann nicht gelöscht werden.",
"No_Results": "Keine Ergebnisse",
"NotFound": "Nicht gefunden",
"NotFoundHelp": "Die gesuchte Seite konnte nicht gefunden werden.",
"NotInShopping": "{food} befindet sich nicht auf Ihrer Einkaufsliste.",
"Note": "Notiz",
"Number of Objects": "Anzahl von Objekten",

View File

@@ -220,6 +220,8 @@
"NoCategory": "Δεν έχει επιλεγεί κατηγορία.",
"No_ID": "Το ID δεν βρέθηκε, αδύνατη η διαγραφή.",
"No_Results": "Δεν υπάρχουν αποτελέσματα",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "Το φαγητό { food} δεν είναι στη λίστα αγορών σας.",
"Note": "Σημείωση",
"Number of Objects": "Αριθμός αντικειμένων",

View File

@@ -245,6 +245,8 @@
"NoMoreUndo": "No changes to be undone.",
"No_ID": "ID not found, cannot delete.",
"No_Results": "No Results",
"NotFound": "Not found",
"NotFoundHelp": "The page or object you are looking for could not be found.",
"NotInShopping": "{food} is not in your shopping list.",
"Note": "Note",
"Number of Objects": "Number of Objects",

View File

@@ -245,6 +245,8 @@
"NoMoreUndo": "No hay cambios que deshacer.",
"No_ID": "No se ha encontrado el ID, no se puede borrar.",
"No_Results": "No hay resutado",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} no esta en tu lista de la compra.",
"Note": "Nota",
"Number of Objects": "Número de Objetos",

View File

@@ -130,6 +130,8 @@
"Next_Period": "Seuraava Jakso",
"No_ID": "Poistaminen epäonnistui, ID:tä ei löytynyt.",
"No_Results": "Ei Tuloksia",
"NotFound": "",
"NotFoundHelp": "",
"Note": "Lisätiedot",
"Nutrition": "Ravitsemus",
"Ok": "Avaa",

View File

@@ -244,6 +244,8 @@
"NoMoreUndo": "Aucun changement à annuler.",
"No_ID": "ID introuvable, impossible de supprimer.",
"No_Results": "Aucun résultat",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "Laliment {food} nest pas dans votre liste de courses.",
"Note": "Notes",
"Number of Objects": "Nombre d'objets",

View File

@@ -245,6 +245,8 @@
"NoMoreUndo": "אין עוד שינויים לשחזור.",
"No_ID": "מזהה לא נמצא, בלתי ניתן למחיקה.",
"No_Results": "אין תוצאות",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} אינו רשימת הקניות.",
"Note": "הערה",
"Number of Objects": "מספר אובייקטים",

View File

@@ -222,6 +222,8 @@
"NoCategory": "Nincs kategória kiválasztva.",
"No_ID": "Azonosító nem található, ezért nem törölhető.",
"No_Results": "Nincsenek találatok",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} nincs a bevásárlólistáján.",
"Note": "Megjegyzés",
"Number of Objects": "Objektumok száma",

View File

@@ -86,6 +86,8 @@
"New_Keyword": "Նոր բանալի բառ",
"New_Recipe": "Նոր բաղադրատոմս",
"No_Results": "Արդյունքներ չկան",
"NotFound": "",
"NotFoundHelp": "",
"Nutrition": "",
"Ok": "",
"Open": "",

View File

@@ -206,6 +206,8 @@
"NoCategory": "",
"No_ID": "",
"No_Results": "",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "",
"Note": "Catatan",
"Nutrition": "Nutrisi",

View File

@@ -244,6 +244,8 @@
"NoMoreUndo": "",
"No_ID": "",
"No_Results": "",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "",
"Note": "",
"Number of Objects": "",

View File

@@ -211,6 +211,8 @@
"NoCategory": "Nessuna categoria selezionata.",
"No_ID": "ID non trovato, non è possibile eliminare.",
"No_Results": "Nessun risultato",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} non è nella tua lista della spesa.",
"Note": "Nota",
"Nutrition": "Nutrienti",

View File

@@ -225,6 +225,8 @@
"NoCategory": "",
"No_ID": "",
"No_Results": "",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "",
"Note": "",
"Number of Objects": "",

View File

@@ -218,6 +218,8 @@
"NoCategory": "Ingen kategori valgt.",
"No_ID": "ID ikke funnet, kan ikke slette.",
"No_Results": "Ingen resultat",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} er ikke i handlelisten din.",
"Note": "Merk",
"Number of Objects": "Antall objekter",

View File

@@ -222,6 +222,8 @@
"NoCategory": "Geen categorie geselecteerd.",
"No_ID": "ID niet gevonden, verwijderen niet mogelijk.",
"No_Results": "Geen resultaten",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} staat niet op je boodschappenlijst.",
"Note": "Notitie",
"Number of Objects": "Aantal Objecten",

View File

@@ -246,6 +246,8 @@
"NoMoreUndo": "Brak zmian do wycofania.",
"No_ID": "ID nie znaleziono, nie można usunąć.",
"No_Results": "Brak wyników",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} nie ma na Twojej liście zakupów.",
"Note": "Notatka",
"Number of Objects": "Ilość obiektów",

View File

@@ -175,6 +175,8 @@
"NoCategory": "Nenhuma categoria selecionada.",
"No_ID": "identificação não encontrada, impossível eliminar.",
"No_Results": "Sem resultados",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} não está na sua lista de compras.",
"Note": "Nota",
"Nutrition": "Nutrição",

View File

@@ -235,6 +235,8 @@
"NoMoreUndo": "Nenhuma alteração para desfazer.",
"No_ID": "ID não encontrado, impossível deletar.",
"No_Results": "Sem Resultados",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} não está na sua lista de compras.",
"Note": "Nota",
"Number of Objects": "Número de Objetos",

View File

@@ -215,6 +215,8 @@
"NoCategory": "Nicio categorie selectată.",
"No_ID": "ID-ul nu a fost găsit, nu se poate șterge.",
"No_Results": "Fără rezultate",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} nu se află în lista de cumpărături.",
"Note": "Notă",
"Nutrition": "Nutriție",

View File

@@ -166,6 +166,8 @@
"NoCategory": "Категория не выбрана.",
"No_ID": "ID не найден, удаление не возможно.",
"No_Results": "Результаты отсутствуют",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} отсутствует в вашем списке покупок.",
"Note": "Заметка",
"Nutrition": "Питательность",

View File

@@ -159,6 +159,8 @@
"NoCategory": "Nobena kategorija ni izbrana.",
"No_ID": "ID ni najden, ne morem izbrisati.",
"No_Results": "Ni rezultatov",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} ni v tvojem nakupovalnem listku.",
"Note": "Opomba",
"Nutrition": "Prehrana",

View File

@@ -246,6 +246,8 @@
"NoMoreUndo": "Inga ändringar att ångra.",
"No_ID": "ID hittades inte, kan inte radera.",
"No_Results": "Inget resultat",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} finns inte i din inköpslista.",
"Note": "Anteckning",
"Number of Objects": "Antal objekt",

View File

@@ -245,6 +245,8 @@
"NoMoreUndo": "Yapılacak değişiklik yok.",
"No_ID": "ID bulunamadı, silinemez.",
"No_Results": "Sonuç Yok",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} alışveriş listenizde yok.",
"Note": "Not",
"Number of Objects": "Nesne Sayısı",

View File

@@ -192,6 +192,8 @@
"NoCategory": "Жодна категорія не вибрана.",
"No_ID": "ID не знайдено, неможливо видалити.",
"No_Results": "Немає Результату",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "{food} немає в вашому списку покупок.",
"Note": "Нотатка",
"Nutrition": "Харчова цінність",

View File

@@ -240,6 +240,8 @@
"NoMoreUndo": "没有可撤消的更改。",
"No_ID": "未找到标识,不能删除。",
"No_Results": "没有结果",
"NotFound": "",
"NotFoundHelp": "",
"NotInShopping": "购物清单中没有 {food}。",
"Note": "笔记",
"Number of Objects": "对象数量",

View File

@@ -62,6 +62,8 @@
"Monday": "",
"New": "",
"New_Recipe": "",
"NotFound": "",
"NotFoundHelp": "",
"Nutrition": "",
"Ok": "",
"Open": "",

View File

@@ -23,7 +23,8 @@
{{ $t(genericModel.model.localizationKey) }}</span>
<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})"></model-edit-dialog>
<model-edit-dialog :close-after-create="false" :model="model"
@create="loadItems({page: tablePage, itemsPerPage: useUserPreferenceStore().deviceSettings.general_tableItemsPerPage})"></model-edit-dialog>
</v-btn>
</v-col>
</v-row>
@@ -56,27 +57,32 @@
<script setup lang="ts">
import {nextTick, onBeforeMount, onMounted, PropType, ref, watch} from "vue";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {onBeforeMount, PropType, ref, watch} from "vue";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {useI18n} from "vue-i18n";
import {
TFood,
TUnit,
EditorSupportedModels,
GenericModel,
getGenericModelFromString,
Model,
TAutomation,
TCookLog,
TFood,
TKeyword,
TSupermarketCategory,
TPropertyType,
TSupermarket,
TSupermarketCategory,
TUnit,
TUnitConversion,
TAutomation,
TUserFile, TCookLog, TViewLog, Model, EditorSupportedModels
TUserFile,
TViewLog
} from "@/types/Models";
import {VDataTable} from "vuetify/components";
import {useUrlSearchParams} from "@vueuse/core";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import {useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {ResponseError} from "@/openapi";
type VDataTableProps = InstanceType<typeof VDataTable>['$props']
@@ -149,6 +155,7 @@ onBeforeMount(() => {
// TODO proper typescript signature, this is just taken from vuetify example, must be a better solution
function loadItems({page, itemsPerPage, search, sortBy, groupBy}) {
loading.value = true
window.scrollTo({top: 0, behavior: 'smooth'})
// TODO workaround for initial page bug see https://github.com/vuetifyjs/vuetify/issues/17966
if (page == 1 && Number(params.page) > 1 && !tablePageInitialized.value) {
page = Number(params.page)
@@ -161,7 +168,7 @@ function loadItems({page, itemsPerPage, search, sortBy, groupBy}) {
items.value = r.results
itemCount.value = r.count
}).catch((err: any) => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
loading.value = false
tablePage.value = page // TODO remove once page bug is fixed
@@ -171,6 +178,7 @@ function loadItems({page, itemsPerPage, search, sortBy, groupBy}) {
function changeModel(m: Model) {
tablePage.value = 1
router.push({name: 'ModelListPage', params: {model: m.name}})
window.scrollTo({top: 0, behavior: 'smooth'})
}
</script>

View File

@@ -26,6 +26,7 @@ export enum PreparedMessage {
UPDATE_SUCCESS = 'UPDATE_SUCCESS',
CREATE_SUCCESS = 'CREATE_SUCCESS',
DELETE_SUCCESS = 'DELETE_SUCCESS',
NOT_FOUND = 'NOT_FOUND',
}
/**
@@ -142,6 +143,9 @@ export const useMessageStore = defineStore('message_store', () => {
if (preparedMessage == PreparedMessage.CREATE_SUCCESS) {
addMessage(MessageType.SUCCESS, {title: t('Created'), text: ''} as StructuredMessage, 6000, data)
}
if (preparedMessage == PreparedMessage.NOT_FOUND) {
addMessage(MessageType.WARNING, {title: t('NotFound'), text: t('NotFoundHelp')} as StructuredMessage, 6000, data)
}
}
/**

View File

@@ -11,13 +11,21 @@ type VDataTableProps = InstanceType<typeof VDataTable>['$props']
* @return instance of GenericModel
*/
export function getGenericModelFromString(modelName: string, t: any) {
if (SUPPORTED_MODELS.has(modelName)) {
return new GenericModel(SUPPORTED_MODELS.get(modelName), t)
if (SUPPORTED_MODELS.has(modelName.toLowerCase())) {
return new GenericModel(SUPPORTED_MODELS.get(modelName.toLowerCase()), t)
} else {
throw Error(`Model ${modelName} not in SUPPORTED_MODELS`)
}
}
/**
* register a given model instance in the supported models list
* @param model model to register
*/
function registerModel(model: Model){
SUPPORTED_MODELS.set(model.name.toLowerCase(), model)
}
/**
* common list parameters shared by all generic models
*/
@@ -79,7 +87,7 @@ export const TFood = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TFood.name, TFood)
registerModel(TFood)
export const TUnit = {
name: 'Unit',
@@ -95,7 +103,7 @@ export const TUnit = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TUnit.name, TUnit)
registerModel(TUnit)
export const TKeyword = {
name: 'Keyword',
@@ -110,7 +118,7 @@ export const TKeyword = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TKeyword.name, TKeyword)
registerModel(TKeyword)
export const TRecipe = {
name: 'Recipe',
@@ -125,7 +133,7 @@ export const TRecipe = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TRecipe.name, TRecipe)
registerModel(TRecipe)
export const TMealType = {
name: 'MealType',
@@ -140,7 +148,7 @@ export const TMealType = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TMealType.name, TMealType)
registerModel(TMealType)
export const TUser = {
name: 'User',
@@ -159,7 +167,7 @@ export const TUser = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TUser.name, TUser)
registerModel(TUser)
export const TSupermarket = {
name: 'Supermarket',
@@ -174,7 +182,7 @@ export const TSupermarket = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TSupermarket.name, TSupermarket)
registerModel(TSupermarket)
export const TSupermarketCategory = {
name: 'SupermarketCategory',
@@ -189,7 +197,7 @@ export const TSupermarketCategory = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TSupermarketCategory.name, TSupermarketCategory)
registerModel(TSupermarketCategory)
export const TPropertyType = {
name: 'PropertyType',
@@ -204,7 +212,7 @@ export const TPropertyType = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TPropertyType.name, TPropertyType)
registerModel(TPropertyType)
export const TProperty = {
name: 'Property',
@@ -220,7 +228,7 @@ export const TProperty = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TProperty.name, TProperty)
registerModel(TProperty)
export const TUnitConversion = {
name: 'UnitConversion',
@@ -239,7 +247,7 @@ export const TUnitConversion = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TUnitConversion.name, TUnitConversion)
registerModel(TUnitConversion)
export const TUserFile = {
name: 'UserFile',
@@ -254,7 +262,7 @@ export const TUserFile = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TUserFile.name, TUserFile)
registerModel(TUserFile)
export const TAutomation = {
name: 'Automation',
@@ -270,7 +278,7 @@ export const TAutomation = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TAutomation.name, TAutomation)
registerModel(TAutomation)
export const TCookLog = {
name: 'CookLog',
@@ -286,7 +294,7 @@ export const TCookLog = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TCookLog.name, TCookLog)
registerModel(TCookLog)
export const TViewLog = {
name: 'ViewLog',
@@ -302,7 +310,7 @@ export const TViewLog = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TViewLog.name, TViewLog)
registerModel(TViewLog)
export const TAccessToken = {
name: 'AccessToken',
@@ -318,7 +326,7 @@ export const TAccessToken = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TAccessToken.name, TAccessToken)
registerModel(TAccessToken)
export const TUserSpace = {
name: 'UserSpace',
@@ -335,7 +343,7 @@ export const TUserSpace = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TUserSpace.name, TUserSpace)
registerModel(TUserSpace)
export const TInviteLink = {
name: 'InviteLink',
@@ -353,7 +361,7 @@ export const TInviteLink = {
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
SUPPORTED_MODELS.set(TInviteLink.name, TInviteLink)
registerModel(TInviteLink)
export const TFoodInheritField = {
name: 'FoodInheritField',
@@ -368,7 +376,7 @@ export const TFoodInheritField = {
isPaginated: false,
} as Model
SUPPORTED_MODELS.set(TFoodInheritField.name, TFoodInheritField)
registerModel(TFoodInheritField)
/**