632 lines
14 KiB
Plaintext
632 lines
14 KiB
Plaintext
package layouts
|
|
|
|
// Base provides the basic HTML structure for all pages
|
|
templ Base(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 rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
|
|
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
|
|
|
|
|
<style>
|
|
body {
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
/* Storage meter styles */
|
|
.storage-meter {
|
|
height: 12px;
|
|
background-color: var(--gruvbox-bg2);
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
margin: 0.25rem 0;
|
|
}
|
|
|
|
.storage-meter.small {
|
|
height: 6px;
|
|
margin: 0.15rem 0;
|
|
}
|
|
|
|
.storage-progress {
|
|
height: 100%;
|
|
background-color: var(--gruvbox-blue);
|
|
border-radius: 6px;
|
|
transition: width 0.5s ease;
|
|
}
|
|
|
|
/* Icon styles */
|
|
.storage-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 8px;
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.status-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.status-icon.error {
|
|
color: var(--gruvbox-red);
|
|
}
|
|
|
|
.status-icon.warning {
|
|
color: var(--gruvbox-yellow);
|
|
}
|
|
|
|
.status-icon.success {
|
|
color: var(--gruvbox-green);
|
|
}
|
|
|
|
/* Backup tool tags */
|
|
.backup-tool-tag {
|
|
display: inline-block;
|
|
padding: 0.15rem 0.5rem;
|
|
border-radius: 4px;
|
|
font-size: 0.8rem;
|
|
margin-right: 0.5rem;
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
/* Chart styles */
|
|
.donut-chart {
|
|
width: 120px;
|
|
height: 120px;
|
|
}
|
|
|
|
/* Gruvbox Material Dark Theme for Backea */
|
|
:root {
|
|
/* Gruvbox Hard Dark Palette */
|
|
--gruvbox-bg-hard: #1d2021;
|
|
--gruvbox-bg0: #282828;
|
|
--gruvbox-bg1: #3c3836;
|
|
--gruvbox-bg2: #504945;
|
|
--gruvbox-bg3: #665c54;
|
|
--gruvbox-bg4: #7c6f64;
|
|
|
|
--gruvbox-fg: #fbf1c7;
|
|
--gruvbox-fg-dim: #d5c4a1;
|
|
|
|
--gruvbox-red: #fb4934;
|
|
--gruvbox-green: #b8bb26;
|
|
--gruvbox-yellow: #fabd2f;
|
|
--gruvbox-blue: #83a598;
|
|
--gruvbox-purple: #d3869b;
|
|
--gruvbox-aqua: #8ec07c;
|
|
--gruvbox-orange: #fe8019;
|
|
--gruvbox-gray: #928374;
|
|
}
|
|
/* Base dark mode styles */
|
|
.gruvbox-dark {
|
|
background-color: var(--gruvbox-bg-hard);
|
|
color: var(--gruvbox-fg);
|
|
min-height: 100vh;
|
|
}
|
|
/* Background colors */
|
|
.gruvbox-bg-hard { background-color: var(--gruvbox-bg-hard); }
|
|
.gruvbox-bg0 { background-color: var(--gruvbox-bg0); }
|
|
.gruvbox-bg1 { background-color: var(--gruvbox-bg1); }
|
|
.gruvbox-bg2 { background-color: var(--gruvbox-bg2); }
|
|
/* Text colors */
|
|
.gruvbox-fg { color: var(--gruvbox-fg); }
|
|
.gruvbox-red { color: var(--gruvbox-red); }
|
|
.gruvbox-green { color: var(--gruvbox-green); }
|
|
.gruvbox-yellow { color: var(--gruvbox-yellow); }
|
|
.gruvbox-blue { color: var(--gruvbox-blue); }
|
|
.gruvbox-purple { color: var(--gruvbox-purple); }
|
|
.gruvbox-aqua { color: var(--gruvbox-aqua); }
|
|
.gruvbox-orange { color: var(--gruvbox-orange); }
|
|
.gruvbox-gray { color: var(--gruvbox-gray); }
|
|
/* Border colors */
|
|
.gruvbox-red-border { border-color: var(--gruvbox-red); }
|
|
.gruvbox-green-border { border-color: var(--gruvbox-green); }
|
|
.gruvbox-yellow-border { border-color: var(--gruvbox-yellow); }
|
|
.gruvbox-blue-border { border-color: var(--gruvbox-blue); }
|
|
.gruvbox-purple-border { border-color: var(--gruvbox-purple); }
|
|
.gruvbox-aqua-border { border-color: var(--gruvbox-aqua); }
|
|
/* Base dark mode styles */
|
|
html, body {
|
|
background-color: var(--gruvbox-bg-hard);
|
|
color: var(--gruvbox-fg);
|
|
margin: 0;
|
|
padding: 0;
|
|
min-height: 100vh;
|
|
width: 100%;
|
|
}
|
|
/* Table Styles */
|
|
.gruvbox-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
border-color: var(--gruvbox-bg3);
|
|
}
|
|
.gruvbox-table th {
|
|
font-weight: bold;
|
|
border-bottom: 1px solid var(--gruvbox-bg3);
|
|
}
|
|
.gruvbox-table tr {
|
|
border-bottom: 1px solid var(--gruvbox-bg2);
|
|
}
|
|
.gruvbox-table td, .gruvbox-table th {
|
|
padding: 0.5rem;
|
|
}
|
|
/* Link styles */
|
|
.gruvbox-link {
|
|
color: var(--gruvbox-blue);
|
|
text-decoration: none;
|
|
transition: color 0.2s ease;
|
|
}
|
|
.gruvbox-link:hover {
|
|
color: var(--gruvbox-aqua);
|
|
text-decoration: underline;
|
|
}
|
|
/* Warning container */
|
|
.gruvbox-warning {
|
|
background-color: var(--gruvbox-bg1);
|
|
border: 1px solid var(--gruvbox-yellow);
|
|
color: var(--gruvbox-yellow);
|
|
padding: 1rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
/* Add Beer CSS compatibility for spacing and layout classes */
|
|
.responsive {
|
|
width: 100%;
|
|
padding: 1rem;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
.round {
|
|
border-radius: 0.25rem;
|
|
}
|
|
.padding {
|
|
padding: 1rem;
|
|
}
|
|
.padding-small {
|
|
padding: 0.5rem;
|
|
}
|
|
.margin-bottom {
|
|
margin-bottom: 1rem;
|
|
}
|
|
.margin-bottom-large {
|
|
margin-bottom: 2rem;
|
|
}
|
|
.margin-top {
|
|
margin-top: 1rem;
|
|
}
|
|
.margin-top-small {
|
|
margin-top: 0.5rem;
|
|
}
|
|
.border {
|
|
border: 1px solid;
|
|
}
|
|
.border-bottom {
|
|
border-bottom: 1px solid var(--gruvbox-bg3);
|
|
}
|
|
.left-align {
|
|
text-align: left;
|
|
}
|
|
.right-align {
|
|
text-align: right;
|
|
}
|
|
.center-align {
|
|
text-align: center;
|
|
}
|
|
.overflow {
|
|
overflow-x: auto;
|
|
}
|
|
.extra-large {
|
|
font-size: 2rem;
|
|
}
|
|
.large {
|
|
font-size: 1.5rem;
|
|
}
|
|
.medium {
|
|
font-weight: 500;
|
|
}
|
|
/* Skeleton loading styles */
|
|
.skeleton-table {
|
|
max-width: 95%;
|
|
border-collapse: collapse;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.skeleton-header {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 8px;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(168, 153, 132, 0.3);
|
|
}
|
|
|
|
.skeleton-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 8px;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(168, 153, 132, 0.1);
|
|
}
|
|
|
|
.skeleton-cell {
|
|
height: 1.2rem;
|
|
background: linear-gradient(90deg, rgba(168, 153, 132, 0.1) 25%, rgba(168, 153, 132, 0.2) 50%, rgba(168, 153, 132, 0.1) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s infinite;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% {
|
|
background-position: 200% 0;
|
|
}
|
|
100% {
|
|
background-position: -200% 0;
|
|
}
|
|
}
|
|
|
|
/* Loading indicator */
|
|
.loading-spinner {
|
|
width: 30px;
|
|
height: 30px;
|
|
margin: 20px auto;
|
|
border: 3px solid rgba(168, 153, 132, 0.3);
|
|
border-top: 3px solid #b8bb26; /* gruvbox green */
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.htmx-indicator {
|
|
display: none;
|
|
}
|
|
|
|
.htmx-request .htmx-indicator {
|
|
display: block;
|
|
}
|
|
|
|
.htmx-request.htmx-indicator {
|
|
display: block;
|
|
}
|
|
|
|
/* Improve spacing in tables */
|
|
.overflow {
|
|
overflow-x: auto;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.table-skeleton {
|
|
background-color: var(--gruvbox-bg1);
|
|
border-radius: 4px;
|
|
padding: 16px;
|
|
}
|
|
|
|
/* Fade in animation for loaded content */
|
|
.group-backups-table {
|
|
animation: fadeIn 0.3s ease-in-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
.skeleton-text {
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: linear-gradient(90deg, rgba(168, 153, 132, 0.1) 25%, rgba(168, 153, 132, 0.2) 50%, rgba(168, 153, 132, 0.1) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s infinite;
|
|
border-radius: 4px;
|
|
color: transparent !important;
|
|
width: 40px;
|
|
display: inline-block;
|
|
}
|
|
|
|
/* Skeleton table styles remain the same */
|
|
.skeleton-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.skeleton-header {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 8px;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(168, 153, 132, 0.3);
|
|
}
|
|
|
|
.skeleton-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(6, 1fr);
|
|
gap: 8px;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(168, 153, 132, 0.1);
|
|
}
|
|
|
|
.skeleton-cell {
|
|
height: 1.2rem;
|
|
background: linear-gradient(90deg, rgba(168, 153, 132, 0.1) 25%, rgba(168, 153, 132, 0.2) 50%, rgba(168, 153, 132, 0.1) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s infinite;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% {
|
|
background-position: 200% 0;
|
|
}
|
|
100% {
|
|
background-position: -200% 0;
|
|
}
|
|
}
|
|
|
|
/* Loading indicator */
|
|
.loading-spinner {
|
|
width: 30px;
|
|
height: 30px;
|
|
margin: 20px auto;
|
|
border: 3px solid rgba(168, 153, 132, 0.3);
|
|
border-top: 3px solid #b8bb26; /* gruvbox green */
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.htmx-indicator {
|
|
display: none;
|
|
}
|
|
|
|
.htmx-request .htmx-indicator {
|
|
display: block;
|
|
}
|
|
|
|
.htmx-request.htmx-indicator {
|
|
display: block;
|
|
}
|
|
|
|
/* Entry animation for both header and content */
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.group-backups-table,
|
|
#group-header-* {
|
|
animation: fadeIn 0.3s ease-out;
|
|
}
|
|
|
|
/* Button styles */
|
|
.gruvbox-button {
|
|
display: inline-block;
|
|
padding: 0.5rem 1rem;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: var(--gruvbox-fg);
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
font-size: 0.9rem;
|
|
text-decoration: none;
|
|
transition: background-color 0.2s ease, transform 0.1s ease;
|
|
}
|
|
|
|
.gruvbox-button:hover {
|
|
background-color: var(--gruvbox-bg3);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.gruvbox-button:active {
|
|
transform: translateY(1px);
|
|
}
|
|
|
|
/* Inline spinner for buttons */
|
|
.inline-spinner {
|
|
width: 16px;
|
|
height: 16px;
|
|
margin-left: 8px;
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/* Animation for table transitions */
|
|
.group-backups-table {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.htmx-swapping {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Make the table header sticky */
|
|
.gruvbox-table thead {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
background-color: var(--gruvbox-bg1);
|
|
}
|
|
|
|
/* Action buttons styling */
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 8px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.action-button {
|
|
font-size: 0.8rem;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
text-align: center;
|
|
border: none;
|
|
transition: background-color 0.15s ease, transform 0.1s ease;
|
|
}
|
|
|
|
.restore-button {
|
|
background-color: var(--gruvbox-blue);
|
|
color: var(--gruvbox-bg-hard);
|
|
}
|
|
|
|
.download-button {
|
|
background-color: var(--gruvbox-green);
|
|
color: var(--gruvbox-bg-hard);
|
|
}
|
|
|
|
.action-button:hover {
|
|
transform: translateY(-1px);
|
|
filter: brightness(1.1);
|
|
}
|
|
|
|
.action-button:active {
|
|
transform: translateY(1px);
|
|
}
|
|
|
|
/* Make sure the table has enough width */
|
|
.gruvbox-table {
|
|
width: 100%;
|
|
table-layout: auto;
|
|
}
|
|
|
|
/* Ensure columns have appropriate widths */
|
|
.gruvbox-table th:last-child,
|
|
.gruvbox-table td:last-child {
|
|
width: 1%;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Responsive adjustments for small screens */
|
|
@media (max-width: 768px) {
|
|
.action-buttons {
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.action-button {
|
|
font-size: 0.7rem;
|
|
padding: 0.2rem 0.4rem;
|
|
}
|
|
}
|
|
/* Add these styles to your CSS file */
|
|
|
|
.restore-modal {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 90%;
|
|
max-width: 500px;
|
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
|
|
z-index: 1000;
|
|
padding: 2rem;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.restore-modal:before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: -1;
|
|
}
|
|
|
|
.backup-details {
|
|
margin-bottom: 1rem;
|
|
padding: 0.5rem;
|
|
background: rgba(40, 40, 40, 0.4);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.gruvbox-input {
|
|
width: 100%;
|
|
padding: 0.5rem;
|
|
background-color: #1d2021;
|
|
border: 1px solid #3c3836;
|
|
color: #ebdbb2;
|
|
border-radius: 4px;
|
|
font-family: inherit;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.gruvbox-input:focus {
|
|
outline: none;
|
|
border-color: #fabd2f;
|
|
}
|
|
|
|
.form-buttons {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 1.5rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
|
|
<body class="gruvbox-bg0 gruvbox-fg">
|
|
<header class="gruvbox-bg-hard padding">
|
|
<div class="responsive">
|
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
<a href="/" class="gruvbox-yellow no-underline">
|
|
<h1 class="large">Backea</h1>
|
|
</a>
|
|
<nav>
|
|
<ul style="display: flex; gap: 1rem;">
|
|
<li><a href="/" class="gruvbox-link">Home</a></li>
|
|
<li><a href="/about" class="gruvbox-link">About</a></li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Notification area for displaying feedback -->
|
|
<div id="notification-area"></div>
|
|
|
|
<main class="padding">
|
|
{ children... }
|
|
</main>
|
|
|
|
<footer class="gruvbox-bg-hard padding margin-top">
|
|
<div class="responsive">
|
|
<p class="text-center">
|
|
<small class="gruvbox-fg-dim">Backea - Unified Backup Dashboard</small>
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
|
|
</html>
|
|
} |