Merge pull request #79 from expsa/odex_theme_branch

Odex theme branch
This commit is contained in:
Tahir Hassan 2026-01-07 01:06:00 +04:00 committed by GitHub
commit c253d07d42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 141 additions and 60 deletions

View File

@ -33,6 +33,7 @@
'expert_theme/static/src/scss/expert_login.scss',
'expert_theme/static/src/scss/login_minimal.scss',
'expert_theme/static/src/js/expert_login_template.js',
'expert_theme/static/src/scss/frontend.scss',
],
'web.assets_backend': [
'expert_theme/static/src/fonts/en_fonts.scss',

View File

@ -10,8 +10,7 @@ class IntroLoaderController(http.Controller):
img_b64 = request.env['ir.config_parameter'].sudo().get_param('intro_loader.image_data')
if not img_b64:
# Return a 1x1 transparent pixel or default Odoo logo if none set
return request.redirect('/web/static/img/logo2.png')
return request.redirect('/expert_theme/static/src/img/logo.webp')
image_data = base64.b64decode(img_b64)
headers = [('Content-Type', 'image/png')]

View File

@ -8,6 +8,17 @@ class ResConfigSettings(models.TransientModel):
# Text Configuration
intro_loader_text = fields.Char(string="Loader Text", config_parameter='intro_loader.text', default="Loading...")
intro_loader_show_text = fields.Boolean(string="Show Text", config_parameter='intro_loader.show_text')
# Text Styling
intro_loader_text_color = fields.Char(
string="Text Color",
config_parameter='intro_loader.text_color',
default="#6c757d" # Standard Bootstrap muted gray
)
intro_loader_text_size = fields.Integer(
string="Text Size (px)",
config_parameter='intro_loader.text_size',
default=24
)
# Spinner Configuration
intro_loader_show_spinner = fields.Boolean(string="Show Spinner", config_parameter='intro_loader.show_spinner')
@ -21,6 +32,20 @@ class ResConfigSettings(models.TransientModel):
('none', 'Static')
], string="Image Animation", config_parameter='intro_loader.animation', default='pulse')
# Exit Animation Configuration
intro_loader_exit_type = fields.Selection([
('fade', 'Fade Out'),
('slide_up', 'Slide Up (Curtain)'),
('zoom_out', 'Zoom Out'),
('circle_mask', 'Circle Mask')
], string="Exit Animation", config_parameter='intro_loader.exit_type', default='fade')
intro_loader_exit_duration = fields.Float(
string="Exit Duration (Seconds)",
config_parameter='intro_loader.exit_duration',
default=0.8
)
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -9,26 +9,23 @@ patch(WebClient.prototype, {
super.setup();
onMounted(() => {
// Select the loader element
const loader = document.getElementById("o_intro_loader");
if (loader) {
// Optional: Check sessionStorage if you want it ONCE per browser session
// const hasLoaded = sessionStorage.getItem('odoo_intro_shown');
// Add the hidden class to trigger CSS transition
// We add a small delay (e.g., 500ms) to ensure the UI is visually stable behind it
// Read duration from HTML attribute (default to 800ms if missing)
const duration = parseInt(loader.dataset.durationMs) || 800;
// Add delay for visual stability (optional 500ms)
setTimeout(() => {
loader.classList.add("o_hidden");
// Trigger CSS animation
loader.classList.add("o_hiddens");
// Cleanup DOM after animation finishes (optional but cleaner)
// Remove from DOM exactly when animation finishes
setTimeout(() => {
loader.remove();
}, 1000);
}, duration + 100); // +100ms buffer
}, 500);
// sessionStorage.setItem('odoo_intro_shown', 'true');
}
});
}

View File

@ -376,4 +376,4 @@ body {
.expert-home-container {
padding: 15px;
}
}
}

View File

@ -0,0 +1,3 @@
.o_loader_content{
display: none !important;
}

View File

@ -6,7 +6,10 @@
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.8s ease-out, visibility 0.8s;
/* Default Transition Base */
transition-timing-function: ease-in-out;
transition-duration: var(--exit-duration, 0.8s); /* Use dynamic var */
.o_loader_content {
display: flex;
@ -33,10 +36,45 @@
animation: loader-spin 1s linear infinite;
}
&.o_hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
// &.o_hiddens {
// opacity: 0;
// visibility: hidden;
// pointer-events: none;
// }
/* 1. Fade Out */
&.exit-fade {
transition-property: opacity, visibility;
&.o_hiddens {
opacity: 0;
// visibility: hidden;
}
}
/* 2. Slide Up (Curtain) */
&.exit-slide_up {
transition-property: transform;
&.o_hiddens {
transform: translateY(-100%);
}
}
/* 3. Zoom Out */
&.exit-zoom_out {
transition-property: opacity, transform;
&.o_hiddens {
opacity: 0;
transform: scale(1.5); /* Expands out while fading */
}
}
/* 4. Circle Mask (Advanced CSS Clip Path) */
&.exit-circle_mask {
transition-property: clip-path;
clip-path: circle(150% at 50% 50%);
&.o_hiddens {
clip-path: circle(0% at 50% 50%);
}
}
}

View File

@ -6,24 +6,38 @@
<field name="inherit_id" ref="base.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<app data-string="Odex Intro Loader" string="Odex Intro Loader"
<app data-string="Odex Theme" string="Odex Theme"
name="expert_theme">
<block title="Intro Loader" id="intro_loader_settings">
<setting id="loader_global_enable"
help="Show a full screen loader on page load.">
<field name="intro_loader_active" />
</setting>
<setting invisible="not intro_loader_active">
<setting string="Exit Animation" invisible="not intro_loader_active"
help="Customize how the loader disappears when Odoo is ready.">
<group>
<field name="intro_loader_exit_type" />
<field name="intro_loader_exit_duration" />
</group>
</setting>
<setting string="Visuals" invisible="not intro_loader_active"
help="Customize the appearance of the intro loader.">
<group>
<field name="intro_loader_image" widget="image" class="oe_avatar" />
<field name="intro_loader_animation" />
</group>
</setting>
<setting string="Content" invisible="not intro_loader_active"
help="Customize the content of the intro loader.">
<group>
<field name="intro_loader_show_spinner" />
<field name="intro_loader_show_text" />
<field name="intro_loader_text"
invisible="not intro_loader_show_text" />
<field name="intro_loader_text_color" widget="color"
invisible="not intro_loader_show_text" />
<field name="intro_loader_text_size"
invisible="not intro_loader_show_text" />
</group>
</setting>
</block>

View File

@ -2,25 +2,43 @@
<odoo>
<template id="layout_intro_loader" inherit_id="web.layout">
<xpath expr="//body" position="before">
<t t-set="loader_active" t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.active')"/>
<t t-if="loader_active">
<t t-set="anim_type" t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.animation') or 'pulse'"/>
<t t-set="show_text" t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.show_text')"/>
<t t-set="loader_text" t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.text')"/>
<t t-set="show_spinner" t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.show_spinner')"/>
<t t-set="loader_active"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.active')" />
<t t-if="loader_active">
<t t-set="text_color"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.text_color') or '#6c757d'" />
<t t-set="text_size"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.text_size') or '24'" />
<t t-set="anim_type"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.animation') or 'pulse'" />
<t t-set="show_text"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.show_text')" />
<t t-set="loader_text"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.text')" />
<t t-set="show_spinner"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.show_spinner')" />
<t t-set="exit_type"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.exit_type') or 'fade'" />
<t t-set="exit_duration"
t-value="request.env['ir.config_parameter'].sudo().get_param('intro_loader.exit_duration') or 0.8" />
<div id="o_intro_loader"
t-attf-class="o_intro_loader exit-#{exit_type}"
t-attf-style="--exit-duration: #{exit_duration}s;"
t-att-data-duration-ms="float(exit_duration) * 1000">
<div id="o_intro_loader" class="o_intro_loader">
<div class="o_loader_content">
<img src="/intro_loader/image"
t-attf-class="o_loader_logo mb-4 anim-#{anim_type}"/>
<img src="/intro_loader/image"
t-attf-class="o_loader_logo mb-4 anim-#{anim_type}" />
<t t-if="show_spinner">
<div class="o_loader_spinner"></div>
</t>
<t t-if="show_text">
<h3 class="text-muted mt-3" t-esc="loader_text"/>
<h3 class="text-muted mt-3" t-esc="loader_text"
t-attf-style="color: #{text_color}; font-size: #{text_size}px; font-weight: 500;" />
</t>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -116,7 +116,7 @@ export class SidebarMenu extends Component {
}
// Load sidebar menu icon URL
this.state.sidebarMenuIconUrl = result.sidebar_icon_url || '/odex_sidebar_backend_theme2/static/src/img/logo1.png';
this.state.sidebarMenuIconUrl = result.sidebar_icon_url || '/odex_sidebar_backend_theme2/static/src/img/logo.webp';
} catch (error) {
console.error('Error loading sidebar setting:', error);

View File

@ -9,31 +9,17 @@
<app data-string="Odex Sidebar Menu" string="Odex Sidebar Menu"
name="odex_sidebar_backend_theme2">
<block title="Odex Sidebar Menu" name="odex_sidebar_settings_block">
<setting help="Enable or disable the sidebar menu in the backend interface">
<field name="sidebar_menu_enable" />
</setting>
<setting help="Enable or disable the top navigation bar menu section in the backend interface">
<field name="disable_nav_menu_section" />
</setting>
<setting>
<div class="content-group">
<div class="row mt16">
<label for="sidebar_menu_enable" />
<div class="text-muted">
Enable or disable the sidebar menu in the backend
interface
</div>
<field name="sidebar_menu_enable" />
</div>
<div class="row mt16">
<label for="disable_nav_menu_section" />
<div class="text-muted">
Enable or disable the top navigation bar menu section
in the backend interface
</div>
<field name="disable_nav_menu_section" />
</div>
<div class="row mt16">
<label for="sidebar_menu_icon" />
<field name="sidebar_menu_icon" widget="image"
options="{'size': [128, 128]}"/>
</div>
</div>
<group>
<field name="sidebar_menu_icon" widget="image"
options="{'size': [128, 128]}"/>
</group>
</setting>
</block>
</app>