## Vendor-in-Token Architecture (Complete Migration) - Migrate all vendor API endpoints from require_vendor_context() to token_vendor_id - Update permission dependencies to extract vendor from JWT token - Add vendor exceptions: VendorAccessDeniedException, VendorOwnerOnlyException, InsufficientVendorPermissionsException - Shop endpoints retain require_vendor_context() for URL-based detection - Add AUTH-004 architecture rule enforcing vendor context patterns - Fix marketplace router missing /marketplace prefix ## Exception Pattern Fixes (API-003/API-004) - Services raise domain exceptions, endpoints let them bubble up - Add code_quality and content_page exception modules - Move business logic from endpoints to services (admin, auth, content_page) - Fix exception handling in admin, shop, and vendor endpoints ## Tailwind CSS Consolidation - Consolidate CSS to per-area files (admin, vendor, shop, platform) - Remove shared/cdn-fallback.html and shared/css/tailwind.min.css - Update all templates to use area-specific Tailwind output files - Remove Node.js config (package.json, postcss.config.js, tailwind.config.js) ## Documentation & Cleanup - Update vendor-in-token-architecture.md with completed migration status - Update architecture-rules.md with new rules - Move migration docs to docs/development/migration/ - Remove duplicate/obsolete documentation files - Merge pytest.ini settings into pyproject.toml 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
9.2 KiB
Admin Sidebar Navigation
Overview
The admin sidebar provides navigation across all admin pages. It features collapsible sections with state persistence, active page indicators, and responsive mobile support.
File Location: app/templates/admin/partials/sidebar.html
Sidebar Structure
The sidebar is organized into the following sections:
| Section | Collapsible | Pages |
|---|---|---|
| Dashboard | No | Dashboard |
| Platform Administration | Yes | Companies, Vendors, Users, Customers, Marketplace |
| Content Management | Yes | Platform Homepage, Content Pages, Vendor Themes |
| Developer Tools | Yes | Components, Icons, Testing Hub, Code Quality |
| Platform Monitoring | Yes | Import Jobs, Application Logs |
| Settings | No | Settings |
Collapsible Sections
How It Works
Sections can be expanded/collapsed by clicking the section header. The state is persisted to localStorage so sections remain open/closed across page navigation and browser sessions.
State Management
File: static/admin/js/init-alpine.js
// Default state: Platform Administration open, others closed
const defaultSections = {
platformAdmin: true,
contentMgmt: false,
devTools: false,
monitoring: false
};
// State stored in localStorage under this key
const SIDEBAR_STORAGE_KEY = 'admin_sidebar_sections';
Available Methods
| Method | Description |
|---|---|
toggleSection(section) |
Toggle a section open/closed |
expandSectionForCurrentPage() |
Auto-expand section containing current page |
openSections.platformAdmin |
Check if Platform Administration is open |
openSections.contentMgmt |
Check if Content Management is open |
openSections.devTools |
Check if Developer Tools is open |
openSections.monitoring |
Check if Platform Monitoring is open |
CSS Transitions
Sections animate smoothly using CSS transitions (no plugins required):
<ul
x-show="openSections.platformAdmin"
x-transition:enter="transition-all duration-200 ease-out"
x-transition:enter-start="opacity-0 -translate-y-2"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition-all duration-150 ease-in"
x-transition:leave-start="opacity-100 translate-y-0"
x-transition:leave-end="opacity-0 -translate-y-2"
class="mt-1 overflow-hidden"
>
Chevron Icon Rotation
The chevron icon rotates 180 degrees when a section is expanded:
<span
x-html="$icon('chevron-down', 'w-4 h-4 transition-transform duration-200')"
:class="{ 'rotate-180': openSections.platformAdmin }"
></span>
Page-to-Section Mapping
Pages are mapped to their parent sections for auto-expansion:
const pageSectionMap = {
// Platform Administration
companies: 'platformAdmin',
vendors: 'platformAdmin',
users: 'platformAdmin',
customers: 'platformAdmin',
marketplace: 'platformAdmin',
// Content Management
'platform-homepage': 'contentMgmt',
'content-pages': 'contentMgmt',
'vendor-theme': 'contentMgmt',
// Developer Tools
components: 'devTools',
icons: 'devTools',
testing: 'devTools',
'code-quality': 'devTools',
// Platform Monitoring
imports: 'monitoring',
logs: 'monitoring'
};
Complete Page Mapping
| Page | currentPage Value |
Section | URL |
|---|---|---|---|
| Dashboard | 'dashboard' |
(always visible) | /admin/dashboard |
| Companies | 'companies' |
Platform Administration | /admin/companies |
| Vendors | 'vendors' |
Platform Administration | /admin/vendors |
| Users | 'users' |
Platform Administration | /admin/users |
| Customers | 'customers' |
Platform Administration | /admin/customers |
| Marketplace | 'marketplace' |
Platform Administration | /admin/marketplace |
| Platform Homepage | 'platform-homepage' |
Content Management | /admin/platform-homepage |
| Content Pages | 'content-pages' |
Content Management | /admin/content-pages |
| Vendor Themes | 'vendor-theme' |
Content Management | /admin/vendor-themes |
| Components | 'components' |
Developer Tools | /admin/components |
| Icons | 'icons' |
Developer Tools | /admin/icons |
| Testing Hub | 'testing' |
Developer Tools | /admin/testing |
| Code Quality | 'code-quality' |
Developer Tools | /admin/code-quality |
| Import Jobs | 'imports' |
Platform Monitoring | /admin/imports |
| Application Logs | 'logs' |
Platform Monitoring | /admin/logs |
| Settings | 'settings' |
(always visible) | /admin/settings |
Active Page Indicator
Each menu item shows a purple vertical bar when active:
<li class="relative px-6 py-3">
<!-- Purple bar indicator (shows when page is active) -->
<span x-show="currentPage === 'vendors'"
class="absolute inset-y-0 left-0 w-1 bg-purple-600 rounded-tr-lg rounded-br-lg"
aria-hidden="true"></span>
<!-- Link with active text styling -->
<a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 hover:text-gray-800 dark:hover:text-gray-200"
:class="currentPage === 'vendors' ? 'text-gray-800 dark:text-gray-100' : ''"
href="/admin/vendors">
<span x-html="$icon('shopping-bag')"></span>
<span class="ml-4">Vendors</span>
</a>
</li>
Setting currentPage in Components
Each page component must set currentPage to match the sidebar:
function adminVendors() {
return {
...data(), // Inherit base (includes openSections)
currentPage: 'vendors', // Must match sidebar check
// ... rest of component
};
}
Jinja2 Macros
The sidebar uses Jinja2 macros for DRY code:
section_header
Creates a collapsible section header with chevron:
{% macro section_header(title, section_key) %}
<button
@click="toggleSection('{{ section_key }}')"
class="flex items-center justify-between w-full px-6 py-2 ..."
>
<span>{{ title }}</span>
<span x-html="$icon('chevron-down', '...')"
:class="{ 'rotate-180': openSections.{{ section_key }} }"></span>
</button>
{% endmacro %}
section_content
Wraps section items with collapse animation:
{% macro section_content(section_key) %}
<ul x-show="openSections.{{ section_key }}" x-transition:...>
{{ caller() }}
</ul>
{% endmacro %}
menu_item
Creates a menu item with active indicator:
{% macro menu_item(page_id, url, icon, label) %}
<li class="relative px-6 py-3">
<span x-show="currentPage === '{{ page_id }}'" class="..."></span>
<a href="{{ url }}">
<span x-html="$icon('{{ icon }}')"></span>
<span class="ml-4">{{ label }}</span>
</a>
</li>
{% endmacro %}
Usage Example
{{ section_header('Platform Administration', 'platformAdmin') }}
{% call section_content('platformAdmin') %}
{{ menu_item('companies', '/admin/companies', 'office-building', 'Companies') }}
{{ menu_item('vendors', '/admin/vendors', 'shopping-bag', 'Vendors') }}
{% endcall %}
Adding a New Page
Step 1: Add Route
Add the route in app/routes/admin_pages.py:
@router.get("/new-page", response_class=HTMLResponse, include_in_schema=False)
async def admin_new_page(
request: Request,
current_user: User = Depends(get_current_admin_from_cookie_or_header),
):
return templates.TemplateResponse(
"admin/new-page.html",
{"request": request, "user": current_user},
)
Step 2: Create Template
Create app/templates/admin/new-page.html:
{% extends "admin/base.html" %}
{% block title %}New Page{% endblock %}
{% block alpine_data %}adminNewPage(){% endblock %}
{% block content %}
<!-- Your content -->
{% endblock %}
Step 3: Create JavaScript Component
Create static/admin/js/new-page.js:
function adminNewPage() {
return {
...data(),
currentPage: 'new-page', // Must match sidebar
// ...
};
}
Step 4: Add to Sidebar
Edit app/templates/admin/partials/sidebar.html:
{# Add to appropriate section #}
{{ menu_item('new-page', '/admin/new-page', 'icon-name', 'New Page') }}
Step 5: Update Page-Section Map (if in collapsible section)
Edit static/admin/js/init-alpine.js:
const pageSectionMap = {
// ... existing mappings
'new-page': 'platformAdmin', // Add mapping
};
Mobile Sidebar
The sidebar has a mobile version that slides in from the left:
- Toggle: Click hamburger menu in header
- Close: Click outside, press Escape, or navigate
- State: Controlled by
isSideMenuOpen
// In base data()
isSideMenuOpen: false,
toggleSideMenu() {
this.isSideMenuOpen = !this.isSideMenuOpen
},
closeSideMenu() {
this.isSideMenuOpen = false
}
Testing Checklist
- Sections expand/collapse on header click
- Chevron rotates when section opens/closes
- Section state persists after page reload
- Section state persists across different pages
- Active page shows purple bar indicator
- Active page text is highlighted
- Mobile sidebar opens/closes correctly
- Collapsible sections work on mobile
- All navigation links work correctly