backend-server-v2/web/templates/layout.html

273 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.Title}}</title>
<script>
window.__TC_IS_AUTHENTICATED = {{if .CurrentUser}}true{{else}}false{{end}};
window.__TC_SERVER_THEME = {{printf "%q" .UserTheme}};
</script>
<script src="/static/vendor/theme.js?v={{.BuildHash}}"></script>
<link rel="stylesheet" href="/static/css/app.css?v={{.BuildHash}}">
<script src="/static/vendor/htmx.min.js"></script>
</head>
<body class="flex min-h-screen flex-col bg-white text-gray-900 antialiased dark:bg-gray-900 dark:text-gray-100">
{{template "navbar" .}}
<main class="mx-auto w-full max-w-7xl flex-1 p-6">
{{template "_flash.html" .}}
{{template "content" .}}
</main>
<footer class="border-t border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">
<div class="mx-auto flex max-w-7xl items-center justify-between gap-3 px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
<span>Trustcontact</span>
<button id="themeToggle" type="button" aria-label="Toggle dark mode" aria-pressed="false" onclick="window.toggleTheme && window.toggleTheme()" class="rounded-lg bg-gray-100 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700">
Dark mode
</button>
</div>
</footer>
<script>
(function () {
var DEFAULT_LANG = 'it';
var STORAGE_KEY = 'tc_lang';
var SERVER_LANG = {{printf "%q" .UserLang}};
var IS_AUTHENTICATED = {{if .CurrentUser}}true{{else}}false{{end}};
var dictionaries = {
it: {
'nav.open_main_menu': 'Apri menu principale', 'nav.open_user_menu': 'Apri menu utente', 'nav.dashboard': 'Dashboard', 'nav.users': 'Users', 'nav.admin': 'Admin', 'nav.login': 'Login', 'nav.signup': 'Signup', 'nav.logout': 'Logout',
'home.subtitle': 'Accedi o registrati per continuare.', 'home.login': 'Accedi', 'home.signup': 'Registrati',
'login.title': 'Login', 'login.subtitle': 'Accedi al tuo account.', 'form.email': 'Email', 'form.password': 'Password', 'login.submit': 'Entra', 'login.forgot': 'Password dimenticata?', 'login.create_account': 'Crea account',
'signup.title': 'Registrazione', 'signup.subtitle': 'Crea il tuo account.', 'signup.submit': 'Registrati', 'signup.has_account': 'Hai già un account?', 'signup.login': 'Accedi',
'forgot.title': 'Password dimenticata', 'forgot.subtitle': 'Inserisci la tua email per ricevere il link di reset.', 'forgot.submit': 'Invia link reset', 'forgot.back_login': 'Torna al login',
'reset.title': 'Reset password', 'reset.subtitle': 'Imposta una nuova password.', 'reset.new_password': 'Nuova password', 'reset.submit': 'Aggiorna password', 'reset.invalid_token': 'Token mancante o non valido.',
'verify.title': 'Verifica email', 'verify.p1': 'Controlla la casella di posta e apri il link di verifica ricevuto.', 'verify.p2': 'Se il link è scaduto, ripeti la registrazione o contatta supporto.', 'verify.go_login': 'Vai al login',
'private.title': 'Dashboard', 'private.back_prefix': 'Bentornato', 'private.generic': 'Benvenuto.', 'private.quick_links': 'Link rapidi',
'admin.dashboard.title': 'Admin Dashboard', 'admin.dashboard.area': 'Area amministrazione.', 'admin.users_count': 'Utenti', 'admin.current_role': 'Ruolo corrente', 'admin.navigation': 'Navigazione', 'admin.manage_users': 'Gestione utenti',
'users.title': 'Users', 'users.subtitle': 'Ricerca, ordinamento e paging server-side via HTMX.', 'users.new_user': 'Nuovo Utente', 'users.search': 'Search', 'users.search_placeholder': 'Cerca nome o email', 'users.page_size': 'Page size', 'users.search_button': 'Cerca', 'users.user_detail': 'Dettaglio utente', 'users.actions': 'Azioni', 'users.open': 'Apri', 'users.none': 'Nessun utente trovato.', 'users.total': 'Totale', 'users.users_label': 'utenti', 'users.page': 'Pagina', 'users.prev': 'Prev', 'users.next': 'Next', 'users.close': 'Chiudi',
'users.new_user_modal_title': 'Nuovo utente', 'users.new_user_modal_placeholder': 'Placeholder UI Flowbite. La creazione utente può essere collegata a una route backend quando disponibile.',
'table.id': 'ID', 'table.name': 'Name', 'table.email': 'Email', 'table.role': 'Role', 'user.role_admin': 'admin', 'user.role_user': 'user', 'user.verified': 'Verificato', 'user.created': 'Creato', 'user.yes': 'sì', 'user.no': 'no',
'audit.title': 'Audit Logs', 'audit.activity': 'Attività', 'audit.security': 'Sicurezza', 'audit.timestamp': 'Timestamp', 'audit.actor': 'Attore', 'audit.action': 'Azione', 'audit.placeholder': 'Placeholder log di sicurezza.'
},
en: {
'nav.open_main_menu': 'Open main menu', 'nav.open_user_menu': 'Open user menu', 'nav.dashboard': 'Dashboard', 'nav.users': 'Users', 'nav.admin': 'Admin', 'nav.login': 'Login', 'nav.signup': 'Signup', 'nav.logout': 'Logout',
'home.subtitle': 'Login or sign up to continue.', 'home.login': 'Login', 'home.signup': 'Sign up',
'login.title': 'Login', 'login.subtitle': 'Access your account.', 'form.email': 'Email', 'form.password': 'Password', 'login.submit': 'Sign in', 'login.forgot': 'Forgot password?', 'login.create_account': 'Create account',
'signup.title': 'Signup', 'signup.subtitle': 'Create your account.', 'signup.submit': 'Sign up', 'signup.has_account': 'Already have an account?', 'signup.login': 'Login',
'forgot.title': 'Forgot Password', 'forgot.subtitle': 'Enter your email to receive a reset link.', 'forgot.submit': 'Send reset link', 'forgot.back_login': 'Back to login',
'reset.title': 'Reset Password', 'reset.subtitle': 'Set a new password.', 'reset.new_password': 'New password', 'reset.submit': 'Update password', 'reset.invalid_token': 'Missing or invalid token.',
'verify.title': 'Verify email', 'verify.p1': 'Check your inbox and open the verification link.', 'verify.p2': 'If the link expired, sign up again or contact support.', 'verify.go_login': 'Go to login',
'private.title': 'Dashboard', 'private.back_prefix': 'Signed in as', 'private.generic': 'Signed in.', 'private.quick_links': 'Quick links',
'admin.dashboard.title': 'Admin Dashboard', 'admin.dashboard.area': 'Administration area.', 'admin.users_count': 'Users', 'admin.current_role': 'Current role', 'admin.navigation': 'Navigation', 'admin.manage_users': 'Manage users',
'users.title': 'Users', 'users.subtitle': 'Search, sorting and server-side paging via HTMX.', 'users.new_user': 'New user', 'users.search': 'Search', 'users.search_placeholder': 'Search by name or email', 'users.page_size': 'Page size', 'users.search_button': 'Search', 'users.user_detail': 'User details', 'users.actions': 'Actions', 'users.open': 'Open', 'users.none': 'No users found.', 'users.total': 'Total', 'users.users_label': 'users', 'users.page': 'Page', 'users.prev': 'Prev', 'users.next': 'Next', 'users.close': 'Close',
'users.new_user_modal_title': 'New user', 'users.new_user_modal_placeholder': 'Flowbite placeholder UI. Connect creation to backend route when available.',
'table.id': 'ID', 'table.name': 'Name', 'table.email': 'Email', 'table.role': 'Role', 'user.role_admin': 'admin', 'user.role_user': 'user', 'user.verified': 'Verified', 'user.created': 'Created', 'user.yes': 'yes', 'user.no': 'no',
'audit.title': 'Audit Logs', 'audit.activity': 'Activity', 'audit.security': 'Security', 'audit.timestamp': 'Timestamp', 'audit.actor': 'Actor', 'audit.action': 'Action', 'audit.placeholder': 'Security logs placeholder.'
},
en_us: {},
de: {
'nav.open_main_menu': 'Hauptmenü öffnen', 'nav.open_user_menu': 'Benutzermenü öffnen', 'nav.dashboard': 'Dashboard', 'nav.users': 'Benutzer', 'nav.admin': 'Admin', 'nav.login': 'Login', 'nav.signup': 'Registrieren', 'nav.logout': 'Abmelden',
'home.subtitle': 'Melden Sie sich an oder registrieren Sie sich, um fortzufahren.', 'home.login': 'Anmelden', 'home.signup': 'Registrieren', 'login.title': 'Login', 'login.subtitle': 'Melden Sie sich in Ihrem Konto an.', 'form.email': 'E-Mail', 'form.password': 'Passwort', 'login.submit': 'Anmelden', 'login.forgot': 'Passwort vergessen?', 'login.create_account': 'Konto erstellen',
'signup.title': 'Registrierung', 'signup.subtitle': 'Erstellen Sie Ihr Konto.', 'signup.submit': 'Registrieren', 'signup.has_account': 'Haben Sie bereits ein Konto?', 'signup.login': 'Anmelden',
'forgot.title': 'Passwort vergessen', 'forgot.subtitle': 'Geben Sie Ihre E-Mail ein, um einen Reset-Link zu erhalten.', 'forgot.submit': 'Reset-Link senden', 'forgot.back_login': 'Zurück zum Login',
'reset.title': 'Passwort zurücksetzen', 'reset.subtitle': 'Legen Sie ein neues Passwort fest.', 'reset.new_password': 'Neues Passwort', 'reset.submit': 'Passwort aktualisieren', 'reset.invalid_token': 'Token fehlt oder ist ungültig.',
'verify.title': 'E-Mail verifizieren', 'verify.p1': 'Öffnen Sie die Verifizierungs-E-Mail in Ihrem Posteingang.', 'verify.p2': 'Wenn der Link abgelaufen ist, registrieren Sie sich erneut oder kontaktieren Sie den Support.', 'verify.go_login': 'Zum Login',
'private.title': 'Dashboard', 'private.back_prefix': 'Willkommen zurück', 'private.generic': 'Willkommen.', 'private.quick_links': 'Schnelllinks',
'admin.dashboard.title': 'Admin-Dashboard', 'admin.dashboard.area': 'Administrationsbereich.', 'admin.users_count': 'Benutzer', 'admin.current_role': 'Aktuelle Rolle', 'admin.navigation': 'Navigation', 'admin.manage_users': 'Benutzer verwalten',
'users.title': 'Benutzer', 'users.subtitle': 'Suche, Sortierung und serverseitiges Paging via HTMX.', 'users.new_user': 'Neuer Benutzer', 'users.search': 'Suche', 'users.search_placeholder': 'Nach Name oder E-Mail suchen', 'users.page_size': 'Seitengröße', 'users.search_button': 'Suchen', 'users.user_detail': 'Benutzerdetails', 'users.actions': 'Aktionen', 'users.open': 'Öffnen', 'users.none': 'Keine Benutzer gefunden.', 'users.total': 'Gesamt', 'users.users_label': 'Benutzer', 'users.page': 'Seite', 'users.prev': 'Zurück', 'users.next': 'Weiter', 'users.close': 'Schließen',
'users.new_user_modal_title': 'Neuer Benutzer', 'users.new_user_modal_placeholder': 'Flowbite-Placeholder-UI. Bei Bedarf mit Backend-Route verbinden.',
'table.id': 'ID', 'table.name': 'Name', 'table.email': 'E-Mail', 'table.role': 'Rolle', 'user.role_admin': 'admin', 'user.role_user': 'user', 'user.verified': 'Verifiziert', 'user.created': 'Erstellt', 'user.yes': 'ja', 'user.no': 'nein',
'audit.title': 'Audit-Logs', 'audit.activity': 'Aktivität', 'audit.security': 'Sicherheit', 'audit.timestamp': 'Zeitstempel', 'audit.actor': 'Akteur', 'audit.action': 'Aktion', 'audit.placeholder': 'Platzhalter für Sicherheitsprotokolle.'
},
fr: {
'nav.open_main_menu': 'Ouvrir le menu principal', 'nav.open_user_menu': 'Ouvrir le menu utilisateur', 'nav.dashboard': 'Tableau de bord', 'nav.users': 'Utilisateurs', 'nav.admin': 'Admin', 'nav.login': 'Connexion', 'nav.signup': 'Inscription', 'nav.logout': 'Déconnexion',
'home.subtitle': 'Connectez-vous ou inscrivez-vous pour continuer.', 'home.login': 'Connexion', 'home.signup': 'Inscription', 'login.title': 'Connexion', 'login.subtitle': 'Accédez à votre compte.', 'form.email': 'Email', 'form.password': 'Mot de passe', 'login.submit': 'Se connecter', 'login.forgot': 'Mot de passe oublié ?', 'login.create_account': 'Créer un compte',
'signup.title': 'Inscription', 'signup.subtitle': 'Créez votre compte.', 'signup.submit': 'Sinscrire', 'signup.has_account': 'Vous avez déjà un compte ?', 'signup.login': 'Connexion',
'forgot.title': 'Mot de passe oublié', 'forgot.subtitle': 'Entrez votre email pour recevoir un lien de réinitialisation.', 'forgot.submit': 'Envoyer le lien', 'forgot.back_login': 'Retour à la connexion',
'reset.title': 'Réinitialiser le mot de passe', 'reset.subtitle': 'Définissez un nouveau mot de passe.', 'reset.new_password': 'Nouveau mot de passe', 'reset.submit': 'Mettre à jour', 'reset.invalid_token': 'Jeton manquant ou invalide.',
'verify.title': 'Vérifier lemail', 'verify.p1': 'Vérifiez votre boîte mail et ouvrez le lien de vérification.', 'verify.p2': 'Si le lien a expiré, réinscrivez-vous ou contactez le support.', 'verify.go_login': 'Aller à la connexion',
'private.title': 'Tableau de bord', 'private.back_prefix': 'Bon retour', 'private.generic': 'Bienvenue.', 'private.quick_links': 'Liens rapides',
'admin.dashboard.title': 'Tableau de bord admin', 'admin.dashboard.area': 'Zone dadministration.', 'admin.users_count': 'Utilisateurs', 'admin.current_role': 'Rôle actuel', 'admin.navigation': 'Navigation', 'admin.manage_users': 'Gérer les utilisateurs',
'users.title': 'Utilisateurs', 'users.subtitle': 'Recherche, tri et pagination côté serveur via HTMX.', 'users.new_user': 'Nouvel utilisateur', 'users.search': 'Recherche', 'users.search_placeholder': 'Rechercher par nom ou email', 'users.page_size': 'Taille de page', 'users.search_button': 'Rechercher', 'users.user_detail': 'Détails utilisateur', 'users.actions': 'Actions', 'users.open': 'Ouvrir', 'users.none': 'Aucun utilisateur trouvé.', 'users.total': 'Total', 'users.users_label': 'utilisateurs', 'users.page': 'Page', 'users.prev': 'Préc.', 'users.next': 'Suiv.', 'users.close': 'Fermer',
'users.new_user_modal_title': 'Nouvel utilisateur', 'users.new_user_modal_placeholder': 'UI Flowbite placeholder. Connecter à une route backend si nécessaire.',
'table.id': 'ID', 'table.name': 'Nom', 'table.email': 'Email', 'table.role': 'Rôle', 'user.role_admin': 'admin', 'user.role_user': 'user', 'user.verified': 'Vérifié', 'user.created': 'Créé', 'user.yes': 'oui', 'user.no': 'non',
'audit.title': 'Journaux daudit', 'audit.activity': 'Activité', 'audit.security': 'Sécurité', 'audit.timestamp': 'Horodatage', 'audit.actor': 'Acteur', 'audit.action': 'Action', 'audit.placeholder': 'Espace réservé des journaux de sécurité.'
}
};
dictionaries.en_us = Object.assign({}, dictionaries.en);
dictionaries.de_ch = Object.assign({}, dictionaries.de);
dictionaries.fr_ch = Object.assign({}, dictionaries.fr);
function normalizeLang(lang) {
if (!lang) return '';
return String(lang).trim().toLowerCase().replace('-', '_');
}
function isSupportedLang(lang) {
return !!dictionaries[normalizeLang(lang)];
}
function getLang() {
var serverLang = normalizeLang(SERVER_LANG);
if (IS_AUTHENTICATED && isSupportedLang(serverLang)) {
localStorage.setItem(STORAGE_KEY, serverLang);
return serverLang;
}
var stored = localStorage.getItem(STORAGE_KEY);
return isSupportedLang(stored) ? normalizeLang(stored) : DEFAULT_LANG;
}
function t(key, lang) {
if (dictionaries[lang] && dictionaries[lang][key]) return dictionaries[lang][key];
if (dictionaries.it[key]) return dictionaries.it[key];
return key;
}
function localeFromLang(lang) {
var localeMap = {
it: 'it-IT',
en: 'en-GB',
en_us: 'en-US',
de: 'de-DE',
fr: 'fr-FR',
de_ch: 'de-CH',
fr_ch: 'fr-CH'
};
return localeMap[lang] || 'it-IT';
}
function localizeDate(rawValue, lang) {
if (!rawValue) return '';
var date = new Date(rawValue);
if (isNaN(date.getTime())) return rawValue;
return new Intl.DateTimeFormat(localeFromLang(lang), {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).format(date);
}
function applyTranslations(root) {
var lang = getLang();
document.documentElement.setAttribute('lang', lang.replace('_', '-'));
var langSelect = document.getElementById('lang-select');
var flag = document.getElementById('lang-flag');
if (langSelect && langSelect.value !== lang) langSelect.value = lang;
if (flag) {
var flags = {
it: '/static/vendor/flags/it.svg',
en: '/static/vendor/flags/en.svg',
en_us: '/static/vendor/flags/en_us.svg',
de: '/static/vendor/flags/de.svg',
fr: '/static/vendor/flags/fr.svg',
de_ch: '/static/vendor/flags/ch.svg',
fr_ch: '/static/vendor/flags/ch.svg'
};
var labels = {
it: 'Italiano',
en: 'English',
en_us: 'English USA',
de: 'Deutsch',
fr: 'Français',
de_ch: 'Deutsch CH',
fr_ch: 'Français CH'
};
flag.src = flags[lang] || '/static/vendor/flags/it.svg';
flag.alt = labels[lang] || 'Lingua';
flag.style.width = '32px';
flag.style.height = '22px';
}
(root || document).querySelectorAll('[data-i18n]').forEach(function (el) {
el.textContent = t(el.getAttribute('data-i18n'), lang);
});
(root || document).querySelectorAll('[data-i18n-placeholder]').forEach(function (el) {
el.setAttribute('placeholder', t(el.getAttribute('data-i18n-placeholder'), lang));
});
(root || document).querySelectorAll('[data-localize-date]').forEach(function (el) {
var rawValue = el.getAttribute('data-localize-date');
el.textContent = localizeDate(rawValue, lang);
});
}
var langSelect = document.getElementById('lang-select');
if (langSelect) {
langSelect.addEventListener('change', function () {
var selectedLang = normalizeLang(langSelect.value);
localStorage.setItem(STORAGE_KEY, selectedLang);
applyTranslations(document);
});
}
function initNavbarComponents(root) {
(root || document).querySelectorAll('[data-collapse-toggle]').forEach(function (button) {
if (button.dataset.tcBound === '1') return;
button.dataset.tcBound = '1';
var targetId = button.getAttribute('data-collapse-toggle');
var target = document.getElementById(targetId);
if (!target) return;
button.addEventListener('click', function () {
var isHidden = target.classList.contains('hidden');
target.classList.toggle('hidden', !isHidden);
button.setAttribute('aria-expanded', isHidden ? 'true' : 'false');
});
});
var userButton = document.getElementById('user-menu-button');
var userDropdown = document.getElementById('user-dropdown');
if (userButton && userDropdown && userButton.dataset.tcBound !== '1') {
userButton.dataset.tcBound = '1';
userButton.addEventListener('click', function (event) {
event.preventDefault();
var isHidden = userDropdown.classList.contains('hidden');
userDropdown.classList.toggle('hidden', !isHidden);
userButton.setAttribute('aria-expanded', isHidden ? 'true' : 'false');
});
}
if (!window.__tcNavbarDocBound) {
window.__tcNavbarDocBound = true;
document.addEventListener('click', function (event) {
var btn = document.getElementById('user-menu-button');
var menu = document.getElementById('user-dropdown');
if (!btn || !menu || menu.classList.contains('hidden')) return;
if (btn.contains(event.target) || menu.contains(event.target)) return;
menu.classList.add('hidden');
btn.setAttribute('aria-expanded', 'false');
});
document.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') return;
var btn = document.getElementById('user-menu-button');
var menu = document.getElementById('user-dropdown');
if (!btn || !menu) return;
menu.classList.add('hidden');
btn.setAttribute('aria-expanded', 'false');
});
}
}
initNavbarComponents(document);
applyTranslations(document);
if (typeof window.initThemeToggle === 'function') {
window.initThemeToggle();
}
document.body.addEventListener('htmx:afterSwap', function (evt) {
initNavbarComponents(evt.target || document);
applyTranslations(evt.target || document);
if (typeof window.initThemeToggle === 'function') {
window.initThemeToggle();
}
});
})();
</script>
</body>
</html>