feat: implement admin-users management with super admin restriction

- Add /admin/admin-users routes for managing admin users (super admin only)
- Remove vendor role from user creation form (vendors created via company hierarchy)
- Add admin-users.html and admin-user-detail.html templates
- Add admin-users.js and admin-user-detail.js for frontend logic
- Move database operations to admin_platform_service (list, get, create, delete, toggle status)
- Update sidebar to show Admin Users section only for super admins
- Add isSuperAdmin computed property to init-alpine.js
- Fix /api/v1 prefix issues in JS files (apiClient already adds prefix)
- Update architecture rule JS-012 to catch more variable patterns (url, endpoint, path)
- Replace inline SVGs with $icon() helper in select-platform.html

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 21:28:46 +01:00
parent 9d28210cf1
commit 7e68b93132
16 changed files with 1691 additions and 325 deletions

View File

@@ -22,7 +22,10 @@ Routes:
- GET /vendors/{vendor_code}/domains → Vendor domains management (auth required)
- GET /vendor-themes → Vendor themes selection page (auth required)
- GET /vendors/{vendor_code}/theme → Vendor theme editor (auth required)
- GET /users → User management page (auth required)
- GET /admin-users → Admin users management (super admin only)
- GET /admin-users/create → Create admin user (super admin only)
- GET /admin-users/{id} → Admin user detail (super admin only)
- GET /users → Redirects to /admin/admin-users
- GET /customers → Customer management page (auth required)
- GET /inventory → Inventory management page (auth required)
- GET /orders → Orders management page (auth required)
@@ -396,22 +399,28 @@ async def admin_vendor_theme_page(
# ============================================================================
# USER MANAGEMENT ROUTES
# ADMIN USER MANAGEMENT ROUTES (Super Admin Only)
# ============================================================================
@router.get("/users", response_class=HTMLResponse, include_in_schema=False)
async def admin_users_page(
@router.get("/admin-users", response_class=HTMLResponse, include_in_schema=False)
async def admin_users_list_page(
request: Request,
current_user: User = Depends(get_current_admin_from_cookie_or_header),
db: Session = Depends(get_db),
):
"""
Render users management page.
Shows list of all platform users.
Render admin users management page.
Shows list of all admin users (super admins and platform admins).
Super admin only.
"""
from fastapi import HTTPException
if not current_user.is_super_admin:
raise HTTPException(status_code=403, detail="Super admin access required")
return templates.TemplateResponse(
"admin/users.html",
"admin/admin-users.html",
{
"request": request,
"user": current_user,
@@ -419,15 +428,21 @@ async def admin_users_page(
)
@router.get("/users/create", response_class=HTMLResponse, include_in_schema=False)
@router.get("/admin-users/create", response_class=HTMLResponse, include_in_schema=False)
async def admin_user_create_page(
request: Request,
current_user: User = Depends(get_current_admin_from_cookie_or_header),
db: Session = Depends(get_db),
):
"""
Render user creation form.
Render admin user creation form.
Super admin only.
"""
from fastapi import HTTPException
if not current_user.is_super_admin:
raise HTTPException(status_code=403, detail="Super admin access required")
return templates.TemplateResponse(
"admin/user-create.html",
{
@@ -437,7 +452,9 @@ async def admin_user_create_page(
)
@router.get("/users/{user_id}", response_class=HTMLResponse, include_in_schema=False)
@router.get(
"/admin-users/{user_id}", response_class=HTMLResponse, include_in_schema=False
)
async def admin_user_detail_page(
request: Request,
user_id: int = Path(..., description="User ID"),
@@ -445,10 +462,16 @@ async def admin_user_detail_page(
db: Session = Depends(get_db),
):
"""
Render user detail view.
Render admin user detail view.
Super admin only.
"""
from fastapi import HTTPException
if not current_user.is_super_admin:
raise HTTPException(status_code=403, detail="Super admin access required")
return templates.TemplateResponse(
"admin/user-detail.html",
"admin/admin-user-detail.html",
{
"request": request,
"user": current_user,
@@ -458,7 +481,7 @@ async def admin_user_detail_page(
@router.get(
"/users/{user_id}/edit", response_class=HTMLResponse, include_in_schema=False
"/admin-users/{user_id}/edit", response_class=HTMLResponse, include_in_schema=False
)
async def admin_user_edit_page(
request: Request,
@@ -467,8 +490,14 @@ async def admin_user_edit_page(
db: Session = Depends(get_db),
):
"""
Render user edit form.
Render admin user edit form.
Super admin only.
"""
from fastapi import HTTPException
if not current_user.is_super_admin:
raise HTTPException(status_code=403, detail="Super admin access required")
return templates.TemplateResponse(
"admin/user-edit.html",
{
@@ -479,6 +508,47 @@ async def admin_user_edit_page(
)
# ============================================================================
# USER MANAGEMENT ROUTES (Legacy - Redirects)
# ============================================================================
@router.get("/users", response_class=RedirectResponse, include_in_schema=False)
async def admin_users_page_redirect():
"""
Redirect old /admin/users to /admin/admin-users.
"""
return RedirectResponse(url="/admin/admin-users", status_code=302)
@router.get("/users/create", response_class=RedirectResponse, include_in_schema=False)
async def admin_user_create_page_redirect():
"""
Redirect old /admin/users/create to /admin/admin-users/create.
"""
return RedirectResponse(url="/admin/admin-users/create", status_code=302)
@router.get(
"/users/{user_id}", response_class=RedirectResponse, include_in_schema=False
)
async def admin_user_detail_page_redirect(user_id: int = Path(..., description="User ID")):
"""
Redirect old /admin/users/{id} to /admin/admin-users/{id}.
"""
return RedirectResponse(url=f"/admin/admin-users/{user_id}", status_code=302)
@router.get(
"/users/{user_id}/edit", response_class=RedirectResponse, include_in_schema=False
)
async def admin_user_edit_page_redirect(user_id: int = Path(..., description="User ID")):
"""
Redirect old /admin/users/{id}/edit to /admin/admin-users/{id}/edit.
"""
return RedirectResponse(url=f"/admin/admin-users/{user_id}/edit", status_code=302)
# ============================================================================
# CUSTOMER MANAGEMENT ROUTES
# ============================================================================