Skip to content

Commit

Permalink
proof of concept front end url generation
Browse files Browse the repository at this point in the history
  • Loading branch information
carlinmack committed Feb 17, 2025
1 parent f782f39 commit 5e10c8f
Showing 1 changed file with 42 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,47 +139,32 @@ class DisconnectedFormFeedback extends Component {
}

/**
* Render error messages inline (if 1) or as list (if multiple).
* Return object with human readbable labels as keys and error messages as
* values given an errors object.
*
* @param {Array<String>} messages
* @returns String or React node
* @param {object} errors
* @returns object
*/
renderErrorMessages(messages) {
const uniqueMessages = [...new Set(messages)];
if (uniqueMessages.length === 1) {
return messages[0];
} else {
return (
<ul>
{uniqueMessages.map((m) => (
<li key={m}>{m}</li>
))}
</ul>
);
getErrorPaths(obj) {
const paths = new Set(); // Use a Set to avoid duplicates

// Iterate over the top-level keys
for (const topLevelKey of Object.keys(obj)) {
if (Object.hasOwn(obj, topLevelKey)) {
const nestedObj = obj[topLevelKey];

// Iterate over the nested keys
for (const nestedKey of Object.keys(nestedObj)) {
if (Object.hasOwn(nestedObj, nestedKey)) {
// Construct the path and add it to the Set
paths.add(`${topLevelKey}.${nestedKey}`);
}
}
}
}
}

/**
* Return array of error messages from errorValue object.
*
* The error message(s) might be deeply nested in the errorValue e.g.
*
* errorValue = [
* {
* title: "Missing value"
* }
* ];
*
* @param {object} errorValue
* @returns array of Strings (error messages)
*/
toErrorMessages(errorValue) {
let messages = [];
let store = (l) => {
messages.push(l);
};
leafTraverse(errorValue, store);
return messages;
// Convert the Set to an array and return
return Array.from(paths);
}

/**
Expand All @@ -189,58 +174,21 @@ class DisconnectedFormFeedback extends Component {
* @param {object} errors
* @returns object
*/
toLabelledErrorMessages(errors) {
// Step 0 - Create object with collapsed 1st and 2nd level keys
// e.g., {metadata: {creators: ,,,}} => {"metadata.creators": ...}
// For now, only for metadata, files and access.embargo
const metadata = errors.metadata || {};
const step0Metadata = Object.entries(metadata).map(([key, value]) => {
return ["metadata." + key, value];
});
const files = errors.files || {};
const step0Files = Object.entries(files).map(([key, value]) => {
return ["files." + key, value];
});
const access = errors.access?.embargo || {};
const step0Access = Object.entries(access).map(([key, value]) => {
return ["access.embargo." + key, value];
});
const pids = errors.pids || {};
const step0Pids = _isObject(pids)
? Object.entries(pids).map(([key, value]) => {
return ["pids." + key, value];
})
: [["pids", pids]];
const customFields = errors.custom_fields || {};
const step0CustomFields = Object.entries(customFields).map(([key, value]) => {
return ["custom_fields." + key, value];
});
const step0 = Object.fromEntries(
step0Metadata
.concat(step0Files)
.concat(step0Access)
.concat(step0Pids)
.concat(step0CustomFields)
);

// Step 1 - Transform each error value into array of error messages
const step1 = Object.fromEntries(
Object.entries(step0).map(([key, value]) => {
return [key, this.toErrorMessages(value)];
})
);

// Step 2 - Group error messages by label
// (different error keys can map to same label e.g. title and
// additional_titles)
const labelledErrorMessages = {};
for (const key in step1) {
const label = this.labels[key] || "Unknown field";
let messages = labelledErrorMessages[label] || [];
labelledErrorMessages[label] = messages.concat(step1[key]);
}

return labelledErrorMessages;
getErrorMessages(arr) {
const labels = arr
.map((x) => document.querySelector(`label[for^="${x}"]`))
.filter(Boolean);
const sections = [...new Set(labels.map((x) => x.closest(".accordion")))];
const outputSections = sections.map((x) => [
x.id,
x.querySelector(".title").innerText,
]);
const message = outputSections.map((x, i) => (
<a key={i} href={"#" + x[0]}>

Check warning on line 187 in invenio_rdm_records/assets/semantic-ui/js/invenio_rdm_records/src/deposit/errors/FormFeedback.js

View workflow job for this annotation

GitHub Actions / JS / Tests (18.x)

Do not use Array index in keys

Check warning on line 187 in invenio_rdm_records/assets/semantic-ui/js/invenio_rdm_records/src/deposit/errors/FormFeedback.js

View workflow job for this annotation

GitHub Actions / JS / Tests (20.x)

Do not use Array index in keys
{x[1]}
</a>
));
return message;
}

render() {
Expand All @@ -258,12 +206,8 @@ class DisconnectedFormFeedback extends Component {
return null;
}

const labelledMessages = this.toLabelledErrorMessages(errors);
const listErrors = Object.entries(labelledMessages).map(([label, messages]) => (
<Message.Item key={label}>
<b>{label}</b>: {this.renderErrorMessages(messages)}
</Message.Item>
));
const errorPaths = this.getErrorPaths(errors);
const errorSections = this.getErrorMessages(errorPaths);

// errors not related to validation, following a different format {status:.., message:..}
const backendErrorMessage = errors.message;
Expand All @@ -278,8 +222,9 @@ class DisconnectedFormFeedback extends Component {
>
<Grid container>
<Grid.Column width={15} textAlign="left">
<strong>{backendErrorMessage || message}</strong>
{listErrors.length > 0 && <Message.List>{listErrors}</Message.List>}
<strong>
{backendErrorMessage || message} {errorSections}
</strong>
</Grid.Column>
</Grid>
</Message>
Expand Down

0 comments on commit 5e10c8f

Please sign in to comment.