forked from geolba/tethys.backend
Arno Kaimbacher
49bd96ee77
- **AdminuserController.ts**: enable editing `first_name` and `last_name` for user creation and updates - **MimetypeController.ts**: add creation support for mimetypes with selectable extensions - **Models**: add `Mimetype` model (mime_type.ts); add `SnakeCaseNamingStrategy` for User model - **Validators**: - **updateDatasetValidator**: increase title length to 255 and description length to 2500 - **User Validators**: refine `createUserValidator` and `updateUserValidator` to include `first_name` and `last_name` - **vanilla_error_reporter**: improve error reporting for wildcard fields - **SKOS Query**: refine keyword request in `SearchCategoryAutocomplete.vue` - **UI Enhancements**: - improve icon design in wizard (Wizard.vue) - add components for mimetype creation (Create.vue and button in Index.vue) - **Routes**: update `routes.ts` to include new AdonisJS routes
185 lines
6.7 KiB
Vue
185 lines
6.7 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref } from 'vue';
|
|
// import { MainService } from '@/Stores/main';
|
|
import { StyleService } from '@/Stores/style.service';
|
|
import { mdiTrashCan } from '@mdi/js';
|
|
// import CardBoxModal from '@/Components/CardBoxModal.vue';
|
|
// import TableCheckboxCell from '@/Components/TableCheckboxCell.vue';
|
|
import BaseLevel from '@/Components/BaseLevel.vue';
|
|
import BaseButtons from '@/Components/BaseButtons.vue';
|
|
import BaseButton from '@/Components/BaseButton.vue';
|
|
import { Subject } from '@/Dataset';
|
|
// import FormField from '@/Components/FormField.vue';
|
|
import FormControl from '@/Components/FormControl.vue';
|
|
import SearchCategoryAutocomplete from '@/Components/SearchCategoryAutocomplete.vue';
|
|
|
|
const props = defineProps({
|
|
checkable: Boolean,
|
|
keywords: {
|
|
type: Array<Subject>,
|
|
default: () => [],
|
|
},
|
|
subjectTypes: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
errors: {
|
|
type: Object,
|
|
default: () => ({}),
|
|
},
|
|
});
|
|
|
|
const styleService = StyleService();
|
|
// const mainService = MainService();
|
|
const items = computed(() => props.keywords);
|
|
|
|
// const isModalActive = ref(false);
|
|
// const isModalDangerActive = ref(false);
|
|
const perPage = ref(5);
|
|
const currentPage = ref(0);
|
|
// const checkedRows = ref([]);
|
|
|
|
const itemsPaginated = computed(() => {
|
|
return items.value.slice(perPage.value * currentPage.value, perPage.value * (currentPage.value + 1));
|
|
});
|
|
|
|
const numPages = computed(() => Math.ceil(items.value.length / perPage.value));
|
|
|
|
const currentPageHuman = computed(() => currentPage.value + 1);
|
|
|
|
const pagesList = computed(() => {
|
|
const pagesList: Array<number> = [];
|
|
|
|
for (let i = 0; i < numPages.value; i++) {
|
|
pagesList.push(i);
|
|
}
|
|
|
|
return pagesList;
|
|
});
|
|
|
|
const removeItem = (key) => {
|
|
items.value.splice(key, 1);
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<!-- <CardBoxModal v-model="isModalActive" title="Sample modal">
|
|
<p>Lorem ipsum dolor sit amet <b>adipiscing elit</b></p>
|
|
<p>This is sample modal</p>
|
|
</CardBoxModal>
|
|
|
|
<CardBoxModal v-model="isModalDangerActive" large-title="Please confirm" button="danger" has-cancel>
|
|
<p>Lorem ipsum dolor sit amet <b>adipiscing elit</b></p>
|
|
<p>This is sample modal</p>
|
|
</CardBoxModal> -->
|
|
|
|
<!-- <div v-if="checkedRows.length" class="p-3 bg-gray-100/50 dark:bg-slate-800">
|
|
<span v-for="checkedRow in checkedRows" :key="checkedRow.id"
|
|
class="inline-block px-2 py-1 rounded-sm mr-2 text-sm bg-gray-100 dark:bg-slate-700">
|
|
{{ checkedRow.name }}
|
|
</span>
|
|
</div> -->
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<!-- <th v-if="checkable" /> -->
|
|
<!-- <th class="hidden lg:table-cell"></th> -->
|
|
<th scope="col">Type</th>
|
|
<th scope="col">Value</th>
|
|
<th scope="col">Language</th>
|
|
|
|
<th scope="col" />
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(item, index) in itemsPaginated" :key="index">
|
|
|
|
<td data-label="Type" scope="row">
|
|
<FormControl required v-model="item.type" @update:modelValue="() => {item.external_key = undefined; item.value= '';}" :type="'select'" placeholder="[Enter Language]" :options="props.subjectTypes">
|
|
<div class="text-red-400 text-sm" v-if="errors[`subjects.${index}.type`]">
|
|
{{ errors[`subjects.${index}.type`].join(', ') }}
|
|
</div>
|
|
</FormControl>
|
|
</td>
|
|
|
|
<td data-label="Value" scope="row">
|
|
<SearchCategoryAutocomplete
|
|
v-if="item.type !== 'uncontrolled'"
|
|
v-model="item.value"
|
|
@subject="
|
|
(result) => {
|
|
item.language = result.language;
|
|
item.external_key = result.uri;
|
|
}
|
|
"
|
|
>
|
|
<div class="text-red-400 text-sm" v-if="errors[`subjects.${index}.value`]">
|
|
{{ errors[`subjects.${index}.value`].join(', ') }}
|
|
</div>
|
|
</SearchCategoryAutocomplete>
|
|
|
|
<FormControl v-else required v-model="item.value" type="text" placeholder="[enter keyword value]" :borderless="true">
|
|
<div class="text-red-400 text-sm" v-if="errors[`subjects.${index}.value`]">
|
|
{{ errors[`subjects.${index}.value`].join(', ') }}
|
|
</div>
|
|
</FormControl>
|
|
</td>
|
|
|
|
<td data-label="Language" scope="row">
|
|
<FormControl
|
|
required
|
|
v-model="item.language"
|
|
:type="'select'"
|
|
placeholder="[Enter Lang]"
|
|
:options="{ de: 'de', en: 'en' }"
|
|
:is-read-only="item.type != 'uncontrolled'"
|
|
>
|
|
<div class="text-red-400 text-sm" v-if="errors[`subjects.${index}.language`]">
|
|
{{ errors[`subjects.${index}.language`].join(', ') }}
|
|
</div>
|
|
</FormControl>
|
|
</td>
|
|
<td class="before:hidden lg:w-1 whitespace-nowrap" scope="row">
|
|
<BaseButtons type="justify-start lg:justify-end" no-wrap>
|
|
<!-- <BaseButton color="info" :icon="mdiEye" small @click="isModalActive = true" /> -->
|
|
<BaseButton v-if="index > 2" color="danger" :icon="mdiTrashCan" small @click.prevent="removeItem(index)" />
|
|
</BaseButtons>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- :class="[ pagesList.length > 1 ? 'block' : 'hidden']" -->
|
|
<div class="p-3 lg:px-6 border-t border-gray-100 dark:border-slate-800">
|
|
<BaseLevel>
|
|
<BaseButtons>
|
|
<BaseButton
|
|
v-for="page in pagesList"
|
|
:key="page"
|
|
:active="page === currentPage"
|
|
:label="page + 1"
|
|
small
|
|
:outline="styleService.darkMode"
|
|
@click="currentPage = page"
|
|
/>
|
|
</BaseButtons>
|
|
<small>Page {{ currentPageHuman }} of {{ numPages }}</small>
|
|
</BaseLevel>
|
|
</div>
|
|
|
|
<div class="text-red-400 text-sm" v-if="errors.subjects && Array.isArray(errors.subjects)">
|
|
{{ errors.subjects.join(', ') }}
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* tr:nth-child(even) {
|
|
background: gray;
|
|
}
|
|
|
|
tr:nth-child(od) {
|
|
background: white;
|
|
} */
|
|
</style> |