frontend_layout.xml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <odoo>
  3. <data noupdate="0">
  4. <!--
  5. M22 Early Sidebar State Script
  6. Inyecta un script en el head que aplica el estado del sidebar
  7. ANTES de que la página se renderice, evitando el flash visual.
  8. -->
  9. <template id="m22_sidebar_early_init" inherit_id="web.layout" priority="1">
  10. <xpath expr="//head" position="inside">
  11. <t t-if="request.session.uid and request.website.sudo().theme_id and request.website.sudo().theme_id.name == 'theme_m22tc'">
  12. <script>
  13. // Apply sidebar collapsed state immediately before render
  14. (function() {
  15. try {
  16. var isCollapsed = localStorage.getItem('m22_sidebar_collapsed') === 'true';
  17. if (isCollapsed) {
  18. document.documentElement.classList.add('m22-sidebar-collapsed-init');
  19. }
  20. } catch(e) {}
  21. })();
  22. </script>
  23. </t>
  24. </xpath>
  25. </template>
  26. <!--
  27. M22 Authenticated Layout - Layout Primario para Usuarios Logueados
  28. Estrategia: Crear un layout primario que herede de portal.frontend_layout
  29. Este layout será la base para todas las páginas que requieran sidebar
  30. -->
  31. <record id="m22_authenticated_layout_view" model="ir.ui.view">
  32. <field name="name">M22 Authenticated Layout</field>
  33. <field name="key">theme_m22tc.m22_authenticated_layout_ir</field>
  34. <field name="inherit_id" ref="portal.frontend_layout"/>
  35. <field name="priority">1000</field>
  36. <field name="arch" type="xml">
  37. <!-- Solo aplicar cuando el usuario está logueado -->
  38. <xpath expr="//div[@id='wrapwrap']" position="attributes">
  39. <attribute name="t-attf-class" add="#{request.session.uid and not request.website.is_public_user() and request.website.sudo().theme_id and request.website.sudo().theme_id.name == 'theme_m22tc' and 'o_has_m22_sidebar' or ''}" separator=" "/>
  40. </xpath>
  41. <!-- Inyectar el sidebar ANTES del main si el usuario está logueado -->
  42. <xpath expr="//div[@id='wrapwrap']/main" position="before">
  43. <t t-if="request.session.uid and not request.website.is_public_user() and request.website.sudo().theme_id and request.website.sudo().theme_id.name == 'theme_m22tc'">
  44. <!-- Get Sidebar Menu Items -->
  45. <t t-set="sidebar_menu_root" t-value="request.env.ref('theme_m22tc.menu_portal_sidebar', raise_if_not_found=False)"/>
  46. <t t-set="sidebar_items"
  47. t-value="sidebar_menu_root.child_id.filtered(lambda m: not m.website_id or m.website_id.id == request.website.id) if (sidebar_menu_root and request.website) else []"/>
  48. <t t-set="current_path" t-value="request.httprequest.path"/>
  49. <!-- SIDEBAR - Fixed Full Height -->
  50. <aside id="m22_sidebar" class="m22-sidebar d-flex flex-column" data-collapsed="false">
  51. <!-- Sidebar Header with Logo -->
  52. <div class="sidebar-header px-3 py-3">
  53. <div class="d-flex align-items-center justify-content-between w-100">
  54. <a href="/my/home" class="sidebar-logo d-flex align-items-center text-decoration-none">
  55. <img t-att-src="request.website.image_url(request.website, 'logo')"
  56. alt="Logo"
  57. class="sidebar-logo-img"
  58. style="max-height: 28px; width: auto; filter: brightness(0) invert(1);"/>
  59. </a>
  60. <button class="sidebar-collapse-btn" type="button" title="Colapsar menú">
  61. <i class="fa fa-chevron-left"></i>
  62. </button>
  63. </div>
  64. </div>
  65. <!-- Sidebar Navigation -->
  66. <div class="sidebar-nav flex-grow-1">
  67. <ul class="nav nav-pills flex-column m-0 p-0">
  68. <t t-foreach="sidebar_items" t-as="menu">
  69. <li class="nav-item">
  70. <t t-set="is_active" t-value="(menu.url == '/my/home' and current_path == '/my/home') or (menu.url != '/my/home' and menu.url and menu.url in current_path)"/>
  71. <a t-att-href="menu.url"
  72. t-att-class="'nav-link %s' % ('active' if is_active else '')"
  73. t-att-title="menu.name">
  74. <i t-if="menu.m22_icon_class"
  75. t-attf-class="fa #{menu.m22_icon_class} sidebar-icon"></i>
  76. <span class="sidebar-text" t-esc="menu.name"/>
  77. </a>
  78. </li>
  79. </t>
  80. </ul>
  81. </div>
  82. <!-- Sidebar Footer (User Info) -->
  83. <div class="sidebar-footer">
  84. <a href="/my/account" class="sidebar-user-info text-decoration-none" title="Mi Cuenta">
  85. <div class="user-avatar">
  86. <i class="fa fa-user"></i>
  87. </div>
  88. <div class="sidebar-text ms-3 d-flex flex-column overflow-hidden">
  89. <span class="fw-semibold text-white text-truncate" t-esc="request.env.user.name"/>
  90. <span class="small text-white-50">Mi Cuenta</span>
  91. </div>
  92. </a>
  93. <a href="/web/session/logout?redirect=/web/login" class="sidebar-logout" title="Cerrar Sesión">
  94. <i class="fa fa-sign-out"></i>
  95. <span class="sidebar-text ms-2">Cerrar Sesión</span>
  96. </a>
  97. </div>
  98. </aside>
  99. <!-- Sidebar Backdrop (Mobile) -->
  100. <div id="m22_sidebar_backdrop" class="m22-sidebar-backdrop d-lg-none"></div>
  101. <!-- Mobile Bottom Navigation -->
  102. <nav class="m22-bottom-nav d-lg-none fixed-bottom">
  103. <div class="d-flex justify-content-around align-items-center">
  104. <!-- Primary items: First 3 items -->
  105. <t t-set="primary_items" t-value="sidebar_items[:3] if len(sidebar_items) >= 3 else sidebar_items"/>
  106. <t t-foreach="primary_items" t-as="menu">
  107. <a t-att-href="menu.url" class="bottom-nav-item d-flex flex-column align-items-center text-decoration-none">
  108. <i t-if="menu.m22_icon_class" t-attf-class="fa #{menu.m22_icon_class}"></i>
  109. <span class="bottom-nav-label" t-esc="menu.name"/>
  110. </a>
  111. </t>
  112. <!-- Account: Always visible -->
  113. <a href="/my/account" class="bottom-nav-item d-flex flex-column align-items-center text-decoration-none">
  114. <i class="fa fa-user"></i>
  115. <span class="bottom-nav-label">Cuenta</span>
  116. </a>
  117. <!-- More Button: Opens bottom sheet - Always last -->
  118. <t t-if="len(sidebar_items) > 3">
  119. <button type="button"
  120. class="bottom-nav-item d-flex flex-column align-items-center text-decoration-none border-0 bg-transparent"
  121. id="m22_more_btn"
  122. aria-label="Más opciones">
  123. <i class="fa fa-ellipsis-h"></i>
  124. <span class="bottom-nav-label">Más</span>
  125. </button>
  126. </t>
  127. </div>
  128. </nav>
  129. <!-- Bottom Sheet: iOS Style with Tailwind -->
  130. <div id="m22_bottom_sheet"
  131. class="m22-bottom-sheet-container hidden"
  132. style="display: none; position: fixed; inset: 0; z-index: 1050; pointer-events: none;"
  133. role="dialog"
  134. aria-modal="true"
  135. aria-labelledby="bottom-sheet-title">
  136. <!-- Backdrop -->
  137. <div id="m22_bottom_sheet_backdrop"
  138. class="m22-bottom-sheet-backdrop"
  139. style="position: absolute; inset: 0; opacity: 0; transition: opacity 0.3s ease;"></div>
  140. <!-- Bottom Sheet Container -->
  141. <div id="m22_bottom_sheet_content"
  142. class="m22-bottom-sheet-content"
  143. style="position: absolute; bottom: 0; left: 0; right: 0; transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); max-height: 85vh; display: flex; flex-direction: column; pointer-events: auto;">
  144. <!-- Handle Indicator -->
  145. <div class="flex justify-center pt-3 pb-2">
  146. <div class="w-12 h-1 bg-gray-300 dark:bg-gray-600 rounded-full"></div>
  147. </div>
  148. <!-- Header -->
  149. <div class="px-6 pb-4 border-b border-gray-200 dark:border-gray-700">
  150. <h3 id="bottom-sheet-title" class="text-lg font-semibold text-gray-900 dark:text-white">Más opciones</h3>
  151. </div>
  152. <!-- Content: Additional menu items -->
  153. <div class="flex-1 overflow-y-auto px-4 py-4">
  154. <div class="space-y-1">
  155. <t t-set="additional_items" t-value="sidebar_items[3:] if len(sidebar_items) > 3 else []"/>
  156. <t t-foreach="additional_items" t-as="menu">
  157. <a t-att-href="menu.url"
  158. class="m22-sheet-item flex items-center gap-3 px-4 py-3 rounded-xl text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-150 text-decoration-none">
  159. <i t-if="menu.m22_icon_class"
  160. t-attf-class="fa #{menu.m22_icon_class} text-xl text-gray-600 dark:text-gray-400 w-6 text-center"></i>
  161. <span class="flex-1 font-medium" t-esc="menu.name"/>
  162. <i class="fa fa-chevron-right text-gray-400 text-sm"></i>
  163. </a>
  164. </t>
  165. </div>
  166. </div>
  167. <!-- Close Button -->
  168. <div class="px-4 pb-6 pt-2 border-t border-gray-200 dark:border-gray-700">
  169. <button type="button"
  170. id="m22_bottom_sheet_close"
  171. class="w-full py-3 px-4 bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white rounded-xl font-medium hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-150">
  172. Cerrar
  173. </button>
  174. </div>
  175. </div>
  176. </div>
  177. </t>
  178. </xpath>
  179. <!-- Agregar clases al main para compensar el sidebar -->
  180. <xpath expr="//div[@id='wrapwrap']/main" position="attributes">
  181. <attribute name="t-attf-class" add="#{request.website.sudo().theme_id and request.website.sudo().theme_id.name == 'theme_m22tc' and not request.env.user._is_public() and 'o_main_with_sidebar' or ''}" separator=" "/>
  182. </xpath>
  183. </field>
  184. </record>
  185. </data>
  186. </odoo>