Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
18 KiB
Menu Management Architecture
The Orion platform provides a module-driven menu system where each module defines its own menu items. The MenuDiscoveryService aggregates menus from all enabled modules, applying visibility configuration and permission filtering.
Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ MODULE DEFINITIONS (Source of Truth) │
│ app/modules/*/definition.py │
│ │
│ Each module defines its menu items per FrontendType: │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ catalog.definition.py │ │ orders.definition.py │ │
│ │ menus={ADMIN: [...], │ │ menus={ADMIN: [...], │ │
│ │ STORE: [...]} │ │ STORE: [...]} │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ MENU DISCOVERY SERVICE │
│ app/modules/core/services/menu_discovery_service.py │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 1. Collect menu items from all enabled modules │ │
│ │ 2. Filter by user permissions (super_admin_only) │ │
│ │ 3. Apply visibility overrides (AdminMenuConfig) │ │
│ │ 4. Sort by section/item order │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ VISIBILITY CONFIGURATION │
│ app/modules/core/models/admin_menu_config.py │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ AdminMenuConfig Table │ │
│ │ Stores visibility overrides (hidden items only) │ │
│ │ - Platform scope: applies to platform admins/stores │ │
│ │ - User scope: applies to specific super admin │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ FILTERED MENU OUTPUT │
│ │
│ Module Menus - Disabled Modules - Hidden Items = Visible Menu │
└─────────────────────────────────────────────────────────────────────────┘
Frontend Types
The system supports four distinct frontend types:
| Frontend | Description | Users |
|---|---|---|
PLATFORM |
Public marketing pages | Unauthenticated visitors |
ADMIN |
Admin panel | Super admins, platform admins |
STORE |
Store dashboard | Stores on a platform |
STOREFRONT |
Customer-facing shop | Shop customers |
from app.modules.enums import FrontendType
# Use in code
FrontendType.PLATFORM # "platform"
FrontendType.ADMIN # "admin"
FrontendType.STORE # "store"
FrontendType.STOREFRONT # "storefront"
Module-Driven Menus
Each module defines its menu items in definition.py using dataclasses:
Menu Item Definition
# app/modules/base.py
@dataclass
class MenuItemDefinition:
"""Single menu item definition."""
id: str # Unique identifier (e.g., "catalog.products")
label_key: str # i18n key for label
icon: str # Lucide icon name
route: str # URL path
order: int = 100 # Sort order within section
is_mandatory: bool = False # Cannot be hidden by user
is_super_admin_only: bool = False # Only visible to super admins
@dataclass
class MenuSectionDefinition:
"""Section containing menu items."""
id: str # Section identifier
label_key: str # i18n key for section label
icon: str # Section icon
order: int = 100 # Sort order
items: list[MenuItemDefinition] = field(default_factory=list)
is_super_admin_only: bool = False
Example Module Definition
# app/modules/catalog/definition.py
from app.modules.base import ModuleDefinition, MenuSectionDefinition, MenuItemDefinition
from app.modules.enums import FrontendType
catalog_module = ModuleDefinition(
code="catalog",
name="Product Catalog",
description="Product and category management",
version="1.0.0",
is_core=True,
menus={
FrontendType.ADMIN: [
MenuSectionDefinition(
id="catalog",
label_key="menu.catalog",
icon="package",
order=30,
items=[
MenuItemDefinition(
id="products",
label_key="menu.products",
icon="box",
route="/admin/products",
order=10,
is_mandatory=True
),
MenuItemDefinition(
id="categories",
label_key="menu.categories",
icon="folder-tree",
route="/admin/categories",
order=20
),
]
)
],
FrontendType.STORE: [
MenuSectionDefinition(
id="products",
label_key="menu.my_products",
icon="package",
order=10,
items=[
MenuItemDefinition(
id="products",
label_key="menu.products",
icon="box",
route="/store/{store_code}/products",
order=10,
is_mandatory=True
),
]
)
],
}
)
Menu Discovery Service
The MenuDiscoveryService aggregates menus from all enabled modules:
from app.modules.core.services.menu_discovery_service import menu_discovery_service
# Get menu for a frontend type
sections = menu_discovery_service.get_menu_for_frontend(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=1,
user_id=current_user.id,
is_super_admin=current_user.is_super_admin,
)
# Get all menu items for configuration UI
all_items = menu_discovery_service.get_all_menu_items(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=1,
)
Discovery Flow
- Collect: Get menu definitions from all modules in
MODULESregistry - Filter by Module: Only include menus from enabled modules for the platform
- Filter by Permissions: Remove
super_admin_onlyitems for non-super admins - Apply Visibility: Check
AdminMenuConfigfor hidden items - Sort: Order sections and items by their
orderfield - Return: List of
MenuSectionDefinitionwith filtered items
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. These are marked with is_mandatory=True in their definition:
MenuItemDefinition(
id="dashboard",
label_key="menu.dashboard",
icon="home",
route="/admin/dashboard",
is_mandatory=True, # Cannot be hidden
)
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)
- Store 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)
Store Frontend:
Store → Check platform config → Fall back to default (all visible)
Database Model
AdminMenuConfig Table
CREATE TABLE admin_menu_configs (
id SERIAL PRIMARY KEY,
frontend_type VARCHAR(10) NOT NULL, -- 'admin' or 'store'
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
from app.modules.core.models import AdminMenuConfig
from app.modules.enums import FrontendType
# 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 store dashboard
AdminMenuConfig(
frontend_type=FrontendType.STORE,
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
Automatic Menu Discovery
When a module is enabled/disabled for a platform, the menu discovery service automatically includes/excludes its menu items:
# Module enablement check happens automatically in MenuDiscoveryService
enabled_modules = module_service.get_enabled_module_codes(db, platform_id)
# Only modules in enabled_modules have their menus included
for module_code, module_def in MODULES.items():
if module_code in enabled_modules:
# Include this module's menus
pass
Three-Layer Filtering
The final visible menu is computed by three layers:
- Module Definitions: All possible menu items from modules
- Module Enablement: Items from disabled modules are hidden
- Visibility Config: Explicitly hidden items are removed
Final Menu = Module Menu Items
- Items from Disabled Modules
- Items with is_visible=False in config
- Items not matching user role (super_admin_only)
Menu Service Integration
The MenuService provides the interface used by templates:
from app.modules.core.services import menu_service
# Get menu for rendering in templates
menu_data = menu_service.get_menu_for_rendering(
db=db,
frontend_type=FrontendType.ADMIN,
platform_id=platform_id,
user_id=user_id,
is_super_admin=is_super_admin,
store_code=store_code, # For store frontend
)
# Returns legacy format for template compatibility:
# {
# "frontend_type": "admin",
# "sections": [
# {
# "id": "main",
# "label": None,
# "items": [{"id": "dashboard", "label": "Dashboard", ...}]
# },
# ...
# ]
# }
Access Control
Route Protection
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:
- Module enablement
- Visibility configuration
- User role (super admin check)
{% 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 %}
<a href="{{ item.url }}">{{ item.label }}</a>
{% 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 stores 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
Adding Menu Items to a Module
- Define menu sections and items in your module's
definition.py:
# app/modules/mymodule/definition.py
from app.modules.base import ModuleDefinition, MenuSectionDefinition, MenuItemDefinition
from app.modules.enums import FrontendType
mymodule = ModuleDefinition(
code="mymodule",
name="My Module",
description="Description of my module",
version="1.0.0",
menus={
FrontendType.ADMIN: [
MenuSectionDefinition(
id="mymodule",
label_key="mymodule.menu.section",
icon="star",
order=50, # Position in sidebar
items=[
MenuItemDefinition(
id="mymodule-main",
label_key="mymodule.menu.main",
icon="star",
route="/admin/mymodule",
order=10,
),
]
)
],
}
)
- Add translation keys in your module's locales:
// app/modules/mymodule/locales/en.json
{
"mymodule.menu.section": "My Module",
"mymodule.menu.main": "Main Page"
}
- The menu is automatically discovered - no registration needed.
Best Practices
Do
- Define menus in module
definition.pyusing dataclasses - Use translation keys (
label_key) for labels - Set appropriate
ordervalues for positioning - Mark essential items as
is_mandatory=True - Use
is_super_admin_only=Truefor admin-only features
Don't
- Hardcode menu item labels (use i18n keys)
- Store
is_visible=Truein database (default state, wastes space) - Allow hiding mandatory items via API
- Create menu items outside of module definitions
Related Documentation
- Module System - Module architecture and definitions
- Multi-Tenant System - Platform isolation
- Feature Gating - Tier-based access