ppt-tool/frontend/app/(presentation-generator)/services/api/api-error-handler.ts
Vadym Samoilenko cf21ba4516 Phase 1-2: Foundation + Admin Panel & Client Management
Phase 1 (Foundation):
- Project restructure (presenton-main → backend/ + frontend/)
- Database schema (8 new models, Alembic config, seed script)
- Auth (Azure AD SSO + dev bypass, JWT sessions, AuthMiddleware)
- RBAC (access_service, rbac_middleware, admin routers)
- Audit logging (fire-and-forget, AuditMiddleware, admin router)
- i18n (react-i18next with 5 namespace files)

Phase 2 (Admin Panel & Client Management):
- Admin panel shell (sidebar layout, role guard, 12 pages)
- Redux admin slice with 18 async thunks
- User management (role changes, deactivation)
- Client management (CRUD, brand config, team management)
- Brand config editor (colors, fonts, logos, voice rules)
- Master deck upload & parser (PPTX → HTML → React pipeline)
- Audit log viewer with filters and CSV/JSON export

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:37:17 +00:00

121 lines
No EOL
3.7 KiB
TypeScript

// API Error Response Interface
interface ApiErrorResponse {
detail?: string;
message?: string;
error?: string;
}
// API Response Handler Utility
export class ApiResponseHandler {
static async handleResponse(response: Response, defaultErrorMessage: string): Promise<any> {
// Handle successful responses
if (response.ok) {
// Handle 204 No Content responses
if (response.status === 204) {
return true;
}
// Try to parse JSON response
try {
return await response.json();
} catch (error) {
// If JSON parsing fails but response is ok, return empty object
return {};
}
}
// Handle error responses
let errorMessage = defaultErrorMessage;
try {
const errorData: ApiErrorResponse = await response.json();
// Extract error message in order of preference
if (errorData.detail) {
errorMessage = errorData.detail;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (errorData.error) {
errorMessage = errorData.error;
}
} catch (parseError) {
// If JSON parsing fails, use status-based messages
errorMessage = this.getStatusBasedErrorMessage(response.status, defaultErrorMessage);
}
// Throw error with appropriate message
throw new Error(errorMessage);
}
static async handleResponseWithResult(response: Response, defaultErrorMessage: string): Promise<{success: boolean, message?: string}> {
try {
// Handle successful responses
if (response.ok) {
return { success: true };
}
// Handle error responses
let errorMessage = defaultErrorMessage;
try {
const errorData: ApiErrorResponse = await response.json();
// Extract error message in order of preference
if (errorData.detail) {
errorMessage = errorData.detail;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (errorData.error) {
errorMessage = errorData.error;
}
} catch (parseError) {
// If JSON parsing fails, use status-based messages
errorMessage = this.getStatusBasedErrorMessage(response.status, defaultErrorMessage);
}
return {
success: false,
message: errorMessage,
};
} catch (error) {
return {
success: false,
message: error instanceof Error ? error.message : defaultErrorMessage,
};
}
}
private static getStatusBasedErrorMessage(status: number, defaultMessage: string): string {
switch (status) {
case 400:
return "Bad request. Please check your input and try again.";
case 401:
return "Unauthorized. Please log in and try again.";
case 403:
return "Access forbidden. You don't have permission to perform this action.";
case 404:
return "Resource not found. The requested item may have been deleted or moved.";
case 409:
return "Conflict. The resource already exists or there's a conflict with the current state.";
case 422:
return "Validation error. Please check your input and try again.";
case 429:
return "Too many requests. Please wait a moment and try again.";
case 500:
return "Internal server error. Please try again later.";
case 502:
return "Bad gateway. The server is temporarily unavailable.";
case 503:
return "Service unavailable. Please try again later.";
case 504:
return "Gateway timeout. The request took too long to process.";
default:
return defaultMessage;
}
}
}
export type { ApiErrorResponse };