Refactor Odex Web Client: Update module structure, enhance SCSS styles, and improve navbar functionality

This commit is contained in:
Altahir Hassan 2026-01-05 15:59:46 +04:00
parent 144fffbb1a
commit 9bcf8e25cb
23 changed files with 62 additions and 84 deletions

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import version

View File

@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
{
'name': 'Odex30 Web',
'category': 'Odex30-base',
'author': 'Expert Co. Ltd.',
'name': 'Odex Web Client',
'category': 'Hidden',
'version': '1.0',
'description': """
Odex Web Client.
===========================
This module modifies the web addon to provide Odex design and responsiveness.
Web Client customizations for Odex
""",
'summary': 'Web Client customizations for Odex',
'author': 'Exp SA',
'website': 'https://exp-sa.com',
'company': 'Expert Ltd.',
'depends': ['web', 'base_setup'],
'auto_install': ['web'],
'data': [
@ -27,7 +28,7 @@ This module modifies the web addon to provide Odex design and responsiveness.
('before', 'web/static/src/scss/bootstrap_overridden.scss', 'odex30_web/static/src/scss/bootstrap_overridden.scss'),
],
'web.assets_frontend': [
'odex30_web/static/src/webclient/home_menu/home_menu_background.scss',
'odex30_web/static/src/webclient/home_menu/home_menu_background.scss', # used by login page
'odex30_web/static/src/webclient/navbar/navbar.scss',
],
'web.assets_backend': [
@ -42,6 +43,7 @@ This module modifies the web addon to provide Odex design and responsiveness.
'odex30_web/static/src/views/**/*.xml',
('remove', 'odex30_web/static/src/views/pivot/**'),
# Don't include dark mode files in light mode
('remove', 'odex30_web/static/src/**/*.dark.scss'),
],
'web.assets_backend_lazy': [
@ -49,37 +51,29 @@ This module modifies the web addon to provide Odex design and responsiveness.
],
'web.assets_backend_lazy_dark': [
('include', 'web.dark_mode_variables'),
# web._assets_backend_helpers
('before', 'odex30_web/static/src/scss/bootstrap_overridden.scss', 'odex30_web/static/src/scss/bootstrap_overridden.dark.scss'),
('after', 'web/static/lib/bootstrap/scss/_functions.scss', 'odex30_web/static/src/scss/bs_functions_overridden.dark.scss'),
],
'web.assets_web': [
('replace', 'web/static/src/main.js', 'odex30_web/static/src/main.js'),
],
# ========= Dark Mode =========
"web.dark_mode_variables": [
# web._assets_primary_variables
('before', 'odex30_web/static/src/scss/primary_variables.scss', 'odex30_web/static/src/scss/primary_variables.dark.scss'),
('before', 'odex30_web/static/src/**/*.variables.scss', 'odex30_web/static/src/**/*.variables.dark.scss'),
# web._assets_secondary_variables
('before', 'odex30_web/static/src/scss/secondary_variables.scss', 'odex30_web/static/src/scss/secondary_variables.dark.scss'),
],
"web.assets_web_dark": [
('include', 'web.dark_mode_variables'),
# web._assets_backend_helpers
('before', 'odex30_web/static/src/scss/bootstrap_overridden.scss', 'odex30_web/static/src/scss/bootstrap_overridden.dark.scss'),
('after', 'web/static/lib/bootstrap/scss/_functions.scss', 'odex30_web/static/src/scss/bs_functions_overridden.dark.scss'),
# assets_backend
'odex30_web/static/src/**/*.dark.scss',
],
'web.tests_assets': [
'odex30_web/static/tests/*.js',
],
"web.assets_tests": [
"odex30_web/static/tests/tours/**/*.js",
],
'web.assets_unit_tests': [
'odex30_web/static/tests/**/*.test.js',
],
'web.qunit_suite_tests': [
'odex30_web/static/tests/views/**/*.js',
'odex30_web/static/tests/webclient/**/*.js',
('remove', 'odex30_web/static/tests/**/*.test.js'),
],
},
'license': 'OEEL-1',
'license': 'LGPL-3',
}

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import ir_http
from . import res_users_settings

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import json
@ -10,10 +11,10 @@ class Http(models.AbstractModel):
@classmethod
def _post_logout(cls):
super()._post_logout()
request.future_response.set_cookie('color_scheme', max_age=0)
def webclient_rendering_context(self):
""" Extend the rendering context with session info."""
return {
'session_info': self.session_info(),
}
@ -21,17 +22,6 @@ class Http(models.AbstractModel):
def session_info(self):
ICP = self.env['ir.config_parameter'].sudo()
if self.env.user.has_group('base.group_system'):
warn_enterprise = 'admin'
elif self.env.user._is_internal():
warn_enterprise = 'user'
else:
warn_enterprise = False
result = super(Http, self).session_info()
result['support_url'] = "https://www.odoo.com/help"
if warn_enterprise:
result['warning'] = warn_enterprise
result['expiration_date'] = ICP.get_param('database.expiration_date')
result['expiration_reason'] = ICP.get_param('database.expiration_reason')
result['support_url'] = "https://exp-sa.com/support_center"
return result

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from odoo import fields, models

View File

@ -1,3 +1,4 @@
// Custom SCSS for enterprise version of notebook tabs
.o_notebook {
--notebook-link-border-color: #{$border-color};

View File

@ -1,11 +1,11 @@
/** @odoo-module **/
import { startWebClient } from "@web/start";
import { WebClientOdex } from "./webclient/webclient";
import { WebClientEnterprise } from "./webclient/webclient";
/**
* This file starts the webclient. In the manifest, it replaces
* This file starts the enterprise webclient. In the manifest, it replaces
* the community main.js to load a different webclient class
* (WebClientOdex instead of WebClient)
* (WebClientEnterprise instead of WebClient)
*/
startWebClient(WebClientOdex);
startWebClient(WebClientEnterprise);

View File

@ -61,7 +61,6 @@ $component-active-bg: $o-gray-300 !default;
// == Typography
$mark-bg: #ffdebc !default;
$mark-color: shift-color($mark-bg, -75%) !default;
// == Tables
$table-bg: $o-view-background-color !default;

View File

@ -1,5 +1,5 @@
$o-colors-original: lighten(#000, 46.7%), #e74e4e, #f4b660, #F7CD1F, #6cedeb, #8d5482,
#f07b50, #2C8397, #475577, #dc0457, #30C381, #9365B8 !default;
$o-colors-original: lighten(#000, 46.7%), #f07b50, #f4b660, #F7CD1F, #6cedeb, #8d5482,
#e74e4e, #2C8397, #475577, #dc0457, #30C381, #9365B8 !default;
$o-colors-secondary-original: #aa4b6b, #30C381, #97743a, #F7CD1F, #4285F4, #8E24AA,
#D6145F, #173e43, #348F50, #AA3A38, #795548, #5e0231,

View File

@ -1,5 +1,5 @@
.o_field_property_definition_type, .o_field_property_definition_type_menu {
img {
.o_field_property_dropdown > img {
-webkit-filter: invert(100%);
filter: invert(100%);
}
@ -11,3 +11,9 @@
}
}
}
.o_field_property_definition_type_popover.popover {
.dropdown-item img {
-webkit-filter: invert(100%);
filter: invert(100%);
}
}

View File

@ -60,9 +60,6 @@ export const patchListRendererDesktop = () => ({
// we set them to not editable too.
return false;
}
if (action.res_model === "account.bank.statement.line") {
return false; // bank reconciliation isn't editable
}
return Boolean(action.res_model);
};
const onUiUpdated = () => {

View File

@ -3,7 +3,7 @@ import { BurgerMenu } from "@web/webclient/burger_menu/burger_menu";
import { useService } from "@web/core/utils/hooks";
import { registry } from "@web/core/registry";
export class EnterpriseBurgerMenu extends BurgerMenu {
export class OdexBurgerMenu extends BurgerMenu {
setup() {
super.setup();
this.hm = useService("home_menu");
@ -15,7 +15,7 @@ export class EnterpriseBurgerMenu extends BurgerMenu {
}
const systrayItem = {
Component: EnterpriseBurgerMenu,
Component: OdexBurgerMenu,
};
registry.category("systray").add("burger_menu", systrayItem, { sequence: 0, force: true });

View File

@ -4,7 +4,6 @@ import { hasTouch, isIosApp, isMacOS } from "@web/core/browser/feature_detection
import { useHotkey } from "@web/core/hotkeys/hotkey_hook";
import { user } from "@web/core/user";
import { useService } from "@web/core/utils/hooks";
import { ExpirationPanel } from "./expiration_panel";
import { useSortable } from "@web/core/utils/sortable_owl";
import {
@ -37,7 +36,7 @@ class FooterComponent extends Component {
*/
export class HomeMenu extends Component {
static template = "odex30_web.HomeMenu";
static components = { ExpirationPanel };
static components = { };
static props = {
apps: {
type: Array,
@ -93,7 +92,6 @@ export class HomeMenu extends Component {
this.command = useService("command");
this.menus = useService("menu");
this.homeMenuService = useService("home_menu");
this.subscription = useState(useService("enterprise_subscription"));
this.ui = useService("ui");
this.state = useState({
focusedIndex: null,
@ -351,9 +349,7 @@ export class HomeMenu extends Component {
_onInputSearch() {
const onClose = () => {
this._focusInput();
if (this.inputRef.el) {
this.inputRef.el.value = "";
}
this.inputRef.el.value = "";
};
const searchValue = this.compositionStart ? "/" : `/${this.inputRef.el.value.trim()}`;
this.compositionStart = false;

View File

@ -1,5 +1,9 @@
.o_home_menu_background {
// 'Home menu background' design is shared with enterprise login
// screens and it's located in './home_menu_background.scss'
// When applied on webclient (note: we do not specify the webclient class
// here to avoid breaking studio custom style)
&:not(.o_home_menu_background_custom):not(.o_in_studio) .o_main_navbar {
background: transparent;
border-bottom-color: transparent;
@ -36,8 +40,8 @@
.o_app_icon {
width: $o-home-menu-app-icon-max-width;
aspect-ratio: 1;
padding: $o-home-menu-app-icon-padding;
background-color: var(--AppSwitcherIcon-background, #{$o-home-menu-app-icon-background-color});
padding: 10px;
background-color: var(--AppSwitcherIcon-background, rgba(#fff, 1));
object-fit: cover;
transform-origin: center bottom;
transition: box-shadow ease-in 0.1s, transform ease-in 0.1s;

View File

@ -1,8 +1,6 @@
$o-home-menu-font-size-base: 1rem;
$o-home-menu-container-size: 850px;
$o-home-menu-app-icon-max-width: 70px;
$o-home-menu-app-icon-padding: 10px;
$o-home-menu-app-icon-background-color: rgba(#fff, 1);
$o-home-menu-caption-color: $o-gray-700 !default;
$o-home-menu-caption-shadow: none !default;

View File

@ -10,10 +10,7 @@
t-att-aria-expanded="displayedApps.length ? 'true' : 'false'"
aria-autocomplete="list"
aria-haspopup="listbox"
autocomplete="off"
/>
<!-- When the subscription has expired, the expiration panel is show over the whole UI instead of here -->
<ExpirationPanel t-if="subscription.warningType and !subscription.isWarningHidden and subscription.daysLeft &lt;= 30 and subscription.daysLeft > 0"/>
<div t-if="displayedApps.length" role="listbox" class="o_apps row user-select-none mt-5 mx-0">
<div t-foreach="displayedApps" t-as="app" t-key="app.id" class="col-3 col-md-2 o_draggable mb-3 px-0">
<a t-att-id="'result_app_' + app_index"

View File

@ -4,6 +4,6 @@
size: cover;
attachment: fixed;
color: var(--homeMenu-bg-color, #{$o-gray-200});
image: var(--homeMenu-bg-image, url("/odex30_web/static/img/background-light.svg"));
image: var(--homeMenu-bg-image, url("/odex30_web/static/img/background-light.svg"));
}
}

View File

@ -5,8 +5,8 @@ import { useService, useBus } from "@web/core/utils/hooks";
import { _t } from "@web/core/l10n/translation";
import { useState, useEffect, useRef } from "@odoo/owl";
export class OdexNavBar extends NavBar {
static template = "odex30_web.OdexNavBar";
export class EnterpriseNavBar extends NavBar {
static template = "odex30_web.EnterpriseNavBar";
setup() {
super.setup();
this.hm = useState(useService("home_menu"));

View File

@ -14,7 +14,8 @@
}
}
// Ensuring SuperUser Design menu is not compressed in Enterprise
// ============================================================================
body.o_is_superuser .o_menu_systray {
border-image-outset: map-get($border-widths, 5);
}

View File

@ -1,4 +1,5 @@
// = Enterprise Main Navbar Variables
// ============================================================================
$o-navbar-background: $o-white !default;
$o-navbar-padding-v: 10px !default;
$o-navbar-border-bottom: 0 !default;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="odex30_web.OdexNavBar" t-inherit="web.NavBar" t-inherit-mode="primary">
<t t-name="odex30_web.EnterpriseNavBar" t-inherit="web.NavBar" t-inherit-mode="primary">
<xpath expr="//nav" position="attributes">
<attribute name="t-ref">nav</attribute>
</xpath>
@ -45,7 +45,7 @@
<xpath expr="//DropdownItem[@t-esc='currentApp.name']" position="replace"/>
</t>
<t t-name="odex30_web.OdexNavBar.SectionsMenu" t-inherit="web.NavBar.SectionsMenu" t-inherit-mode="extension">
<t t-name="odex30_web.EnterpriseNavBar.SectionsMenu" t-inherit="web.NavBar.SectionsMenu" t-inherit-mode="extension">
<xpath expr="//Dropdown/button" position="attributes">
<attribute name="class" add="fw-normal" separator=" "/>
</xpath>

View File

@ -5,20 +5,10 @@
<xpath expr="//h3" position="replace">
<h3 class="px-0">
Odoo <t t-esc="serverVersion"/> (Enterprise Edition)
Odex 30
</h3>
</xpath>
<xpath expr="//*[@id='license']" position="replace">
<a id="license" target="_blank" href="https://www.odoo.com/documentation/master/legal/licenses.html" style="text-decoration: underline;">Odoo Enterprise Edition License V1.0</a>
</xpath>
<xpath expr="//h3" position="after">
<t t-if="expirationDate">
<h5>Database expiration: <t t-esc="expirationDate"/></h5>
</t>
</xpath>
</t>
</templates>

View File

@ -2,12 +2,12 @@
import { WebClient } from "@web/webclient/webclient";
import { useService } from "@web/core/utils/hooks";
import { OdexNavBar } from "./navbar/navbar";
import { EnterpriseNavBar } from "./navbar/navbar";
export class WebClientOdex extends WebClient {
export class WebClientEnterprise extends WebClient {
static components = {
...WebClient.components,
NavBar: OdexNavBar,
NavBar: EnterpriseNavBar,
};
setup() {
super.setup();