Merge pull request #25507 from GursheenK/announcement-widget-navbar
feat: navbar announcement widget
This commit is contained in:
commit
d1210f7edc
6 changed files with 191 additions and 139 deletions
|
|
@ -1,7 +1,4 @@
|
|||
// Copyright (c) 2020, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Navbar Settings", {
|
||||
// refresh: function(frm) {
|
||||
// }
|
||||
});
|
||||
frappe.ui.form.on("Navbar Settings", {});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
"logo_width",
|
||||
"section_break_2",
|
||||
"settings_dropdown",
|
||||
"help_dropdown"
|
||||
"help_dropdown",
|
||||
"announcements_section",
|
||||
"announcement_widget"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -49,11 +51,23 @@
|
|||
"fieldname": "logo_width",
|
||||
"fieldtype": "Int",
|
||||
"label": "Logo Width"
|
||||
},
|
||||
{
|
||||
"fieldname": "announcements_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Announcements"
|
||||
},
|
||||
{
|
||||
"description": "These announcements will appear inside a dismissible alert below the Navbar.",
|
||||
"fieldname": "announcement_widget",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Announcement Widget",
|
||||
"max_height": "10em"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:30.561647",
|
||||
"modified": "2024-03-23 17:03:30.561647",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Navbar Settings",
|
||||
|
|
@ -75,4 +89,4 @@
|
|||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class NavbarSettings(Document):
|
|||
from frappe.core.doctype.navbar_item.navbar_item import NavbarItem
|
||||
from frappe.types import DF
|
||||
|
||||
announcement_widget: DF.TextEditor | None
|
||||
app_logo: DF.AttachImage | None
|
||||
help_dropdown: DF.Table[NavbarItem]
|
||||
logo_width: DF.Int
|
||||
|
|
|
|||
|
|
@ -1,139 +1,153 @@
|
|||
<header class="navbar navbar-expand sticky-top" role="navigation">
|
||||
<div class="container">
|
||||
<a class="navbar-brand navbar-home" href="/app">
|
||||
<img
|
||||
class="app-logo"
|
||||
style="width: {{ navbar_settings.logo_width || 28 }}px"
|
||||
src="{{ frappe.boot.app_logo_url }}"
|
||||
alt="{{ __("App Logo") }}"
|
||||
>
|
||||
</a>
|
||||
<ul class="nav navbar-nav d-none d-sm-flex" id="navbar-breadcrumbs"></ul>
|
||||
<div class="collapse navbar-collapse justify-content-end">
|
||||
<form class="form-inline fill-width justify-content-end" role="search" onsubmit="return false;">
|
||||
{% if (frappe.boot.read_only) { %}
|
||||
<span class="indicator-pill yellow no-indicator-dot read-only-banner" title="{%= __("Your site is undergoing maintenance or being updated.") %}">
|
||||
{%= __("Read Only Mode") %}
|
||||
</span>
|
||||
{% } %}
|
||||
{% if (frappe.boot.user.impersonated_by) { %}
|
||||
<span class="indicator-pill red no-indicator-dot" title="{%= __("You are impersonating as another user.") %}">
|
||||
{%= __("Impersonating {0}", [frappe.boot.user.name]) %}
|
||||
</span>
|
||||
{% } %}
|
||||
<div class="input-group search-bar text-muted hidden">
|
||||
<input
|
||||
id="navbar-search"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="{%= __('Search or type a command ({0})', [frappe.utils.is_mac() ? '⌘ + G' : 'Ctrl + G']) %}"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<span class="search-icon">
|
||||
<svg class="icon icon-sm"><use href="#icon-search"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown dropdown-notifications dropdown-mobile hidden">
|
||||
<button
|
||||
class="btn-reset nav-link notifications-icon text-muted"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="notifications-seen">
|
||||
<span class="sr-only">{{ __("No new notifications") }}</span>
|
||||
<svg class="es-icon icon-sm" style="stroke:none;"><use href="#es-line-notifications"></use></svg>
|
||||
<div class="sticky-top">
|
||||
<header class="navbar navbar-expand" role="navigation">
|
||||
<div class="container">
|
||||
<a class="navbar-brand navbar-home" href="/app">
|
||||
<img
|
||||
class="app-logo"
|
||||
style="width: {{ navbar_settings.logo_width || 28 }}px"
|
||||
src="{{ frappe.boot.app_logo_url }}"
|
||||
alt="{{ __("App Logo") }}"
|
||||
>
|
||||
</a>
|
||||
<ul class="nav navbar-nav d-none d-sm-flex" id="navbar-breadcrumbs"></ul>
|
||||
<div class="collapse navbar-collapse justify-content-end">
|
||||
<form class="form-inline fill-width justify-content-end" role="search" onsubmit="return false;">
|
||||
{% if (frappe.boot.read_only) { %}
|
||||
<span class="indicator-pill yellow no-indicator-dot read-only-banner" title="{%= __("Your site is undergoing maintenance or being updated.") %}">
|
||||
{%= __("Read Only Mode") %}
|
||||
</span>
|
||||
<span class="notifications-unseen">
|
||||
<span class="sr-only">{{ __("You have unseen notifications") }}</span>
|
||||
<svg class="es-icon icon-sm"><use href="#es-line-notifications-unseen"></use></svg>
|
||||
{% } %}
|
||||
{% if (frappe.boot.user.impersonated_by) { %}
|
||||
<span class="indicator-pill red no-indicator-dot" title="{%= __("You are impersonating as another user.") %}">
|
||||
{%= __("Impersonating {0}", [frappe.boot.user.name]) %}
|
||||
</span>
|
||||
</button>
|
||||
<div class="dropdown-menu notifications-list dropdown-menu-right" role="menu">
|
||||
<div class="notification-list-header">
|
||||
<div class="header-items"></div>
|
||||
<div class="header-actions"></div>
|
||||
{% } %}
|
||||
<div class="input-group search-bar text-muted hidden">
|
||||
<input
|
||||
id="navbar-search"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="{%= __('Search or type a command ({0})', [frappe.utils.is_mac() ? '⌘ + G' : 'Ctrl + G']) %}"
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<span class="search-icon">
|
||||
<svg class="icon icon-sm"><use href="#icon-search"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown dropdown-notifications dropdown-mobile hidden">
|
||||
<button
|
||||
class="btn-reset nav-link notifications-icon text-muted"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="notifications-seen">
|
||||
<span class="sr-only">{{ __("No new notifications") }}</span>
|
||||
<svg class="es-icon icon-sm" style="stroke:none;"><use href="#es-line-notifications"></use></svg>
|
||||
</span>
|
||||
<span class="notifications-unseen">
|
||||
<span class="sr-only">{{ __("You have unseen notifications") }}</span>
|
||||
<svg class="es-icon icon-sm"><use href="#es-line-notifications-unseen"></use></svg>
|
||||
</span>
|
||||
</button>
|
||||
<div class="dropdown-menu notifications-list dropdown-menu-right" role="menu">
|
||||
<div class="notification-list-header">
|
||||
<div class="header-items"></div>
|
||||
<div class="header-actions"></div>
|
||||
</div>
|
||||
<div class="notification-list-body">
|
||||
<div class="panel-notifications"></div>
|
||||
<div class="panel-events"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-list-body">
|
||||
<div class="panel-notifications"></div>
|
||||
<div class="panel-events"></div>
|
||||
</li>
|
||||
<li class="nav-item dropdown dropdown-message dropdown-mobile hidden">
|
||||
<button
|
||||
class="btn-reset nav-link notifications-icon text-muted"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
>
|
||||
<span>
|
||||
<svg class="es-icon icon-sm"><use href="#es-line-chat-alt"></use></svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="vertical-bar d-none d-sm-block"></li>
|
||||
<li class="nav-item dropdown dropdown-help dropdown-mobile d-none d-lg-block">
|
||||
<button
|
||||
class="btn-reset nav-link"
|
||||
data-toggle="dropdown"
|
||||
aria-controls="toolbar-help"
|
||||
aria-label="{{ __("Help Dropdown") }}"
|
||||
>
|
||||
<span>
|
||||
{{ __("Help") }}
|
||||
<svg class="es-icon icon-xs"><use href="#es-line-down"></use></svg>
|
||||
</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" id="toolbar-help" role="menu">
|
||||
<div id="help-links"></div>
|
||||
<div class="dropdown-divider documentation-links"></div>
|
||||
{% for item in navbar_settings.help_dropdown %}
|
||||
{% if (!item.hidden) { %}
|
||||
{% if (item.route) { %}
|
||||
<a class="dropdown-item" href="{{ item.route }}">
|
||||
{%= __(item.item_label) %}
|
||||
</a>
|
||||
{% } else if (item.action) { %}
|
||||
<button class="btn-reset dropdown-item" onclick="return {{ item.action }}">
|
||||
{%= __(item.item_label) %}
|
||||
</button>
|
||||
{% } else { %}
|
||||
<div class="dropdown-divider"></div>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown dropdown-message dropdown-mobile hidden">
|
||||
<button
|
||||
class="btn-reset nav-link notifications-icon text-muted"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
>
|
||||
<span>
|
||||
<svg class="es-icon icon-sm"><use href="#es-line-chat-alt"></use></svg>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="vertical-bar d-none d-sm-block"></li>
|
||||
<li class="nav-item dropdown dropdown-help dropdown-mobile d-none d-lg-block">
|
||||
<button
|
||||
class="btn-reset nav-link"
|
||||
data-toggle="dropdown"
|
||||
aria-controls="toolbar-help"
|
||||
aria-label="{{ __("Help Dropdown") }}"
|
||||
>
|
||||
<span>
|
||||
{{ __("Help") }}
|
||||
<svg class="es-icon icon-xs"><use href="#es-line-down"></use></svg>
|
||||
</span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" id="toolbar-help" role="menu">
|
||||
<div id="help-links"></div>
|
||||
<div class="dropdown-divider documentation-links"></div>
|
||||
{% for item in navbar_settings.help_dropdown %}
|
||||
{% if (!item.hidden) { %}
|
||||
{% if (item.route) { %}
|
||||
<a class="dropdown-item" href="{{ item.route }}">
|
||||
{%= __(item.item_label) %}
|
||||
</a>
|
||||
{% } else if (item.action) { %}
|
||||
<button class="btn-reset dropdown-item" onclick="return {{ item.action }}">
|
||||
{%= __(item.item_label) %}
|
||||
</button>
|
||||
{% } else { %}
|
||||
<div class="dropdown-divider"></div>
|
||||
</li>
|
||||
<li class="nav-item dropdown dropdown-navbar-user dropdown-mobile">
|
||||
<button
|
||||
class="btn-reset nav-link"
|
||||
data-toggle="dropdown"
|
||||
aria-label="{{ __("User Menu") }}"
|
||||
>
|
||||
{{ avatar }}
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" id="toolbar-user" role="menu">
|
||||
{% for item in navbar_settings.settings_dropdown %}
|
||||
{% if (!item.hidden) { %}
|
||||
{% if (item.route) { %}
|
||||
<a class="dropdown-item" href="{{ item.route }}">
|
||||
{%= __(item.item_label) %}
|
||||
</a>
|
||||
{% } else if (item.action) { %}
|
||||
<button class="btn-reset dropdown-item" onclick="return {{ item.action }}">
|
||||
{%= __(item.item_label) %}
|
||||
</button>
|
||||
{% } else { %}
|
||||
<div class="dropdown-divider"></div>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown dropdown-navbar-user dropdown-mobile">
|
||||
<button
|
||||
class="btn-reset nav-link"
|
||||
data-toggle="dropdown"
|
||||
aria-label="{{ __("User Menu") }}"
|
||||
>
|
||||
{{ avatar }}
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right" id="toolbar-user" role="menu">
|
||||
{% for item in navbar_settings.settings_dropdown %}
|
||||
{% if (!item.hidden) { %}
|
||||
{% if (item.route) { %}
|
||||
<a class="dropdown-item" href="{{ item.route }}">
|
||||
{%= __(item.item_label) %}
|
||||
</a>
|
||||
{% } else if (item.action) { %}
|
||||
<button class="btn-reset dropdown-item" onclick="return {{ item.action }}">
|
||||
{%= __(item.item_label) %}
|
||||
</button>
|
||||
{% } else { %}
|
||||
<div class="dropdown-divider"></div>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if !localStorage.getItem("dismissed_announcement_widget") && strip_html(navbar_settings.announcement_widget) != '' %}
|
||||
<div class="announcement-widget form-message p-2 m-0" style="position: relative; z-index: -1; border-radius: 0; background-color: var(--bg-blue);">
|
||||
<div class="container flex justify-between align-center mx-auto">
|
||||
{{ navbar_settings.announcement_widget }}
|
||||
<div class="close-message p-0 mr-2" style="position: relative;">
|
||||
{{ frappe.utils.icon("close") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
|
@ -21,6 +21,7 @@ frappe.ui.toolbar.Toolbar = class {
|
|||
this.setup_notifications();
|
||||
this.setup_help();
|
||||
this.setup_read_only_mode();
|
||||
this.setup_announcement_widget();
|
||||
this.make();
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +57,30 @@ frappe.ui.toolbar.Toolbar = class {
|
|||
});
|
||||
}
|
||||
|
||||
setup_announcement_widget() {
|
||||
let current_announcement = frappe.boot.navbar_settings.announcement_widget;
|
||||
|
||||
if (!current_announcement) return;
|
||||
|
||||
// If an unseen announcement is added, overlook dismiss flag
|
||||
if (current_announcement != localStorage.getItem("announcement_widget")) {
|
||||
localStorage.removeItem("dismissed_announcement_widget");
|
||||
localStorage.setItem("announcement_widget", current_announcement);
|
||||
}
|
||||
|
||||
// When an announcement is closed, add dismiss flag
|
||||
if (!localStorage.getItem("dismissed_announcement_widget")) {
|
||||
let announcement_widget = $(".announcement-widget");
|
||||
let close_message = announcement_widget.find(".close-message");
|
||||
close_message.on(
|
||||
"click",
|
||||
() =>
|
||||
localStorage.setItem("dismissed_announcement_widget", true) ||
|
||||
announcement_widget.addClass("hidden")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setup_help() {
|
||||
if (!frappe.boot.desk_settings.notifications) {
|
||||
// hide the help section
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ def get():
|
|||
|
||||
bootinfo["desk_theme"] = frappe.db.get_value("User", frappe.session.user, "desk_theme") or "Light"
|
||||
bootinfo["user"]["impersonated_by"] = frappe.session.data.get("impersonated_by")
|
||||
bootinfo["navbar_settings"] = frappe.get_cached_doc("Navbar Settings")
|
||||
|
||||
return bootinfo
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue