# Menu Management Architecture The Wizamart platform provides a flexible menu system that supports per-platform and per-user customization. This document explains the menu architecture, configuration options, and how menus integrate with the module system. ## Overview ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ MENU REGISTRY (Source of Truth) │ │ app/config/menu_registry.py │ │ │ │ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ │ │ ADMIN_MENU_REGISTRY │ │ VENDOR_MENU_REGISTRY │ │ │ │ (Admin panel menus) │ │ (Vendor dashboard menus) │ │ │ └─────────────────────────────┘ └─────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ VISIBILITY CONFIGURATION │ │ models/database/admin_menu_config.py │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ AdminMenuConfig Table │ │ │ │ Stores visibility overrides (hidden items only) │ │ │ │ - Platform scope: applies to platform admins/vendors │ │ │ │ - User scope: applies to specific super admin │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ FILTERED MENU OUTPUT │ │ │ │ Registry - Hidden Items - Disabled Modules = Visible Menu Items │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## Frontend Types The system supports two distinct frontend types: | Frontend | Description | Users | |----------|-------------|-------| | `ADMIN` | Admin panel | Super admins, platform admins | | `VENDOR` | Vendor dashboard | Vendors on a platform | ```python from models.database.admin_menu_config import FrontendType # Use in code FrontendType.ADMIN # "admin" FrontendType.VENDOR # "vendor" ``` ## Menu Registry The menu registry (`app/config/menu_registry.py`) is the **single source of truth** for menu structure. The database only stores visibility overrides. ### Admin Menu Structure ```python ADMIN_MENU_REGISTRY = { "frontend_type": FrontendType.ADMIN, "sections": [ { "id": "main", "label": None, # No header "items": [ {"id": "dashboard", "label": "Dashboard", "icon": "home", "url": "/admin/dashboard"}, ], }, { "id": "superAdmin", "label": "Super Admin", "super_admin_only": True, # Only visible to super admins "items": [ {"id": "admin-users", "label": "Admin Users", "icon": "shield", "url": "/admin/admin-users"}, ], }, # ... more sections ], } ``` ### Admin Menu Sections | Section ID | Label | Description | |------------|-------|-------------| | `main` | (none) | Dashboard - always at top | | `superAdmin` | Super Admin | Super admin only tools | | `platformAdmin` | Platform Administration | Companies, vendors, messages | | `vendorOps` | Vendor Operations | Products, customers, inventory, orders | | `marketplace` | Marketplace | Letzshop integration | | `billing` | Billing & Subscriptions | Tiers, subscriptions, billing history | | `contentMgmt` | Content Management | Platforms, content pages, themes | | `devTools` | Developer Tools | Components, icons | | `platformHealth` | Platform Health | Capacity, testing, code quality | | `monitoring` | Platform Monitoring | Imports, tasks, logs, notifications | | `settingsSection` | Platform Settings | General settings, email templates, my menu | ### Vendor Menu Sections | Section ID | Label | Description | |------------|-------|-------------| | `main` | (none) | Dashboard, analytics | | `products` | Products & Inventory | Products, inventory, marketplace import | | `sales` | Sales & Orders | Orders, Letzshop orders, invoices | | `customers` | Customers | Customers, messages, notifications | | `shop` | Shop & Content | Content pages, media library | | `account` | Account & Settings | Team, profile, billing, settings | ### Menu Item Properties Each menu item has these properties: | Property | Type | Description | |----------|------|-------------| | `id` | string | Unique identifier (used for config and access control) | | `label` | string | Display text | | `icon` | string | Heroicons icon name | | `url` | string | Route URL | | `super_admin_only` | boolean | (Optional) Only visible to super admins | ## Visibility Configuration ### Opt-Out Model The system uses an **opt-out model**: - All menu items are **visible by default** - Only **hidden** items are stored in the database - This keeps the database small and makes the default state explicit ### Mandatory Items Certain menu items cannot be hidden: ```python MANDATORY_MENU_ITEMS = { FrontendType.ADMIN: frozenset({ "dashboard", # Landing page "companies", # Platform admin core "vendors", # Platform admin core "admin-users", # Super admin core "settings", # Configuration access "my-menu", # Must be able to undo changes }), FrontendType.VENDOR: frozenset({ "dashboard", # Landing page "settings", # Configuration access }), } ``` ### Scope Types Menu configuration supports two scopes: | Scope | Field | Description | Use Case | |-------|-------|-------------|----------| | Platform | `platform_id` | Applies to all users on platform | Hide features not used by platform | | User | `user_id` | Applies to specific super admin | Personal preference customization | **Important Rules:** - Exactly one scope must be set (platform XOR user) - User scope is only allowed for admin frontend (super admins only) - Vendor frontend only supports platform scope ### Resolution Order **Admin Frontend:** ``` Platform admin → Check platform config → Fall back to default (all visible) Super admin → Check user config → Fall back to default (all visible) ``` **Vendor Frontend:** ``` Vendor → Check platform config → Fall back to default (all visible) ``` ## Database Model ### AdminMenuConfig Table ```sql CREATE TABLE admin_menu_configs ( id SERIAL PRIMARY KEY, frontend_type VARCHAR(10) NOT NULL, -- 'admin' or 'vendor' platform_id INTEGER REFERENCES platforms(id), user_id INTEGER REFERENCES users(id), menu_item_id VARCHAR(50) NOT NULL, is_visible BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMP, updated_at TIMESTAMP, -- Constraints CONSTRAINT uq_frontend_platform_menu_config UNIQUE (frontend_type, platform_id, menu_item_id), CONSTRAINT uq_frontend_user_menu_config UNIQUE (frontend_type, user_id, menu_item_id), CONSTRAINT ck_admin_menu_config_scope CHECK ((platform_id IS NOT NULL AND user_id IS NULL) OR (platform_id IS NULL AND user_id IS NOT NULL)), CONSTRAINT ck_user_scope_admin_only CHECK ((user_id IS NULL) OR (frontend_type = 'admin')) ); ``` ### Examples ```python # Platform "OMS" hides inventory from admin panel AdminMenuConfig( frontend_type=FrontendType.ADMIN, platform_id=1, menu_item_id="inventory", is_visible=False ) # Platform "OMS" hides letzshop from vendor dashboard AdminMenuConfig( frontend_type=FrontendType.VENDOR, platform_id=1, menu_item_id="letzshop", is_visible=False ) # Super admin "john" hides code-quality from their admin panel AdminMenuConfig( frontend_type=FrontendType.ADMIN, user_id=5, menu_item_id="code-quality", is_visible=False ) ``` ## Module Integration Menu items are associated with modules. When a module is disabled for a platform, its menu items are automatically hidden. ### Module to Menu Item Mapping Each module defines its menu items per frontend: ```python # In module definition billing_module = ModuleDefinition( code="billing", menu_items={ FrontendType.ADMIN: ["subscription-tiers", "subscriptions", "billing-history"], FrontendType.VENDOR: ["billing", "invoices"], }, ) ``` ### Menu Filtering Logic ```python from app.modules.service import module_service # Get available menu items (module-aware) available_items = module_service.get_module_menu_items( db, platform_id, FrontendType.ADMIN ) # Check if specific menu item's module is enabled is_available = module_service.is_menu_item_module_enabled( db, platform_id, "subscription-tiers", FrontendType.ADMIN ) ``` ### Three-Layer Filtering The final visible menu is computed by three layers: 1. **Registry**: All possible menu items 2. **Module Enablement**: Items from disabled modules are hidden 3. **Visibility Config**: Explicitly hidden items are removed ``` Final Menu = Registry Items - Items from Disabled Modules - Items with is_visible=False in config - Items not matching user role (super_admin_only) ``` ## Helper Functions ### Getting Menu Items ```python from app.config.menu_registry import ( get_all_menu_item_ids, get_menu_item, is_super_admin_only_item, ADMIN_MENU_REGISTRY, VENDOR_MENU_REGISTRY, ) # Get all item IDs for a frontend admin_items = get_all_menu_item_ids(FrontendType.ADMIN) # Returns: {'dashboard', 'admin-users', 'companies', ...} # Get specific item details item = get_menu_item(FrontendType.ADMIN, "subscription-tiers") # Returns: { # 'id': 'subscription-tiers', # 'label': 'Subscription Tiers', # 'icon': 'tag', # 'url': '/admin/subscription-tiers', # 'section_id': 'billing', # 'section_label': 'Billing & Subscriptions' # } # Check if item requires super admin is_restricted = is_super_admin_only_item("admin-users") # True ``` ### Menu Enums ```python from app.config.menu_registry import AdminMenuItem, VendorMenuItem # Use enums for type safety AdminMenuItem.DASHBOARD.value # "dashboard" AdminMenuItem.INVENTORY.value # "inventory" VendorMenuItem.PRODUCTS.value # "products" VendorMenuItem.ANALYTICS.value # "analytics" ``` ## Access Control ### Route Protection ```python from app.api.deps import require_menu_access @router.get("/admin/inventory") async def inventory_page( _access: bool = Depends(require_menu_access("inventory", FrontendType.ADMIN)) ): # Only accessible if menu item is visible for user's context pass ``` ### Sidebar Rendering The sidebar template filters items based on: 1. Module enablement 2. Visibility configuration 3. User role (super admin check) ```jinja2 {% for section in menu_sections %} {% if not section.super_admin_only or current_user.is_super_admin %} {% for item in section.items %} {% if item.id in visible_menu_items %} {{ item.label }} {% endif %} {% endfor %} {% endif %} {% endfor %} ``` ## UI for Menu Configuration ### Platform Admin Menu Config Located at `/admin/platform-menu-config` (accessible by super admins): - Configure which menu items are visible for platform admins - Configure which menu items are visible for vendors on this platform - Mandatory items cannot be unchecked ### Personal Menu Config (Super Admins) Located at `/admin/my-menu`: - Super admins can customize their own admin panel menu - Personal preferences that don't affect other users - Useful for hiding rarely-used features ## Best Practices ### Do - Use menu item enums (`AdminMenuItem`, `VendorMenuItem`) for type safety - Check module enablement before showing module-specific menu items - Respect mandatory items - don't try to hide them - Use `require_menu_access` dependency to protect routes ### Don't - Hardcode menu item IDs as strings (use enums) - Store `is_visible=True` in database (default state, wastes space) - Allow hiding mandatory items via API - Create menu items without registering them in the registry ## Related Documentation - [Module System](module-system.md) - Module architecture and menu item mapping - [Multi-Tenant System](multi-tenant.md) - Platform isolation - [Feature Gating](../implementation/feature-gating-system.md) - Tier-based access