diff --git a/README.md b/README.md index 3e8891e..f547746 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,29 @@ A dedicated compact layout for portrait-orientation small screens. Designed for | Feature | Standard | Portrait | |---|---|---| | Layout | Two-column (order left, products right) | Single pane with bottom tabs | +| Categories | Horizontal scrollable chips | Compact hierarchical select dropdown | | Product grid | `auto-fill minmax(115px)` | Fixed 2 columns | | Product images | Full aspect ratio | Capped at 90px height | -| Numpad | Shown inline in left pane | Dedicated "Numpad" tab | +| Numpad | Shown inline in left pane | Dedicated "Numpad" tab (shows only selected line) | | Actions (Pay/Send/Checker) | Inside left pane action pad | Always-visible bottom strip | | Navbar | Full labels | Compact with smaller padding | +#### Category Dropdown + +In portrait mode, the category selector is transformed into a compact dropdown list. It lists all active POS categories formatted hierarchically with indentation, making it extremely easy to filter products on small touch screens. + +#### Selected Line Actions (Note & Delete) + +The Numpad tab contains actions for modifying the selected orderline: +- **Note**: Triggers the standard customer note input dialog. +- **Delete**: Instantly removes the selected line from the cart. + +#### Numpad Single Line Focus + +To prevent overlaps on 5.8-inch screens: +- **Cart Tab**: Shows only the order lines (full height). The numpad is hidden. +- **Numpad Tab**: Hides all non-selected lines and hides the order summary block, showing only the selected line and the numpad/actions. If no item is selected, a friendly guide card is displayed. + #### Table Checker Button The **Checker** button is available in the bottom pay strip in portrait mode. It allows waiters or cashiers to quickly print a basic table checker receipt. @@ -119,6 +136,9 @@ pos_ui_optimization/ │ └── src/ │ ├── app/ │ │ ├── components/ +│ │ │ ├── category_selector/ +│ │ │ │ ├── category_selector_patch.js # Hierarchical dropdown list +│ │ │ │ └── category_selector_patch.xml │ │ │ ├── navbar/ │ │ │ │ ├── navbar_patch.js # Display Mode toggle in burger menu │ │ │ │ └── navbar_patch.xml diff --git a/__manifest__.py b/__manifest__.py index 570e32d..40124fd 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -33,6 +33,9 @@ Features # Navbar display-mode toggle 'pos_ui_optimization/static/src/app/components/navbar/navbar_patch.js', 'pos_ui_optimization/static/src/app/components/navbar/navbar_patch.xml', + # Category selector patch + 'pos_ui_optimization/static/src/app/components/category_selector/category_selector_patch.js', + 'pos_ui_optimization/static/src/app/components/category_selector/category_selector_patch.xml', # ProductScreen portrait tab state + layout 'pos_ui_optimization/static/src/app/screens/product_screen/portrait_mode_patch.js', 'pos_ui_optimization/static/src/app/screens/product_screen/portrait_screen.xml', diff --git a/static/src/app/components/category_selector/category_selector_patch.js b/static/src/app/components/category_selector/category_selector_patch.js new file mode 100644 index 0000000..3220bf0 --- /dev/null +++ b/static/src/app/components/category_selector/category_selector_patch.js @@ -0,0 +1,37 @@ +/** @odoo-module **/ + +import { CategorySelector } from "@point_of_sale/app/components/category_selector/category_selector"; +import { patch } from "@web/core/utils/patch"; +import { useService } from "@web/core/utils/hooks"; + +patch(CategorySelector.prototype, { + setup() { + super.setup(); + this.portraitMode = useService("portrait_mode"); + }, + + get allCategoriesTree() { + const all = this.pos.models["pos.category"].getAll() || []; + const roots = all.filter(c => !c.parent_id).sort((a, b) => (a.sequence || 0) - (b.sequence || 0)); + const result = []; + + const traverse = (category, depth) => { + if (!category.hasProductsToShow) return; + result.push({ + id: category.id, + name: "\u00A0\u00A0".repeat(depth * 2) + (depth > 0 ? "• " : "") + category.name, + }); + if (category.child_ids && category.child_ids.length) { + const children = [...category.child_ids].sort((a, b) => (a.sequence || 0) - (b.sequence || 0)); + for (const child of children) { + traverse(child, depth + 1); + } + } + }; + + for (const root of roots) { + traverse(root, 0); + } + return result; + } +}); diff --git a/static/src/app/components/category_selector/category_selector_patch.xml b/static/src/app/components/category_selector/category_selector_patch.xml new file mode 100644 index 0000000..d11623d --- /dev/null +++ b/static/src/app/components/category_selector/category_selector_patch.xml @@ -0,0 +1,40 @@ + + + + +
+ +
+
+ + + +
+
+
+
diff --git a/static/src/app/screens/product_screen/portrait_mode_patch.js b/static/src/app/screens/product_screen/portrait_mode_patch.js index 393aec3..853acc9 100644 --- a/static/src/app/screens/product_screen/portrait_mode_patch.js +++ b/static/src/app/screens/product_screen/portrait_mode_patch.js @@ -1,6 +1,7 @@ /** @odoo-module **/ import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen"; +import { NoteButton } from "@point_of_sale/app/screens/product_screen/control_buttons/orderline_note_button/orderline_note_button"; import { patch } from "@web/core/utils/patch"; import { useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; @@ -19,4 +20,16 @@ patch(ProductScreen.prototype, { switchPortraitTab(tab) { this.portraitState.tab = tab; }, + + onDeleteLine() { + const orderline = this.pos.getOrder().getSelectedOrderline(); + if (orderline) { + this.pos.getOrder().removeOrderline(orderline); + } + } }); + +ProductScreen.components = { + ...ProductScreen.components, + NoteButton, +}; diff --git a/static/src/app/screens/product_screen/portrait_screen.xml b/static/src/app/screens/product_screen/portrait_screen.xml index e39c989..8d1336d 100644 --- a/static/src/app/screens/product_screen/portrait_screen.xml +++ b/static/src/app/screens/product_screen/portrait_screen.xml @@ -16,10 +16,44 @@ + + {'d-none': portraitMode.isPortrait and portraitTab === 'numpad' and (!currentOrder or currentOrder.isEmpty() or !pos.getOrder()?.uiState.selected_orderline_uuid)} + + + + +
+ +
No Item Selected
+

Tap on any item in the Cart tab to select it, then return here to modify it.

+
+
+ + + +
+ + +
+
+ + - -