hotfix (dataset): enhance dataset editing and validation
- Modified the TableKeywords component to remove the external_key reset when the type is updated, only resetting the value. - Updated the DatasetController to pass authorization checks (`can.edit`, `can.delete`) to the edit view. - Updated the arrayContainsTypes validation rule to improve the error messages for titles and descriptions, clarifying the requirements for main and translated entries. - Updated the Dataset Edit view to: - Remove unused code and comments. - Add authorization checks to the save button. - Add a release button. - Add icons to the save and release buttons. - Add a computed property `hasUnsavedChanges` to determine if there are unsaved changes in the form.
This commit is contained in:
parent
2cb33a779c
commit
c3ae4327b7
4 changed files with 83 additions and 55 deletions
|
@ -926,7 +926,7 @@ export default class DatasetController {
|
|||
// throw new GeneralException(trans('exceptions.publish.release.update_error'));
|
||||
}
|
||||
|
||||
public async edit({ request, inertia, response }: HttpContext) {
|
||||
public async edit({ request, inertia, response, auth }: HttpContext) {
|
||||
const id = request.param('id');
|
||||
const datasetQuery = Dataset.query().where('id', id);
|
||||
datasetQuery
|
||||
|
@ -1015,6 +1015,10 @@ export default class DatasetController {
|
|||
referenceIdentifierTypes: Object.entries(ReferenceIdentifierTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||
relationTypes: Object.entries(RelationTypes).map(([key, value]) => ({ value: key, label: value })),
|
||||
doctypes: DatasetTypes,
|
||||
can: {
|
||||
edit: await auth.user?.can(['dataset-edit']),
|
||||
delete: await auth.user?.can(['dataset-delete']),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ const isKeywordReadOnly = (item: Subject) => {
|
|||
|
||||
<td data-label="Type" scope="row">
|
||||
<FormControl required v-model="item.type"
|
||||
@update:modelValue="() => { item.external_key = undefined; item.value = ''; }" :type="'select'"
|
||||
@update:modelValue="() => { 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(', ') }}
|
||||
|
|
|
@ -468,15 +468,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
<!-- <div class="mb-4">
|
||||
<label for="description" class="block text-gray-700 font-bold mb-2">Description:</label>
|
||||
<textarea id="description"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
v-model="form.type"></textarea>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
<div class="mb-4">
|
||||
<!-- <label for="project" class="block text-gray-700 font-bold mb-2">Project:</label>
|
||||
<select
|
||||
|
@ -498,25 +489,15 @@
|
|||
{{ form.errors['files'].join(', ') }}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Add more input fields for the other properties of the dataset -->
|
||||
<!-- <button
|
||||
type="submit"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
|
||||
>
|
||||
Save
|
||||
</button> -->
|
||||
|
||||
<template #footer>
|
||||
<BaseButtons>
|
||||
<BaseButton @click.stop="submit" :disabled="form.processing" label="Save" color="info"
|
||||
:class="{ 'opacity-25': form.processing }" small>
|
||||
<BaseButton v-if="can.edit" @click.stop="submit" :disabled="form.processing" label="Save"
|
||||
color="info" :icon="mdiDisc" :class="{ 'opacity-25': form.processing }" small>
|
||||
</BaseButton>
|
||||
<!-- <button :disabled="form.processing" :class="{ 'opacity-25': form.processing }"
|
||||
class="text-base hover:scale-110 focus:outline-none flex justify-center px-4 py-2 rounded font-bold cursor-pointer hover:bg-teal-200 bg-teal-100 text-teal-700 border duration-200 ease-in-out border-teal-600 transition"
|
||||
@click.stop="submit">
|
||||
Save
|
||||
</button> -->
|
||||
<BaseButton v-if="can.edit" :route-name="stardust.route('dataset.release', [dataset.id])"
|
||||
color="info" :icon="mdiLockOpen" :label="'Release'" small
|
||||
:disabled="form.processing"
|
||||
:class="{ 'opacity-25': form.processing }" />
|
||||
</BaseButtons>
|
||||
</template>
|
||||
</CardBox>
|
||||
|
@ -570,7 +551,9 @@ import {
|
|||
mdiBookOpenPageVariant,
|
||||
mdiEarthPlus,
|
||||
mdiAlertBoxOutline,
|
||||
mdiRestore
|
||||
mdiRestore,
|
||||
mdiLockOpen,
|
||||
mdiDisc
|
||||
} from '@mdi/js';
|
||||
import { notify } from '@/notiwind';
|
||||
import NotificationBar from '@/Components/NotificationBar.vue';
|
||||
|
@ -624,8 +607,10 @@ const props = defineProps({
|
|||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
|
||||
can: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
});
|
||||
const flash: ComputedRef<any> = computed(() => {
|
||||
|
@ -650,31 +635,70 @@ const fitBounds: LatLngBoundsExpression = [
|
|||
];
|
||||
const mapId = 'test';
|
||||
|
||||
// const downloadFile = async (id: string): Promise<string> => {
|
||||
// const response = await axios.get<Blob>(`/api/download/${id}`, {
|
||||
// responseType: 'blob',
|
||||
// });
|
||||
// const url = URL.createObjectURL(response.data);
|
||||
// setTimeout(() => {
|
||||
// URL.revokeObjectURL(url);
|
||||
// }, 1000);
|
||||
// return url;
|
||||
// };
|
||||
|
||||
// for (const file of props.dataset.files) {
|
||||
// // console.log(`${file.name} path is ${file.filePath} here.`);
|
||||
// file.fileSrc = ref("");
|
||||
// // downloadFile(file.id).then((value: string) => {
|
||||
// // file.fileSrc = ref(value);
|
||||
// // form = useForm<Dataset>(props.dataset as Dataset);
|
||||
// // });
|
||||
// }
|
||||
|
||||
props.dataset.filesToDelete = [];
|
||||
props.dataset.subjectsToDelete = [];
|
||||
props.dataset.referencesToDelete = [];
|
||||
let form = useForm<Dataset>(props.dataset as Dataset);
|
||||
|
||||
// Add this computed property to the script section
|
||||
const hasUnsavedChanges = computed(() => {
|
||||
// Check if form is processing
|
||||
if (form.processing) return true;
|
||||
|
||||
// Compare current form state with original dataset
|
||||
// Check basic properties
|
||||
if (form.language !== props.dataset.language) return true;
|
||||
if (form.type !== props.dataset.type) return true;
|
||||
if (form.project_id !== props.dataset.project_id) return true;
|
||||
if (form.embargo_date !== props.dataset.embargo_date) return true;
|
||||
|
||||
// Check if licenses have changed
|
||||
const originalLicenses = Array.isArray(props.dataset.licenses)
|
||||
? props.dataset.licenses.map(l => typeof l === 'object' ? l.id.toString() : l)
|
||||
: [];
|
||||
const currentLicenses = Array.isArray(form.licenses)
|
||||
? form.licenses.map(l => typeof l === 'object' ? l.id.toString() : l)
|
||||
: [];
|
||||
if (JSON.stringify(currentLicenses) !== JSON.stringify(originalLicenses)) return true;
|
||||
|
||||
// Check if titles have changed
|
||||
if (JSON.stringify(form.titles) !== JSON.stringify(props.dataset.titles)) return true;
|
||||
|
||||
// Check if descriptions have changed
|
||||
if (JSON.stringify(form.descriptions) !== JSON.stringify(props.dataset.descriptions)) return true;
|
||||
|
||||
// Check if authors have changed
|
||||
if (JSON.stringify(form.authors) !== JSON.stringify(props.dataset.authors)) return true;
|
||||
|
||||
// Check if contributors have changed
|
||||
if (JSON.stringify(form.contributors) !== JSON.stringify(props.dataset.contributors)) return true;
|
||||
|
||||
// Check if subjects have changed
|
||||
// if (JSON.stringify(form.subjects) !== JSON.stringify(props.dataset.subjects)) return true;
|
||||
let test = JSON.stringify(form.subjects);
|
||||
let test2 = JSON.stringify(props.dataset.subjects);
|
||||
if (test !== test2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if references have changed
|
||||
if (JSON.stringify(form.references) !== JSON.stringify(props.dataset.references)) return true;
|
||||
|
||||
// Check if coverage has changed
|
||||
if (JSON.stringify(form.coverage) !== JSON.stringify(props.dataset.coverage)) return true;
|
||||
|
||||
// Check if files have changed
|
||||
if (form.files?.length !== props.dataset.files?.length) return true;
|
||||
if (form.filesToDelete?.length > 0) return true;
|
||||
|
||||
// Check if there are new files to upload
|
||||
if (form.files?.some(file => !file.id)) return true;
|
||||
|
||||
// No changes detected
|
||||
return false;
|
||||
});
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
let route = stardust.route('dataset.update', [props.dataset.id]);
|
||||
|
||||
|
|
|
@ -44,20 +44,20 @@ async function arrayContainsTypes(value: unknown, options: Options, field: Field
|
|||
if (field.getFieldPath() === 'titles') {
|
||||
// For titles we expect one main and minimum one translated title.
|
||||
if (!hasTypeA && !hasTypeB) {
|
||||
errorMessage = 'For titles, define one main title and minimum one translated title.';
|
||||
errorMessage = 'For titles, define at least one main title and at least one Translated title as MAIN TITLE.';
|
||||
} else if (!hasTypeA) {
|
||||
errorMessage = 'For titles, define one main title.';
|
||||
errorMessage = 'For titles, define at least one main title.';
|
||||
} else if (!hasTypeB) {
|
||||
errorMessage = 'For titles, define minimum one translated title.';
|
||||
errorMessage = 'For Titles, define at least one Translated title as MAIN TITLE.';
|
||||
}
|
||||
} else if (field.getFieldPath() === 'descriptions') {
|
||||
// For descriptions we expect one abstracts description and minimum one translated description.
|
||||
if (!hasTypeA && !hasTypeB) {
|
||||
errorMessage = 'For descriptions, define one abstract description and minimum one translated description.';
|
||||
errorMessage = 'For descriptions, define at least one abstract and at least one Translated description as MAIN ABSTRACT.';
|
||||
} else if (!hasTypeA) {
|
||||
errorMessage = 'For descriptions, define one abstract description.';
|
||||
errorMessage = 'For descriptions, define at least one abstract.';
|
||||
} else if (!hasTypeB) {
|
||||
errorMessage = 'For descriptions, define minimum one translated description.';
|
||||
errorMessage = 'For Descriptions, define at least one Translated description as MAIN ABSTRACT.';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue