feat: Enhance dataset management and improve frontend components
Some checks failed
CI Pipeline / japa-tests (push) Failing after 1m0s
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:
parent
49bd96ee77
commit
f67b736a88
23 changed files with 2392 additions and 2759 deletions
|
@ -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');
|
||||
|
|
82
start/rules/allowed_extensions_mimetypes.ts
Normal file
82
start/rules/allowed_extensions_mimetypes.ts
Normal 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));
|
||||
});
|
61
start/rules/dependent_array_min_length.ts
Normal file
61
start/rules/dependent_array_min_length.ts
Normal 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));
|
||||
});
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue