Squashed commit of the following:
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 40s

commit 579f0878e5240dc17db69be1e0b0c0f5af7ef9fe
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Tue Jun 9 09:25:44 2026 +0200

    feat: Refactor error handling in Dataset Edit form and improve validation messages

    - Updated error handling in the Dataset Edit form to use a centralized formatError function for displaying validation messages.
    - Enhanced user feedback by ensuring that error messages are displayed consistently across various fields.
    - Modified the validation rule for arrayContainsTypes to provide clearer error messages for missing main and translated titles/abstracts.
    - Introduced a new ValidationService to manage manual construction of validation errors.
    - Updated Vite configuration to streamline asset loading and improve performance.
    - Adjusted Inertia setup to utilize dynamic imports for page-specific assets.
    - Cleaned up unnecessary comments and code in various files for better readability.

commit 5efddc2a58c0e164fef585cc7344c06155dbc2c1
Author: Arno Kaimbacher <arno.kaimbacher@geosphere.at>
Date:   Mon Jan 12 17:02:47 2026 +0100

    feat: add dataset change detection and form submission composables

    - Implemented `useDatasetChangeDetection` for tracking unsaved changes in dataset forms, including comparisons for licenses, basic properties, files, coverage, and more.
    - Added `useDatasetFormSubmission` for handling dataset form submissions with validation, success/error handling, and auto-save functionality.
This commit is contained in:
Kaimbacher 2026-06-09 09:35:15 +02:00
commit 9368a0dd8d
38 changed files with 5588 additions and 6181 deletions

View file

@ -1,203 +1,50 @@
// import { ValidationError } from '../errors/validation_error.js';
import { errors } from '@vinejs/vine';
import type { ErrorReporterContract, FieldContext } from '@vinejs/vine/types';
import string from '@poppinss/utils/string';
import { errors } from '@vinejs/vine'
import type { ErrorReporterContract, FieldContext } from '@vinejs/vine/types'
/**
* Shape of the Vanilla error node
*/
export type VanillaErrorNode = {
[field: string]: string[];
};
export interface MessagesBagContract {
get(pointer: string, rule: string, message: string, arrayExpressionPointer?: string, args?: any): string;
}
/**
* Message bag exposes the API to pull the most appropriate message for a
* given validation failure.
*/
export class MessagesBag implements MessagesBagContract {
messages: Message;
wildCardCallback;
constructor(messages: string[]) {
this.messages = messages;
this.wildCardCallback = typeof this.messages['*'] === 'function' ? this.messages['*'] : undefined;
}
/**
* Transform message by replace placeholders with runtime values
*/
transform(message: any, rule: string, pointer: string, args: any) {
/**
* No interpolation required
*/
if (!message.includes('{{')) {
return message;
}
return string.interpolate(message, { rule, field: pointer, options: args || {} });
}
/**
* Returns the most appropriate message for the validation failure.
*/
get(pointer: string, rule: string, message: string, arrayExpressionPointer: string, args: any) {
let validationMessage = this.messages[`${pointer}.${rule}`];
/**
* Fetch message for the array expression pointer if it exists
*/
if (!validationMessage && arrayExpressionPointer) {
validationMessage = this.messages[`${arrayExpressionPointer}.${rule}`];
}
/**
* Fallback to the message for the rule
*/
if (!validationMessage) {
validationMessage = this.messages[rule];
}
/**
* Transform and return message. The wildcard callback is invoked when custom message
* is not defined
*/
return validationMessage
? this.transform(validationMessage, rule, pointer, args)
: this.wildCardCallback
? this.wildCardCallback(pointer, rule, arrayExpressionPointer, args)
: message;
}
}
/**
* Shape of the error message collected by the SimpleErrorReporter
*/
type SimpleError = {
message: string;
field: string;
rule: string;
index?: number;
meta?: Record<string, any>;
};
export interface Message {
[key: string]: any;
}
/**
* Simple error reporter collects error messages as an array of object.
* Each object has following properties.
*
* - message: string
* - field: string
* - rule: string
* - index?: number (in case of an array member)
* - args?: Record<string, any>
* Der VanillaErrorReporter sammelt Validierungsfehler im Standardformat,
* damit die AdonisJS Session-Middleware sie korrekt verarbeiten (reducen) kann.
*/
export class VanillaErrorReporter implements ErrorReporterContract {
// private messages;
// private bail;
/**
* Boolean, um zu prüfen, ob Fehler vorliegen
*/
hasErrors: boolean = false
/**
* Sammlung der Fehler als Array (erforderlich für AdonisJS 6 Session)
*/
errors: any[] = []
/**
* Diese Methode wird von VineJS für jeden Validierungsfehler aufgerufen
*/
report(
message: string,
rule: string,
field: FieldContext,
meta?: Record<string, any>
): void {
this.hasErrors = true
/**
* Boolean to know one or more errors have been reported
*/
hasErrors: boolean = false;
/**
* Collection of errors
*/
// errors: SimpleError[] = [];
errors: Message = {};
/**
* Report an error.
* Wir pushen das Objekt in das Array.
* Das Feld 'field' erhält den vollständigen Pfad (z.B. "user.email").
*/
this.errors.push({
message,
rule,
field: field.getFieldPath(),
...meta,
});
}
// constructor(messages: MessagesBagContract) {
// this.messages = messages;
// }
report(message: string, rule: string, field: FieldContext, meta?: Record<string, any> | undefined): void {
// const error: SimpleError = {
// message,
// rule,
// field: field.getFieldPath()
// };
// if (meta) {
// error.meta = meta;
// }
// if (field.isArrayMember) {
// error.index = field.name as number;
// }
// this.errors.push(error);
this.hasErrors = true;
// if (this.errors[field.getFieldPath()]) {
// this.errors[field.getFieldPath()]?.push(message);
// } else {
// this.errors[field.getFieldPath()] = [message];
// }
const error: SimpleError = {
message,
rule,
field: field.getFieldPath(), // ?field.wildCardPath.split('.')[0] : field.getFieldPath(),
};
// field: 'titles.0.value'
// message: 'Main Title is required'
// rule: 'required' "required"
if (meta) {
error.meta = meta;
}
// if (field.isArrayMember) {
// error.index = field.name;
// }
this.hasErrors = true;
// var test = field.getFieldPath();
// this.errors.push(error);
// if (this.errors[error.field]) {
// this.errors[error.field]?.push(message);
// }
if (field.isArrayMember) {
// Check if the field has wildCardPath and if the error field already exists
if (this.errors[error.field]) {
// Do nothing, as we don't want to push further messages
} else {
// If the error field already exists, push the message
if (this.errors[error.field]) {
this.errors[error.field].push(message);
} else {
this.errors[error.field] = [message];
}
}
} else {
if (this.errors[error.field]) {
this.errors[error.field]?.push(message);
} else {
this.errors[error.field] = [message];
}
}
// } else {
// // normal field
// this.errors[field.field] = [message];
// }
/**
* Collecting errors as per the JSONAPI spec
*/
// this.errors.push({
// code: rule,
// detail: message,
// source: {
// pointer: field.wildCardPath,
// },
// ...(meta ? { meta } : {}),
// });
// let pointer: string = field.wildCardPath as string; //'display_name'
// // if (field.isArrayMember) {
// // this.errors[pointer] = field.name;
// // }
// this.errors[pointer] = this.errors[pointer] || [];
// // this.errors[pointer].push(message);
// this.errors[pointer].push(this.messages.get(pointer, rule, message, arrayExpressionPointer, args));
}
/**
* Returns an instance of the validation error
*/
createError() {
return new errors.E_VALIDATION_ERROR(this.errors);
}
}
export {};
/**
* Erstellt die eigentliche Exception.
* Da 'this.errors' nun ein Array ist, funktioniert .reduce()
* in der Session-Middleware reibungslos.
*/
createError() {
return new errors.E_VALIDATION_ERROR(this.errors);
}
}