migrating vendor frontend to new architecture

This commit is contained in:
2025-10-31 20:51:30 +01:00
parent 9420483ae6
commit 9611c03a36
25 changed files with 1618 additions and 286 deletions

View File

@@ -170,6 +170,97 @@ def setup_exception_handlers(app):
"""Handle all 404 errors with consistent format."""
logger.warning(f"404 Not Found: {request.method} {request.url}")
# Check if this is a browser request (wants HTML)
accept_header = request.headers.get("accept", "")
# Browser requests typically have "text/html" in accept header
# API requests typically have "application/json"
if "text/html" in accept_header:
# Return simple HTML 404 page for browser
from fastapi.responses import HTMLResponse
html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Page Not Found</title>
<style>
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
color: white;
}}
.container {{
text-align: center;
padding: 2rem;
max-width: 600px;
}}
h1 {{
font-size: 8rem;
font-weight: 700;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}}
h2 {{
font-size: 2rem;
font-weight: 400;
margin-bottom: 1rem;
}}
p {{
font-size: 1.2rem;
margin-bottom: 2rem;
opacity: 0.9;
}}
.btn {{
display: inline-block;
padding: 1rem 2.5rem;
background: white;
color: #667eea;
text-decoration: none;
border-radius: 50px;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}}
.btn:hover {{
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}}
.path {{
margin-top: 2rem;
font-size: 0.9rem;
opacity: 0.7;
font-family: 'Courier New', monospace;
word-break: break-all;
}}
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<h2>Page Not Found</h2>
<p>Sorry, the page you're looking for doesn't exist or has been moved.</p>
<a href="/" class="btn">Go Home</a>
<div class="path">Path: {request.url.path}</div>
</div>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=404)
# Return JSON for API requests
return JSONResponse(
status_code=404,
content={
@@ -250,4 +341,4 @@ def raise_auth_error(message: str = "Authentication failed") -> None:
def raise_permission_error(message: str = "Access denied") -> None:
"""Convenience function to raise AuthorizationException."""
from .base import AuthorizationException
raise AuthorizationException(message)
raise AuthorizationException(message)

View File

@@ -79,7 +79,7 @@ class UnauthorizedVendorAccessException(AuthorizationException):
class InvalidVendorDataException(ValidationException):
"""Raised when vendor data is invalid."""
"""Raised when vendor data is invalid or incomplete."""
def __init__(
self,
@@ -95,21 +95,6 @@ class InvalidVendorDataException(ValidationException):
self.error_code = "INVALID_VENDOR_DATA"
class MaxVendorsReachedException(BusinessLogicException):
"""Raised when user tries to create more vendors than allowed."""
def __init__(self, max_vendors: int, user_id: Optional[int] = None):
details = {"max_vendors": max_vendors}
if user_id:
details["user_id"] = user_id
super().__init__(
message=f"Maximum number of vendors reached ({max_vendors})",
error_code="MAX_VENDORS_REACHED",
details=details,
)
class VendorValidationException(ValidationException):
"""Raised when vendor validation fails."""
@@ -129,3 +114,36 @@ class VendorValidationException(ValidationException):
details=details,
)
self.error_code = "VENDOR_VALIDATION_FAILED"
class IncompleteVendorDataException(ValidationException):
"""Raised when vendor data is missing required fields."""
def __init__(
self,
vendor_code: str,
missing_fields: list,
):
super().__init__(
message=f"Vendor '{vendor_code}' is missing required fields: {', '.join(missing_fields)}",
details={
"vendor_code": vendor_code,
"missing_fields": missing_fields,
},
)
self.error_code = "INCOMPLETE_VENDOR_DATA"
class MaxVendorsReachedException(BusinessLogicException):
"""Raised when user tries to create more vendors than allowed."""
def __init__(self, max_vendors: int, user_id: Optional[int] = None):
details = {"max_vendors": max_vendors}
if user_id:
details["user_id"] = user_id
super().__init__(
message=f"Maximum number of vendors reached ({max_vendors})",
error_code="MAX_VENDORS_REACHED",
details=details,
)