diff --git a/client/src/model.js b/client/src/model.js index 4144e3c076..da550fd29e 100644 --- a/client/src/model.js +++ b/client/src/model.js @@ -599,7 +599,8 @@ class Model { /** * Delete the record in the backend. * - * @param {{wait?: boolean} & Object.} [options] Options. + * @param {{wait?: boolean} & Object.} [options] Options. If `wait`, unsubscribing and + * removal from the collection will wait for a successful response. * @returns {Promise} * @fires Model#sync * @copyright Credits to Backbone.js. diff --git a/client/src/views/modals/compose-email.js b/client/src/views/modals/compose-email.js index 036c52397f..f0f43a55af 100644 --- a/client/src/views/modals/compose-email.js +++ b/client/src/views/modals/compose-email.js @@ -300,7 +300,10 @@ class ComposeEmailModalView extends EditModalView { beforeCollapse() { if (this.wasModified) { this.actionSaveDraft({skipNotModifiedWarning: true}) - .then(() => this.getRecordView().setConfirmLeaveOut(false)); + .then(() => { + this.getRecordView().setConfirmLeaveOut(false); + this.getRouter().removeWindowLeaveOutObject(this); + }); } return super.beforeCollapse(); diff --git a/client/src/views/record/list.js b/client/src/views/record/list.js index c9c466a3e9..4d5b674279 100644 --- a/client/src/views/record/list.js +++ b/client/src/views/record/list.js @@ -3523,7 +3523,12 @@ class ListRecordView extends View { } // noinspection JSUnusedGlobalSymbols - actionQuickRemove(data) { + /** + * @protected + * @param {{id?: string}} [data] + * @return {Promise} + */ + async actionQuickRemove(data) { data = data || {}; const id = data.id; @@ -3533,6 +3538,11 @@ class ListRecordView extends View { } const model = this.collection.get(id); + const index = this.collection.indexOf(model); + + if (!model) { + throw new Error("No model."); + } if (!this.getAcl().checkModel(model, 'delete')) { Espo.Ui.error(this.translate('Access denied')); @@ -3540,27 +3550,30 @@ class ListRecordView extends View { return; } - this.confirm({ + await this.confirm({ message: this.translate('removeRecordConfirmation', 'messages', this.scope), confirmText: this.translate('Remove'), - }, () => { - this.collection.trigger('model-removing', id); - this.collection.remove(model); - - Espo.Ui.notifyWait(); - - model.destroy({wait: true, fromList: true}) - .then(() => { - Espo.Ui.success(this.translate('Removed')); - - this.trigger('after:delete', model); - this.removeRecordFromList(id); - }) - .catch(() => { - // @todo Revert to the same position. - this.collection.push(model); - }); }); + + this.collection.trigger('model-removing', id); + this.collection.remove(model); + + Espo.Ui.notifyWait(); + + try { + await model.destroy({wait: true, fromList: true}); + } catch (e) { + if (!this.collection.models.includes(model)) { + this.collection.add(model, {at: index}); + } + + return; + } + + Espo.Ui.success(this.translate('Removed')); + + this.trigger('after:delete', model); + this.removeRecordFromList(id); } /** diff --git a/client/src/views/record/panels/relationship.js b/client/src/views/record/panels/relationship.js index 93014d2d1c..39496079bd 100644 --- a/client/src/views/record/panels/relationship.js +++ b/client/src/views/record/panels/relationship.js @@ -821,31 +821,45 @@ class RelationshipPanelView extends BottomPanelView { * A `remove-related` action. * * @protected + * @param {{id?: string}} [data] + * @return {Promise} */ - actionRemoveRelated(data) { + async actionRemoveRelated(data) { const id = data.id; - this.confirm({ + const model = this.collection.get(id); + const index = this.collection.indexOf(model); + + if (!model) { + throw new Error("No model."); + } + + await this.confirm({ message: this.translate('removeRecordConfirmation', 'messages'), confirmText: this.translate('Remove'), - }, () => { - const model = this.collection.get(id); - - Espo.Ui.notifyWait(); - - model - .destroy() - .then(() => { - Espo.Ui.success(this.translate('Removed')); - - this.collection.fetch(); - - this.model.trigger('after:unrelate'); - this.model.trigger('after:unrelate:' + this.link); - - this.processSyncBack(); - }); }); + + Espo.Ui.notifyWait(); + + try { + await model.destroy({wait: true}); + } catch (e) { + if (!this.collection.models.includes(model)) { + this.collection.add(model, {at: index}); + } + + return; + } + + + Espo.Ui.success(this.translate('Removed')); + + this.collection.fetch().then(() => {}); + + this.model.trigger('after:unrelate'); + this.model.trigger(`after:unrelate:${this.link}`); + + this.processSyncBack(); } // noinspection JSUnusedGlobalSymbols diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index 36989fac9f..c2e0645748 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -4107,17 +4107,15 @@ body > .autocomplete-suggestions.text-search-suggestions { width: var(--220px); } - &:has(.popover-content > .list > table) { - padding-left: 0; - padding-right: 0; - } - .popover-content { &:has(> .list > table) { padding: 0; } > .list > table { + margin-left: var(--minus-1px); + margin-right: var(--minus-1px); + > tbody { > tr { > td:first-child {