hotfix(dataset): improve dataset classification UI and data handling

- Updated the Dataset and Submitter Category Vue components to enhance the UI for library classification. The collection names and numbers are now displayed with distinct styling using `span` elements with specific classes for better readability.
- Modified the DatasetController and Editor/DatasetController to filter collection roles by 'ddc' and 'ccs' names when preloading collections, improving data retrieval efficiency.
- Added `left_id` and `right_id` columns to the `collections` table in the `dataset_7_collections.ts` migration file.
- Added a migration to reorder the collection_roles table.
This commit is contained in:
Kaimbacher 2025-05-06 17:43:37 +02:00
parent be6b38d0a3
commit 0bf442be96
9 changed files with 3058 additions and 467 deletions

View file

@ -936,6 +936,7 @@ export default class DatasetsController {
} }
const collectionRoles = await CollectionRole.query() const collectionRoles = await CollectionRole.query()
.whereIn('name', ['ddc', 'ccs'])
.preload('collections', (coll: Collection) => { .preload('collections', (coll: Collection) => {
// preloa only top level collection with noparent_id // preloa only top level collection with noparent_id
coll.whereNull('parent_id').orderBy('number', 'asc'); coll.whereNull('parent_id').orderBy('number', 'asc');

View file

@ -1500,6 +1500,7 @@ export default class DatasetController {
} }
const collectionRoles = await CollectionRole.query() const collectionRoles = await CollectionRole.query()
.whereIn('name', ['ddc', 'ccs'])
.preload('collections', (coll: Collection) => { .preload('collections', (coll: Collection) => {
// preloa only top level collection with noparent_id // preloa only top level collection with noparent_id
coll.whereNull('parent_id').orderBy('number', 'asc'); coll.whereNull('parent_id').orderBy('number', 'asc');

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -32,3 +32,21 @@ export default class CollectionsRoles extends BaseSchema {
// visible_oai boolean NOT NULL DEFAULT true, // visible_oai boolean NOT NULL DEFAULT true,
// CONSTRAINT collections_roles_pkey PRIMARY KEY (id) // CONSTRAINT collections_roles_pkey PRIMARY KEY (id)
// ) // )
// change to normal intzeger:
// ALTER TABLE collections_roles ALTER COLUMN id DROP DEFAULT;
// DROP SEQUENCE IF EXISTS collections_roles_id_seq;
// -- Step 1: Temporarily change one ID to a value not currently used
// UPDATE collections_roles SET id = 99 WHERE name = 'ccs';
// -- Step 2: Change 'ddc' ID to 2 (the old 'ccs' ID)
// UPDATE collections_roles SET id = 2 WHERE name = 'ddc';
// -- Step 3: Change the temporary ID (99) to 3 (the old 'ddc' ID)
// UPDATE collections_roles SET id = 3 WHERE name = 'ccs';
// UPDATE collections_roles SET id = 99 WHERE name = 'bk';
// UPDATE collections_roles SET id = 1 WHERE name = 'institutes';
// UPDATE collections_roles SET id = 4 WHERE name = 'pacs';
// UPDATE collections_roles SET id = 7 WHERE name = 'bk';

View file

@ -25,6 +25,8 @@ export default class Collections extends BaseSchema {
.onUpdate('CASCADE'); .onUpdate('CASCADE');
table.boolean('visible').notNullable().defaultTo(true); table.boolean('visible').notNullable().defaultTo(true);
table.boolean('visible_publish').notNullable().defaultTo(true); table.boolean('visible_publish').notNullable().defaultTo(true);
table.integer('left_id').unsigned();
table.integer('right_id').unsigned();
}); });
} }
@ -59,3 +61,26 @@ export default class Collections extends BaseSchema {
// change to normal intzeger: // change to normal intzeger:
// ALTER TABLE collections ALTER COLUMN id DROP DEFAULT; // ALTER TABLE collections ALTER COLUMN id DROP DEFAULT;
// DROP SEQUENCE IF EXISTS collections_id_seq; // DROP SEQUENCE IF EXISTS collections_id_seq;
// ALTER TABLE collections
// ADD COLUMN left_id INTEGER;
// COMMENT ON COLUMN collections.left_id IS 'comment';
// ALTER TABLE collections
// ADD COLUMN right_id INTEGER;
// COMMENT ON COLUMN collections.right_id IS 'comment';
// -- Step 1: Drop the existing default
// ALTER TABLE collections
// ALTER COLUMN visible DROP DEFAULT,
// ALTER COLUMN visible_publish DROP DEFAULT;
// -- Step 2: Change column types with proper casting
// ALTER TABLE collections
// ALTER COLUMN visible TYPE smallint USING CASE WHEN visible THEN 1 ELSE 0 END,
// ALTER COLUMN visible_publish TYPE smallint USING CASE WHEN visible_publish THEN 1 ELSE 0 END;
// -- Step 3: Set new defaults as smallint
// ALTER TABLE collections
// ALTER COLUMN visible SET DEFAULT 1,
// ALTER COLUMN visible_publish SET DEFAULT 1;

899
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,8 @@
'cursor-pointer p-2 border border-gray-200 rounded hover:bg-sky-50 text-sky-700 text-sm': true, 'cursor-pointer p-2 border border-gray-200 rounded hover:bg-sky-50 text-sky-700 text-sm': true,
'bg-sky-100 border-sky-500': selectedToplevelCollection && selectedToplevelCollection.id === col.id 'bg-sky-100 border-sky-500': selectedToplevelCollection && selectedToplevelCollection.id === col.id
}" @click="onToplevelCollectionSelected(col)"> }" @click="onToplevelCollectionSelected(col)">
{{ `${col.name} (${col.number})` }} <span class="text-sky-700">{{ col.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ col.number }}</span>
</li> </li>
<li v-if="collections.length === 0" class="text-gray-800 dark:text-slate-400"> <li v-if="collections.length === 0" class="text-gray-800 dark:text-slate-400">
No collections available. No collections available.
@ -55,7 +56,8 @@
<template #item="{ element: parent }"> <template #item="{ element: parent }">
<li :key="parent.id" :draggable="!parent.inUse" :class="getChildClasses(parent)" <li :key="parent.id" :draggable="!parent.inUse" :class="getChildClasses(parent)"
@click="onCollectionSelected(parent)"> @click="onCollectionSelected(parent)">
{{ `${parent.name} (${parent.number})` }} <span class="text-sky-700">{{ parent.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ parent.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
@ -79,11 +81,13 @@
'p-2 border border-gray-200 rounded text-sm', 'p-2 border border-gray-200 rounded text-sm',
element.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move' element.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move'
]"> ]">
{{ `${element.name} (${element.number})` }} <span class="text-sky-700">{{ element.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ element.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
</CardBox> </CardBox>
<!-- Narrower Collections (Children) --> <!-- Narrower Collections (Children) -->
<CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data> <CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data>
<h2 class="text-lg font-bold text-gray-800 dark:text-slate-400 mb-2">Narrower Collections</h2> <h2 class="text-lg font-bold text-gray-800 dark:text-slate-400 mb-2">Narrower Collections</h2>
@ -92,7 +96,8 @@
<template #item="{ element: child }"> <template #item="{ element: child }">
<li :key="child.id" :draggable="!child.inUse" :class="getChildClasses(child)" <li :key="child.id" :draggable="!child.inUse" :class="getChildClasses(child)"
@click="onCollectionSelected(child)"> @click="onCollectionSelected(child)">
{{ `${child.name} (${child.number})` }} <span class="text-sky-700">{{ child.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ child.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
@ -118,7 +123,8 @@
<template #item="{ element }"> <template #item="{ element }">
<div :key="element.id" <div :key="element.id"
class="p-2 m-1 bg-sky-200 text-sky-800 rounded flex items-center gap-2 h-7"> class="p-2 m-1 bg-sky-200 text-sky-800 rounded flex items-center gap-2 h-7">
<span>{{ element.name }} ({{ element.number }})</span> <span class="text-sky-700">{{ element.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ element.number }}</span>
<button <button
@click="selectedCollectionList = selectedCollectionList.filter(item => item.id !== element.id)" @click="selectedCollectionList = selectedCollectionList.filter(item => item.id !== element.id)"
class="hover:text-sky-600 flex items-center"> class="hover:text-sky-600 flex items-center">

View file

@ -1,5 +1,6 @@
<template> <template>
<LayoutAuthenticated> <LayoutAuthenticated>
<Head title="Classify"></Head> <Head title="Classify"></Head>
<SectionMain> <SectionMain>
<SectionTitleLineWithButton :icon="mdiLibraryShelves" title="Library Classification" main> <SectionTitleLineWithButton :icon="mdiLibraryShelves" title="Library Classification" main>
@ -33,17 +34,17 @@
</h2> </h2>
<ul class="flex flex-wrap gap-2"> <ul class="flex flex-wrap gap-2">
<li v-for="col in collections" :key="col.id" :class="{ <li v-for="col in collections" :key="col.id" :class="{
'cursor-pointer p-2 border border-gray-200 rounded hover:bg-sky-50 text-sky-700 text-sm': true, 'cursor-pointer p-2 border border-gray-200 rounded hover:bg-sky-50 flex items-center': true,
'bg-sky-100 border-sky-500': selectedToplevelCollection && selectedToplevelCollection.id === col.id 'bg-sky-100 border-sky-500': selectedToplevelCollection && selectedToplevelCollection.id === col.id
}" @click="onToplevelCollectionSelected(col)"> }" @click="onToplevelCollectionSelected(col)">
{{ `${col.name} (${col.number})` }} <span class="text-sky-700">{{ col.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ col.number }}</span>
</li> </li>
<li v-if="collections.length === 0" class="text-gray-800 dark:text-slate-400"> <li v-if="collections.length === 0" class="text-gray-800 dark:text-slate-400">
No collections available. No collections available.
</li> </li>
</ul> </ul>
</CardBox> </CardBox>
<!-- Collections Listing --> <!-- Collections Listing -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
@ -55,7 +56,8 @@
<template #item="{ element: parent }"> <template #item="{ element: parent }">
<li :key="parent.id" :draggable="!parent.inUse" :class="getChildClasses(parent)" <li :key="parent.id" :draggable="!parent.inUse" :class="getChildClasses(parent)"
@click="onCollectionSelected(parent)"> @click="onCollectionSelected(parent)">
{{ `${parent.name} (${parent.number})` }} <span class="text-sky-700">{{ parent.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ parent.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
@ -69,21 +71,25 @@
<!-- Selected Collection Details --> <!-- Selected Collection Details -->
<CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data> <CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data>
<h3 class="text-xl font-bold text-gray-800 dark:text-slate-400 mb-2">Selected Collection</h3> <h3 class="text-xl font-bold text-gray-800 dark:text-slate-400 mb-2">Selected Collection</h3>
<!-- <p :class="[ <!-- <p :class="[
'cursor-pointer p-2 border border-gray-200 rounded text-sm', 'cursor-pointer p-2 border border-gray-200 rounded text-sm',
selectedCollection.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move' selectedCollection.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move'
]"></p> --> ]"></p> -->
<draggable v-model="selectedCollectionArray" :group="{ name: 'collections', pull: 'clone', put: false }" tag="ul" class="flex flex-wrap gap-2 max-h-60 overflow-y-auto"> <draggable v-model="selectedCollectionArray"
:group="{ name: 'collections', pull: 'clone', put: false }" tag="ul"
class="flex flex-wrap gap-2 max-h-60 overflow-y-auto">
<template #item="{ element }"> <template #item="{ element }">
<li :key="element.id" :class="[ <li :key="element.id" :class="[
'p-2 border border-gray-200 rounded text-sm', 'p-2 border border-gray-200 rounded text-sm',
element.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move' element.inUse ? 'bg-gray-200 text-gray-500 drag-none' : 'bg-green-50 text-green-700 hover:bg-green-100 hover:underline cursor-move'
]"> ]">
{{ `${element.name} (${element.number})` }} <span class="text-sky-700">{{ element.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ element.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
</CardBox> </CardBox>
<!-- Narrower Collections (Children) --> <!-- Narrower Collections (Children) -->
<CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data> <CardBox v-if="selectedCollection" class="rounded-lg p-4" has-form-data>
<h2 class="text-lg font-bold text-gray-800 dark:text-slate-400 mb-2">Narrower Collections</h2> <h2 class="text-lg font-bold text-gray-800 dark:text-slate-400 mb-2">Narrower Collections</h2>
@ -92,7 +98,8 @@
<template #item="{ element: child }"> <template #item="{ element: child }">
<li :key="child.id" :draggable="!child.inUse" :class="getChildClasses(child)" <li :key="child.id" :draggable="!child.inUse" :class="getChildClasses(child)"
@click="onCollectionSelected(child)"> @click="onCollectionSelected(child)">
{{ `${child.name} (${child.number})` }} <span class="text-sky-700">{{ child.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ child.number }}</span>
</li> </li>
</template> </template>
</draggable> </draggable>
@ -106,19 +113,21 @@
</div> </div>
<div class="mb-4 rounded-lg"> <div class="mb-4 rounded-lg">
<div v-if="selectedCollection || selectedCollectionList.length > 0" class="bg-gray-100 shadow rounded-lg p-6 mb-6" :class="{ 'opacity-50': selectedCollection && selectedCollectionList.length === 0 }"> <div v-if="selectedCollection || selectedCollectionList.length > 0"
class="bg-gray-100 shadow rounded-lg p-6 mb-6"
:class="{ 'opacity-50': selectedCollection && selectedCollectionList.length === 0 }">
<p class="mb-4 text-gray-700">Please drag your collections here to classify your previously created <p class="mb-4 text-gray-700">Please drag your collections here to classify your previously created
dataset dataset
according to library classification standards.</p> according to library classification standards.</p>
<draggable v-model="selectedCollectionList" :group="{ name: 'collections' }" <draggable v-model="selectedCollectionList" :group="{ name: 'collections' }"
class="min-h-36 border-dashed border-2 border-gray-400 p-4 text-sm flex flex-wrap gap-2 max-h-60 overflow-y-auto" class="min-h-36 border-dashed border-2 border-gray-400 p-4 text-sm flex flex-wrap gap-2 max-h-60 overflow-y-auto"
tag="ul" tag="ul" :disabled="selectedCollection === null && selectedCollectionList.length > 0"
:disabled="selectedCollection === null && selectedCollectionList.length > 0"
:style="{ opacity: (selectedCollection === null && selectedCollectionList.length > 0) ? 0.5 : 1, pointerEvents: (selectedCollection === null && selectedCollectionList.length > 0) ? 'none' : 'auto' }"> :style="{ opacity: (selectedCollection === null && selectedCollectionList.length > 0) ? 0.5 : 1, pointerEvents: (selectedCollection === null && selectedCollectionList.length > 0) ? 'none' : 'auto' }">
<template #item="{ element }"> <template #item="{ element }">
<div :key="element.id" <div :key="element.id"
class="p-2 m-1 bg-sky-200 text-sky-800 rounded flex items-center gap-2 h-7"> class="p-2 m-1 bg-sky-200 text-sky-800 rounded flex items-center gap-2 h-7">
<span>{{ element.name }} ({{ element.number }})</span> <span class="text-sky-700">{{ element.name }}</span>
<span class="ml-2 px-2 py-0.5 bg-gray-200 text-gray-700 text-xs rounded-full">{{ element.number }}</span>
<button <button
@click="selectedCollectionList = selectedCollectionList.filter(item => item.id !== element.id)" @click="selectedCollectionList = selectedCollectionList.filter(item => item.id !== element.id)"
class="hover:text-sky-600 flex items-center"> class="hover:text-sky-600 flex items-center">
@ -199,10 +208,10 @@ const selectedCollectionList: Ref<Collection[]> = ref<Collection[]>([]);
// Wrap selectedCollection in an array for draggable (always expects an array) // Wrap selectedCollection in an array for draggable (always expects an array)
const selectedCollectionArray = computed({ const selectedCollectionArray = computed({
get: () => (selectedCollection.value ? [selectedCollection.value] : []), get: () => (selectedCollection.value ? [selectedCollection.value] : []),
set: (value: Collection[]) => { set: (value: Collection[]) => {
selectedCollection.value = value.length ? value[0] : null selectedCollection.value = value.length ? value[0] : null
} }
}) })