108 lines
2.9 KiB
Text
108 lines
2.9 KiB
Text
package templates
|
|
|
|
import "io"
|
|
import "context"
|
|
|
|
// Render renders a component to an io.Writer
|
|
func Render(w io.Writer, component templ.Component) error {
|
|
return component.Render(context.Background(), w)
|
|
}
|
|
|
|
// Layout is the base layout for all pages
|
|
templ Layout(title string) {
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<title>{ title }</title>
|
|
<link
|
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
|
rel="stylesheet"
|
|
/>
|
|
<link
|
|
rel="stylesheet"
|
|
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
|
|
/>
|
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
<style>
|
|
body {
|
|
padding-top: 20px;
|
|
background-color: #f8f9fa;
|
|
}
|
|
.card {
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
.config-warning {
|
|
margin-bottom: 20px;
|
|
}
|
|
.toast-container {
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 1050;
|
|
}
|
|
.update-badge {
|
|
font-size: 0.8em;
|
|
margin-left: 10px;
|
|
}
|
|
.htmx-indicator {
|
|
opacity: 0;
|
|
transition: opacity 300ms ease-in;
|
|
}
|
|
.htmx-request .htmx-indicator {
|
|
opacity: 1;
|
|
}
|
|
.htmx-request.htmx-indicator {
|
|
opacity: 1;
|
|
}
|
|
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
{ children... }
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// Simple toast function for HTMX responses
|
|
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
|
const xhr = evt.detail.xhr;
|
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
const successMessage = xhr.getResponseHeader('HX-Success-Message');
|
|
if (successMessage) {
|
|
showToast(successMessage, 'success');
|
|
}
|
|
} else {
|
|
const errorMessage = xhr.getResponseHeader('HX-Error-Message') || 'An error occurred';
|
|
showToast(errorMessage, 'danger');
|
|
}
|
|
});
|
|
|
|
function showToast(message, type = "success") {
|
|
const toastContainer = document.querySelector(".toast-container");
|
|
const toast = document.createElement("div");
|
|
toast.className = `toast align-items-center text-white bg-${type}`;
|
|
toast.setAttribute("role", "alert");
|
|
toast.setAttribute("aria-live", "assertive");
|
|
toast.setAttribute("aria-atomic", "true");
|
|
|
|
toast.innerHTML = `
|
|
<div class="d-flex">
|
|
<div class="toast-body">${message}</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
`;
|
|
|
|
toastContainer.appendChild(toast);
|
|
const bsToast = new bootstrap.Toast(toast);
|
|
bsToast.show();
|
|
|
|
toast.addEventListener("hidden.bs.toast", () => {
|
|
toast.remove();
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
}
|