diff --git a/frappe/core/doctype/communication/communication.js b/frappe/core/doctype/communication/communication.js index cdbdf97852..2ca3e7dea0 100644 --- a/frappe/core/doctype/communication/communication.js +++ b/frappe/core/doctype/communication/communication.js @@ -139,7 +139,7 @@ frappe.ui.form.on("Communication", { }); }, function () { - frappe.show_alert('Document not Relinked') + frappe.show_alert({message:__('Document not Relinked'), 'indicator': 'info'}) } ); } diff --git a/frappe/core/doctype/user_permission/user_permission_list.js b/frappe/core/doctype/user_permission/user_permission_list.js index 3e822f0007..bd3cdffe96 100644 --- a/frappe/core/doctype/user_permission/user_permission_list.js +++ b/frappe/core/doctype/user_permission/user_permission_list.js @@ -145,7 +145,7 @@ frappe.listview_settings['User Permission'] = { } frappe.show_alert({ message, - indicator: 'green' + indicator: 'info' }); list_view.refresh(); }); diff --git a/frappe/core/page/workspace/workspace.js b/frappe/core/page/workspace/workspace.js index 12683ceae4..06a5a01d9d 100644 --- a/frappe/core/page/workspace/workspace.js +++ b/frappe/core/page/workspace/workspace.js @@ -16,6 +16,7 @@ class Workspace { this.wrapper = $(wrapper); this.page = wrapper.page; this.prepare_container(); + this.setup_dropdown(); this.pages = {}; this.sidebar_items = {}; this.sidebar_categories = [ @@ -115,7 +116,7 @@ class Workspace { const make_sidebar_category_item = item => { if (item.name == this.get_page_to_show()) { item.selected = true; - this.current_page = item.name; + this.current_page_name = item.name; } let $item = get_sidebar_item(item); @@ -130,18 +131,20 @@ class Workspace { } show_page(page) { - if (this.current_page && this.pages[this.current_page]) { - this.pages[this.current_page].hide(); + if (this.current_page_name && this.pages[this.current_page_name]) { + this.pages[this.current_page_name].hide(); } - if (this.sidebar_items && this.sidebar_items[this.current_page]) { - this.sidebar_items[this.current_page].removeClass("selected"); + if (this.sidebar_items && this.sidebar_items[this.current_page_name]) { + this.sidebar_items[this.current_page_name].removeClass("selected"); this.sidebar_items[page].addClass("selected"); } - this.current_page = page; + this.current_page_name = page; localStorage.current_desk_page = page; this.pages[page] ? this.pages[page].show() : this.make_page(page); + this.current_page = this.pages[page]; + this.setup_dropdown(); } make_page(page) { @@ -153,6 +156,47 @@ class Workspace { this.pages[page] = $page; return $page; } + + customize() { + if (this.current_page && this.current_page.allow_customization) { + this.page.clear_menu() + this.current_page.customize(); + + this.page.set_primary_action( + __("Save Customizations"), + () => { + this.current_page.save_customization(); + this.page.clear_primary_action(); + this.setup_dropdown(); + }, + null, + __("Saving") + ) + + this.page.set_secondary_action( + __("Discard"), + () => { + this.current_page.reload(); + this.page.clear_secondary_action(); + this.setup_dropdown(); + } + ) + } + } + + setup_dropdown() { + this.page.clear_menu(); + + this.page.add_menu_item('Customize', () => { + this.customize(); + }, 1); + + // this.page.add_menu_item('Reset', () => { + // }, 1); + + // this.page.add_menu_item('Hide Page', () => { + // }, 1) + } } class DesktopPage { @@ -235,9 +279,6 @@ class DesktopPage { // We need to remove this as the chart group will be visible during customization $('.widget.onboarding-widget-box').hide(); - this.customize_link.hide(); - this.save_or_discard_link.show(); - Object.keys(this.sections).forEach(section => { this.sections[section].customize(); }); diff --git a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js index c3cf701d92..8d780969e1 100644 --- a/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js +++ b/frappe/data_migration/doctype/data_migration_connector/data_migration_connector.js @@ -30,7 +30,7 @@ frappe.ui.form.on('Data Migration Connector', { frm.set_value('connector_type', 'Custom'); frm.set_value('python_module', r.message); frm.save(); - frappe.show_alert(__(`New module created ${r.message}`)); + frappe.show_alert({message: __(`New module created ${r.message}`), indicator: 'success'}); d.hide(); } }); diff --git a/frappe/desk/page/translation_tool/translation_tool.js b/frappe/desk/page/translation_tool/translation_tool.js index 892bab32ce..08cbdbfe8c 100644 --- a/frappe/desk/page/translation_tool/translation_tool.js +++ b/frappe/desk/page/translation_tool/translation_tool.js @@ -347,7 +347,7 @@ class TranslationTool { ) .then(() => { frappe.dom.unfreeze(); - frappe.show_alert(__('Successfully Submitted!')); + frappe.show_alert({ message:__('Successfully Submitted!'), indicator: 'success'}); this.edited_translations = {}; this.update_header(); this.fetch_messages_then_render(); diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 7ad8e5be88..756e2edb1f 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -25,7 +25,7 @@ - + @@ -209,11 +209,11 @@ + fill="#ACB5BD" stroke="none" stroke-width="0"> + fill="#ACB5BD" stroke="none" stroke-width="0"> @@ -230,9 +230,8 @@ - - + + @@ -519,4 +518,16 @@ + + + + + + + + + + + + diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 819f039273..aab7d7ddba 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -216,7 +216,7 @@ frappe.Application = Class.extend({ s.hide(); d.hide();//hide waiting indication if (!passed["message"]) { - frappe.show_alert("Login Failed please try again", 5); + frappe.show_alert({message: __("Login Failed please try again"), indicator: 'error'}, 5); me.email_password_prompt(email_account, user, i); } else { if (i + 1 < email_account.length) { diff --git a/frappe/public/js/frappe/form/print.js b/frappe/public/js/frappe/form/print.js index eb1b1464a7..bfb6240f17 100644 --- a/frappe/public/js/frappe/form/print.js +++ b/frappe/public/js/frappe/form/print.js @@ -284,16 +284,18 @@ frappe.ui.form.PrintPreview = Class.extend({ }); } else { frappe.show_alert({ - message: __('PDF printing via "Raw Print" is not yet supported. Please remove the printer mapping in Printer Settings and try again.'), - indicator: 'blue' + message: __('PDF printing via "Raw Print" is not supported.'), + subtitle: __('Please remove the printer mapping in Printer Settings and try again.'), + indicator: 'info' }, 14); //Note: need to solve "Error: Cannot parse (FILE) as a PDF file" to enable qz pdf printing. } } else if (me.is_raw_printing()) { // printer not mapped in localstorage and the current print format is raw printing frappe.show_alert({ - message: __('Please set a printer mapping for this print format in the Printer Settings'), - indicator: 'blue' + message: __('Printer mapping not set.'), + subtitle: __('Please set a printer mapping for this print format in the Printer Settings'), + indicator: 'warning' }, 14); me.printer_setting_dialog(); } else { diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 8386cb6c7e..6d00750e78 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -36,7 +36,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { freeze_message: freeze_message }); } else { - !frm.is_dirty() && frappe.show_alert({message: __("No changes in document"), indicator: "blue"}); + !frm.is_dirty() && frappe.show_alert({message: __("No changes in document"), indicator: "orange"}); $(btn).prop("disabled", false); } }; diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index 1238bf141c..d7521b1c59 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -202,7 +202,7 @@ frappe.ui.form.ScriptManager = Class.extend({ this.trigger('setup'); }, log_error: function(caller, e) { - frappe.show_alert("Error in Client Script."); + frappe.show_alert({message: __("Error in Client Script."), indicator: "error"}); console.group && console.group(); console.log("----- error in client script -----"); console.log("method: " + caller); diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index b84b2765fa..5afee9b8fb 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -71,7 +71,7 @@ frappe.ui.form.Toolbar = Class.extend({ }, show_unchanged_document_alert: function() { frappe.show_alert({ - indicator: "yellow", + indicator: "info", message: __("Unchanged") }); }, diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index ea4de99249..21155492b3 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -29,7 +29,8 @@ frappe.call = function(opts) { if (!frappe.is_online()) { frappe.show_alert({ indicator: 'orange', - message: __('You are not connected to Internet. Retry after sometime.') + message: __('Connection Lost'), + subtitle: __('You are not connected to Internet. Retry after sometime.') }, 3); opts.always && opts.always(); return $.ajax(); diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index ab20feeedd..8652fde4aa 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -348,43 +348,53 @@ frappe.hide_progress = function() { // Floating Message frappe.show_alert = function(message, seconds=7, actions={}) { - if(typeof message==='string') { + let indicator_icon_map = { + 'orange': "solid-warning", + 'yellow': "solid-warning", + 'blue': "solid-success", + 'green': "solid-success", + 'red': "solid-red" + }; + + if (typeof message==='string') { message = { message: message }; } - if(!$('#dialog-container').length) { + + if (!$('#dialog-container').length) { $('').appendTo('body'); } - let body_html; - - if (message.body) { - body_html = message.body; + let icon; + if (message.indicator) { + icon = indicator_icon_map[message.indicator.toLowerCase()] || 'solid-' + message.indicator; + } else { + icon = 'solid-info' } const div = $(` - + + + ${frappe.utils.icon(icon, 'lg')} + ${message.message} + + ${message.subtitle || '' } + - × + ${frappe.utils.icon('close-alt')} `); - if(message.indicator) { - div.find('.alert-message').append(``); + div.hide().appendTo("#alert-container").show(); + + if (message.body) { + div.find('.alert-body').show().html(message.body); } - div.find('.alert-message').append(message.message); - - if (body_html) { - div.find('.alert-body').show().html(body_html); - } - - div.hide().appendTo("#alert-container").show() - .css('transform', 'translateX(0)'); - div.find('.close, button').click(function() { - div.remove(); + div.addClass('out') + setTimeout(() => div.remove(), 800); return false; }); @@ -392,7 +402,17 @@ frappe.show_alert = function(message, seconds=7, actions={}) { div.find(`[data-action=${key}]`).on('click', actions[key]); }); - div.delay(seconds * 1000).fadeOut(300); + if (seconds > 2) { + // Delay for animation + seconds = seconds - 0.8 + } + + setTimeout(() => { + div.addClass('out') + setTimeout(() => div.remove(), 800); + return false; + }, seconds * 1000) + return div; } diff --git a/frappe/public/js/frappe/ui/toolbar/toolbar.js b/frappe/public/js/frappe/ui/toolbar/toolbar.js index fff4db83ad..f5d001e4fe 100644 --- a/frappe/public/js/frappe/ui/toolbar/toolbar.js +++ b/frappe/public/js/frappe/ui/toolbar/toolbar.js @@ -221,7 +221,7 @@ frappe.ui.toolbar.clear_cache = frappe.utils.throttle(function() { frappe.xcall('frappe.sessions.clear').then(message => { frappe.show_alert({ message: message, - indicator: 'green' + indicator: 'info' }); location.reload(true); }); diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index a3efa12521..d634cdf1f4 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -336,7 +336,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { ], primary_action: ({ column, insert_before }) => { if (!columns_in_picker.map(col => col.value).includes(column)) { - frappe.show_alert(__('Invalid column')); + frappe.show_alert({message: __('Invalid column'), indicator: 'orange'}); d.hide(); return; } diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index e72e3369df..ae822bf3f1 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -24,21 +24,21 @@ export default class Widget { this.in_customize_mode = true; this.action_area.empty(); + options.allow_sorting && + this.add_custom_button( + frappe.utils.icon('drag', 'xs'), + null, + "drag-handle", + ); + options.allow_delete && this.add_custom_button( - '', + frappe.utils.icon('delete', 'xs'), () => this.delete(), "", `${__('Delete')}` ); - options.allow_sorting && - this.add_custom_button( - '', - null, - "drag-handle", - ); - if (options.allow_hiding) { if (this.hidden) { this.widget.removeClass("hidden"); @@ -62,7 +62,7 @@ export default class Widget { options.allow_edit && this.add_custom_button( - '', + frappe.utils.icon("edit", "xs"), () => this.edit() ); @@ -92,7 +92,10 @@ export default class Widget { ${ this.shadow ? "widget-shadow" : " " } " data-widget-name="${this.name ? this.name : ''}"> - + + + + @@ -102,6 +105,7 @@ export default class Widget { `); this.title_field = this.widget.find(".widget-title"); + this.subtitle_field = this.widget.find(".widget-subtitle"); this.body = this.widget.find(".widget-body"); this.action_area = this.widget.find(".widget-control"); this.head = this.widget.find(".widget-head"); @@ -122,6 +126,7 @@ export default class Widget { this.title_field[0].setAttribute('title', this.label); } } + this.subtitle && this.subtitle_field.html(this.subtitle); } add_custom_button(html, action, class_name = "", title="") { diff --git a/frappe/public/js/frappe/widgets/onboarding_widget.js b/frappe/public/js/frappe/widgets/onboarding_widget.js index 5d6de572ce..097fd890a6 100644 --- a/frappe/public/js/frappe/widgets/onboarding_widget.js +++ b/frappe/public/js/frappe/widgets/onboarding_widget.js @@ -432,16 +432,6 @@ export default class OnboardingWidget extends Widget { return false; } - set_title(title) { - super.set_title(title); - if (this.subtitle) { - let subtitle = $( - `${this.subtitle}` - ); - subtitle.appendTo(this.title_field); - } - } - set_actions() { this.action_area.empty(); if (!this.user_can_dismiss) return; diff --git a/frappe/public/js/frappe/widgets/shortcut_widget.js b/frappe/public/js/frappe/widgets/shortcut_widget.js index c610e32a77..84c48009ef 100644 --- a/frappe/public/js/frappe/widgets/shortcut_widget.js +++ b/frappe/public/js/frappe/widgets/shortcut_widget.js @@ -80,7 +80,7 @@ export default class ShortcutWidget extends Widget { ? this.color.toLowerCase() : 'grey'; - const buttons = $(`${label}`); + const buttons = $(`${label}`); buttons.appendTo(this.action_area); } } \ No newline at end of file diff --git a/frappe/public/less/desk.less b/frappe/public/less/desk.less index b856472e50..c3cb0cdc34 100644 --- a/frappe/public/less/desk.less +++ b/frappe/public/less/desk.less @@ -49,74 +49,6 @@ a[disabled="disabled"] { margin-top: 3px; } -/* alert */ - - -#alert-container { - position: fixed; - bottom: 0px; - right: 20px; - z-index: 1050; -} - -#alert-container .desk-alert { - box-shadow: 0 0px 5px rgba(0, 0, 0, 0.1); - max-width: 400px; - min-width: 200px; - max-height: 200px; - background-color: @light-yellow; - border: 1px solid @border-color; - - // word-break: break-all; - overflow-y: auto; - position: relative; - transform: translateX(calc(100% + 20px)); - transition: transform 300ms ease; - padding: 0px; - - .alert-message { - padding: 10px 40px 10px 10px; - - .indicator::before { - margin-bottom: 1px; - } - } - - .close { - position: absolute; - top: 10px; - right: 10px; - color: inherit; - opacity: 1; - font-size: inherit; - } - - .next-action-container { - display: flex; - - .next-action { - border: none; - background: none; - width: 100%; - border-top: 1px solid @border-color; - border-right: 1px solid @border-color; - padding: 7px; - outline: none; - font-size: 12px; - font-weight: bold; - color: @text-light; - - &:hover { - background-color: darken(@light-yellow, 5%); - } - - &:last-child { - border-right: none; - } - } - } -} - .missing-image { background-color: @light-bg; line-height: 140px; diff --git a/frappe/public/scss/desktop.scss b/frappe/public/scss/desktop.scss index eb68954b61..ae73e50e0f 100644 --- a/frappe/public/scss/desktop.scss +++ b/frappe/public/scss/desktop.scss @@ -132,14 +132,14 @@ $panel-bg: $gray-50; flex-direction: column; min-height: 1px; padding: 15px; - border-radius: 8px; + border-radius: var(--border-radius-md); height: 100%; &.widget-shadow { - box-shadow: 0px 1px 4px rgba(17, 43, 66, 0.1); + box-shadow: 0px 1px 4px rgba(17, 43, 66, 0.1), 0px 2px 6px rgba(17, 43, 66, 0.08); &:hover { - box-shadow: 0px 1px 4px rgba(17, 43, 66, 0.1), 0px 2px 6px rgba(17, 43, 66, 0.08); + box-shadow: 0px 2px 8px rgba(17, 43, 66, 0.1), 0px 3px 12px rgba(17, 43, 66, 0.08); } } @@ -211,6 +211,7 @@ $panel-bg: $gray-50; min-height: 65px; background-color: $disabled-background; color: $text-muted; + border: 1px dashed var(--grey-300); display: flex; align-content: center; justify-content: center; @@ -369,6 +370,10 @@ $panel-bg: $gray-50; margin-top: 5px; margin-bottom: 5px; } + + .indicator-pill { + font-weight: 500; + } } &.links-widget-box { diff --git a/frappe/public/scss/main.scss b/frappe/public/scss/main.scss index 0b9db2fb99..f9934eef51 100644 --- a/frappe/public/scss/main.scss +++ b/frappe/public/scss/main.scss @@ -6,6 +6,7 @@ @import "list"; @import "navbar"; @import "modal"; +@import "toast"; @import "breadcrumb"; @import "indicator"; @import "page"; diff --git a/frappe/public/scss/toast.scss b/frappe/public/scss/toast.scss new file mode 100644 index 0000000000..f845327ffe --- /dev/null +++ b/frappe/public/scss/toast.scss @@ -0,0 +1,120 @@ +#alert-container { + position: fixed; + bottom: 0px; + right: 20px; + z-index: 1050; +} + +#alert-container .desk-alert { + box-shadow: 0px 1px 4px rgba(17, 43, 66, 0.1), 0px 2px 6px rgba(17, 43, 66, 0.08); + width: 400px; + min-height: 50px; + max-height: 200px; + background-color: $white; + + -webkit-animation-name: backInRight; + animation-name: backInRight; + animation-duration: 600ms; + + // word-break: break-all; + overflow-y: auto; + position: relative; + // transform: translateX(calc(100% + 20px)); + // transition: transform 300ms ease; + padding: 0px; + border-radius: var(--border-radius-md); + + .alert-message-container { + padding: 16px 40px 16px 16px; + + .icon { + margin-right: 10px; + } + + .alert-title-container { + display: flex; + align-items: center; + } + + .alert-message { + font-weight: 500; + color: var(--grey-900); + line-height: 20px; + } + + .alert-subtitle { + font-size: 13px; + padding-left: 34px; + } + } + + .close { + position: absolute; + top: 10px; + right: 10px; + color: inherit; + opacity: 1; + font-size: inherit; + } + + .next-action-container { + display: flex; + + .next-action { + border: none; + background: none; + width: 100%; + border-top: 1px solid $border-color; + border-right: 1px solid $border-color; + padding: 7px; + outline: none; + font-size: 12px; + font-weight: 500; + color: $text-muted; + + &:hover { + color: var(--grey-900) + } + + &:last-child { + border-right: none; + } + } + } + + &.out { + -webkit-animation-name: backOutRight; + animation-name: backOutRight; + animation-duration: 1.6s; + } +} + +@keyframes backInRight { + 0% { + -webkit-transform: translateX(2000px) scale(0.8); + transform: translateX(2000px) scale(0.8); + opacity: 0.7; + } + + 80% { + -webkit-transform: translateX(0px) scale(0.8); + transform: translateX(0px) scale(0.8); + opacity: 0.7; + } + + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} + +@keyframes backOutRight { + 0% { opacity: 1; } + + 100% { + -webkit-transform: translateX(2000px); + transform: translateX(2000px); + opacity: 0.7; + } +} diff --git a/frappe/public/scss/variables.scss b/frappe/public/scss/variables.scss index 0dae5cf0fc..9d744b6fc9 100644 --- a/frappe/public/scss/variables.scss +++ b/frappe/public/scss/variables.scss @@ -25,6 +25,7 @@ $light: $gray-50 !default; :root { --border-sm: 4px; --border-radius: 6px; + --border-radius-md: 8px; --border-radius-lg: 12px; --text-xs: 11px;