feat: Enhance dataset management and improve frontend components
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m0s

- Added preloads 'allowed_extensions_mimetypes' and 'dependent_array_min_length' in adonisrc.ts
- Updated @symfony/webpack-encore from ^4.6.1 to ^5.0.1
- AdminuserController: Implemented pagination for 10 records in index method
- Enabled reviewers to reject datasets to editors with email notifications (DatasetController.ts)
- Submitter DatasetController: Files now loaded in ascending order (sort_order) in edit mode
- file.ts: Removed serialization of fileData due to browser issues
- Modified FileUpload.vue to mark already uploaded files as deleted
- Improved keyword search in SearchCategoryAutocomplete.vue
- Started development on Category.vue for submitters to categorize DDC
- Added new route /dataset/categorize in routes.ts
- Introduced 2 new rules in start/rules: allowed_extensions_mimetypes.ts and dependent_array_min_length.ts
- Performed npm updates
This commit is contained in:
Kaimbacher 2024-11-29 15:46:26 +01:00
parent 49bd96ee77
commit f67b736a88
23 changed files with 2392 additions and 2759 deletions

View file

@ -254,6 +254,10 @@ router.group(() => {
.use([middleware.auth(), middleware.can(['dataset-delete'])]);
router.get('/person', [PersonController, 'index']).as('person.index').use([middleware.auth()]);
router.get('/dataset/categorize', ({ inertia }: HttpContext) => {
return inertia.render('Submitter/Dataset/Category');
})
})
.prefix('submitter');

View file

@ -0,0 +1,82 @@
/*
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/allowedExtensionsMimetypes
|--------------------------------------------------------------------------
|*/
import { FieldContext } from '@vinejs/vine/types';
import vine from '@vinejs/vine';
// import { VineString } from '@vinejs/vine';
import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider';
import type { MultipartFile } from '@adonisjs/core/bodyparser';
// import db from '@adonisjs/lucid/services/db';
import MimeType from '#models/mime_type';
/**
* Options accepted by the unique rule
*/
// type Options = {
// mainLanguageField: string;
// typeField: string;
// };
type Options = {
// size: string | number;
// extnames: string[];
clientNameSizeLimit: number;
};
async function allowedMimetypeExtensions(file: VineMultipartFile | unknown, options: Options | unknown, field: FieldContext) {
// if (typeof value !== 'string' && typeof value != 'number') {
// return;
// }
if (!isBodyParserFile(file)) {
return;
}
const validatedFile = file as MultipartFile;
const mimeType = validatedFile?.headers['content-type']; // Get MIME type from the file
const fileExtension = validatedFile?.extname?.toLocaleLowerCase() as string; // Get file extension from the file
// validate if file extension is allowed in combination with mimetype
const mimeRecord = await MimeType.query().select('file_extension').where('name', mimeType).andWhere('enabled', true).first();
if (!mimeRecord) {
const allowedMimetypes = await MimeType.query().select('name').where('enabled', true);
// Transform allowed MIME types to a concatenated string
const allowedMimetypesString = allowedMimetypes.map((mime) => mime.name).join(', ');
// throw new Error('Invalid MIME type');
// Report error with the concatenated allowed MIME types
field.report(
`Invalid MIME type ${mimeType}. Allowed MIME types are: ${allowedMimetypesString}`,
'allowedMimetypeExtensions',
field,
);
} else {
const allowedExtensions = mimeRecord.file_extension.split('|');
// Validate if the file's extension is in the allowed extensions
if (!allowedExtensions.includes(fileExtension)) {
//throw new Error(`File extension ${fileExtension} is not allowed for MIME type ${mimeType}`);
field.report(
`File extension ${fileExtension} is not allowed for MIME type ${mimeType}. Allowed extensions are: ${mimeRecord.file_extension}`,
'allowedMimetypeExtensions',
field
);
}
// if (validatedFile.clientName.length > options.clientNameSizeLimit) {
// field.report(`Filename length should be less or equal than ${options.clientNameSizeLimit} characters`, 'filenameLength', field);
// }
}
}
export const allowedMimetypeExtensionsRule = vine.createRule(allowedMimetypeExtensions);
declare module '#providers/vinejs_provider' {
interface VineMultipartFile {
allowedMimetypeExtensions(options?: Options): this;
}
}
VineMultipartFile.macro('allowedMimetypeExtensions', function (this: VineMultipartFile, options: Options) {
return this.use(allowedMimetypeExtensionsRule(options));
});

View file

@ -0,0 +1,61 @@
/*
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/dependentArrayMinLength
|--------------------------------------------------------------------------
|*/
import { FieldContext } from '@vinejs/vine/types';
import vine, { VineArray } from '@vinejs/vine';
import { SchemaTypes } from '@vinejs/vine/types';
/**
* Options accepted by the dependentArrayMinLength rule
*/
type Options = {
min: number;
dependentArray: string;
};
async function dependentArrayMinLength(value: unknown, options: Options, field: FieldContext) {
const fileInputs = field.data[options.dependentArray]; // Access the dependent array
const isArrayValue = Array.isArray(value);
const isArrayFileInputs = Array.isArray(fileInputs);
if (isArrayValue && isArrayFileInputs) {
if (value.length >= options.min) {
return true; // Valid if the main array length meets the minimum
} else if (value.length === 0 && fileInputs.length >= options.min) {
return true; // Valid if the main array is empty and the dependent array meets the minimum
} else {
field.report(
`At least {{ min }} item for {{field}} field must be defined`,
'array.dependentArrayMinLength',
field,
options,
);
}
} else {
// Report if either value or dependentArray is not an array
field.report(
`Both the {{field}} field and dependent array {{dependentArray}} must be arrays.`,
'array.dependentArrayMinLength',
field,
options,
);
}
return false; // Invalid if none of the conditions are met
}
export const dependentArrayMinLengthRule = vine.createRule(dependentArrayMinLength);
// Extend the VineArray interface with the same type parameters
declare module '@vinejs/vine' {
interface VineArray<Schema extends SchemaTypes> {
dependentArrayMinLength(options: Options): this;
}
}
VineArray.macro('dependentArrayMinLength', function <Schema extends SchemaTypes>(this: VineArray<Schema>, options: Options) {
return this.use(dependentArrayMinLengthRule(options));
});