hotfix: enhance editor dataset management and UI improvements
- Implemented dataset editing functionality for editor roles, including fetching, updating, and categorizing datasets. - Added routes and controller actions for editing, updating, and categorizing datasets within the editor interface. - Integrated UI components for managing dataset metadata, subjects, references, and files. - Enhanced keyword management with features for adding, editing, and deleting keywords, including handling keywords used by multiple datasets. - Improved reference management with features for adding, editing, and deleting dataset references. - Added validation for dataset updates using the `updateEditorDatasetValidator`. - Updated the dataset edit form to include components for managing titles, descriptions, authors, contributors, licenses, coverage, subjects, references, and files. - Implemented transaction management for dataset updates to ensure data consistency. - Added a download route for files associated with datasets. - Improved the UI for displaying and interacting with datasets in the editor index view, including adding edit and categorize buttons. - Fixed an issue where the file size was not correctly calculated. - Added a tooltip to the keyword value column in the TableKeywords component to explain the editability of keywords. - Added a section to display keywords that are marked for deletion. - Added a section to display references that are marked for deletion. - Added a restore button to the references to delete section to restore references. - Updated the SearchCategoryAutocomplete component to support read-only mode. - Updated the FormControl component to support read-only mode. - Added icons and styling improvements to various components. - Added a default value for subjectsToDelete and referencesToDelete in the dataset model. - Updated the FooterBar component to use the JustboilLogo component. - Updated the app.ts file to fetch chart data without a year parameter. - Updated the Login.vue file to invert the logo in dark mode. - Updated the AccountInfo.vue file to add a Head component.
This commit is contained in:
parent
10d159a57a
commit
f04c1f6327
30 changed files with 2284 additions and 539 deletions
|
@ -295,7 +295,7 @@ const fetchCollections = async (collectionId: number) => {
|
|||
return alreadyDropped ? { ...collection, inUse: true } : { ...collection, inUse: false };
|
||||
});
|
||||
// Check if selected collection is in the selected list
|
||||
if (selectedCollection.value && selectedCollectionList.value.find(dc => dc.id === selectedCollection.value.id)) {
|
||||
if (selectedCollection.value && selectedCollectionList.value.find(dc => dc.id === selectedCollection.value?.id)) {
|
||||
selectedCollection.value = { ...selectedCollection.value, inUse: true };
|
||||
} else if (selectedCollection.value) {
|
||||
selectedCollection.value = { ...selectedCollection.value, inUse: false };
|
||||
|
|
|
@ -544,15 +544,15 @@ Removes a selected keyword
|
|||
<div class="flex items-center">
|
||||
<!-- <label>{{ form.titles[0].language }}</label>
|
||||
<label>{{ form.language }}</label> -->
|
||||
<icon-wizard :is-current="formStep == 1" :is-checked="formStep > 1" :label="'Language'">
|
||||
<icon-wizard :is-current="formStep == 1" :is-checked="formStep > 1" :label="'Step 1'">
|
||||
<icon-language></icon-language>
|
||||
</icon-wizard>
|
||||
|
||||
<icon-wizard :is-current="formStep == 2" :is-checked="formStep > 2" :label="'Mandatory'">
|
||||
<icon-wizard :is-current="formStep == 2" :is-checked="formStep > 2" :label="'Step 2'">
|
||||
<icon-mandatory></icon-mandatory>
|
||||
</icon-wizard>
|
||||
|
||||
<icon-wizard :is-current="formStep == 3" :is-checked="formStep > 3" :label="'Recommended'">
|
||||
<icon-wizard :is-current="formStep == 3" :is-checked="formStep > 3" :label="'Step 3'">
|
||||
<icon-recommendet></icon-recommendet>
|
||||
</icon-wizard>
|
||||
|
||||
|
@ -588,7 +588,7 @@ Removes a selected keyword
|
|||
<input class="form-checkbox" name="rights" id="rights" type="checkbox" v-model="dataset.rights" />
|
||||
terms and conditions
|
||||
</label> -->
|
||||
<FormField label="Rights" help="You must agree to continue" wrap-body
|
||||
<FormField label="Rights" help="You must agree that you have read the Terms and Conditions. Please click on the 'i' icon to find a read the policy" wrap-body
|
||||
:class="{ 'text-red-400': form.errors.rights }" class="mt-8 w-full mx-2 flex-1 flex-col">
|
||||
<label for="rights" class="checkbox mr-6 mb-3 last:mr-0">
|
||||
<input type="checkbox" id="rights" required v-model="form.rights" />
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
<!-- (2) licenses -->
|
||||
<FormField label="Licenses" wrap-body :class="{ 'text-red-400': form.errors.licenses }"
|
||||
class="mt-8 w-full mx-2 flex-1">
|
||||
<FormCheckRadioGroup type="radio" v-model="form.licenses" name="licenses" is-column :options="licenses" />
|
||||
<FormCheckRadioGroup type="radio" v-model="form.licenses" name="licenses" is-column
|
||||
:options="licenses" />
|
||||
</FormField>
|
||||
|
||||
<div class="flex flex-col md:flex-row">
|
||||
|
@ -163,7 +164,8 @@
|
|||
:class="{ 'text-red-400': form.errors['descriptions.0.value'] }"
|
||||
class="w-full mr-1 flex-1">
|
||||
<FormControl required v-model="form.descriptions[0].value" type="textarea"
|
||||
placeholder="[enter main abstract]" :show-char-count="true" :max-input-length="2500">
|
||||
placeholder="[enter main abstract]" :show-char-count="true"
|
||||
:max-input-length="2500">
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.value'])">
|
||||
{{ form.errors['descriptions.0.value'].join(', ') }}
|
||||
|
@ -176,7 +178,7 @@
|
|||
<FormControl required v-model="form.descriptions[0].language" type="text"
|
||||
:is-read-only="true">
|
||||
<div class="text-red-400 text-sm" v-if="form.errors['descriptions.0.value'] && Array.isArray(form.errors['descriptions.0.language'])
|
||||
">
|
||||
">
|
||||
{{ form.errors['descriptions.0.language'].join(', ') }}
|
||||
</div>
|
||||
</FormControl>
|
||||
|
@ -243,8 +245,9 @@
|
|||
<SearchAutocomplete source="/api/persons" :response-property="'first_name'"
|
||||
placeholder="search in person table...." v-on:person="onAddAuthor"></SearchAutocomplete>
|
||||
|
||||
<TablePersons :persons="form.authors" v-if="form.authors.length > 0" :relation="'authors'"/>
|
||||
<div class="text-red-400 text-sm" v-if="form.errors.authors && Array.isArray(form.errors.authors)">
|
||||
<TablePersons :persons="form.authors" v-if="form.authors.length > 0" :relation="'authors'" />
|
||||
<div class="text-red-400 text-sm"
|
||||
v-if="form.errors.authors && Array.isArray(form.errors.authors)">
|
||||
{{ form.errors.authors.join(', ') }}
|
||||
</div>
|
||||
</CardBox>
|
||||
|
@ -334,8 +337,8 @@
|
|||
</FormField>
|
||||
</div>
|
||||
|
||||
<CardBox class="mb-6 shadow" has-table title="Dataset References" :icon="mdiEarthPlus" :header-icon="mdiPlusCircle"
|
||||
v-on:header-icon-click="addReference">
|
||||
<CardBox class="mb-6 shadow" has-table title="Dataset References" :icon="mdiEarthPlus"
|
||||
:header-icon="mdiPlusCircle" v-on:header-icon-click="addReference">
|
||||
<!-- Message when no references exist -->
|
||||
<div v-if="form.references.length === 0" class="text-center py-4">
|
||||
<p class="text-gray-600">No references added yet.</p>
|
||||
|
@ -408,6 +411,42 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- References to delete section -->
|
||||
<div v-if="form.referencesToDelete && form.referencesToDelete.length > 0" class="mt-8">
|
||||
<h1 class="pt-8 pb-3 font-semibold sm:text-lg text-gray-900">References To Delete</h1>
|
||||
<ul class="flex flex-1 flex-wrap -m-1">
|
||||
<li v-for="(element, index) in form.referencesToDelete" :key="index"
|
||||
class="block p-1 w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/6 xl:w-1/8 h-40">
|
||||
<article tabindex="0"
|
||||
class="bg-red-100 group w-full h-full rounded-md cursor-pointer relative shadow-sm overflow-hidden">
|
||||
<section
|
||||
class="flex flex-col rounded-md text-xs break-words w-full h-full z-20 absolute top-0 py-2 px-3">
|
||||
<h1 class="flex-1 text-gray-700 group-hover:text-blue-800 font-medium text-sm mb-1 truncate overflow-hidden whitespace-nowrap">
|
||||
{{ element.value }}
|
||||
</h1>
|
||||
<div class="flex flex-col mt-auto">
|
||||
<p class="p-1 size text-xs text-gray-700">
|
||||
<span class="font-semibold">Type:</span> {{ element.type }}
|
||||
</p>
|
||||
<p class="p-1 size text-xs text-gray-700">
|
||||
<span class="font-semibold">Relation:</span> {{ element.relation }}
|
||||
</p>
|
||||
<div class="flex justify-end mt-1">
|
||||
<button
|
||||
class="restore ml-auto focus:outline-none hover:bg-gray-300 p-1 rounded-md text-gray-800"
|
||||
@click.prevent="restoreReference(index)">
|
||||
<svg viewBox="0 0 24 24" class="w-5 h-5">
|
||||
<path fill="currentColor" :d="mdiRestore"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</CardBox>
|
||||
|
||||
<BaseDivider />
|
||||
|
@ -420,7 +459,7 @@
|
|||
</li>
|
||||
</ul> -->
|
||||
<TableKeywords :keywords="form.subjects" :errors="form.errors" :subjectTypes="subjectTypes"
|
||||
v-if="form.subjects.length > 0" />
|
||||
v-model:subjects-to-delete="form.subjectsToDelete" v-if="form.subjects.length > 0" />
|
||||
</CardBox>
|
||||
|
||||
</div>
|
||||
|
@ -447,7 +486,9 @@
|
|||
</select> -->
|
||||
</div>
|
||||
|
||||
<FileUploadComponent v-model:files="form.files" v-model:filesToDelete="form.filesToDelete" :showClearButton="false"></FileUploadComponent>
|
||||
<FileUploadComponent v-model:files="form.files" v-model:filesToDelete="form.filesToDelete"
|
||||
:showClearButton="false">
|
||||
</FileUploadComponent>
|
||||
|
||||
<div class="text-red-400 text-sm" v-if="form.errors['file'] && Array.isArray(form.errors['files'])">
|
||||
{{ form.errors['files'].join(', ') }}
|
||||
|
@ -475,8 +516,8 @@
|
|||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
<!-- Loading Spinner -->
|
||||
<div v-if="form.processing"
|
||||
<!-- Loading Spinner -->
|
||||
<div v-if="form.processing"
|
||||
class="fixed inset-0 flex items-center justify-center bg-gray-500 bg-opacity-50 z-50">
|
||||
<svg class="animate-spin h-12 w-12 text-white" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
|
@ -527,6 +568,7 @@ import {
|
|||
mdiBookOpenPageVariant,
|
||||
mdiEarthPlus,
|
||||
mdiAlertBoxOutline,
|
||||
mdiRestore
|
||||
} from '@mdi/js';
|
||||
import { notify } from '@/notiwind';
|
||||
import NotificationBar from '@/Components/NotificationBar.vue';
|
||||
|
@ -633,6 +675,8 @@ const mapId = 'test';
|
|||
// }
|
||||
|
||||
props.dataset.filesToDelete = [];
|
||||
props.dataset.subjectsToDelete = [];
|
||||
props.dataset.referencesToDelete = [];
|
||||
let form = useForm<Dataset>(props.dataset as Dataset);
|
||||
|
||||
// const mainService = MainService();
|
||||
|
@ -701,13 +745,13 @@ const submit = async (): Promise<void> => {
|
|||
// const metadata = JSON.stringify({ sort_order: obj.sort_order });
|
||||
// const metadataBlob = new Blob([metadata + '\n'], { type: 'application/json' });
|
||||
const file = new File([obj.blob], `${obj.label}?sortorder=${obj.sort_order}`, options,);
|
||||
|
||||
|
||||
// const file = new File([obj.blob], `${obj.label}`, options);
|
||||
|
||||
|
||||
|
||||
|
||||
// fileUploads[obj.sort_order] = file;
|
||||
fileUploads.push(file);
|
||||
} else {
|
||||
} else {
|
||||
// return normal request input
|
||||
fileInputs.push(obj);
|
||||
}
|
||||
|
@ -744,7 +788,9 @@ const submit = async (): Promise<void> => {
|
|||
// formStep.value++;
|
||||
// form.filesToDelete = [];
|
||||
// Clear the array using splice
|
||||
form.filesToDelete?.splice(0, form.filesToDelete.length);
|
||||
form.filesToDelete?.splice(0, form.filesToDelete.length);
|
||||
form.subjectsToDelete?.splice(0, form.subjectsToDelete.length);
|
||||
form.referencesToDelete?.splice(0, form.referencesToDelete.length);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -794,7 +840,7 @@ const onAddContributor = (person: Person) => {
|
|||
};
|
||||
|
||||
const addKeyword = () => {
|
||||
let newSubject: Subject = { value: 'test', language: '', type: 'uncontrolled', dataset_count: 0 };
|
||||
let newSubject: Subject = { value: '', language: '', type: 'uncontrolled' };
|
||||
//this.dataset.files.push(uploadedFiles[i]);
|
||||
form.subjects.push(newSubject);
|
||||
};
|
||||
|
@ -806,9 +852,35 @@ const addReference = () => {
|
|||
};
|
||||
|
||||
const removeReference = (key: any) => {
|
||||
const reference = form.references[key];
|
||||
|
||||
// If the reference has an ID, it exists in the database
|
||||
// and should be added to referencesToDelete
|
||||
if (reference.id) {
|
||||
// Initialize referencesToDelete array if it doesn't exist
|
||||
if (!form.referencesToDelete) {
|
||||
form.referencesToDelete = [];
|
||||
}
|
||||
|
||||
// Add to referencesToDelete
|
||||
form.referencesToDelete.push(reference);
|
||||
}
|
||||
|
||||
// Remove from form.references array
|
||||
form.references.splice(key, 1);
|
||||
};
|
||||
|
||||
const restoreReference = (index: number) => {
|
||||
// Get the reference from referencesToDelete
|
||||
const reference = form.referencesToDelete[index];
|
||||
|
||||
// Add it back to form.references
|
||||
form.references.push(reference);
|
||||
|
||||
// Remove it from referencesToDelete
|
||||
form.referencesToDelete.splice(index, 1);
|
||||
};
|
||||
|
||||
const onMapInitialized = (newItem: any) => {
|
||||
console.log(newItem);
|
||||
};
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue