Files
CDN-APP-INSIDER/views/profile.ejs
Dinawo de8c5ccb84
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
Update v1.1.1-beta1
2025-06-14 22:01:39 +02:00

1113 lines
40 KiB
Plaintext

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profil Utilisateur - Interface Moderne</title>
<link rel="icon" href="/public/assets/homelab_logo.png" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
* {
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background-color: hsl(var(--background));
color: hsl(var(--foreground));
transition: background-color 0.3s ease, color 0.3s ease;
margin: 0;
padding: 0;
min-height: 100vh;
background-image: url('<%= user.wallpaper %>');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
}
.backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
z-index: 1;
}
.container {
position: relative;
z-index: 2;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.profile-card {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 16px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.05);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
overflow: hidden;
width: 100%;
max-width: 800px;
animation: slideIn 0.5s ease-out;
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Header du profil */
.profile-header {
padding: 2rem;
text-align: center;
background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(var(--accent)) 100%);
color: hsl(var(--primary-foreground));
position: relative;
overflow: hidden;
}
.profile-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="2" fill="white" opacity="0.1"/><circle cx="80" cy="80" r="2" fill="white" opacity="0.1"/><circle cx="40" cy="60" r="1" fill="white" opacity="0.05"/></svg>');
opacity: 0.3;
}
.profile-picture {
position: relative;
z-index: 2;
margin-bottom: 1rem;
}
.profile-picture img {
width: 120px;
height: 120px;
border-radius: 50%;
border: 4px solid rgba(255, 255, 255, 0.2);
object-fit: cover;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
transition: transform 0.3s ease;
}
.profile-picture img:hover {
transform: scale(1.05);
}
.user-info {
position: relative;
z-index: 2;
}
.user-name {
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.5rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.user-details {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.user-detail {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 25px;
font-size: 0.9rem;
}
.role-badge {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
}
/* Interface en test notice */
.beta-notice {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: white;
padding: 1rem 2rem;
text-align: center;
border-radius: 0;
margin: 0;
position: relative;
overflow: hidden;
}
.beta-notice::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shine 3s infinite;
}
@keyframes shine {
0% { left: -100%; }
100% { left: 100%; }
}
.beta-notice i {
margin-right: 0.5rem;
animation: pulse 2s infinite;
}
/* Onglets */
.tabs-container {
background: hsl(var(--card));
border-bottom: 1px solid hsl(var(--border));
}
.tabs {
display: flex;
overflow-x: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.tabs::-webkit-scrollbar {
display: none;
}
.tab {
padding: 1.25rem 2rem;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
display: flex;
align-items: center;
gap: 0.5rem;
background: transparent;
color: hsl(var(--muted-foreground));
border: none;
font-size: 0.95rem;
font-weight: 500;
position: relative;
border-bottom: 3px solid transparent;
}
.tab:hover {
background-color: hsl(var(--accent));
color: hsl(var(--accent-foreground));
}
.tab.active {
background: hsl(var(--primary));
color: hsl(var(--primary-foreground));
border-bottom-color: hsl(var(--primary));
font-weight: 600;
}
.tab.active::after {
content: '';
position: absolute;
bottom: -3px;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, hsl(var(--primary)), hsl(var(--ring)));
border-radius: 3px 3px 0 0;
}
.tab i {
font-size: 1.1rem;
}
/* Contenu des onglets */
.tab-content {
display: none;
padding: 2rem;
animation: fadeIn 0.3s ease-out;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Formulaires modernes */
.form-section {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
transition: all 0.3s ease;
}
.form-section:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
.form-section-title {
font-size: 1.1rem;
font-weight: 600;
color: hsl(var(--foreground));
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-section-title i {
color: hsl(var(--primary));
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: hsl(var(--foreground));
font-size: 0.9rem;
}
.form-control {
width: 100%;
padding: 0.875rem 1rem;
border: 1px solid hsl(var(--border));
border-radius: 8px;
background-color: hsl(var(--background));
color: hsl(var(--foreground));
font-size: 0.95rem;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: hsl(var(--ring));
box-shadow: 0 0 0 3px hsla(var(--ring), 0.1);
transform: translateY(-1px);
}
.form-control::placeholder {
color: hsl(var(--muted-foreground));
} /* Boutons modernes */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.875rem 1.5rem;
border-radius: 25px;
font-weight: 500;
font-size: 0.95rem;
transition: all 0.3s ease;
cursor: pointer;
border: none;
text-decoration: none;
position: relative;
overflow: hidden;
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn:hover::before {
left: 100%;
}
.btn:active {
transform: scale(0.98);
}
.btn-primary {
background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(var(--ring)) 100%);
color: hsl(var(--primary-foreground));
box-shadow: 0 4px 14px 0 rgba(0, 0, 0, 0.1);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px 0 rgba(0, 0, 0, 0.15);
}
.btn-secondary {
background: hsl(var(--secondary));
color: hsl(var(--secondary-foreground));
border: 1px solid hsl(var(--border));
}
.btn-secondary:hover {
background: hsl(var(--accent));
color: hsl(var(--accent-foreground));
transform: translateY(-1px);
}
.btn-full {
width: 100%;
}
.btn-icon-only {
padding: 0.75rem;
border-radius: 50%;
}
/* Actions rapides */
.quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.quick-action {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 12px;
padding: 1.5rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
color: hsl(var(--foreground));
}
.quick-action:hover {
transform: translateY(-4px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
text-decoration: none;
color: hsl(var(--foreground));
}
.quick-action-icon {
font-size: 2rem;
margin-bottom: 1rem;
color: hsl(var(--primary));
}
.quick-action-title {
font-weight: 600;
margin-bottom: 0.5rem;
}
.quick-action-desc {
font-size: 0.85rem;
color: hsl(var(--muted-foreground));
}
/* Theme switcher */
.theme-switcher {
position: fixed;
top: 2rem;
right: 2rem;
z-index: 10;
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 50%;
padding: 0.75rem;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);
}
.theme-switcher:hover {
transform: scale(1.1);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.theme-switcher svg {
width: 1.25rem;
height: 1.25rem;
color: hsl(var(--foreground));
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.profile-header {
padding: 1.5rem;
}
.user-name {
font-size: 1.5rem;
}
.user-details {
gap: 1rem;
}
.tab {
padding: 1rem 1.5rem;
font-size: 0.9rem;
}
.tab-content {
padding: 1.5rem;
}
.theme-switcher {
top: 1rem;
right: 1rem;
}
}
/* Toast notifications */
.swal2-toast {
background-color: hsl(var(--card)) !important;
color: hsl(var(--foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
border-radius: var(--radius) !important;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important;
}
.dark .swal2-toast {
background-color: hsl(var(--card)) !important;
color: hsl(var(--foreground)) !important;
border: 1px solid hsl(var(--border)) !important;
}
/* Loading states */
.loading {
opacity: 0.7;
pointer-events: none;
}
.loading .btn {
position: relative;
}
.loading .btn::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
margin: auto;
border: 2px solid transparent;
border-top-color: currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="dark">
<div class="backdrop"></div>
<div class="container">
<!-- Notice Interface en Test -->
<div class="beta-notice">
<i class="fas fa-flask"></i>
<strong>Interface en cours de test</strong> - Cette nouvelle interface moderne est actuellement en phase de test.
Selon les retours des utilisateurs, elle sera déployée sur toute l'application (gestion admin, téléversement, etc.).
</div>
<div class="profile-card">
<!-- Header du profil -->
<div class="profile-header">
<div class="profile-picture">
<img src="<%= user.profilePicture || 'https://api.dicebear.com/7.x/initials/svg?seed=' + user.name %>"
alt="Photo de profil"
id="profileImage" />
</div>
<div class="user-info">
<h1 class="user-name">Bienvenue, <%= user.name %> !</h1>
<div class="user-details">
<div class="user-detail">
<i class="fas fa-id-card"></i>
<span>ID: <%= user.id %></span>
</div>
<div class="user-detail">
<i class="fas fa-shield-alt"></i>
<span class="role-badge"><%= user.role %></span>
</div>
</div>
</div>
</div>
<!-- Onglets -->
<div class="tabs-container">
<div class="tabs">
<button class="tab active" data-tab="overview">
<i class="fas fa-chart-line"></i>
Aperçu
</button>
<button class="tab" data-tab="appearance">
<i class="fas fa-palette"></i>
Apparence
</button>
<button class="tab" data-tab="profile">
<i class="fas fa-user-edit"></i>
Profil
</button>
<button class="tab" data-tab="security">
<i class="fas fa-lock"></i>
Sécurité
</button>
</div>
</div>
<!-- Contenu de l'onglet Aperçu -->
<div class="tab-content active" id="overview">
<div class="quick-actions">
<a href="/dpanel/dashboard" class="quick-action">
<div class="quick-action-icon">
<i class="fas fa-tachometer-alt"></i>
</div>
<div class="quick-action-title">Dashboard</div>
<div class="quick-action-desc">Accéder au tableau de bord</div>
</a>
<a href="/dpanel/upload" class="quick-action">
<div class="quick-action-icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
<div class="quick-action-title">Téléverser</div>
<div class="quick-action-desc">Ajouter de nouveaux fichiers</div>
</a>
<div class="quick-action" onclick="switchTab('appearance')">
<div class="quick-action-icon">
<i class="fas fa-image"></i>
</div>
<div class="quick-action-title">Personnaliser</div>
<div class="quick-action-desc">Modifier l'apparence</div>
</div>
</div>
</div> <!-- Contenu de l'onglet Apparence -->
<div class="tab-content" id="appearance">
<div class="form-section">
<div class="form-section-title">
<i class="fas fa-palette"></i>
Thème de l'interface
</div>
<div class="form-group">
<label class="form-label">Mode d'affichage</label>
<div style="display: flex; gap: 1rem; margin-top: 0.5rem;">
<button type="button" class="btn btn-secondary" id="lightModeBtn">
<i class="fas fa-sun"></i>
Mode clair
</button>
<button type="button" class="btn btn-secondary" id="darkModeBtn">
<i class="fas fa-moon"></i>
Mode sombre
</button>
</div>
</div>
</div>
<div class="form-section">
<div class="form-section-title">
<i class="fas fa-image"></i>
Fond d'écran personnalisé
</div>
<form id="wallpaperForm">
<div class="form-group">
<label class="form-label" for="wallpaperUrl">URL du fond d'écran</label>
<input type="url"
id="wallpaperUrl"
name="wallpaperUrl"
class="form-control"
placeholder="https://exemple.com/image.jpg"
required>
</div>
<button type="submit" class="btn btn-primary btn-full">
<i class="fas fa-image"></i>
Mettre à jour le fond d'écran
</button>
</form>
</div>
</div>
<!-- Contenu de l'onglet Profil -->
<div class="tab-content" id="profile">
<div class="form-section">
<div class="form-section-title">
<i class="fas fa-user-circle"></i>
Photo de profil
</div>
<form id="profilePictureForm">
<div class="form-group">
<label class="form-label" for="profilePictureUrl">URL de la photo de profil</label>
<input type="url"
id="profilePictureUrl"
name="profilePictureUrl"
class="form-control"
placeholder="https://exemple.com/photo.jpg"
required>
</div>
<button type="submit" class="btn btn-primary btn-full">
<i class="fas fa-user-circle"></i>
Mettre à jour la photo de profil
</button>
</form>
</div>
</div> <!-- Contenu de l'onglet Sécurité -->
<div class="tab-content" id="security">
<div class="form-section">
<div class="form-section-title">
<i class="fas fa-key"></i>
Gestion des clés API
</div>
<div class="form-group">
<label class="form-label">Votre clé API actuelle</label>
<div style="display: flex; gap: 1rem; align-items: center;">
<input type="text"
id="apiKeyDisplay"
class="form-control"
value="sk-****************************"
readonly
style="flex: 1;">
<button type="button" class="btn btn-secondary" id="showApiKey">
<i class="fas fa-eye"></i>
Afficher
</button>
<button type="button" class="btn btn-secondary" id="copyApiKey">
<i class="fas fa-copy"></i>
Copier
</button>
</div>
</div>
<div style="display: flex; gap: 1rem; margin-top: 1rem;">
<button type="button" class="btn btn-primary" id="generateApiKey">
<i class="fas fa-sync-alt"></i>
Générer une nouvelle clé
</button>
<button type="button" class="btn btn-secondary" id="revokeApiKey">
<i class="fas fa-trash"></i>
Révoquer la clé
</button>
</div>
<div style="padding: 1rem; background: hsl(var(--muted)); border-radius: 8px; margin-top: 1rem;">
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
<i class="fas fa-info-circle" style="color: hsl(var(--primary));"></i>
<strong>Important :</strong>
</div>
<p style="margin: 0; font-size: 0.85rem; color: hsl(var(--muted-foreground));">
Votre clé API vous permet d'accéder aux services du CDN via les API.
Gardez-la confidentielle et ne la partagez jamais.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Theme Switcher -->
<button class="theme-switcher" id="themeSwitcher">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
</svg>
</button> <script>
// =================== VARIABLES GLOBALES ===================
const body = document.body;
const themeSwitcher = document.getElementById('themeSwitcher');
// =================== GESTION DU THÈME ===================
function setTheme(theme) {
if (theme === 'dark') {
body.classList.add('dark');
} else {
body.classList.remove('dark');
}
localStorage.setItem('theme', theme);
}
// Initialisation du thème
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
setTheme('dark');
}
themeSwitcher.addEventListener('click', function() {
if (body.classList.contains('dark')) {
setTheme('light');
} else {
setTheme('dark');
}
});
// =================== GESTION DES ONGLETS ===================
function switchTab(tabName) {
// Désactiver tous les onglets
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Masquer tout le contenu
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Activer l'onglet et le contenu sélectionnés
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
document.getElementById(tabName).classList.add('active');
}
// Gestionnaires d'événements pour les onglets
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
switchTab(tabName);
});
});
// =================== NOTIFICATIONS ===================
function showToast(icon, title) {
Swal.fire({
icon: icon,
title: title,
showConfirmButton: false,
timer: 3000,
toast: true,
position: 'top-end',
customClass: {
container: 'swal2-toast'
}
});
}
// =================== GESTION DES FORMULAIRES ===================
function setFormLoading(form, loading) {
const submitBtn = form.querySelector('button[type="submit"]');
if (loading) {
form.classList.add('loading');
submitBtn.disabled = true;
} else {
form.classList.remove('loading');
submitBtn.disabled = false;
}
}
// =================== MISE À JOUR DU FOND D'ÉCRAN ===================
document.getElementById('wallpaperForm').addEventListener('submit', async function(e) {
e.preventDefault();
const form = this;
const urlInput = document.getElementById('wallpaperUrl').value.trim();
if (!urlInput) {
showToast('warning', 'Veuillez entrer une URL valide');
return;
}
setFormLoading(form, true);
try {
const response = await fetch('/api/dpanel/dashboard/backgroundcustom/wallpaper', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
wallpaperUrl: urlInput,
userId: '<%= user.id %>'
})
});
if (response.ok) {
const data = await response.json();
document.body.style.backgroundImage = `url('${data.wallpaper}')`;
form.reset();
showToast('success', 'Fond d\'écran mis à jour avec succès !');
} else {
const error = await response.json();
showToast('error', error.message || 'Échec de la mise à jour du fond d\'écran');
}
} catch (error) {
console.error('Erreur:', error);
showToast('error', 'Erreur de connexion au serveur');
} finally {
setFormLoading(form, false);
}
}); // =================== MISE À JOUR DE LA PHOTO DE PROFIL ===================
document.getElementById('profilePictureForm').addEventListener('submit', async function(e) {
e.preventDefault();
const form = this;
const urlInput = document.getElementById('profilePictureUrl').value.trim();
if (!urlInput) {
showToast('warning', 'Veuillez entrer une URL valide');
return;
}
setFormLoading(form, true);
try {
const response = await fetch('/api/dpanel/dashboard/profilpicture', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
profilePictureUrl: urlInput,
userId: '<%= user.id %>'
})
});
if (response.ok) {
const data = await response.json();
document.getElementById('profileImage').src = data.profilePicture;
form.reset();
showToast('success', 'Photo de profil mise à jour avec succès !');
} else {
const error = await response.json();
showToast('error', error.message || 'Échec de la mise à jour de la photo de profil');
}
} catch (error) {
console.error('Erreur:', error);
showToast('error', 'Erreur de connexion au serveur');
} finally {
setFormLoading(form, false);
}
});
// =================== GESTION DES BOUTONS DE THÈME ===================
document.getElementById('lightModeBtn').addEventListener('click', function() {
setTheme('light');
showToast('success', 'Thème clair activé');
});
document.getElementById('darkModeBtn').addEventListener('click', function() {
setTheme('dark');
showToast('success', 'Thème sombre activé');
}); // =================== GESTION DES CLÉS API ===================
let currentApiKey = '<%= user.token || "" %>';
let isApiKeyVisible = false;
document.getElementById('showApiKey').addEventListener('click', function() {
const apiKeyInput = document.getElementById('apiKeyDisplay');
const button = this;
if (isApiKeyVisible) {
apiKeyInput.value = 'sk-****************************';
button.innerHTML = '<i class="fas fa-eye"></i> Afficher';
isApiKeyVisible = false;
} else {
if (currentApiKey) {
apiKeyInput.value = currentApiKey;
button.innerHTML = '<i class="fas fa-eye-slash"></i> Masquer';
isApiKeyVisible = true;
} else {
apiKeyInput.value = 'Aucune clé API trouvée';
showToast('warning', 'Aucune clé API trouvée pour cet utilisateur');
}
}
});
document.getElementById('copyApiKey').addEventListener('click', function() {
const apiKeyInput = document.getElementById('apiKeyDisplay');
if (isApiKeyVisible) {
navigator.clipboard.writeText(currentApiKey).then(() => {
showToast('success', 'Clé API copiée dans le presse-papiers');
}).catch(() => {
showToast('error', 'Impossible de copier la clé API');
});
} else {
showToast('warning', 'Veuillez d\'abord afficher la clé API');
}
}); document.getElementById('generateApiKey').addEventListener('click', async function() {
const result = await Swal.fire({
title: 'Générer une nouvelle clé API ?',
text: 'Cette action invalidera votre clé API actuelle.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Oui, générer',
cancelButtonText: 'Annuler',
confirmButtonColor: 'hsl(var(--primary))'
});
if (result.isConfirmed) {
try {
const response = await fetch('/api/dpanel/generate-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: '<%= user.name %>',
id: '<%= user.id %>'
})
});
if (response.ok) {
const data = await response.json();
currentApiKey = data.token;
if (isApiKeyVisible) {
document.getElementById('apiKeyDisplay').value = data.token;
} else {
document.getElementById('apiKeyDisplay').value = '•'.repeat(32);
}
showToast('success', 'Nouvelle clé API générée avec succès');
} else {
const errorData = await response.json();
showToast('error', errorData.error || 'Erreur lors de la génération de la clé API');
}
} catch (error) {
console.error('Erreur:', error);
showToast('error', 'Erreur lors de la génération de la clé API');
}
}
}); document.getElementById('revokeApiKey').addEventListener('click', async function() {
const result = await Swal.fire({
title: 'Révoquer la clé API ?',
text: 'Cette action supprimera définitivement votre clé API.',
icon: 'error',
showCancelButton: true,
confirmButtonText: 'Oui, révoquer',
cancelButtonText: 'Annuler',
confirmButtonColor: 'hsl(var(--destructive))'
});
if (result.isConfirmed) {
try {
const response = await fetch('/api/dpanel/revoke-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: '<%= user.id %>'
})
});
if (response.ok) {
const data = await response.json();
currentApiKey = '';
document.getElementById('apiKeyDisplay').value = 'Aucune clé API';
isApiKeyVisible = false;
document.getElementById('showApiKey').innerHTML = '<i class="fas fa-eye"></i> Afficher';
showToast('success', data.message || 'Clé API révoquée avec succès');
} else {
const errorData = await response.json();
showToast('error', errorData.error || 'Erreur lors de la révocation de la clé API');
}
} catch (error) {
console.error('Erreur:', error);
showToast('error', 'Erreur lors de la révocation de la clé API');
}
}
});
// =================== GESTION DES IMAGES ===================
document.getElementById('profileImage').addEventListener('error', function() {
this.src = 'https://api.dicebear.com/7.x/initials/svg?seed=<%= user.name %>';
});
// =================== INITIALISATION ===================
document.addEventListener('DOMContentLoaded', function() {
// Animation d'entrée
setTimeout(() => {
document.querySelector('.profile-card').style.opacity = '1';
document.querySelector('.profile-card').style.transform = 'translateY(0) scale(1)';
}, 100);
console.log('🎨 Interface moderne du profil chargée !');
console.log('✨ Cette interface est en phase de test');
});
</script>
</body>
</html>