Remove multiple login and signup templates from expert_theme, consolidating to a more streamlined design approach.

This commit is contained in:
Altahir Hassan 2026-01-05 15:58:18 +04:00
parent 01b9498193
commit 575bb504ee
15 changed files with 737 additions and 2473 deletions

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
{
'name': 'Expert Theme',
'version': '18.0.1.0.0',
'category': 'Theme/Backend',
'version': '1.0.0',
'category': 'Themes',
'summary': 'Custom backend theme with installed modules home page',
'description': """
Expert Theme
@ -14,8 +14,8 @@
- Easy navigation to installed applications
""",
'author': 'Expert',
'website': '',
'depends': ['base', 'web'],
'website': 'https://www.exp-sa.com',
'depends': ['base', 'web','website'],
'data': [
'security/ir.model.access.csv',
'data/expert_login_template_data.xml',
@ -25,23 +25,23 @@
'views/expert_home_views.xml',
'views/expert_menu_views.xml',
],
'assets': {
'web.assets_backend': [
'expert_theme/static/src/css/expert_theme_config.css',
'expert_theme/static/src/css/expert_theme.css',
'expert_theme/static/src/js/expert_theme_dynamic.js',
'expert_theme/static/src/js/expert_theme_config.js',
'expert_theme/static/src/js/expert_home.js',
'expert_theme/static/src/js/expert_login_template_list.js',
'expert_theme/static/src/xml/expert_home.xml',
],
'web.assets_frontend': [
'expert_theme/static/src/css/expert_login.css',
'expert_theme/static/src/css/login_modern.css',
'expert_theme/static/src/css/login_minimal.css',
'expert_theme/static/src/js/expert_login_template.js',
],
},
'assets': {
'web.assets_frontend': [
'expert_theme/static/src/scss/login_modern.scss',
'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',
],
'web.assets_backend': [
'expert_theme/static/src/scss/expert_theme_config.scss',
'expert_theme/static/src/scss/expert_theme.scss',
'expert_theme/static/src/js/expert_theme_dynamic.js',
'expert_theme/static/src/js/expert_theme_config.js',
'expert_theme/static/src/js/expert_home.js',
'expert_theme/static/src/js/expert_login_template_list.js',
'expert_theme/static/src/xml/expert_home.xml',
]
},
'installable': True,
'auto_install': False,
'application': False,

View File

@ -55,46 +55,6 @@ class ExpertController(http.Controller):
'error': str(e)
})
@http.route('/web/login', type='http', auth='none', methods=['GET', 'POST'], csrf=False)
def web_login(self, redirect=None, **kw):
"""Override Odoo login to use expert login templates when configured.
- POST: delegate to standard Odoo login logic (authentication, redirects, etc.).
- GET: render the active expert login template if not 'default', otherwise use standard login.
"""
# Import Home from its new location in Odoo 18
from odoo.addons.web.controllers.home import Home
# Handle login submission via standard controller
if request.httprequest.method == 'POST':
home = Home()
return home.web_login(redirect=redirect, **kw)
# GET: decide which template to render
try:
login_template = request.env['expert.login.template'].sudo().get_active_template()
template_name = login_template.get_template_name()
except Exception as e:
_logger.error("Error getting active login template: %s", e)
template_name = 'web.login'
# For default template, just call the standard login
if template_name == 'web.login':
home = Home()
return home.web_login(redirect=redirect, **kw)
# Try to render the selected expert template; fall back to default on error
try:
return request.render(template_name, {
'redirect': redirect or '',
'login': kw.get('login', ''),
'login_template': login_template,
})
except Exception as e:
_logger.error("Error rendering login template '%s': %s", template_name, e)
home = Home()
return home.web_login(redirect=redirect, **kw)
@http.route('/expert_theme/get_login_template_styles', type='http', auth='public', methods=['GET'])
def get_login_template_styles(self):
"""Get CSS styles for the active login template (public access for login page)"""
@ -181,106 +141,4 @@ class ExpertController(http.Controller):
'error': str(e)
})
@http.route('/web/signup', type='http', auth='public', methods=['GET', 'POST'], website=True, csrf=False)
def web_auth_signup(self, redirect=None, **kw):
"""Override Odoo signup to use expert signup templates when modern template is active.
- POST: delegate to standard Odoo signup logic (authentication, redirects, etc.).
- GET: render the active expert signup template if modern template is active, otherwise use standard signup.
"""
# Import AuthSignupHome from auth_signup module
try:
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
except ImportError:
# If auth_signup is not installed, return 404
from werkzeug.exceptions import NotFound
raise NotFound()
# Handle signup submission via standard controller
if request.httprequest.method == 'POST':
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_signup(redirect=redirect, **kw)
# GET: decide which template to render
try:
login_template = request.env['expert.login.template'].sudo().get_active_template()
template_name = login_template.get_signup_template_name()
except Exception as e:
_logger.error("Error getting active signup template: %s", e)
template_name = 'auth_signup.signup'
# For default template, just call the standard signup
if template_name == 'auth_signup.signup':
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_signup(redirect=redirect, **kw)
# Try to render the selected expert template; fall back to default on error
try:
# Get signup context from auth_signup
auth_signup_home = AuthSignupHome()
qcontext = auth_signup_home.get_auth_signup_qcontext()
qcontext.update({
'redirect': redirect or '',
'login': kw.get('login', ''),
'login_template': login_template,
})
response = request.render(template_name, qcontext)
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
return response
except Exception as e:
_logger.error("Error rendering signup template '%s': %s", template_name, e)
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_signup(redirect=redirect, **kw)
@http.route('/web/reset_password', type='http', auth='public', methods=['GET', 'POST'], website=True, csrf=False)
def web_auth_reset_password(self, redirect=None, **kw):
"""Override Odoo reset password to use expert reset password templates when modern template is active.
- POST: delegate to standard Odoo reset password logic (authentication, redirects, etc.).
- GET: render the active expert reset password template if modern template is active, otherwise use standard reset password.
"""
# Import AuthSignupHome from auth_signup module
try:
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
except ImportError:
# If auth_signup is not installed, return 404
from werkzeug.exceptions import NotFound
raise NotFound()
# Handle reset password submission via standard controller
if request.httprequest.method == 'POST':
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_reset_password(redirect=redirect, **kw)
# GET: decide which template to render
try:
login_template = request.env['expert.login.template'].sudo().get_active_template()
template_name = login_template.get_reset_password_template_name()
except Exception as e:
_logger.error("Error getting active reset password template: %s", e)
template_name = 'auth_signup.reset_password'
# For default template, just call the standard reset password
if template_name == 'auth_signup.reset_password':
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_reset_password(redirect=redirect, **kw)
# Try to render the selected expert template; fall back to default on error
try:
# Get reset password context from auth_signup
auth_signup_home = AuthSignupHome()
qcontext = auth_signup_home.get_auth_signup_qcontext()
qcontext.update({
'redirect': redirect or '',
'login': kw.get('login', ''),
'login_template': login_template,
})
response = request.render(template_name, qcontext)
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
return response
except Exception as e:
_logger.error("Error rendering reset password template '%s': %s", template_name, e)
auth_signup_home = AuthSignupHome()
return auth_signup_home.web_auth_reset_password(redirect=redirect, **kw)

View File

@ -9,7 +9,7 @@ class ExpertLoginTemplate(models.Model):
_rec_name = 'name'
_order = 'sequence, name'
name = fields.Char(string='Template Name', required=True, help='Name of the login page template')
name = fields.Char(string='Template Name', required=True, translate=True, help='Name of the login page template')
active = fields.Boolean(string='Active', default=False, help='Only one template can be active at a time. Activate this template to use it on the login page.')
sequence = fields.Integer(string='Sequence', default=10, help='Order of display')
@ -37,47 +37,47 @@ class ExpertLoginTemplate(models.Model):
corporate_template_logo = fields.Binary(string='Corporate Template Logo', help='Company logo to display on Corporate login, signup, and reset password pages')
# Modern Template Text Fields
modern_login_title = fields.Char(string='Login Title', default='Welcome to Expert 👋', help='Title text for Modern template login page')
modern_login_subtitle = fields.Char(string='Login Subtitle', default='Kindly fill in your details below to sign in to your account', help='Subtitle text for Modern template login page')
modern_login_button_text = fields.Char(string='Login Button Text', default='Sign In', help='Button text for Modern template login page')
modern_login_title = fields.Char(string='Login Title', default='Welcome to Expert 👋', translate=True, help='Title text for Modern template login page')
modern_login_subtitle = fields.Char(string='Login Subtitle', default='Kindly fill in your details below to sign in to your account', translate=True, help='Subtitle text for Modern template login page')
modern_login_button_text = fields.Char(string='Login Button Text', default='Sign In', translate=True, help='Button text for Modern template login page')
modern_login_button_bg_color = fields.Char(string='Login Button Background', default='#007bff', help='Background color for login button')
modern_login_button_text_color = fields.Char(string='Login Button Text Color', default='#FFFFFF', help='Text color for login button')
modern_login_button_bg_hover = fields.Char(string='Login Button BG Hover', default='#0056b3', help='Background color for login button on hover')
modern_login_button_text_hover = fields.Char(string='Login Button Text Hover', default='#FFFFFF', help='Text color for login button on hover')
modern_signup_title = fields.Char(string='Signup Title', default='Create an account', help='Title text for Modern template signup page')
modern_signup_subtitle = fields.Char(string='Signup Subtitle', default='Join us today and get started', help='Subtitle text for Modern template signup page')
modern_signup_button_text = fields.Char(string='Signup Button Text', default='Create an account', help='Button text for Modern template signup page')
modern_signup_title = fields.Char(string='Signup Title', default='Create an account', translate=True, help='Title text for Modern template signup page')
modern_signup_subtitle = fields.Char(string='Signup Subtitle', default='Join us today and get started', translate=True, help='Subtitle text for Modern template signup page')
modern_signup_button_text = fields.Char(string='Signup Button Text', default='Create an account', translate=True, help='Button text for Modern template signup page')
modern_signup_button_bg_color = fields.Char(string='Signup Button Background', default='#28a745', help='Background color for signup button')
modern_signup_button_text_color = fields.Char(string='Signup Button Text Color', default='#FFFFFF', help='Text color for signup button')
modern_signup_button_bg_hover = fields.Char(string='Signup Button BG Hover', default='#218838', help='Background color for signup button on hover')
modern_signup_button_text_hover = fields.Char(string='Signup Button Text Hover', default='#FFFFFF', help='Text color for signup button on hover')
modern_reset_title = fields.Char(string='Reset Password Title', default='Reset your password', help='Title text for Modern template reset password page')
modern_reset_subtitle = fields.Char(string='Reset Password Subtitle', default='Enter your email to receive reset instructions', help='Subtitle text for Modern template reset password page')
modern_reset_title = fields.Char(string='Reset Password Title', default='Reset your password', translate=True, help='Title text for Modern template reset password page')
modern_reset_subtitle = fields.Char(string='Reset Password Subtitle', default='Enter your email to receive reset instructions', translate=True, help='Subtitle text for Modern template reset password page')
# Minimal Template Text Fields
minimal_login_title = fields.Char(string='Login Title', default='Welcome to Expert 👋', help='Title text for Minimal template login page')
minimal_login_subtitle = fields.Char(string='Login Subtitle', default='Kindly fill in your details below to sign in to your account', help='Subtitle text for Minimal template login page')
minimal_login_button_text = fields.Char(string='Login Button Text', default='Sign In', help='Button text for Minimal template login page')
minimal_login_title = fields.Char(string='Login Title', default='Welcome to Expert 👋', translate=True, help='Title text for Minimal template login page')
minimal_login_subtitle = fields.Char(string='Login Subtitle', default='Kindly fill in your details below to sign in to your account', translate=True, help='Subtitle text for Minimal template login page')
minimal_login_button_text = fields.Char(string='Login Button Text', default='Sign In', translate=True, help='Button text for Minimal template login page')
minimal_login_button_bg_color = fields.Char(string='Login Button Background', default='#E5E5E5', help='Background color for login button')
minimal_login_button_text_color = fields.Char(string='Login Button Text Color', default='#000000', help='Text color for login button')
minimal_login_button_bg_hover = fields.Char(string='Login Button BG Hover', default='#D0D0D0', help='Background color for login button on hover')
minimal_login_button_text_hover = fields.Char(string='Login Button Text Hover', default='#000000', help='Text color for login button on hover')
minimal_signup_title = fields.Char(string='Signup Title', default='Create an account', help='Title text for Minimal template signup page')
minimal_signup_subtitle = fields.Char(string='Signup Subtitle', default='Join us today and get started', help='Subtitle text for Minimal template signup page')
minimal_signup_button_text = fields.Char(string='Signup Button Text', default='Create an account', help='Button text for Minimal template signup page')
minimal_signup_title = fields.Char(string='Signup Title', default='Create an account', translate=True, help='Title text for Minimal template signup page')
minimal_signup_subtitle = fields.Char(string='Signup Subtitle', default='Join us today and get started', translate=True, help='Subtitle text for Minimal template signup page')
minimal_signup_button_text = fields.Char(string='Signup Button Text', default='Create an account', translate=True, help='Button text for Minimal template signup page')
minimal_signup_button_bg_color = fields.Char(string='Signup Button Background', default='#000000', help='Background color for signup button')
minimal_signup_button_text_color = fields.Char(string='Signup Button Text Color', default='#FFFFFF', help='Text color for signup button')
minimal_signup_button_bg_hover = fields.Char(string='Signup Button BG Hover', default='#333333', help='Background color for signup button on hover')
minimal_signup_button_text_hover = fields.Char(string='Signup Button Text Hover', default='#FFFFFF', help='Text color for signup button on hover')
minimal_reset_title = fields.Char(string='Reset Password Title', default='Reset your password', help='Title text for Minimal template reset password page')
minimal_reset_subtitle = fields.Char(string='Reset Password Subtitle', default='Enter your email to receive reset instructions', help='Subtitle text for Minimal template reset password page')
minimal_reset_title = fields.Char(string='Reset Password Title', default='Reset your password', translate=True, help='Title text for Minimal template reset password page')
minimal_reset_subtitle = fields.Char(string='Reset Password Subtitle', default='Enter your email to receive reset instructions', translate=True, help='Subtitle text for Minimal template reset password page')
# Description
description = fields.Text(string='Description', help='Description of this template')
description = fields.Text(string='Description', translate=True, help='Description of this template')
@api.model
def get_active_template(self):
@ -111,13 +111,14 @@ class ExpertLoginTemplate(models.Model):
def create(self, vals):
"""Ensure only one template is active at a time"""
# If no active value is set, default to False (don't auto-activate new templates)
if 'active' not in vals:
vals['active'] = False
if 'active' not in vals[0]:
vals[0]['active'] = False
# Only deactivate others if this one is being set to active
if vals.get('active'):
print(vals[0])
if vals[0].get('active'):
# Deactivate all other templates (excluding the one being created)
self.search([('active', '=', True)]).write({'active': False})
return super(ExpertLoginTemplate, self).create(vals)
return super(ExpertLoginTemplate, self).create(vals[0])
def write(self, vals):
"""Ensure only one template is active at a time"""
@ -133,6 +134,7 @@ class ExpertLoginTemplate(models.Model):
super(ExpertLoginTemplate, other_templates).write({'active': False})
need_reload = True
view = self.env.ref('custom_auth_theme.view_custom_login_inherit', raise_if_not_found=False)
result = super(ExpertLoginTemplate, self).write(vals)
# Update template views if active state changed

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -1,269 +0,0 @@
/* Expert Theme - Minimal Login Template Styles */
.expert-login-minimal {
background: #FFFFFF;
min-height: 100vh;
padding: 20px;
}
.expert-login-minimal .container-fluid {
height: calc(100vh - 40px);
}
.expert-login-minimal .row {
height: 100%;
margin: 0;
}
/* Left Column: Login Form */
.expert-login-minimal .expert-login-left {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
text-align: center;
}
/* Logo Section */
.expert-login-minimal .expert-login-logo {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40px;
width: 100%;
}
.expert-login-minimal .expert-login-logo .expert-logo-icon {
width: 40px;
height: 40px;
background: #000000;
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.expert-login-minimal .expert-login-logo .expert-logo-icon span {
color: white;
font-weight: bold;
font-size: 20px;
}
.expert-login-minimal .expert-login-logo .expert-logo-text {
color: #000000;
font-size: 20px;
font-weight: 600;
}
/* Welcome Section */
.expert-login-minimal .expert-login-welcome {
width: 100%;
text-align: center;
margin-bottom: 40px;
}
.expert-login-minimal .expert-login-welcome h1 {
color: #000000;
font-size: 32px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
text-align: center;
}
.expert-login-minimal .expert-login-welcome h1 .welcome-emoji {
font-size: 32px;
}
.expert-login-minimal .expert-login-welcome p {
color: rgba(0, 0, 0, 0.7);
font-size: 16px;
margin-bottom: 0;
text-align: center;
}
/* Form Section */
.expert-login-minimal .expert-login-form {
max-width: 450px;
width: 100%;
margin: 0 auto;
background-color: transparent !important;
}
.expert-login-minimal .expert-login-form .form-group {
margin-bottom: 24px;
}
.expert-login-minimal .expert-login-form .form-group label {
font-weight: 500;
color: #000000;
display: block;
margin-bottom: 8px;
font-size: 14px;
text-align: start;
}
.expert-login-minimal .expert-login-form .form-group input[type="text"],
.expert-login-minimal .expert-login-form .form-group input[type="password"] {
background: #FFFFFF !important;
border: 1px solid #000000 !important;
border-radius: 8px;
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
color: #000000 !important;
font-size: 14px;
}
.expert-login-minimal .expert-login-form .form-group input[type="text"]::placeholder,
.expert-login-minimal .expert-login-form .form-group input[type="password"]::placeholder {
color: rgba(0, 0, 0, 0.5) !important;
}
.expert-login-minimal .expert-login-form .form-group input[type="text"]:focus,
.expert-login-minimal .expert-login-form .form-group input[type="password"]:focus {
background: #FFFFFF !important;
border-color: #000000 !important;
outline: none;
color: #000000 !important;
}
.expert-login-minimal .expert-login-form .form-check {
margin-bottom: 32px;
}
.expert-login-minimal .expert-login-form .form-check label {
color: #000000;
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
gap: 5px;
font-size: 14px;
cursor: pointer;
}
.expert-login-minimal .expert-login-form .form-check label input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #000000;
}
.expert-login-minimal .expert-login-form button[type="submit"] {
width: 100%;
background: #E5E5E5;
border: none;
border-radius: 8px;
padding: 14px;
font-weight: 600;
color: #000000;
font-size: 16px;
cursor: pointer;
transition: background 0.2s;
}
.expert-login-minimal .expert-login-form button[type="submit"]:hover {
background: #D5D5D5;
}
.expert-login-minimal .expert-login-form button[type="submit"]:active {
background: #CCCCCC;
}
/* Login Link Section */
.expert-login-minimal .expert-login-link {
margin-top: 24px;
max-width: 450px;
width: 100%;
text-align: center;
}
.expert-login-minimal .expert-login-link p {
color: rgba(0, 0, 0, 0.7);
font-size: 14px;
margin: 0;
}
.expert-login-minimal .expert-login-link p a {
color: #000000;
text-decoration: underline;
font-weight: 500;
}
.expert-login-minimal .expert-login-link p a:hover {
text-decoration: none;
}
/* Right Column: Image Section */
.expert-login-minimal .expert-login-right {
padding: 0;
height: 100%;
overflow: hidden;
border-radius: 0;
}
.expert-login-minimal .expert-login-right .expert-login-image {
width: 100%;
height: 100%;
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.expert-login-minimal .expert-login-right .expert-login-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.expert-login-minimal .expert-minimal-illustration {
width: 600px !important;
height: 600px !important;
object-fit: contain;
}
/* Alert Messages */
.expert-login-minimal .alert {
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 20px;
text-align: start;
}
.expert-login-minimal .alert-danger {
background-color: #F8D7DA;
border: 1px solid #F5C6CB;
color: #721C24;
}
.expert-login-minimal .alert-success {
background-color: #D4EDDA;
border: 1px solid #C3E6CB;
color: #155724;
}
/* Responsive adjustments */
@media (max-width: 991.98px) {
.expert-login-minimal .expert-login-left {
padding: 30px 20px;
}
.expert-login-minimal .expert-login-welcome h1 {
font-size: 28px;
}
.expert-login-minimal .expert-login-welcome h1 .welcome-emoji {
font-size: 28px;
}
.expert-login-minimal .expert-login-welcome p {
font-size: 14px;
}
}

View File

@ -1,234 +0,0 @@
/* Expert Theme - Modern Login Template Styles */
.expert-login-modern {
background: #19181F !important;
min-height: 100vh;
padding: 20px;
}
.expert-login-modern .container-fluid {
height: calc(100vh - 40px);
}
.expert-login-modern .row {
height: 100%;
margin: 0;
}
/* Left Column: Login Form */
.expert-login-modern .expert-login-left {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
text-align: center;
}
/* Logo Section */
.expert-login-modern .expert-login-logo {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40px;
width: 100%;
}
.expert-login-modern .expert-login-logo .expert-logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.expert-login-modern .expert-login-logo .expert-logo-icon span {
color: white;
font-weight: bold;
font-size: 20px;
}
.expert-login-modern .expert-login-logo .expert-logo-text {
color: white;
font-size: 20px;
font-weight: 600;
}
/* Welcome Section */
.expert-login-modern .expert-login-welcome {
width: 100%;
text-align: center;
}
.expert-login-modern .expert-login-welcome h1 {
color: white;
font-size: 36px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
text-align: center;
}
.expert-login-modern .expert-login-welcome h1 .welcome-emoji {
font-size: 36px;
}
.expert-login-modern .expert-login-welcome p {
color: rgba(255, 255, 255, 0.7);
font-size: 16px;
margin-bottom: 40px;
text-align: center;
}
/* Form Section */
.expert-login-modern .expert-login-form {
max-width: 450px;
width: 100%;
margin: 0 auto;
background-color: transparent !important;
}
.expert-login-modern .expert-login-form .form-group {
margin-bottom: 24px;
}
.expert-login-modern .expert-login-form .form-group label {
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
display: block;
margin-bottom: 8px;
font-size: 14px;
text-align: start;
}
.expert-login-modern .expert-login-form .form-group input[type="text"],
.expert-login-modern .expert-login-form .form-group input[type="password"] {
background: rgba(255, 255, 255, 0.1) !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
border-radius: 8px;
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
color: white !important;
font-size: 14px;
}
.expert-login-modern .expert-login-form .form-group input[type="text"]::placeholder,
.expert-login-modern .expert-login-form .form-group input[type="password"]::placeholder {
color: rgba(255, 255, 255, 0.5) !important;
}
.expert-login-modern .expert-login-form .form-group input[type="text"]:focus,
.expert-login-modern .expert-login-form .form-group input[type="password"]:focus {
background: rgba(255, 255, 255, 0.15) !important;
border-color: rgba(255, 255, 255, 0.4) !important;
outline: none;
color: white !important;
}
.expert-login-modern .expert-login-form .form-check {
margin-bottom: 32px;
}
.expert-login-modern .expert-login-form .form-check label {
color: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 14px;
cursor: pointer;
}
.expert-login-modern .expert-login-form .form-check label input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #667eea;
}
.expert-login-modern .expert-login-form button[type="submit"] {
width: 100%;
background: #764ba2;
border: none !important;
border-radius: 8px;
padding: 14px;
font-weight: 600;
color: white;
font-size: 16px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.expert-login-modern .expert-login-form button[type="submit"]:hover {
transform: translateY(-2px);
}
/* Login Link Section */
.expert-login-modern .expert-login-link {
margin-top: 24px;
max-width: 450px;
width: 100%;
text-align: center;
}
.expert-login-modern .expert-login-link p {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
margin: 0;
}
.expert-login-modern .expert-login-link p a {
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.expert-login-modern .expert-login-link p a:hover {
text-decoration: underline;
}
/* Right Column: Image Section */
.expert-login-modern .expert-login-right {
padding: 0;
height: 100%;
overflow: hidden;
border-radius: 20px;
}
.expert-login-modern .expert-login-right .expert-login-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.expert-login-modern .expert-login-right .expert-login-image img {
width: 100% !important;
height: 100% !important;
object-fit: cover !important;
}
/* Responsive adjustments */
@media (max-width: 991.98px) {
.expert-login-modern .expert-login-left {
padding: 30px 20px;
}
.expert-login-modern .expert-login-welcome h1 {
font-size: 28px;
}
.expert-login-modern .expert-login-welcome h1 .welcome-emoji {
font-size: 28px;
}
.expert-login-modern .expert-login-welcome p {
font-size: 14px;
}
}

View File

@ -47,9 +47,9 @@
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
background: linear-gradient(135deg, var(--expert-bg-gradient-start, #f5f7fa) 0%, var(--expert-bg-gradient-end, #c3cfe2) 100%) !important;
min-height: 100vh !important;
margin: 0 !important;
padding: 0 !important;
// min-height: 100vh !important;
// margin: 0 !important;
// padding: 0 !important;
}
/* Ensure the gradient shows on the main content area */
@ -58,7 +58,7 @@ body {
.o_action_manager .o_view_controller .o_content {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
background: linear-gradient(135deg, var(--expert-bg-gradient-start, #f5f7fa) 0%, var(--expert-bg-gradient-end, #c3cfe2) 100%) !important;
min-height: 100vh !important;
// min-height: 100vh !important;
}
/* Override any Odoo background colors that might interfere */

View File

@ -0,0 +1,280 @@
/* Expert Theme - Minimal Login Template Styles */
.expert-login-minimal {
background: #FFFFFF;
min-height: 100vh;
.expert-login-left {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
min-height: 100vh;
.expert-login-form-container {
max-width: 450px;
.expert-login-logo {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 40px;
width: 100%;
.expert-logo-icon {
width: 40px;
height: 40px;
background: #000000;
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
span {
color: white;
font-weight: bold;
font-size: 20px;
}
}
.expert-logo-text {
color: #000000;
font-size: 20px;
font-weight: 600;
}
}
.expert-login-welcome {
width: 100%;
text-align: center;
margin-bottom: 40px;
h1 {
color: #000000;
font-size: 32px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
text-align: center;
.welcome-emoji {
font-size: 32px;
}
}
p {
color: rgba(0, 0, 0, 0.7);
font-size: 16px;
margin-bottom: 0;
text-align: center;
}
}
.expert-login-form {
max-width: 450px;
width: 100%;
margin: 0 auto;
background-color: transparent !important;
.oe_login_form,
.oe_signup_form,
.oe_reset_password_form {
max-width: 450px;
background-color: transparent !important;
label {
font-weight: 500;
color: #000000;
display: block;
margin-bottom: 8px;
font-size: 14px;
text-align: start;
}
input {
&[type="text"],
&[type="password"] {
background: #FFFFFF !important;
border: 1px solid #000000 !important;
border-radius: 8px;
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
color: #000000 !important;
font-size: 14px;
}
&[type="text"]::placeholder,
&[type="password"]::placeholder {
color: rgba(0, 0, 0, 0.5) !important;
}
&[type="text"]:focus,
&[type="password"]:focus {
background: #FFFFFF !important;
border-color: #000000 !important;
outline: none;
color: #000000 !important;
}
}
}
.form-check {
margin-bottom: 32px;
label {
color: #000000;
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
gap: 5px;
font-size: 14px;
cursor: pointer;
input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #000000;
}
}
}
.btn-link {
color: #5a5a5a;
}
.btn-primary {
width: 100%;
background: #E5E5E5;
border: none;
border-radius: 8px;
padding: 14px;
font-weight: 600;
color: #000000;
font-size: 16px;
cursor: pointer;
transition: background 0.2s;
margin-bottom: 10px;
&:hover {
background: #D5D5D5;
}
&:active {
background: #CCCCCC;
}
}
}
.expert-login-link {
margin-top: 24px;
max-width: 450px;
width: 100%;
text-align: center;
p {
color: rgba(0, 0, 0, 0.7);
font-size: 14px;
margin: 0;
a {
color: #000000;
text-decoration: underline;
font-weight: 500;
&:hover {
text-decoration: none;
}
}
}
}
}
}
.expert-login-right {
padding: 0;
height: 100%;
overflow: hidden;
min-height: 100vh;
.expert-login-image {
width: 100%;
min-height: 100vh;
background: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
}
}
.expert-minimal-illustration {
width: 600px !important;
height: 600px !important;
object-fit: contain;
}
.alert {
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 20px;
text-align: start;
}
.alert-danger {
background-color: #F8D7DA;
border: 1px solid #F5C6CB;
color: #721C24;
}
.alert-success {
background-color: #D4EDDA;
border: 1px solid #C3E6CB;
color: #155724;
}
}
/* Left Column: Login Form */
/* Logo Section */
/* Welcome Section */
/* Form Section */
/* Login Link Section */
/* Right Column: Image Section */
/* Alert Messages */
/* Responsive adjustments */
@media (max-width: 991.98px) {
.expert-login-minimal {
.expert-login-left {
padding: 30px 20px;
}
.expert-login-welcome {
h1 {
font-size: 28px;
.welcome-emoji {
font-size: 28px;
}
}
p {
font-size: 14px;
}
}
}
}

View File

@ -1,177 +1,180 @@
/* Expert Theme - Modern Login Template Styles */
.expert-login-modern {
background: #19181F;
min-height: 100vh;
padding: 20px;
.container-fluid {
height: calc(100vh - 40px);
}
.row {
height: 100%;
margin: 0;
}
// Left Column: Login Form
.expert-login-left {
display: flex;
flex-direction: column;
justify-content: center;
padding: 40px;
}
// Logo Section
.expert-login-logo {
display: flex;
align-items: center;
margin-bottom: 40px;
min-height: 100vh;
.expert-logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
.expert-login-form-container {
max-width: 450px;
span {
color: white;
font-weight: bold;
font-size: 20px;
}
}
.expert-logo-text {
color: white;
font-size: 20px;
font-weight: 600;
}
}
// Welcome Section
.expert-login-welcome {
h1 {
color: white;
font-size: 36px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
.welcome-emoji {
font-size: 36px;
}
}
p {
color: rgba(255, 255, 255, 0.7);
font-size: 16px;
margin-bottom: 40px;
}
}
// Form Section
.expert-login-form {
max-width: 450px;
.form-group {
margin-bottom: 24px;
label {
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
display: block;
margin-bottom: 8px;
font-size: 14px;
}
input[type="text"],
input[type="password"] {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
color: white;
font-size: 14px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
&:focus {
background: rgba(255, 255, 255, 0.15) !important;
border-color: rgba(255, 255, 255, 0.4) !important;
outline: none;
color: white;
}
}
}
.form-check {
margin-bottom: 32px;
label {
color: rgba(255, 255, 255, 0.8);
// Logo Section
.expert-login-logo {
display: flex;
align-items: center;
font-size: 14px;
cursor: pointer;
margin-bottom: 40px;
input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #667eea;
.expert-logo-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
span {
color: white;
font-weight: bold;
font-size: 20px;
}
}
.expert-logo-text {
color: white;
font-size: 20px;
font-weight: 600;
}
}
}
button[type="submit"] {
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
padding: 14px;
font-weight: 600;
color: white;
font-size: 16px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
// Welcome Section
.expert-login-welcome {
h1 {
color: white;
font-size: 36px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
.welcome-emoji {
font-size: 36px;
}
}
&:active {
transform: translateY(0);
p {
color: rgba(255, 255, 255, 0.7);
font-size: 16px;
margin-bottom: 40px;
}
}
}
// Login Link Section
.expert-login-link {
margin-top: 24px;
max-width: 450px;
// Form Section
.expert-login-form {
p {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
margin: 0;
.oe_login_form,
.oe_signup_form,
.oe_reset_password_form {
max-width: 450px;
background-color: transparent !important;
a {
color: #667eea;
text-decoration: none;
font-weight: 500;
.form-label,
label {
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
display: block;
margin-bottom: 8px;
font-size: 14px;
}
&:hover {
text-decoration: underline;
.form-control {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 12px 16px;
width: 100%;
box-sizing: border-box;
color: white;
font-size: 14px;
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
&:focus {
background: rgba(255, 255, 255, 0.15) !important;
border-color: rgba(255, 255, 255, 0.4) !important;
outline: none;
color: white;
}
}
.form-check {
margin-bottom: 32px;
label {
color: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
font-size: 14px;
cursor: pointer;
input[type="checkbox"] {
margin-right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #667eea;
}
}
}
.btn-link {
color: #939393;
}
.btn-primary {
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
padding: 14px;
font-weight: 600;
color: white;
font-size: 16px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
margin-bottom: 10px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
&:active {
transform: translateY(0);
}
}
}
}
// Login Link Section
.expert-login-link {
margin-top: 24px;
max-width: 450px;
p {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
margin: 0;
a {
color: #667eea;
text-decoration: none;
font-weight: 500;
&:hover {
text-decoration: underline;
}
}
}
}
@ -183,15 +186,27 @@
height: 100%;
overflow: hidden;
border-radius: 20px 0 0 20px;
min-height: 100vh;
.expert-login-image {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 25%, #667eea 75%, #764ba2 100%);
// background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 25%, #667eea 75%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
padding: 20px;
.expert-login-image-cover {
height: 100%;
width: 100%;
background-color: #667eea;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
border-radius: 16px;
}
.expert-spiral-decoration {
width: 400px;
@ -254,5 +269,4 @@
}
}
}
}
}

View File

@ -14,7 +14,6 @@
<p>Your installed modules are listed below</p>
</div>
<div class="expert-modules-grid" id="expert-modules-container">
<!-- Modules will be loaded here via JavaScript -->
</div>
</div>
</t>

View File

@ -3,31 +3,33 @@
<data>
<!-- Create a new top-level menu for Expert Home -->
<menuitem id="menu_expert_root"
name="Expert Home"
sequence="1"/>
<menuitem id="menu_expert_home"
name="Dashboard"
parent="menu_expert_root"
action="action_expert_home"
sequence="1"/>
<menuitem id="menu_expert_login_templates"
name="Login Page Templates"
parent="menu_expert_root"
action="action_expert_login_template"
sequence="2"/>
<menuitem id="menu_expert_theme_colors"
name="Theme Colors"
parent="menu_expert_root"
action="action_expert_theme_config"
sequence="3"/>
<!-- Also add Theme Colors to the main menu for easier access -->
<menuitem id="menu_theme_colors_main"
name="Theme Colors"
action="action_expert_theme_config"
sequence="100"/>
name="Odex Theme"
web_icon="expert_theme,static/description/icon.png"
sequence="1000" />
<!-- <menuitem id="menu_expert_home"
name="Dashboard"
parent="menu_expert_root"
action="action_expert_home"
sequence="1" /> -->
<menuitem id="menu_expert_theme_colors"
name="Theme Colors"
parent="menu_expert_root"
action="action_expert_theme_config"
sequence="1" />
<menuitem id="menu_expert_login_templates"
name="Login Page Templates"
parent="menu_expert_root"
action="action_expert_login_template"
sequence="2" />
<!-- Also add Theme Colors to the main menu for easier access -->
<!-- <menuitem id="menu_theme_colors_main"
name="Theme Colors"
action="action_expert_theme_config"
sequence="100" /> -->
</data>
</odoo>
</odoo>

File diff suppressed because it is too large Load Diff

View File

@ -1,785 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--
Multiple Login Page Templates with Different HTML Structures
Standalone templates rendered by the /web/login override.
Each template calls web.layout and defines its own login form.
-->
<!-- Template 1: Modern Card Design -->
<template id="login_template_modern_page" name="Modern Login Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-modern">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Login Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.modern_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.modern_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.modern_login_title">
<t t-esc="login_template.modern_login_title"/>
</t>
<t t-else="">
Welcome to Expert <span class="welcome-emoji">👋</span>
</t>
</h1>
<p>
<t t-if="login_template and login_template.modern_login_subtitle">
<t t-esc="login_template.modern_login_subtitle"/>
</t>
<t t-else="">
Kindly fill in your details below to sign in to your account
</t>
</p>
</div>
<!-- Login Form -->
<form class="oe_login_form expert-login-form" role="form" method="post" t-att-action="request.httprequest.path">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<div class="form-group">
<label>Email or Username</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email or username"
t-att-value="login"/>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control"
placeholder="Enter your password"/>
</div>
<div class="form-check">
<label>
<input type="checkbox" name="remember"/>
Remember me
</label>
</div>
<button type="submit" class="btn btn-primary">
<t t-if="login_template and login_template.modern_login_button_text"><t t-esc="login_template.modern_login_button_text"/></t><t t-else="">Sign In</t>
</button>
</form>
<!-- Signup Link -->
<div class="expert-login-link">
<p>
Don't have an account?
<a t-attf-href="/web/signup?{{ keep_query() }}">Sign Up</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.modern_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.modern_template_image.decode('utf-8')"
alt="Login Image" class="img-fluid"/>
<img t-else=""
src="/expert_theme/static/src/img/modern-template-bg.png"
alt="Login Image" class="img-fluid"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Modern Signup Template -->
<template id="signup_template_modern_page" name="Modern Signup Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-modern">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Signup Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.modern_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.modern_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.modern_signup_title">
<t t-esc="login_template.modern_signup_title"/>
</t>
<t t-else="">
Create an account
</t>
</h1>
<p>
<t t-if="login_template and login_template.modern_signup_subtitle">
<t t-esc="login_template.modern_signup_subtitle"/>
</t>
<t t-else="">
Join us today and get started
</t>
</p>
</div>
<!-- Signup Form -->
<form class="oe_signup_form expert-login-form" role="form" method="post" t-if="not message">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<input type="hidden" name="token" t-att-value="token or ''"/>
<div class="form-group">
<label>Your Email</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email"
t-att-value="login"
t-att-readonly="'readonly' if (token and not invalid_token) else None"
autofocus="autofocus"
autocapitalize="off"
required="required"/>
</div>
<div class="form-group" t-if="not (token and not invalid_token)">
<label>Your Name</label>
<input type="text" name="name" class="form-control"
placeholder="e.g. John Doe"
t-att-value="name"
required="required"/>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" id="password" class="form-control"
placeholder="Enter your password"
required="required"
t-att-autofocus="'autofocus' if (token and not invalid_token) else None"/>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
placeholder="Confirm your password"
required="required"/>
</div>
<p class="alert alert-danger" t-if="error" role="alert">
<t t-esc="error"/>
</p>
<button type="submit" class="btn btn-primary">
<t t-if="login_template and login_template.modern_signup_button_text">
<t t-esc="login_template.modern_signup_button_text"/>
</t>
<t t-else="">
Sign Up
</t>
</button>
</form>
<!-- Signup Link -->
<div class="expert-login-link">
<p>
Already have an account?
<a t-attf-href="/web/login?{{ keep_query() }}">Log In</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.modern_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.modern_template_image.decode('utf-8')"
alt="Signup Image" class="img-fluid"/>
<img t-else=""
src="/expert_theme/static/src/img/modern-template-bg.png"
alt="Signup Image" class="img-fluid"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Modern Reset Password Template -->
<template id="reset_password_template_modern_page" name="Modern Reset Password Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-modern">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Reset Password Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.modern_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.modern_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.modern_reset_title">
<t t-esc="login_template.modern_reset_title"/>
</t>
<t t-else="">
Reset Password <span class="welcome-emoji">🔐</span>
</t>
</h1>
<p t-if="not token">
<t t-if="login_template and login_template.modern_reset_subtitle">
<t t-esc="login_template.modern_reset_subtitle"/>
</t>
<t t-else="">
Enter your email address and we'll send you instructions to reset your password
</t>
</p>
<p t-if="token and not invalid_token">
Enter your new password below
</p>
</div>
<!-- Success Message -->
<div t-if="message" class="expert-login-form">
<p class="alert alert-success" role="status">
<t t-esc="message"/>
</p>
<a href="/web/login" class="btn btn-link" style="color: #667eea;">Back to Login</a>
</div>
<!-- Reset Password Form -->
<form class="oe_reset_password_form expert-login-form" role="form" method="post" t-if="not message">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<input type="hidden" name="token" t-att-value="token or ''"/>
<!-- Token-based reset (set new password) -->
<t t-if="token and not invalid_token">
<div class="form-group">
<label>New Password</label>
<input type="password" name="password" id="password" class="form-control"
placeholder="Enter your new password"
required="required"
autofocus="autofocus"/>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
placeholder="Confirm your new password"
required="required"/>
</div>
</t>
<!-- Email-based reset (request reset) -->
<t t-if="not token">
<div class="form-group">
<label>Your Email</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email"
t-att-value="login"
autofocus="autofocus"
required="required"
autocapitalize="off"/>
</div>
</t>
<p class="alert alert-danger" t-if="error" role="alert">
<t t-esc="error"/>
</p>
<button type="submit" class="btn btn-primary">
<t t-if="token and not invalid_token">Reset Password</t>
<t t-if="not token">Send Reset Instructions</t>
</button>
</form>
<!-- Reset Password Link -->
<div class="expert-login-link" t-if="not message">
<p>
<a t-if="not token" t-attf-href="/web/login?{{ keep_query() }}" style="color: #667eea;">Back to Login</a>
<a t-if="invalid_token" href="/web/login" style="color: #667eea;">Back to Login</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.modern_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.modern_template_image.decode('utf-8')"
alt="Reset Password Image" class="img-fluid"/>
<img t-else=""
src="/expert_theme/static/src/img/modern-template-bg.png"
alt="Reset Password Image" class="img-fluid"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Template 2: Minimal Design -->
<template id="login_template_minimal_page" name="Minimal Login Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-minimal">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Login Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.minimal_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.minimal_login_title">
<t t-esc="login_template.minimal_login_title"/>
</t>
<t t-else="">
Welcome to Expert <span class="welcome-emoji">👋</span>
</t>
</h1>
<p>
<t t-if="login_template and login_template.minimal_login_subtitle">
<t t-esc="login_template.minimal_login_subtitle"/>
</t>
<t t-else="">
Kindly fill in your details below to sign in to your account
</t>
</p>
</div>
<!-- Login Form -->
<form class="oe_login_form expert-login-form" role="form" method="post" t-att-action="request.httprequest.path">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<div class="form-group">
<label>Email or Username</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email or username"
t-att-value="login"/>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control"
placeholder="Enter your password"/>
</div>
<div class="form-check">
<label>
<input type="checkbox" name="remember"/>
Remember me
</label>
</div>
<button type="submit" class="btn btn-primary">
<t t-if="login_template and login_template.modern_login_button_text"><t t-esc="login_template.modern_login_button_text"/></t><t t-else="">Sign In</t>
</button>
</form>
<!-- Signup Link -->
<div class="expert-login-link">
<p>
Don't have an account?
<a t-attf-href="/web/signup?{{ keep_query() }}">Sign Up</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.minimal_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_image.decode('utf-8')"
alt="Login Illustration" class="img-fluid expert-minimal-illustration"/>
<img t-else=""
src="/expert_theme/static/src/img/minimal-login-img.png"
alt="Login Illustration" class="img-fluid expert-minimal-illustration"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Minimal Signup Template -->
<template id="signup_template_minimal_page" name="Minimal Signup Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-minimal">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Signup Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.minimal_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.minimal_signup_title">
<t t-esc="login_template.minimal_signup_title"/>
</t>
<t t-else="">
Create an account
</t>
</h1>
<p>
<t t-if="login_template and login_template.minimal_signup_subtitle">
<t t-esc="login_template.minimal_signup_subtitle"/>
</t>
<t t-else="">
<a t-attf-href="/web/login?{{ keep_query() }}" style="color: #000; text-decoration: underline;">log in instead</a>
</t>
</p>
</div>
<!-- Signup Form -->
<form class="oe_signup_form expert-login-form" role="form" method="post" t-if="not message">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<input type="hidden" name="token" t-att-value="token or ''"/>
<div class="form-group" t-if="not (token and not invalid_token)">
<label>Your Name</label>
<input type="text" name="name" class="form-control"
placeholder="e.g. John Doe"
t-att-value="name"
required="required"
t-att-autofocus="'autofocus' if login and not (token and not invalid_token) else None"/>
</div>
<div class="form-group">
<label>Your Email</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email"
t-att-value="login"
t-att-readonly="'readonly' if (token and not invalid_token) else None"
autofocus="autofocus"
autocapitalize="off"
required="required"/>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" id="password" class="form-control"
placeholder="Enter your password"
required="required"
t-att-autofocus="'autofocus' if (token and not invalid_token) else None"/>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
placeholder="Confirm your password"
required="required"/>
</div>
<div class="form-check">
<label>
<input type="checkbox" name="terms" required="required"/>
By creating an account, I agree to our <a href="#" style="color: #000; text-decoration: underline;">Terms of use</a> and <a href="#" style="color: #000; text-decoration: underline;">Privacy Policy</a>
</label>
</div>
<p class="alert alert-danger" t-if="error" role="alert">
<t t-esc="error"/>
</p>
<button type="submit" class="btn btn-primary">
Create an account
</button>
</form>
<!-- Login Link -->
<div class="expert-login-link">
<p>
Already have an account?
<a t-attf-href="/web/login?{{ keep_query() }}" style="color: #000; text-decoration: underline;">Log In</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.minimal_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_image.decode('utf-8')"
alt="Signup Illustration" class="img-fluid expert-minimal-illustration"/>
<img t-else=""
src="/expert_theme/static/src/img/minimal-login-img.png"
alt="Signup Illustration" class="img-fluid expert-minimal-illustration"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Minimal Reset Password Template -->
<template id="reset_password_template_minimal_page" name="Minimal Reset Password Template">
<t t-call="web.layout">
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<t t-call-assets="web.assets_frontend" t-js="false"/>
</t>
<t t-set="body_classname" t-value="'bg-100'"/>
<div class="oe_login_form expert-login-minimal">
<div class="container-fluid p-0">
<div class="row">
<!-- Left Column: Reset Password Form (6 columns) -->
<div class="col-lg-6 col-md-12 expert-login-left">
<!-- Logo and Text -->
<div class="expert-login-logo">
<t t-if="login_template and login_template.minimal_template_logo">
<img t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_logo.decode('utf-8')"
alt="Company Logo"
style="width: 40px; height: 40px; object-fit: contain; margin-right: 12px;"/>
<span class="expert-logo-text">Expert</span>
</t>
<t t-else="">
<div class="expert-logo-icon">
<span>E</span>
</div>
<span class="expert-logo-text">Expert</span>
</t>
</div>
<!-- Welcome Text -->
<div class="expert-login-welcome">
<h1>
<t t-if="login_template and login_template.minimal_reset_title">
<t t-esc="login_template.minimal_reset_title"/>
</t>
<t t-else="">
Reset Password <span class="welcome-emoji">🔐</span>
</t>
</h1>
<p t-if="not token">
<t t-if="login_template and login_template.minimal_reset_subtitle">
<t t-esc="login_template.minimal_reset_subtitle"/>
</t>
<t t-else="">
Enter your email address and we'll send you instructions to reset your password
</t>
</p>
<p t-if="token and not invalid_token">
Enter your new password below
</p>
</div>
<!-- Success Message -->
<div t-if="message" class="expert-login-form">
<p class="alert alert-success" role="status">
<t t-esc="message"/>
</p>
<a href="/web/login" class="btn btn-link" style="color: #000; text-decoration: underline;">Back to Login</a>
</div>
<!-- Reset Password Form -->
<form class="oe_reset_password_form expert-login-form" role="form" method="post" t-if="not message">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<input type="hidden" name="token" t-att-value="token or ''"/>
<!-- Token-based reset (set new password) -->
<t t-if="token and not invalid_token">
<div class="form-group">
<label>New Password</label>
<input type="password" name="password" id="password" class="form-control"
placeholder="Enter your new password"
required="required"
autofocus="autofocus"/>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
placeholder="Confirm your new password"
required="required"/>
</div>
</t>
<!-- Email-based reset (request reset) -->
<t t-if="not token">
<div class="form-group">
<label>Email</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your email"
t-att-value="login"
autofocus="autofocus"
required="required"
autocapitalize="off"/>
</div>
</t>
<p class="alert alert-danger" t-if="error" role="alert">
<t t-esc="error"/>
</p>
<button type="submit" class="btn btn-primary">
<t t-if="token and not invalid_token">Reset Password</t>
<t t-if="not token">Send Reset Instructions</t>
</button>
</form>
<!-- Reset Password Link -->
<div class="expert-login-link" t-if="not message">
<p>
<a t-if="not token" t-attf-href="/web/login?{{ keep_query() }}" style="color: #000; text-decoration: underline;">Back to Login</a>
<a t-if="invalid_token" href="/web/login" style="color: #000; text-decoration: underline;">Back to Login</a>
</p>
</div>
</div>
<!-- Right Column: Image (6 columns) -->
<div class="col-lg-6 col-md-12 d-none d-lg-block expert-login-right">
<div class="expert-login-image">
<img t-if="login_template and login_template.minimal_template_image"
t-att-src="'data:image/png;base64,%s' % login_template.minimal_template_image.decode('utf-8')"
alt="Reset Password Illustration" class="img-fluid expert-minimal-illustration"/>
<img t-else=""
src="/expert_theme/static/src/img/minimal-login-img.png"
alt="Reset Password Illustration" class="img-fluid expert-minimal-illustration"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Template 3: Corporate Design -->
<template id="login_template_corporate_page" name="Corporate Login Template">
<t t-call="web.layout">
<div class="oe_login_form" style="background: #1a1a1a; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px;">
<div style="width: 100%; max-width: 500px; background: #2d2d2d; padding: 60px 50px; border-radius: 8px; border: 1px solid #404040;">
<div class="text-center mb-5">
<h2 style="color: #fff; font-size: 32px; font-weight: 600; margin-bottom: 10px;">Company Portal</h2>
<p style="color: #999; font-size: 14px; margin: 0;">Secure Login Access</p>
</div>
<form class="oe_login_form" role="form" method="post" t-att-action="request.httprequest.path">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="redirect" t-att-value="redirect or ''"/>
<div style="margin-bottom: 25px;">
<label style="color: #ccc; font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; display: block;">Username</label>
<input type="text" name="login" class="form-control"
placeholder="Enter your username"
t-att-value="login"
style="background: #1a1a1a; border: 1px solid #404040; color: #fff; padding: 12px 15px; border-radius: 4px; width: 100%; box-sizing: border-box;"/>
</div>
<div style="margin-bottom: 35px;">
<label style="color: #ccc; font-size: 13px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; display: block;">Password</label>
<input type="password" name="password" class="form-control"
placeholder="Enter your password"
style="background: #1a1a1a; border: 1px solid #404040; color: #fff; padding: 12px 15px; border-radius: 4px; width: 100%; box-sizing: border-box;"/>
</div>
<button type="submit" class="btn btn-block"
style="width: 100%; background: #007bff; color: white; border: none; padding: 14px; font-size: 15px; font-weight: 600; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.5px;">
Access Portal
</button>
</form>
</div>
</div>
</t>
</template>
</odoo>