- Added Alpine.js store for managing error toast state - Implemented server error toast notifications for 5xx errors - Replaced manual JavaScript error handling with Alpine.js implementation - Updated body tag to use HTMX response error event listener - Improved error display with better styling and user experience This change provides a consistent way to show server errors to users across the application using HTMX and Alpine.js, making error handling more maintainable and reusable.
111 lines
4.4 KiB
HTML
111 lines
4.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" data-theme="cupcake">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Email Organizer - Prototype{% endblock %}</title>
|
|
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
|
|
<script src="https://unpkg.com/htmx-ext-loading-states@2.0.0"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" defer></script>
|
|
<link rel="stylesheet" href="https://unpkg.com/tippy.js@6/dist/tippy.css" />
|
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.store('errorToast', {
|
|
show: false,
|
|
message: '',
|
|
hide() {
|
|
this.show = false;
|
|
},
|
|
handleError(detail) {
|
|
|
|
if (detail.xhr.status >= 500 && detail.xhr.status < 600) {
|
|
// Extract error message from response
|
|
let errorMessage = 'Server Error';
|
|
try {
|
|
const responseJson = JSON.parse(detail.xhr.response);
|
|
if (responseJson.error) {
|
|
errorMessage = responseJson.error;
|
|
}
|
|
} catch (e) {
|
|
// If not JSON, use the raw response
|
|
errorMessage = detail.xhr.response || 'Server Error';
|
|
}
|
|
|
|
// Set error message and show toast using Alpine store
|
|
this.message = errorMessage;
|
|
this.show = true;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<style>
|
|
@keyframes shake {
|
|
0%, 100% { transform: translateX(0); }
|
|
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
|
|
20%, 40%, 60%, 80% { transform: translateX(5px); }
|
|
}
|
|
.shake {
|
|
animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
|
|
}
|
|
|
|
.fade-in-htmx.htmx-added {
|
|
opacity: 0;
|
|
}
|
|
|
|
.htmx-added .fade-in-htmx {
|
|
opacity: 0;
|
|
}
|
|
|
|
.fade-in-htmx {
|
|
opacity: 1;
|
|
transition: opacity 300ms ease-out;
|
|
}
|
|
/* Fade out transition for HTMX */
|
|
.fade-out-htmx.htmx-swapping {
|
|
opacity: 0;
|
|
transition: opacity 280ms ease-out;
|
|
}
|
|
/* Fade out transition for HTMX */
|
|
.htmx-swapping .fade-out-htmx {
|
|
opacity: 0;
|
|
transition: opacity 280ms ease-out;
|
|
}
|
|
</style>
|
|
{% block head %}{% endblock %}
|
|
</head>
|
|
<body
|
|
x-on:htmx:response-error.camel="$store.errorToast.handleError($event.detail)"
|
|
class="min-h-screen flex flex-col" x-data="{}" x-on:open-modal.window="$refs.modal.showModal()"
|
|
x-on:close-modal.window="$refs.modal.close()" hx-ext="loading-states" data-loading-delay="200">
|
|
{% block header %}{% endblock %}
|
|
|
|
{% block content %}{% endblock %}
|
|
|
|
{% block modal %}{% endblock %}
|
|
|
|
<!-- Toast for 5xx errors -->
|
|
<div id="error-toast" class="toast toast-top toast-end w-full z-100" x-show="$store.errorToast.show" x-transition.duration.200ms>
|
|
<div class="alert alert-warning backdrop-blur-md bg-error/30 border border-error/20 relative">
|
|
|
|
<span id="error-message" x-text="$store.errorToast.message"></span>
|
|
<button class="btn btn-sm btn-ghost absolute top-2 right-2" @click="$store.errorToast.hide()">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
// Check for global variable to set X-Simulate-500 header
|
|
document.addEventListener('htmx:configRequest', function (evt) {
|
|
if (typeof window.simulate500 === 'boolean' && window.simulate500) {
|
|
evt.detail.headers['X-Simulate-500'] = 'true';
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |