add dashboard, saas master, and reservation views, models, and static assets

This commit is contained in:
Alaguraj0361 2026-10-08 18:54:32 +05:30
parent e0192ad79e
commit c3a9798a41
16 changed files with 739 additions and 321 deletions

View File

@ -394,6 +394,8 @@ class Dine360DashboardController(http.Controller):
@http.route('/dashboard/reservations', type='http', auth='user', website=True)
def dashboard_reservations(self, **kwargs):
values = get_dashboard_context(active_page='reservations')
if values.get('is_saas_master'):
return request.redirect('/dashboard/restaurants')
return request.render('dine360_dashboard.reservations_page_template', values)
# -------------------------------------------------------------
@ -402,6 +404,8 @@ class Dine360DashboardController(http.Controller):
@http.route('/dashboard/tables', type='http', auth='user', website=True)
def dashboard_tables(self, **kwargs):
values = get_dashboard_context(active_page='tables')
if values.get('is_saas_master'):
return request.redirect('/dashboard/restaurants')
return request.render('dine360_dashboard.tables_page_template', values)
# -------------------------------------------------------------

View File

@ -13,6 +13,14 @@ class IrUiMenu(models.Model):
user = self.env.user
is_admin = user.has_group('base.group_system')
db_name = self.env.cr.dbname or ''
is_saas_master = ('Dine360Restaurants' in db_name) or (
'saas.restaurant' in self.env and bool(self.env['saas.restaurant'].sudo().search_count([]))
)
if is_saas_master:
allowed_menus = ['Dine360 SaaS', 'Apps', 'Settings']
else:
allowed_menus = ['Online Orders', 'Website', 'Table Reservations', 'Uber Integration', 'Apps', 'Settings']
if 'root' in menus and 'children' in menus['root']:
@ -23,9 +31,14 @@ class IrUiMenu(models.Model):
continue
name = child_menu.get('name')
# Allow matching menus (with fallback for Table Reservation naming variations)
if is_saas_master:
if name in allowed_menus:
if name in ['Apps', 'Settings'] and not is_admin:
continue
new_children.append(child_id)
else:
# Allow matching menus for tenant restaurant databases
if name in allowed_menus or name == 'Table Reservation':
# Hide Apps and Settings for non-admins
if name in ['Apps', 'Settings'] and not is_admin:
continue
new_children.append(child_id)

View File

@ -1390,6 +1390,12 @@ body.o_restaurant_body #wrap {
}
/* Onboarded Restaurant Card Row - Highlighted Signature Card */
.o_restaurants_list {
display: flex !important;
flex-direction: column !important;
gap: 5px !important;
}
.o_res_row_card {
background: #ffffff;
border: 1.5px solid #e2e8f0;
@ -1402,6 +1408,7 @@ body.o_restaurant_body #wrap {
align-items: center;
box-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.08), 0 2px 6px -1px rgba(15, 23, 42, 0.03);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
margin-bottom: 0 !important;
}
.o_res_row_card:hover {
@ -2231,4 +2238,33 @@ body.o_restaurant_body #wrap {
background: #f8fafc;
}
/* -------------------------------------------------------------
POS & KANBAN GHOST SPACER REMOVAL
------------------------------------------------------------- */
.o_pos_kanban .o_kanban_record,
.o_kanban_dashboard .o_kanban_record {
max-width: 420px !important;
}
.o_kanban_renderer .o_kanban_ghost,
.o_kanban_view .o_kanban_ghost,
.o_kanban_ghost {
display: none !important;
width: 0 !important;
height: 0 !important;
min-height: 0 !important;
max-height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
outline: none !important;
box-shadow: none !important;
background: transparent !important;
background-color: transparent !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
overflow: hidden !important;
}

View File

@ -1,19 +1,20 @@
/* ==========================================================================
Dine360 Modern Theme Login & Authentication Styling
Luxury Bright Culinary Aesthetic - Matching User Reference Screenshot
========================================================================== */
/* 1. Global Reset & Body */
body.o_custom_login_body {
margin: 0 !important;
padding: 0 !important;
background: #090d16 !important;
background: #fafafc !important;
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
min-height: 100vh !important;
overflow-x: hidden !important;
color: #f8fafc !important;
color: #0f172a !important;
}
/* Hide any standard website/frontend navigation & footer */
/* Hide standard website/frontend navigation & footer */
body.o_custom_login_body header,
body.o_custom_login_body footer,
body.o_custom_login_body .o_footer_copyright,
@ -32,228 +33,339 @@ body.o_custom_login_body #wrapwrap {
padding-top: 0 !important;
margin-top: 0 !important;
background: transparent !important;
}
/* 2. Main Wrapper: Immersive Ambient Split */
.o_login_main_wrapper {
display: flex !important;
min-height: 100vh !important;
width: 100vw !important;
background:
radial-gradient(circle at 12% 20%, rgba(229, 0, 0, 0.22) 0%, transparent 45%),
radial-gradient(circle at 88% 80%, rgba(239, 68, 68, 0.15) 0%, transparent 50%),
linear-gradient(135deg, #070a12 0%, #0d1322 55%, #181428 100%) !important;
position: relative !important;
overflow-y: auto !important;
overflow-x: hidden !important;
}
/* 3. Left Side: Brand Hero Showcase */
.o_login_left_side {
flex: 1.25 !important;
/* 2. Top Header Navigation Bar */
.o_login_top_header {
height: 64px !important;
background: #ffffff !important;
border-bottom: 1px solid rgba(226, 232, 240, 0.85) !important;
display: flex !important;
align-items: center !important;
justify-content: space-between !important;
padding: 0 48px !important;
position: relative !important;
z-index: 20 !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02) !important;
box-sizing: border-box !important;
}
.o_top_header_left {
display: flex !important;
align-items: center !important;
gap: 16px !important;
}
.o_top_brand {
display: flex !important;
align-items: center !important;
gap: 10px !important;
text-decoration: none !important;
}
.o_top_brand_icon {
width: 32px !important;
height: 32px !important;
background: #dc2626 !important;
border-radius: 8px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 60px 80px !important;
color: #ffffff !important;
font-size: 0.95rem !important;
}
.o_top_brand_name {
font-size: 1.35rem !important;
font-weight: 800 !important;
color: #0f172a !important;
letter-spacing: -0.5px !important;
}
.o_top_brand_accent {
color: #dc2626 !important;
}
.o_top_header_divider {
width: 1px !important;
height: 20px !important;
background: #cbd5e1 !important;
}
.o_top_header_subtitle {
font-size: 0.88rem !important;
color: #64748b !important;
font-weight: 500 !important;
}
.o_top_header_right {
display: flex !important;
align-items: center !important;
gap: 18px !important;
}
.o_lang_btn {
background: transparent !important;
border: none !important;
color: #334155 !important;
font-size: 0.88rem !important;
font-weight: 500 !important;
display: inline-flex !important;
align-items: center !important;
gap: 6px !important;
padding: 6px 10px !important;
border-radius: 8px !important;
cursor: pointer !important;
transition: background 0.15s ease !important;
}
.o_lang_btn:hover {
background: #f1f5f9 !important;
}
.o_help_pill_btn {
display: inline-flex !important;
align-items: center !important;
gap: 7px !important;
background: #fef2f2 !important;
border: 1px solid #fee2e2 !important;
color: #dc2626 !important;
border-radius: 50px !important;
padding: 7px 16px !important;
font-size: 0.85rem !important;
font-weight: 600 !important;
text-decoration: none !important;
transition: all 0.2s ease !important;
}
.o_help_pill_btn:hover {
background: #fee2e2 !important;
color: #b91c1c !important;
}
/* 3. Main Wrapper: Immersive Backdrop */
.o_login_main_wrapper {
display: flex !important;
min-height: calc(100vh - 64px) !important;
width: 100% !important;
background-color: #fbfbfe !important;
background-image:
linear-gradient(to right, rgba(255, 255, 255, 0.96) 0%, rgba(255, 255, 255, 0.90) 34%, rgba(255, 255, 255, 0.25) 60%, rgba(255, 255, 255, 0.05) 100%),
url('/dine360_dashboard/static/src/img/login_culinary_hero.jpg') !important;
background-size: cover !important;
background-position: center center !important;
background-repeat: no-repeat !important;
position: relative !important;
overflow-y: auto !important;
overflow-x: hidden !important;
align-items: center !important;
justify-content: space-between !important;
padding: 40px 72px !important;
box-sizing: border-box !important;
}
/* Curved Red Accent Behind Card */
.o_curved_red_accent {
position: absolute !important;
right: 0 !important;
top: 5% !important;
width: 500px !important;
height: 550px !important;
pointer-events: none !important;
z-index: 1 !important;
}
.o_curved_red_accent svg {
width: 100% !important;
height: 100% !important;
}
/* 4. Left Side: Brand Hero Showcase */
.o_login_left_side {
flex: 1.25 !important;
max-width: 580px !important;
position: relative !important;
z-index: 2 !important;
}
.o_login_hero_content {
max-width: 540px !important;
max-width: 520px !important;
}
.o_hero_badge {
display: inline-flex !important;
align-items: center !important;
gap: 8px !important;
background: rgba(229, 0, 0, 0.12) !important;
border: 1px solid rgba(229, 0, 0, 0.3) !important;
color: #ff6b6b !important;
font-size: 0.75rem !important;
background: #fef2f2 !important;
border: 1px solid #fee2e2 !important;
color: #dc2626 !important;
font-size: 0.74rem !important;
font-weight: 700 !important;
letter-spacing: 1.5px !important;
letter-spacing: 1.2px !important;
border-radius: 50px !important;
padding: 6px 16px !important;
margin-bottom: 24px !important;
padding: 6px 14px !important;
margin-bottom: 20px !important;
text-transform: uppercase !important;
}
.o_hero_badge_dot {
width: 8px !important;
height: 8px !important;
background: #ef4444 !important;
width: 7px !important;
height: 7px !important;
background: #dc2626 !important;
border-radius: 50% !important;
box-shadow: 0 0 8px #ef4444 !important;
animation: o_pulse_dot 2s infinite ease-in-out !important;
}
@keyframes o_pulse_dot {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.85); }
}
.o_hero_title {
font-size: 3.1rem !important;
font-size: 3.35rem !important;
font-weight: 800 !important;
letter-spacing: -1px !important;
color: #ffffff !important;
line-height: 1.15 !important;
letter-spacing: -1.2px !important;
color: #0f172a !important;
line-height: 1.12 !important;
margin-bottom: 18px !important;
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.5) !important;
}
.o_title_accent {
color: #dc2626 !important;
}
.o_hero_subtitle {
font-size: 1.1rem !important;
color: #94a3b8 !important;
font-size: 1.05rem !important;
color: #64748b !important;
line-height: 1.6 !important;
margin-bottom: 36px !important;
margin-bottom: 30px !important;
}
.o_hero_features {
display: flex !important;
flex-direction: column !important;
gap: 14px !important;
gap: 12px !important;
}
.o_hero_feature_item {
display: flex !important;
align-items: center !important;
gap: 16px !important;
background: rgba(255, 255, 255, 0.03) !important;
border: 1px solid rgba(255, 255, 255, 0.07) !important;
border-radius: 14px !important;
padding: 14px 18px !important;
backdrop-filter: blur(8px) !important;
background: rgba(255, 255, 255, 0.92) !important;
border: 1px solid rgba(226, 232, 240, 0.95) !important;
border-radius: 16px !important;
padding: 13px 18px !important;
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.03) !important;
transition: all 0.25s ease !important;
}
.o_hero_feature_item:hover {
background: rgba(255, 255, 255, 0.06) !important;
border-color: rgba(229, 0, 0, 0.3) !important;
background: #ffffff !important;
border-color: #cbd5e1 !important;
transform: translateX(4px) !important;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.06) !important;
}
.o_feature_icon {
width: 40px !important;
height: 40px !important;
width: 42px !important;
height: 42px !important;
border-radius: 10px !important;
background: rgba(229, 0, 0, 0.15) !important;
color: #ef4444 !important;
background: #dc2626 !important;
color: #ffffff !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-size: 1.1rem !important;
font-size: 1.15rem !important;
flex-shrink: 0 !important;
box-shadow: 0 4px 10px rgba(220, 38, 38, 0.25) !important;
}
.o_feature_icon svg {
fill: #ffffff !important;
}
.o_feature_text h6 {
color: #ffffff !important;
color: #0f172a !important;
font-weight: 700 !important;
font-size: 0.95rem !important;
font-size: 0.94rem !important;
margin: 0 0 2px 0 !important;
}
.o_feature_text p {
color: #94a3b8 !important;
color: #64748b !important;
font-size: 0.82rem !important;
margin: 0 !important;
line-height: 1.4 !important;
}
/* 4. Right Side: Centered Glassmorphic Form */
/* 5. Right Side: Pure White Floating Card */
.o_login_right_side {
flex: 1 !important;
flex: 0.95 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 40px 24px !important;
justify-content: flex-end !important;
position: relative !important;
z-index: 2 !important;
}
.o_login_card {
width: 100% !important;
max-width: 440px !important;
background: rgba(15, 23, 42, 0.8) !important;
backdrop-filter: blur(28px) saturate(190%) !important;
-webkit-backdrop-filter: blur(28px) saturate(190%) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
border-radius: 24px !important;
box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.7), 0 0 40px rgba(229, 0, 0, 0.12) !important;
max-width: 430px !important;
background: #ffffff !important;
border: 1px solid #f1f5f9 !important;
border-radius: 28px !important;
box-shadow: 0 25px 60px -15px rgba(15, 23, 42, 0.12), 0 0 25px rgba(0, 0, 0, 0.03) !important;
padding: 38px 36px !important;
box-sizing: border-box !important;
}
/* Brand Header */
.o_login_logo_container {
text-align: center !important;
margin-bottom: 20px !important;
}
.o_login_brand_header {
display: inline-flex !important;
/* Brand Header inside card */
.o_login_card_logo {
display: flex !important;
align-items: center !important;
justify-content: center !important;
gap: 10px !important;
margin-bottom: 14px !important;
}
.o_brand_icon {
width: 44px !important;
height: 44px !important;
background: linear-gradient(135deg, #e50000 0%, #b91c1c 100%) !important;
background: #dc2626 !important;
border-radius: 12px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
color: #ffffff !important;
font-size: 1.25rem !important;
box-shadow: 0 8px 20px -3px rgba(229, 0, 0, 0.5) !important;
box-shadow: 0 4px 12px rgba(220, 38, 38, 0.28) !important;
}
.o_brand_name {
font-size: 1.85rem !important;
font-weight: 800 !important;
color: #ffffff !important;
color: #0f172a !important;
letter-spacing: -0.5px !important;
}
.o_brand_accent {
color: #ef4444 !important;
}
.o_login_logo_box {
margin-top: 8px !important;
}
.o_company_tag {
background: rgba(239, 68, 68, 0.14) !important;
border: 1px solid rgba(239, 68, 68, 0.3) !important;
color: #fca5a5 !important;
font-size: 0.8rem !important;
font-weight: 600 !important;
padding: 4px 14px !important;
border-radius: 20px !important;
display: inline-block !important;
color: #dc2626 !important;
}
/* Header Text */
.o_login_header_text h3 {
font-size: 1.45rem !important;
font-weight: 700 !important;
color: #ffffff !important;
margin-bottom: 6px !important;
font-size: 1.55rem !important;
font-weight: 800 !important;
color: #0f172a !important;
margin-bottom: 4px !important;
letter-spacing: -0.3px !important;
}
.o_login_header_text p {
font-size: 0.88rem !important;
color: #94a3b8 !important;
margin-bottom: 24px !important;
font-size: 0.85rem !important;
color: #64748b !important;
margin-bottom: 22px !important;
}
/* 5. Inputs & Form Controls */
/* 6. Inputs & Form Controls */
.o_login_form_container label,
.oe_login_form label {
color: #cbd5e1 !important;
color: #334155 !important;
font-weight: 600 !important;
font-size: 0.84rem !important;
margin-bottom: 6px !important;
@ -262,186 +374,231 @@ body.o_custom_login_body #wrapwrap {
.o_login_form_container .form-control,
.oe_login_form .form-control {
background: rgba(255, 255, 255, 0.06) !important;
border: 1.5px solid rgba(255, 255, 255, 0.14) !important;
border-radius: 12px !important;
color: #ffffff !important;
padding: 11px 15px !important;
font-size: 0.95rem !important;
height: auto !important;
background: #ffffff !important;
border: 1.5px solid #e2e8f0 !important;
border-radius: 10px !important;
color: #0f172a !important;
padding: 10px 14px !important;
font-size: 0.92rem !important;
height: 44px !important;
transition: all 0.2s ease !important;
box-shadow: none !important;
}
.o_login_form_container .form-control::placeholder,
.oe_login_form .form-control::placeholder {
color: rgba(255, 255, 255, 0.4) !important;
color: #94a3b8 !important;
}
.o_login_form_container .form-control:focus,
.oe_login_form .form-control:focus {
background: rgba(255, 255, 255, 0.1) !important;
border-color: #ef4444 !important;
color: #ffffff !important;
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.25) !important;
background: #ffffff !important;
border-color: #dc2626 !important;
color: #0f172a !important;
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.12) !important;
outline: none !important;
}
/* Chrome/Safari Autofill Dark Theme Override */
/* Input Icon Wrapper */
.o_input_icon_wrap {
position: relative !important;
display: flex !important;
align-items: center !important;
width: 100% !important;
}
.o_input_icon {
position: absolute !important;
left: 14px !important;
color: #94a3b8 !important;
font-size: 0.95rem !important;
pointer-events: none !important;
z-index: 3 !important;
}
.o_input_icon_wrap .form-control {
padding-left: 38px !important;
padding-right: 38px !important;
}
/* Password Toggle Button */
.o_toggle_password_btn {
position: absolute !important;
right: 8px !important;
top: 50% !important;
transform: translateY(-50%) !important;
background: transparent !important;
border: none !important;
color: #94a3b8 !important;
padding: 6px 8px !important;
font-size: 0.95rem !important;
cursor: pointer !important;
z-index: 4 !important;
}
.o_toggle_password_btn:hover {
color: #334155 !important;
}
/* Database Input Group */
.input-group {
border-radius: 10px !important;
overflow: hidden !important;
}
.input-group #db {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
border-right: none !important;
cursor: default !important;
background: #ffffff !important;
color: #0f172a !important;
font-weight: 500 !important;
}
.input-group .btn-secondary,
.input-group a.btn {
background: #f1f5f9 !important;
border: 1.5px solid #e2e8f0 !important;
border-radius: 0 10px 10px 0 !important;
color: #334155 !important;
font-weight: 600 !important;
font-size: 0.85rem !important;
padding: 10px 15px !important;
transition: all 0.2s ease !important;
display: inline-flex !important;
align-items: center !important;
gap: 6px !important;
text-decoration: none !important;
}
.input-group .btn-secondary:hover,
.input-group a.btn:hover {
background: #e2e8f0 !important;
color: #0f172a !important;
}
/* Chrome/Safari Autofill Light Theme Override */
.oe_login_form input:-webkit-autofill,
.oe_login_form input:-webkit-autofill:hover,
.oe_login_form input:-webkit-autofill:focus,
.o_login_form_container input:-webkit-autofill,
.o_login_form_container input:-webkit-autofill:hover,
.o_login_form_container input:-webkit-autofill:focus {
-webkit-text-fill-color: #ffffff !important;
-webkit-box-shadow: 0 0 0px 1000px #1e293b inset !important;
border-color: rgba(255, 255, 255, 0.2) !important;
-webkit-text-fill-color: #0f172a !important;
-webkit-box-shadow: 0 0 0px 1000px #ffffff inset !important;
border-color: #e2e8f0 !important;
transition: background-color 5000s ease-in-out 0s !important;
}
/* Database Input Group */
.input-group #db {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
cursor: default !important;
}
.input-group .btn-secondary,
.input-group a.btn {
background: rgba(255, 255, 255, 0.08) !important;
border: 1.5px solid rgba(255, 255, 255, 0.14) !important;
border-left: none !important;
border-top-right-radius: 12px !important;
border-bottom-right-radius: 12px !important;
color: #e2e8f0 !important;
font-weight: 600 !important;
font-size: 0.85rem !important;
padding: 11px 16px !important;
transition: all 0.2s ease !important;
display: inline-flex !important;
align-items: center !important;
gap: 6px !important;
}
.input-group .btn-secondary:hover,
.input-group a.btn:hover {
background: #ef4444 !important;
border-color: #ef4444 !important;
color: #ffffff !important;
}
/* 6. Primary Action Button */
.oe_login_buttons .btn-primary {
background: linear-gradient(135deg, #e50000 0%, #b91c1c 100%) !important;
/* 7. Primary Action Button */
.oe_login_buttons .btn-primary,
.o_login_submit_btn {
background: #dc2626 !important;
border: none !important;
border-radius: 12px !important;
padding: 13px !important;
border-radius: 10px !important;
height: 46px !important;
font-weight: 700 !important;
font-size: 1rem !important;
font-size: 0.98rem !important;
color: #ffffff !important;
width: 100% !important;
box-shadow: 0 8px 24px -4px rgba(229, 0, 0, 0.45) !important;
transition: all 0.25s ease !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
gap: 8px !important;
box-shadow: 0 4px 14px rgba(220, 38, 38, 0.32) !important;
transition: all 0.2s ease !important;
cursor: pointer !important;
}
.oe_login_buttons .btn-primary:hover {
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%) !important;
transform: translateY(-2px) !important;
box-shadow: 0 12px 28px -4px rgba(229, 0, 0, 0.6) !important;
.oe_login_buttons .btn-primary:hover,
.o_login_submit_btn:hover {
background: #b91c1c !important;
transform: translateY(-1px) !important;
box-shadow: 0 6px 18px rgba(220, 38, 38, 0.42) !important;
}
.oe_login_buttons .btn-primary:active {
.oe_login_buttons .btn-primary:active,
.o_login_submit_btn:active {
transform: translateY(0) !important;
}
/* Superuser button */
.oe_login_buttons .btn-link {
color: #94a3b8 !important;
font-size: 0.85rem !important;
/* Reset Password link */
.justify-content-between.mt-2 a[href*="/web/signup"] {
display: none !important;
}
.justify-content-between.mt-2 a[href*="/web/reset_password"],
a[href*="/web/reset_password"] {
color: #dc2626 !important;
font-size: 0.84rem !important;
font-weight: 500 !important;
text-decoration: none !important;
}
.oe_login_buttons .btn-link:hover {
color: #ef4444 !important;
a[href*="/web/reset_password"]:hover {
text-decoration: underline !important;
}
/* 7. Alerts */
/* Alerts */
.o_login_form_container .alert-danger,
.oe_login_form .alert-danger {
background: rgba(220, 38, 38, 0.25) !important;
border: 1px solid rgba(220, 38, 38, 0.5) !important;
color: #fca5a5 !important;
border-radius: 12px !important;
padding: 12px 14px !important;
font-size: 0.88rem !important;
background: #fef2f2 !important;
border: 1px solid #fecaca !important;
color: #b91c1c !important;
border-radius: 10px !important;
padding: 10px 14px !important;
font-size: 0.86rem !important;
}
.o_login_form_container .alert-success,
.oe_login_form .alert-success {
background: rgba(16, 185, 129, 0.25) !important;
border: 1px solid rgba(16, 185, 129, 0.5) !important;
color: #6ee7b7 !important;
border-radius: 12px !important;
padding: 12px 14px !important;
font-size: 0.88rem !important;
background: #f0fdf4 !important;
border: 1px solid #bbf7d0 !important;
color: #15803d !important;
border-radius: 10px !important;
padding: 10px 14px !important;
font-size: 0.86rem !important;
}
/* 8. Links and Footer */
.oe_login_buttons a,
.o_login_footer a,
.o_login_link {
color: #94a3b8 !important;
.o_login_footer .o_login_link {
color: #64748b !important;
font-size: 0.85rem !important;
text-decoration: none !important;
font-weight: 500 !important;
display: inline-flex !important;
align-items: center !important;
gap: 6px !important;
transition: color 0.2s ease !important;
}
.oe_login_buttons a:hover,
.o_login_footer a:hover,
.o_login_link:hover {
color: #ef4444 !important;
text-decoration: underline !important;
.o_login_footer .o_login_link:hover {
color: #0f172a !important;
}
.o_login_brand_footer p {
color: #64748b !important;
color: #94a3b8 !important;
font-size: 0.82rem !important;
margin: 0 !important;
}
.o_login_brand_footer a {
color: #ef4444 !important;
font-weight: 600 !important;
text-decoration: none !important;
}
.o_login_brand_footer a:hover {
text-decoration: underline !important;
}
/* Database Selector / List Modal Styling */
.o_database_list .list-group-item {
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
color: #ffffff !important;
border-radius: 10px !important;
margin-bottom: 8px !important;
}
.o_database_list .list-group-item:hover {
background: rgba(229, 0, 0, 0.15) !important;
border-color: rgba(229, 0, 0, 0.4) !important;
.o_powered_brand {
color: #dc2626 !important;
font-weight: 700 !important;
}
/* 9. Responsive Breakpoints */
@media (max-width: 992px) {
.o_login_top_header {
padding: 0 20px !important;
}
.o_login_main_wrapper {
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
padding: 30px 20px !important;
}
.o_login_left_side {
@ -450,7 +607,7 @@ body.o_custom_login_body #wrapwrap {
.o_login_right_side {
width: 100% !important;
padding: 24px 16px !important;
justify-content: center !important;
}
.o_login_card {

View File

@ -267,8 +267,8 @@ body,
padding: 8px 0 !important;
}
.o_kanban_renderer .o_kanban_record,
.o_kanban_view .o_kanban_record {
.o_kanban_renderer .o_kanban_record:not(.o_kanban_ghost),
.o_kanban_view .o_kanban_record:not(.o_kanban_ghost) {
background: #ffffff !important;
border: 1px solid #e2e8f0 !important;
border-radius: 16px !important;
@ -279,13 +279,41 @@ body,
}
/* Hover Effect for Cards */
.o_kanban_renderer .o_kanban_record:hover,
.o_kanban_view .o_kanban_record:hover {
.o_kanban_renderer .o_kanban_record:not(.o_kanban_ghost):hover,
.o_kanban_view .o_kanban_record:not(.o_kanban_ghost):hover {
transform: translateY(-3px) !important;
box-shadow: 0 12px 24px -2px rgba(15, 23, 42, 0.1) !important;
border-color: #fca5a5 !important;
}
/* Constrain dashboard/POS kanban card width so it stays compact without flex stretching */
.o_pos_kanban .o_kanban_record,
.o_kanban_dashboard .o_kanban_record {
max-width: 420px !important;
}
/* Ensure Odoo flex ghost spacers and empty cards remain completely removed and invisible */
.o_kanban_renderer .o_kanban_ghost,
.o_kanban_view .o_kanban_ghost,
.o_kanban_ghost {
display: none !important;
width: 0 !important;
height: 0 !important;
min-height: 0 !important;
max-height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
outline: none !important;
box-shadow: none !important;
background: transparent !important;
background-color: transparent !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
overflow: hidden !important;
}
/* Card Header/Title */
.o_kanban_record .o_kanban_record_title,
.o_kanban_record strong {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 KiB

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

View File

@ -133,7 +133,8 @@
</div>
</div>
<!-- App 2: Table Reservations -->
<!-- App 2: Table Reservations (Tenant Restaurants Only) -->
<t t-if="not is_saas_master">
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
@ -153,7 +154,7 @@
</div>
</div>
<!-- App 3: Table & Floor Manager -->
<!-- App 3: Table & Floor Manager (Tenant Restaurants Only) -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
@ -172,6 +173,7 @@
</div>
</div>
</div>
</t>
<!-- App 4: Point of Sale -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">

View File

@ -34,14 +34,6 @@
<i class="fa fa-cutlery o_nav_icon"/>
<span>Restaurants</span>
</a>
<a href="/dashboard/reservations" t-att-class="'o_nav_link ' + ('active' if active_page == 'reservations' else '')">
<i class="fa fa-calendar o_nav_icon"/>
<span>Reservations</span>
</a>
<a href="/dashboard/tables" t-att-class="'o_nav_link ' + ('active' if active_page == 'tables' else '')">
<i class="fa fa-th o_nav_icon"/>
<span>Table Management</span>
</a>
<a href="/dashboard/apps" t-att-class="'o_nav_link ' + ('active' if active_page == 'apps' else '')">
<i class="fa fa-th-large o_nav_icon"/>
<span>Apps</span>
@ -160,8 +152,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.0"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.0"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.2"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.2"/>
<div class="o_saas_layout">
<!-- Shared Dine360 Sidebar -->

View File

@ -8,8 +8,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.0"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.0"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.2"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.2"/>
<!-- ============================================================== -->
<!-- 1. ADMIN RESTAURANT ONBOARD MASTER DB DASHBOARD -->
@ -191,13 +191,13 @@
<div class="o_substats_col">
<div class="o_substat_item">
<div class="o_substat_icon_box">
<i class="fa fa-calendar text-danger"/>
<i class="fa fa-cutlery text-danger"/>
</div>
<div>
<div class="o_substat_title">Total Reservations</div>
<div class="o_substat_title">Active Restaurants</div>
<div class="d-flex align-items-center gap-2">
<span class="o_substat_val"><t t-esc="saas_dashboard_data['reservations_count']"/></span>
<span class="o_substat_pct text-success"><i class="fa fa-arrow-up"/> +12%</span>
<span class="o_substat_val"><t t-esc="saas_stats['active']"/></span>
<span class="o_substat_pct text-success"><i class="fa fa-arrow-up"/> 100%</span>
</div>
</div>
</div>

View File

@ -6,14 +6,70 @@
<t t-set="body_classname" t-value="'o_custom_login_body'"/>
<t t-set="no_header" t-value="True"/>
<t t-set="no_footer" t-value="True"/>
<t t-set="title">Dine360 Restaurant - Login</t>
<t t-set="title">Dine360 - Smart Restaurant Management</t>
<t t-set="head">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/login_style.css"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/login_style.css?v=3.5"/>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
var btn = document.getElementById('togglePasswordBtn');
var pwd = document.getElementById('password');
var icon = document.getElementById('togglePasswordIcon');
if (btn &amp;&amp; pwd &amp;&amp; icon) {
btn.addEventListener('click', function(e) {
e.preventDefault();
if (pwd.type === 'password') {
pwd.type = 'text';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
} else {
pwd.type = 'password';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
}
});
}
});
</script>
</t>
<!-- Top Navbar Header (Div container to prevent header resets) -->
<div class="o_login_top_header">
<div class="o_top_header_left">
<div class="o_top_brand">
<div class="o_top_brand_icon">
<i class="fa fa-cutlery"/>
</div>
<span class="o_top_brand_name">Dine<span class="o_top_brand_accent">360</span></span>
</div>
<div class="o_top_header_divider"/>
<span class="o_top_header_subtitle">Smart Restaurant Management</span>
</div>
<div class="o_top_header_right">
<div class="o_lang_dropdown">
<button class="o_lang_btn" type="button">
<i class="fa fa-globe"/>
<span>English</span>
<i class="fa fa-angle-down"/>
</button>
</div>
<a href="mailto:support@dine360.com" class="o_help_pill_btn">
<i class="fa fa-headphones"/>
<span>Need Help?</span>
</a>
</div>
</div>
<div class="o_login_main_wrapper">
<!-- Subtle background decorative curved swoosh SVG -->
<div class="o_curved_red_accent">
<svg viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M600 50C450 120 320 280 400 480C440 580 520 620 600 650" stroke="#dc2626" stroke-width="1.2" stroke-opacity="0.3" fill="none"/>
<path d="M600 120C480 180 370 320 440 500C475 585 540 620 600 640" stroke="#fca5a5" stroke-width="0.8" stroke-opacity="0.25" fill="none"/>
</svg>
</div>
<!-- Left Side: Culinary Branding Hero Showcase -->
<div class="o_login_left_side">
<div class="o_login_hero_content">
@ -21,7 +77,10 @@
<span class="o_hero_badge_dot"/>
<span>NEXT-GEN RESTAURANT SUITE</span>
</div>
<h1 class="o_hero_title">Elevate Your Dining Experience</h1>
<h1 class="o_hero_title">
Elevate Your Dining<br/>
<span class="o_title_accent">Experience</span>
</h1>
<p class="o_hero_subtitle">Unified Point of Sale, Kitchen Display System (KDS), Interactive Tables, and Online Orders — all in one modern platform.</p>
<div class="o_hero_features">
@ -29,41 +88,40 @@
<div class="o_feature_icon"><i class="fa fa-cutlery"/></div>
<div class="o_feature_text">
<h6>Interactive Floor Plans</h6>
<p>Real-time visual table booking &amp; occupancy tracking</p>
<p>Real-time visual table booking &amp; occupancy tracking.</p>
</div>
</div>
<div class="o_hero_feature_item">
<div class="o_feature_icon"><i class="fa fa-fire"/></div>
<div class="o_feature_icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C9.5 2 7.4 3.7 6.8 6c-.4-.1-.8-.1-1.2-.1C3 5.9 1 7.9 1 10.5 1 12.8 2.6 14.8 4.8 15.1L5 20h14l.2-4.9c2.2-.3 3.8-2.3 3.8-4.6 0-2.6-2-4.6-4.6-4.6-.4 0-.8 0-1.2.1C16.6 3.7 14.5 2 12 2zm-5 17h10v1H7v-1z"/>
</svg>
</div>
<div class="o_feature_text">
<h6>Real-Time Kitchen Display</h6>
<p>Automated order routing, prep timers, and status syncing</p>
<p>Automated order routing, prep times, and status syncing.</p>
</div>
</div>
<div class="o_hero_feature_item">
<div class="o_feature_icon"><i class="fa fa-mobile"/></div>
<div class="o_feature_text">
<h6>Seamless QR &amp; Online Orders</h6>
<p>Zero-friction customer self-ordering and delivery dispatch</p>
<p>Zero-friction customer self-ordering and delivery dispatch.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Right Side: Glassmorphic Login Card -->
<!-- Right Side: Clean White Floating Login Card -->
<div class="o_login_right_side">
<div class="o_login_card">
<div class="o_login_logo_container">
<div class="o_login_brand_header">
<div class="o_login_card_logo">
<div class="o_brand_icon">
<i class="fa fa-cutlery"/>
</div>
<span class="o_brand_name">Dine<span class="o_brand_accent">360</span></span>
</div>
<div class="o_login_logo_box" t-if="request and request.env and request.env.company and request.env.company.name != 'My Company'">
<span class="o_company_tag" t-esc="request.env.company.name"/>
</div>
</div>
<div class="o_login_header_text text-center">
<h3>Welcome Back</h3>
@ -81,7 +139,7 @@
</div>
<div class="o_login_brand_footer text-center mt-3">
<p>Powered by <a href="https://dine360.com/" target="_blank">Dine360</a> Cloud ERP</p>
<p>Powered by <span class="o_powered_brand">Dine360</span> Cloud ERP</p>
</div>
</div>
</div>
@ -89,4 +147,35 @@
</t>
</xpath>
</template>
<!-- Custom Login Form Fields Inheritance (Icons + Eye Toggle + Arrow Button) -->
<template id="custom_login_form_fields" inherit_id="web.login" name="Custom Dine360 Login Fields" priority="99">
<xpath expr="//div[hasclass('field-login')]" position="replace">
<div class="mb-3 field-login">
<label for="login" class="form-label">Email</label>
<div class="o_input_icon_wrap">
<i class="fa fa-envelope-o o_input_icon"/>
<input type="text" placeholder="Email" name="login" t-att-value="login" id="login" class="form-control" required="required" autocomplete="username" autofocus="autofocus" autocapitalize="off"/>
</div>
</div>
</xpath>
<xpath expr="//div[hasclass('mb-3')][label[@for='password']]" position="replace">
<div class="mb-3 field-password">
<label for="password" class="form-label">Password</label>
<div class="o_input_icon_wrap">
<i class="fa fa-lock o_input_icon"/>
<input type="password" placeholder="Password" name="password" id="password" class="form-control" required="required" autocomplete="current-password" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
<button type="button" class="btn o_toggle_password_btn" id="togglePasswordBtn" tabindex="-1">
<i class="fa fa-eye-slash" id="togglePasswordIcon"/>
</button>
</div>
</div>
</xpath>
<xpath expr="//button[@type='submit']" position="replace">
<button type="submit" class="btn btn-primary o_login_submit_btn">
<span>Log In</span>
<i class="fa fa-arrow-right ms-2"/>
</button>
</xpath>
</template>
</odoo>

View File

@ -30,4 +30,10 @@
parent="menu_restaurant_reservation_root"
action="action_reservation_holiday"
sequence="40"/>
<menuitem id="menu_reservation_custom_fields"
name="Additional Fields"
parent="menu_restaurant_reservation_root"
action="action_reservation_custom_fields"
sequence="50"/>
</odoo>

View File

@ -63,10 +63,4 @@
</field>
</record>
<!-- Submenu under Table Reservation -->
<menuitem id="menu_reservation_custom_fields"
name="Additional Fields"
parent="dine360_reservation.menu_restaurant_reservation_root"
action="action_reservation_custom_fields"
sequence="40"/>
</odoo>

View File

@ -19,11 +19,17 @@ def _async_provisioning_worker(parent_db, saas_res_id, db_name, modules, company
# Remove 'base' from modules list if present since exp_create_database already installs it
cli_modules = [m for m in modules if m != 'base']
cmd = ["odoo", "-i", ",".join(cli_modules), "-d", db_name, "--db_host=db", "-r", "odoo", "-w", "odoo", "--stop-after-init"]
_logger.info(f"Executing background CLI installation: {' '.join(cmd)}")
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
log_file_path = f"/tmp/provision_{db_name}.log"
with open(log_file_path, "w") as log_file:
result = subprocess.run(cmd, stdout=log_file, stderr=subprocess.STDOUT, text=True)
if result.returncode != 0:
raise Exception(f"Odoo CLI installation failed (exit code {result.returncode}): {result.stderr or result.stdout}")
tail_msg = ""
try:
with open(log_file_path, "r") as log_file:
tail_msg = log_file.read()[-2000:]
except Exception:
pass
raise Exception(f"Odoo CLI installation failed (exit code {result.returncode}): {tail_msg}")
_logger.info(f"Background thread: Base modules installed on {db_name}. Configuring company & admin...")
@ -32,17 +38,47 @@ def _async_provisioning_worker(parent_db, saas_res_id, db_name, modules, company
with tenant_registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
# Company setup
# Company setup (safe update only non-empty fields)
company = env['res.company'].search([], limit=1)
company.write(company_vals)
if company and company_vals:
cleaned_company_vals = {k: v for k, v in company_vals.items() if v is not False and v is not None and v != ''}
if cleaned_company_vals:
company.write(cleaned_company_vals)
# Admin user setup (ID 2 is usually admin)
admin_user = env['res.users'].browse(2)
admin_user.write(admin_vals)
if admin_user.exists() and admin_vals:
cleaned_admin_vals = {k: v for k, v in admin_vals.items() if v is not False and v is not None and v != ''}
if cleaned_admin_vals:
admin_user.write(cleaned_admin_vals)
# Set timezone
if timezone and admin_user.exists() and admin_user.partner_id:
admin_user.partner_id.tz = timezone or 'America/New_York'
# Ensure Restaurant POS Configuration is enabled
if 'pos.config' in env:
for p_cfg in env['pos.config'].search([]):
vals_to_write = {}
if hasattr(p_cfg, 'module_pos_restaurant') and not p_cfg.module_pos_restaurant:
vals_to_write['module_pos_restaurant'] = True
if hasattr(p_cfg, 'online_orders_enabled') and not p_cfg.online_orders_enabled:
vals_to_write['online_orders_enabled'] = True
if hasattr(p_cfg, 'use_qz_printer') and not p_cfg.use_qz_printer:
vals_to_write['use_qz_printer'] = True
if vals_to_write:
p_cfg.write(vals_to_write)
# Ensure default draft Uber config exists if uber module is installed
if 'uber.config' in env:
if not env['uber.config'].search([], limit=1):
env['uber.config'].create({
'name': f"{company.name if company else 'Restaurant'} Uber Eats / Direct",
'client_id': 'SANDBOX_CLIENT_ID',
'client_secret': 'SANDBOX_CLIENT_SECRET',
'environment': 'sandbox',
})
# Force Odoo registry signaling update
env.registry.registry_invalidated = True
env.registry.signal_changes()
@ -150,12 +186,49 @@ class SaasRestaurant(models.Model):
currency_id = fields.Many2one('res.currency', string='Base Currency')
timezone = fields.Char(string='Timezone', default='America/New_York')
logo = fields.Binary(string='Restaurant Logo')
# Onboarding Features for Restaurant Tenant
feature_online_orders = fields.Boolean(
string='Online Orders (POS & Web)',
default=True,
help='Installs Dine360 Online Orders with POS integration and KDS.'
)
feature_table_reservation = fields.Boolean(
string='Table Reservation & Theme',
default=True,
help='Installs Dine360 Table Reservation and Theme Customizer.'
)
feature_uber_integration = fields.Boolean(
string='Uber Integration (Eats & Direct)',
default=True,
help='Installs Dine360 Uber Integration for Eats and Direct delivery.'
)
feature_dashboard = fields.Boolean(
string='Modern Unified Dashboard',
default=True,
help='Installs Dine360 Modern Dashboard with unified navigation and module pages.'
)
feature_qz_printer = fields.Boolean(
string='QZ Tray Thermal Printer',
default=True,
help='Installs Dine360 QZ Printer module for Star/Epson direct thermal receipt printing.'
)
activate_table_reservation = fields.Boolean(
string='Activate Table Reservation Theme',
default=False,
help='If checked, automatically installs and configures the themed Table Reservation module on this restaurant database.'
compute='_compute_activate_table_reservation',
inverse='_inverse_activate_table_reservation',
store=True,
help='Legacy flag mapped to Table Reservation.'
)
@api.depends('feature_table_reservation')
def _compute_activate_table_reservation(self):
for rec in self:
rec.activate_table_reservation = rec.feature_table_reservation
def _inverse_activate_table_reservation(self):
for rec in self:
rec.feature_table_reservation = rec.activate_table_reservation
_sql_constraints = [
('unique_subdomain', 'unique(subdomain)', 'This subdomain is already taken!'),
('unique_database_name', 'unique(database_name)', 'This database name is already registered!')
@ -222,13 +295,31 @@ class SaasRestaurant(models.Model):
'password': admin_pass,
}
# Clean values to avoid DB constraint failures (such as null currency_id)
company_vals = {k: v for k, v in company_vals.items() if v is not False and v is not None and v != ''}
admin_vals = {k: v for k, v in admin_vals.items() if v is not False and v is not None and v != ''}
# Only install requested onboard features and their dependencies
modules_to_install = []
if self.feature_online_orders:
modules_to_install.append('dine360_online_orders')
if self.feature_table_reservation:
modules_to_install.append('dine360_theme_reservation')
if self.feature_uber_integration:
modules_to_install.append('dine360_uber')
if self.feature_dashboard:
modules_to_install.append('dine360_dashboard')
if self.feature_qz_printer:
modules_to_install.append('dine360_qz_printer')
if not modules_to_install:
modules_to_install = [
'base', 'contacts', 'sale', 'stock', 'purchase',
'account', 'point_of_sale', 'hr', 'pos_restaurant',
'dine360_table', 'dine360_dashboard'
'dine360_online_orders',
'dine360_theme_reservation',
'dine360_uber',
'dine360_dashboard',
'dine360_qz_printer'
]
if self.activate_table_reservation:
modules_to_install.extend(['website', 'dine360_reservation', 'dine360_theme_reservation'])
# Set status to provisioning
self.status = 'provisioning'

View File

@ -54,7 +54,13 @@
<group string="Tenant Deployment">
<field name="database_name"/>
<field name="subdomain"/>
<field name="activate_table_reservation"/>
</group>
<group string="Restaurant Features (Activated by Default)">
<field name="feature_online_orders"/>
<field name="feature_table_reservation"/>
<field name="feature_uber_integration"/>
<field name="feature_dashboard"/>
<field name="feature_qz_printer"/>
</group>
<group string="Subscription Details">
<field name="plan_id"/>

View File

@ -12,5 +12,5 @@
name="Theme Settings"
parent="dine360_reservation.menu_restaurant_reservation_root"
action="action_reservation_theme_settings"
sequence="50"/>
sequence="60"/>
</odoo>