235 lines
19 KiB
HTML
235 lines
19 KiB
HTML
<!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>
|
||
<script src="/static/vendor/flowbite.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',
|
||
'welcome.title': 'Dashboard', 'welcome.back_prefix': 'Bentornato', 'welcome.generic': 'Benvenuto.', 'welcome.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',
|
||
'welcome.title': 'Dashboard', 'welcome.back_prefix': 'Welcome back', 'welcome.generic': 'Welcome.', 'welcome.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',
|
||
'welcome.title': 'Dashboard', 'welcome.back_prefix': 'Willkommen zurück', 'welcome.generic': 'Willkommen.', 'welcome.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': 'S’inscrire', '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 l’email', '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',
|
||
'welcome.title': 'Tableau de bord', 'welcome.back_prefix': 'Bon retour', 'welcome.generic': 'Bienvenue.', 'welcome.quick_links': 'Liens rapides',
|
||
'admin.dashboard.title': 'Tableau de bord admin', 'admin.dashboard.area': 'Zone d’administration.', '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 d’audit', '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 reinitFlowbiteComponents(target) {
|
||
if (typeof window.initDropdowns === 'function') window.initDropdowns();
|
||
if (typeof window.initModals === 'function') {
|
||
if (!target || target.id === 'usersTableContainer') {
|
||
window.initModals();
|
||
}
|
||
}
|
||
}
|
||
|
||
reinitFlowbiteComponents();
|
||
applyTranslations(document);
|
||
if (typeof window.initThemeToggle === 'function') {
|
||
window.initThemeToggle();
|
||
}
|
||
document.body.addEventListener('htmx:afterSwap', function (evt) {
|
||
reinitFlowbiteComponents(evt.target || null);
|
||
applyTranslations(evt.target || document);
|
||
if (typeof window.initThemeToggle === 'function') {
|
||
window.initThemeToggle();
|
||
}
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|