diff --git a/i18n/translations/en.json b/i18n/translations/en.json index 71c9747425..dfd99d62e6 100644 --- a/i18n/translations/en.json +++ b/i18n/translations/en.json @@ -383,8 +383,12 @@ "form.discard.confirm": "Do you really want to discard all your changes?", "form.locked": "This content is disabled for you as it is currently edited by another user", "form.unsaved": "The current changes have not yet been saved", + "form.save.error": "Your changes could not be saved.", + "form.save.success": "Your changes have been saved", "form.preview": "Preview changes", "form.preview.draft": "Preview draft", + "form.publish.error": "Your changes could not be published.", + "form.publish.success": "Your changes have been published", "hide": "Hide", "hour": "Hour", diff --git a/panel/src/components/Dialogs/Dialog.vue b/panel/src/components/Dialogs/Dialog.vue index 4eb339646f..88966fd328 100644 --- a/panel/src/components/Dialogs/Dialog.vue +++ b/panel/src/components/Dialogs/Dialog.vue @@ -7,7 +7,7 @@ $vnode.data.staticClass, $attrs.class ]" - :data-has-footer="cancelButton || submitButton" + :data-has-footer="Boolean(cancelButton || submitButton)" :data-size="size" method="dialog" @click.stop diff --git a/panel/src/panel/content.js b/panel/src/panel/content.js index e3b4ddaf15..a01261cc44 100644 --- a/panel/src/panel/content.js +++ b/panel/src/panel/content.js @@ -34,6 +34,8 @@ export default (panel) => { return changes; }, + dialog: null, + /** * Removes all unpublished changes */ @@ -145,17 +147,65 @@ export default (panel) => { try { await panel.api.post(api + "/changes/publish", values); + // close the retry dialog if it is still open + this.dialog?.close(); + // update the props for the current view if (this.isCurrent(api)) { panel.view.props.originals = panel.view.props.content; } panel.events.emit("content.publish", { values, api }); + } catch (error) { + this.retry("publish", error, panel.view.props.content, api); } finally { this.isProcessing = false; } }, + /** + * Opens a dialog with the error message + * to retry the given method. + */ + retry(method, error, values, api) { + // log the error to the console to make it + // easier to debug the issue + console.error(error); + + // set the dialog instance + this.dialog = panel.dialog; + + // show a dialog to the user to try again + this.dialog.open({ + component: "k-text-dialog", + props: { + text: panel.t(`form.${method}.error`), + cancelButton: panel.t("close"), + submitButton: { + icon: "refresh", + text: panel.t("retry") + } + }, + on: { + close: () => { + this.dialog = null; + }, + submit: async () => { + this.dialog.isLoading = true; + + // try again with the latest state in the props + await this[method](panel.view.props.content, api); + + // make sure the dialog is closed if the request was successful + this.dialog?.close(); + + // give a more reassuring longer success notification + panel.notification.success(panel.t(`form.${method}.success`)); + } + } + }); + }, + /** * Saves any changes */ @@ -181,6 +231,9 @@ export default (panel) => { this.isProcessing = false; + // close the retry dialog if it is still open + this.dialog?.close(); + // update the lock timestamp if (this.isCurrent(api) === true) { panel.view.props.lock.modified = new Date(); @@ -191,7 +244,7 @@ export default (panel) => { // silent aborted requests, but throw all other errors if (error.name !== "AbortError") { this.isProcessing = false; - throw error; + this.retry("save", error, panel.view.props.content, api); } } },