prod: UI Optimisée mise en production
- Documentation archivée et réorganisée - Backend: Ajout tests, migrations, library service, rate limiting - Frontend: Suppression Flutter, focus sur interface web HTML/JS - Tailwind CSS ajouté pour le style - Améliorations UX et corrections bugs Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AudiOhm - Web Player</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Toast Container -->
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
|
||||
<!-- App Container -->
|
||||
<div id="app">
|
||||
<!-- Loading Screen -->
|
||||
<div id="loading-screen" class="loading-screen">
|
||||
<div class="spinner"></div>
|
||||
<h2>Chargement de AudiOhm...</h2>
|
||||
</div>
|
||||
|
||||
<!-- Login Screen -->
|
||||
<div id="login-screen" class="screen hidden">
|
||||
<div class="login-container">
|
||||
<h1 class="logo">
|
||||
<i class="fas fa-headphones"></i> AudiOhm
|
||||
</h1>
|
||||
<form id="login-form" class="login-form">
|
||||
<div class="form-group">
|
||||
<input type="email" id="login-email" placeholder="Email" required autocomplete="email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="login-password" placeholder="Mot de passe" required autocomplete="current-password">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-sign-in-alt"></i> Se connecter
|
||||
</button>
|
||||
<p class="register-link">
|
||||
Pas encore de compte ? <a href="#" id="show-register">Créer un compte</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<form id="register-form" class="login-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="text" id="register-username" placeholder="Nom d'utilisateur" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="register-email" placeholder="Email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-password" placeholder="Mot de passe" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-user-plus"></i> Créer un compte
|
||||
</button>
|
||||
<p class="register-link">
|
||||
Déjà un compte ? <a href="#" id="show-login">Se connecter</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<div id="auth-error" class="error-message hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main App -->
|
||||
<div id="main-app" class="screen hidden">
|
||||
<!-- Mobile Menu Button -->
|
||||
<button class="mobile-menu-btn" id="mobile-menu-btn">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1 class="logo">
|
||||
<i class="fas fa-headphones"></i> AudiOhm
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a href="#" class="nav-item active" data-page="home">
|
||||
<i class="fas fa-home"></i> Accueil
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-page="search">
|
||||
<i class="fas fa-search"></i> Rechercher
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-page="library">
|
||||
<i class="fas fa-music"></i> Bibliothèque
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<button id="logout-btn" class="btn btn-secondary">
|
||||
<i class="fas fa-sign-out-alt"></i> Déconnexion
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<!-- Home Page -->
|
||||
<div id="home-page" class="page active">
|
||||
<div class="page-header">
|
||||
<h1>Bienvenue sur AudiOhm 🎵</h1>
|
||||
<p>Votre alternative à Spotify avec streaming YouTube</p>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-bolt"></i> Recherche rapide</h2>
|
||||
<div class="search-bar">
|
||||
<input type="text" id="quick-search" placeholder="Rechercher une musique, un artiste...">
|
||||
<button class="btn btn-primary" id="quick-search-btn">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-fire"></i> Musiques tendance</h2>
|
||||
<div class="track-list" id="trending-tracks">
|
||||
<div class="loading">
|
||||
<div class="spinner" style="width: 40px; height: 40px; margin: 0 auto 1rem;"></div>
|
||||
Chargement...
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-clock"></i> Récemment écoutées</h2>
|
||||
<div class="track-list" id="recent-tracks">
|
||||
<p style="color: var(--text-secondary);">Aucune écoute récente</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Search Page -->
|
||||
<div id="search-page" class="page">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-search"></i> Recherche</h1>
|
||||
</div>
|
||||
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Que voulez-vous écouter ?">
|
||||
<button class="btn btn-primary" id="search-btn">
|
||||
<i class="fas fa-search"></i> Rechercher
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="search-results" class="search-results"></div>
|
||||
</div>
|
||||
|
||||
<!-- Library Page -->
|
||||
<div id="library-page" class="page">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-music"></i> Ma Bibliothèque</h1>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-list"></i> Mes Playlists</h2>
|
||||
<div class="playlist-list" id="my-playlists">
|
||||
<div class="loading">
|
||||
<div class="spinner" style="width: 40px; height: 40px; margin: 0 auto 1rem;"></div>
|
||||
Chargement...
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-heart"></i> Titres likés</h2>
|
||||
<div class="track-list" id="liked-tracks">
|
||||
<p style="color: var(--text-secondary);">Aucun titre liké pour le moment</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Player -->
|
||||
<div id="player" class="player">
|
||||
<div class="player-info">
|
||||
<img id="player-cover" src="/static/img/default-cover.png" alt="Cover" class="player-cover">
|
||||
<div class="player-details">
|
||||
<div id="player-title" class="player-title">Aucun titre</div>
|
||||
<div id="player-artist" class="player-artist">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-controls">
|
||||
<button class="btn-control" id="shuffle-btn" title="Aléatoire">
|
||||
<i class="fas fa-random"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="prev-btn" title="Précédent">
|
||||
<i class="fas fa-step-backward"></i>
|
||||
</button>
|
||||
<button class="btn-control btn-play" id="play-btn" title="Play/Pause">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="next-btn" title="Suivant">
|
||||
<i class="fas fa-step-forward"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="repeat-btn" title="Répéter">
|
||||
<i class="fas fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="player-progress">
|
||||
<span id="current-time" class="time">0:00</span>
|
||||
<input type="range" id="progress-bar" min="0" max="100" value="0" class="progress-bar">
|
||||
<span id="total-time" class="time">0:00</span>
|
||||
</div>
|
||||
|
||||
<div class="player-volume">
|
||||
<button class="btn-control" id="mute-btn" title="Muet">
|
||||
<i class="fas fa-volume-up"></i>
|
||||
</button>
|
||||
<input type="range" id="volume-bar" min="0" max="100" value="100" class="volume-bar">
|
||||
</div>
|
||||
|
||||
<div class="player-actions">
|
||||
<button class="btn-control" id="like-btn" title="J'aime">
|
||||
<i class="far fa-heart"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="playlist-btn" title="Ajouter à la playlist">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<audio id="audio-player" preload="none"></audio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Fallback: Hide loading screen after 5 seconds if JS fails
|
||||
setTimeout(function() {
|
||||
const loadingScreen = document.getElementById('loading-screen');
|
||||
if (loadingScreen) {
|
||||
console.error('Loading screen timeout - JS may have failed to load');
|
||||
loadingScreen.style.display = 'none';
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
+695
-157
@@ -1,244 +1,782 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<html lang="fr" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AudiOhm - Web Player</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f0f9ff',
|
||||
100: '#e0f2fe',
|
||||
200: '#bae6fd',
|
||||
300: '#7dd3fc',
|
||||
400: '#38bdf8',
|
||||
500: '#0ea5e9',
|
||||
600: '#0284c7',
|
||||
700: '#0369a1',
|
||||
800: '#075985',
|
||||
900: '#0c4a6e',
|
||||
},
|
||||
accent: {
|
||||
50: '#fdf2f8',
|
||||
100: '#fce7f3',
|
||||
200: '#fbcfe8',
|
||||
300: '#f9a8d4',
|
||||
400: '#f472b6',
|
||||
500: '#ec4899',
|
||||
600: '#db2777',
|
||||
700: '#be185d',
|
||||
800: '#9d174d',
|
||||
900: '#831843',
|
||||
},
|
||||
success: '#10b981',
|
||||
warning: '#f59e0b',
|
||||
error: '#ef4444',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* Custom animations */
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { transform: translateX(100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-slideIn {
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #4b5563;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #6b7280;
|
||||
}
|
||||
|
||||
/* Glassmorphism */
|
||||
.glass {
|
||||
background: rgba(17, 24, 39, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: rgba(31, 41, 55, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Range slider styling */
|
||||
input[type="range"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-track {
|
||||
background: #374151;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin-top: -4px;
|
||||
background-color: #0ea5e9;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb:hover {
|
||||
background-color: #38bdf8;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
/* Larger slider for desktop */
|
||||
@media (min-width: 640px) {
|
||||
input[type="range"]::-webkit-slider-track {
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Screen reader only utility */
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.sr-only.focusable:focus {
|
||||
position: static;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: inherit;
|
||||
margin: inherit;
|
||||
overflow: visible;
|
||||
clip: auto;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* Focus visible styles for keyboard navigation */
|
||||
:focus-visible {
|
||||
outline: 2px solid #0ea5e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Better focus styles for interactive elements */
|
||||
button:focus-visible,
|
||||
a:focus-visible,
|
||||
input:focus-visible,
|
||||
[tabindex]:focus-visible {
|
||||
outline: 2px solid #0ea5e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Library Tabs Styles */
|
||||
.library-tab {
|
||||
background: rgba(31, 41, 55, 0.6);
|
||||
color: #9ca3af;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.library-tab:hover {
|
||||
background: rgba(55, 65, 81, 0.6);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.library-tab.active {
|
||||
background: rgba(14, 165, 233, 0.2);
|
||||
color: #38bdf8;
|
||||
border-color: rgba(56, 189, 248, 0.3);
|
||||
}
|
||||
|
||||
.tab-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-panel.active {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-gradient-to-br from-gray-900 via-slate-900 to-gray-900 min-h-screen text-white font-sans">
|
||||
<!-- Skip Link for Accessibility -->
|
||||
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary-600 focus:text-white focus:rounded-lg">
|
||||
Aller au contenu principal
|
||||
</a>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div id="toast-container" class="toast-container"></div>
|
||||
<div id="toast-container" class="fixed top-4 right-4 z-50 flex flex-col gap-2" role="status" aria-live="polite" aria-atomic="true"></div>
|
||||
|
||||
<!-- App Container -->
|
||||
<div id="app">
|
||||
<div id="app" class="min-h-screen">
|
||||
<!-- Loading Screen -->
|
||||
<div id="loading-screen" class="loading-screen">
|
||||
<div class="spinner"></div>
|
||||
<h2>Chargement de AudiOhm...</h2>
|
||||
<div id="loading-screen" class="fixed inset-0 bg-gray-900 flex flex-col items-center justify-center z-50" role="status" aria-live="polite" aria-busy="true">
|
||||
<div class="relative w-16 h-16 mb-6">
|
||||
<div class="absolute inset-0 border-4 border-primary-500/30 rounded-full"></div>
|
||||
<div class="absolute inset-0 border-4 border-transparent border-t-primary-500 rounded-full animate-spin"></div>
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
Chargement de AudiOhm...
|
||||
</h2>
|
||||
<p class="text-gray-400 mt-2">Préparation de votre expérience musicale</p>
|
||||
</div>
|
||||
|
||||
<!-- Login Screen -->
|
||||
<div id="login-screen" class="screen hidden">
|
||||
<div class="login-container">
|
||||
<h1 class="logo">
|
||||
<i class="fas fa-headphones"></i> AudiOhm
|
||||
</h1>
|
||||
<form id="login-form" class="login-form">
|
||||
<div class="form-group">
|
||||
<input type="email" id="login-email" placeholder="Email" required autocomplete="email">
|
||||
<div id="login-screen" class="hidden fixed inset-0 bg-gradient-to-br from-gray-900 via-slate-900 to-gray-900 flex items-center justify-center p-4" role="dialog" aria-modal="true" aria-labelledby="login-title">
|
||||
<div class="glass-card rounded-2xl p-8 w-full max-w-md animate-fadeIn">
|
||||
<!-- Logo -->
|
||||
<div class="text-center mb-8">
|
||||
<div class="inline-flex items-center justify-center w-20 h-20 rounded-2xl bg-gradient-to-br from-primary-500 to-accent-500 mb-4 shadow-lg shadow-primary-500/25" aria-hidden="true">
|
||||
<i class="fas fa-headphones text-4xl text-white"></i>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="login-password" placeholder="Mot de passe" required autocomplete="current-password">
|
||||
<h1 id="login-title" class="text-3xl font-bold bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
AudiOhm
|
||||
</h1>
|
||||
<p class="text-gray-400 mt-2">Votre musique, illimitée</p>
|
||||
</div>
|
||||
|
||||
<!-- Login Form -->
|
||||
<form id="login-form" class="space-y-4" aria-label="Formulaire de connexion">
|
||||
<div>
|
||||
<label for="login-email" class="block text-sm font-medium text-gray-300 mb-2">Email</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-envelope absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" aria-hidden="true"></i>
|
||||
<input type="email" id="login-email" required
|
||||
class="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="vous@example.com" autocomplete="email" aria-describedby="login-email-hint">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-sign-in-alt"></i> Se connecter
|
||||
|
||||
<div>
|
||||
<label for="login-password" class="block text-sm font-medium text-gray-300 mb-2">Mot de passe</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-lock absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" aria-hidden="true"></i>
|
||||
<input type="password" id="login-password" required
|
||||
class="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="••••••••" autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
class="w-full py-3 px-4 bg-gradient-to-r from-primary-600 to-primary-500 hover:from-primary-500 hover:to-primary-400 rounded-xl font-semibold transition-all transform hover:scale-[1.02] active:scale-[0.98] focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-gray-900 shadow-lg shadow-primary-500/25">
|
||||
<i class="fas fa-sign-in-alt mr-2" aria-hidden="true"></i>
|
||||
Se connecter
|
||||
</button>
|
||||
<p class="register-link">
|
||||
Pas encore de compte ? <a href="#" id="show-register">Créer un compte</a>
|
||||
</p>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-gray-400 text-sm">
|
||||
Pas encore de compte ?
|
||||
<button type="button" id="show-register" class="text-primary-400 hover:text-primary-300 font-medium transition-colors focus:outline-none focus:underline focus:ring-2 focus:ring-primary-400 rounded">
|
||||
Créer un compte
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="register-form" class="login-form hidden">
|
||||
<div class="form-group">
|
||||
<input type="text" id="register-username" placeholder="Nom d'utilisateur" required>
|
||||
<!-- Register Form -->
|
||||
<form id="register-form" class="hidden space-y-4" aria-label="Formulaire d'inscription">
|
||||
<div>
|
||||
<label for="register-username" class="block text-sm font-medium text-gray-300 mb-2">Nom d'utilisateur</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-user absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" aria-hidden="true"></i>
|
||||
<input type="text" id="register-username" required
|
||||
class="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="votre_pseudo" autocomplete="username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="email" id="register-email" placeholder="Email" required>
|
||||
|
||||
<div>
|
||||
<label for="register-email" class="block text-sm font-medium text-gray-300 mb-2">Email</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-envelope absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" aria-hidden="true"></i>
|
||||
<input type="email" id="register-email" required
|
||||
class="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="vous@example.com" autocomplete="email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" id="register-password" placeholder="Mot de passe" required>
|
||||
|
||||
<div>
|
||||
<label for="register-password" class="block text-sm font-medium text-gray-300 mb-2">Mot de passe</label>
|
||||
<div class="relative">
|
||||
<i class="fas fa-lock absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" aria-hidden="true"></i>
|
||||
<input type="password" id="register-password" required minlength="8"
|
||||
class="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="Min. 8 caractères" autocomplete="new-password" aria-describedby="password-requirements">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-user-plus"></i> Créer un compte
|
||||
|
||||
<button type="submit"
|
||||
class="w-full py-3 px-4 bg-gradient-to-r from-accent-600 to-accent-500 hover:from-accent-500 hover:to-accent-400 rounded-xl font-semibold transition-all transform hover:scale-[1.02] active:scale-[0.98] focus:outline-none focus:ring-2 focus:ring-accent-400 focus:ring-offset-2 focus:ring-offset-gray-900 shadow-lg shadow-accent-500/25">
|
||||
<i class="fas fa-user-plus mr-2" aria-hidden="true"></i>
|
||||
Créer un compte
|
||||
</button>
|
||||
<p class="register-link">
|
||||
Déjà un compte ? <a href="#" id="show-login">Se connecter</a>
|
||||
</p>
|
||||
|
||||
<div class="text-center">
|
||||
<p class="text-gray-400 text-sm">
|
||||
Déjà un compte ?
|
||||
<button type="button" id="show-login" class="text-accent-400 hover:text-accent-300 font-medium transition-colors focus:outline-none focus:underline focus:ring-2 focus:ring-accent-400 rounded">
|
||||
Se connecter
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="auth-error" class="error-message hidden"></div>
|
||||
<div id="auth-error" class="hidden mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main App -->
|
||||
<div id="main-app" class="screen hidden">
|
||||
<div id="main-app" class="hidden">
|
||||
<!-- Mobile Menu Button -->
|
||||
<button class="mobile-menu-btn" id="mobile-menu-btn">
|
||||
<i class="fas fa-bars"></i>
|
||||
<button id="mobile-menu-btn" class="lg:hidden fixed top-4 left-4 z-40 p-3 glass rounded-xl hover:bg-gray-800/50 focus:outline-none focus:ring-2 focus:ring-primary-500 transition-all" aria-label="Ouvrir le menu" aria-expanded="false" aria-controls="sidebar">
|
||||
<i class="fas fa-bars text-xl" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1 class="logo">
|
||||
<i class="fas fa-headphones"></i> AudiOhm
|
||||
</h1>
|
||||
<aside id="sidebar" class="fixed left-0 top-0 h-full w-64 glass border-r border-gray-800 z-30 transform -translate-x-full lg:translate-x-0 transition-transform duration-300" aria-label="Navigation principale">
|
||||
<div class="p-6">
|
||||
<!-- Logo -->
|
||||
<div class="flex items-center gap-3 mb-8">
|
||||
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-accent-500 flex items-center justify-center shadow-lg" aria-hidden="true">
|
||||
<i class="fas fa-headphones text-white"></i>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold">AudiOhm</h1>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="space-y-2" aria-label="Navigation principale">
|
||||
<a href="#" data-page="home" class="nav-item active flex items-center gap-3 px-4 py-3 rounded-xl bg-primary-500/10 text-primary-400 transition-all focus:outline-none focus:ring-2 focus:ring-primary-500" role="button" aria-current="page">
|
||||
<i class="fas fa-home w-5" aria-hidden="true"></i>
|
||||
<span>Accueil</span>
|
||||
</a>
|
||||
<a href="#" data-page="search" class="nav-item flex items-center gap-3 px-4 py-3 rounded-xl text-gray-400 hover:bg-gray-800/50 transition-all focus:outline-none focus:ring-2 focus:ring-primary-500" role="button">
|
||||
<i class="fas fa-search w-5" aria-hidden="true"></i>
|
||||
<span>Rechercher</span>
|
||||
</a>
|
||||
<a href="#" data-page="library" class="nav-item flex items-center gap-3 px-4 py-3 rounded-xl text-gray-400 hover:bg-gray-800/50 transition-all focus:outline-none focus:ring-2 focus:ring-primary-500" role="button">
|
||||
<i class="fas fa-music w-5" aria-hidden="true"></i>
|
||||
<span>Bibliothèque</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<a href="#" class="nav-item active" data-page="home">
|
||||
<i class="fas fa-home"></i> Accueil
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-page="search">
|
||||
<i class="fas fa-search"></i> Rechercher
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-page="library">
|
||||
<i class="fas fa-music"></i> Bibliothèque
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<button id="logout-btn" class="btn btn-secondary">
|
||||
<i class="fas fa-sign-out-alt"></i> Déconnexion
|
||||
<!-- Logout -->
|
||||
<div class="absolute bottom-0 left-0 right-0 p-6 border-t border-gray-800">
|
||||
<button id="logout-btn" class="w-full flex items-center justify-center gap-2 px-4 py-3 text-gray-400 hover:text-white hover:bg-gray-800/50 rounded-xl transition-all focus:outline-none focus:ring-2 focus:ring-accent-500" aria-label="Se déconnecter">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
<span>Déconnexion</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<main id="main-content" class="lg:ml-64 min-h-screen pb-20 sm:pb-32" tabindex="-1">
|
||||
<!-- Home Page -->
|
||||
<div id="home-page" class="page active">
|
||||
<div class="page-header">
|
||||
<h1>Bienvenue sur AudiOhm 🎵</h1>
|
||||
<p>Votre alternative à Spotify avec streaming YouTube</p>
|
||||
<div id="home-page" class="page active p-4 sm:p-6 lg:p-10 pt-16 sm:pt-6 lg:pt-10">
|
||||
<!-- Header -->
|
||||
<div class="mb-6 sm:mb-8">
|
||||
<h1 class="text-2xl sm:text-3xl lg:text-4xl font-bold mb-2">
|
||||
<span class="bg-gradient-to-r from-primary-400 to-accent-400 bg-clip-text text-transparent">
|
||||
Bienvenue sur AudiOhm
|
||||
</span>
|
||||
<span class="text-xl sm:text-2xl"> 🎵</span>
|
||||
</h1>
|
||||
<p class="text-sm sm:text-base text-gray-400">Votre alternative à Spotify avec streaming YouTube</p>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-bolt"></i> Recherche rapide</h2>
|
||||
<div class="search-bar">
|
||||
<input type="text" id="quick-search" placeholder="Rechercher une musique, un artiste...">
|
||||
<button class="btn btn-primary" id="quick-search-btn">
|
||||
<i class="fas fa-search"></i>
|
||||
<!-- Quick Search -->
|
||||
<section class="mb-8 sm:mb-10" aria-labelledby="quick-search-heading">
|
||||
<h2 id="quick-search-heading" class="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-bolt text-primary-400" aria-hidden="true"></i>
|
||||
Recherche rapide
|
||||
</h2>
|
||||
<div class="flex flex-col sm:flex-row gap-2 sm:gap-3">
|
||||
<label for="quick-search" class="sr-only">Rechercher une musique</label>
|
||||
<input type="search" id="quick-search"
|
||||
class="flex-1 px-3 sm:px-4 py-2 sm:py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all text-sm"
|
||||
placeholder="Rechercher une musique, un artiste..." aria-describedby="quick-search-hint">
|
||||
<button id="quick-search-btn" class="px-4 sm:px-6 py-2 sm:py-3 bg-primary-600 hover:bg-primary-500 rounded-xl font-medium transition-all focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-gray-900 min-h-[44px] sm:min-h-[48px]" aria-label="Lancer la recherche">
|
||||
<i class="fas fa-search text-sm sm:text-base" aria-hidden="true"></i>
|
||||
<span class="sr-only">Rechercher</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-fire"></i> Musiques tendance</h2>
|
||||
<div class="track-list" id="trending-tracks">
|
||||
<div class="loading">
|
||||
<div class="spinner" style="width: 40px; height: 40px; margin: 0 auto 1rem;"></div>
|
||||
Chargement...
|
||||
<!-- Trending -->
|
||||
<section class="mb-8 sm:mb-10">
|
||||
<h2 class="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-fire text-accent-400"></i>
|
||||
Musiques tendance
|
||||
</h2>
|
||||
<div id="trending-tracks" class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3 sm:gap-4">
|
||||
<div class="flex flex-col items-center justify-center py-16 sm:py-20">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 border-4 border-primary-500/30 border-t-primary-500 rounded-full animate-spin mb-3 sm:mb-4"></div>
|
||||
<p class="text-sm sm:text-base text-gray-400">Chargement...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-clock"></i> Récemment écoutées</h2>
|
||||
<div class="track-list" id="recent-tracks">
|
||||
<p style="color: var(--text-secondary);">Aucune écoute récente</p>
|
||||
<!-- Recent -->
|
||||
<section>
|
||||
<h2 class="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-clock text-primary-400"></i>
|
||||
Récemment écoutées
|
||||
</h2>
|
||||
<div id="recent-tracks" class="text-sm sm:text-base text-gray-400">
|
||||
<p>Aucune écoute récente</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Search Page -->
|
||||
<div id="search-page" class="page">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-search"></i> Recherche</h1>
|
||||
</div>
|
||||
<div id="search-page" class="page hidden p-4 sm:p-6 lg:p-10 pt-16 sm:pt-6 lg:pt-10">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold mb-4 sm:mb-6 flex items-center gap-2 sm:gap-3">
|
||||
<i class="fas fa-search text-primary-400" aria-hidden="true"></i>
|
||||
Recherche
|
||||
</h1>
|
||||
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Que voulez-vous écouter ?">
|
||||
<button class="btn btn-primary" id="search-btn">
|
||||
<i class="fas fa-search"></i> Rechercher
|
||||
<div class="flex flex-col sm:flex-row gap-2 sm:gap-3 mb-6 sm:mb-8">
|
||||
<label for="search-input" class="sr-only">Rechercher de la musique</label>
|
||||
<input type="search" id="search-input"
|
||||
class="flex-1 px-3 sm:px-4 py-2 sm:py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all text-sm"
|
||||
placeholder="Que voulez-vous écouter ?" aria-describedby="search-hint">
|
||||
<button id="search-btn" class="px-4 sm:px-8 py-2 sm:py-3 bg-gradient-to-r from-primary-600 to-primary-500 hover:from-primary-500 hover:to-primary-400 rounded-xl font-semibold transition-all focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-2 focus:ring-offset-gray-900 min-h-[44px] sm:min-h-[48px] text-sm sm:text-base">
|
||||
<i class="fas fa-search mr-0 sm:mr-2" aria-hidden="true"></i>
|
||||
<span class="hidden sm:inline">Rechercher</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="search-results" class="search-results"></div>
|
||||
<div id="search-results" role="list" aria-label="Résultats de recherche" class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3 sm:gap-4"></div>
|
||||
</div>
|
||||
|
||||
<!-- Library Page -->
|
||||
<div id="library-page" class="page">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-music"></i> Ma Bibliothèque</h1>
|
||||
<div id="library-page" class="page hidden p-4 sm:p-6 lg:p-10 pt-16 sm:pt-6 lg:pt-10">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold mb-4 sm:mb-6 flex items-center gap-2 sm:gap-3">
|
||||
<i class="fas fa-music text-accent-400"></i>
|
||||
Ma Bibliothèque
|
||||
</h1>
|
||||
|
||||
<!-- Tabs Navigation -->
|
||||
<div class="flex gap-2 mb-6 overflow-x-auto pb-2" role="tablist" aria-label="Onglets de la bibliothèque">
|
||||
<button id="tab-playlists" class="library-tab active px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
role="tab"
|
||||
aria-selected="true"
|
||||
aria-controls="library-playlists"
|
||||
onclick="switchLibraryTab('playlists')">
|
||||
<i class="fas fa-list mr-2"></i>
|
||||
<span class="hidden sm:inline">Playlists</span>
|
||||
<span class="sm:hidden">Playlists</span>
|
||||
</button>
|
||||
<button id="tab-liked" class="library-tab px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
role="tab"
|
||||
aria-selected="false"
|
||||
aria-controls="library-liked"
|
||||
onclick="switchLibraryTab('liked')">
|
||||
<i class="fas fa-heart mr-2"></i>
|
||||
<span class="hidden sm:inline">Titres likés</span>
|
||||
<span class="sm:hidden">Likés</span>
|
||||
</button>
|
||||
<button id="tab-history" class="library-tab px-4 py-2 rounded-lg font-medium transition-all whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
role="tab"
|
||||
aria-selected="false"
|
||||
aria-controls="library-history"
|
||||
onclick="switchLibraryTab('history')">
|
||||
<i class="fas fa-history mr-2"></i>
|
||||
<span class="hidden sm:inline">Historique</span>
|
||||
<span class="sm:hidden">Historique</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-list"></i> Mes Playlists</h2>
|
||||
<div class="playlist-list" id="my-playlists">
|
||||
<div class="loading">
|
||||
<div class="spinner" style="width: 40px; height: 40px; margin: 0 auto 1rem;"></div>
|
||||
Chargement...
|
||||
</div>
|
||||
<!-- Tab Panels -->
|
||||
<div class="tab-panels">
|
||||
<!-- Playlists Tab -->
|
||||
<div id="library-playlists" class="tab-panel active" role="tabpanel" aria-labelledby="tab-playlists">
|
||||
<section class="mb-8 sm:mb-10">
|
||||
<div class="flex items-center justify-between mb-3 sm:mb-4">
|
||||
<h2 class="text-lg sm:text-xl font-semibold flex items-center gap-2">
|
||||
<i class="fas fa-list text-primary-400"></i>
|
||||
Mes Playlists
|
||||
</h2>
|
||||
<button id="create-playlist-btn" class="px-3 sm:px-4 py-2 bg-primary-600 hover:bg-primary-500 rounded-lg font-medium transition-all text-sm flex items-center gap-2 focus:outline-none focus:ring-2 focus:ring-primary-400" aria-label="Créer une nouvelle playlist">
|
||||
<i class="fas fa-plus"></i>
|
||||
<span class="hidden sm:inline">Créer</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="my-playlists" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
|
||||
<div class="flex flex-col items-center justify-center py-16 sm:py-20">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 border-4 border-primary-500/30 border-t-primary-500 rounded-full animate-spin mb-3 sm:mb-4"></div>
|
||||
<p class="text-sm sm:text-base text-gray-400">Chargement...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2><i class="fas fa-heart"></i> Titres likés</h2>
|
||||
<div class="track-list" id="liked-tracks">
|
||||
<p style="color: var(--text-secondary);">Aucun titre liké pour le moment</p>
|
||||
<!-- Liked Tracks Tab -->
|
||||
<div id="library-liked" class="tab-panel hidden" role="tabpanel" aria-labelledby="tab-liked">
|
||||
<section>
|
||||
<h2 class="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-heart text-accent-400"></i>
|
||||
Titres likés
|
||||
</h2>
|
||||
<div id="liked-tracks" class="space-y-2 max-w-4xl">
|
||||
<div class="flex flex-col items-center justify-center py-16 sm:py-20">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 border-4 border-accent-500/30 border-t-accent-500 rounded-full animate-spin mb-3 sm:mb-4"></div>
|
||||
<p class="text-sm sm:text-base text-gray-400">Chargement...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Listening History Tab -->
|
||||
<div id="library-history" class="tab-panel hidden" role="tabpanel" aria-labelledby="tab-history">
|
||||
<section>
|
||||
<h2 class="text-lg sm:text-xl font-semibold mb-3 sm:mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-history text-primary-400"></i>
|
||||
Historique d'écoute
|
||||
</h2>
|
||||
<div id="listening-history" class="space-y-2 max-w-4xl">
|
||||
<div class="flex flex-col items-center justify-center py-16 sm:py-20">
|
||||
<div class="w-10 h-10 sm:w-12 sm:h-12 border-4 border-primary-500/30 border-t-primary-500 rounded-full animate-spin mb-3 sm:mb-4"></div>
|
||||
<p class="text-sm sm:text-base text-gray-400">Chargement...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Player -->
|
||||
<div id="player" class="player">
|
||||
<div class="player-info">
|
||||
<img id="player-cover" src="/static/img/default-cover.png" alt="Cover" class="player-cover">
|
||||
<div class="player-details">
|
||||
<div id="player-title" class="player-title">Aucun titre</div>
|
||||
<div id="player-artist" class="player-artist">-</div>
|
||||
<div id="player" class="hidden fixed bottom-0 left-0 right-0 glass border-t border-gray-800 px-2 sm:px-4 py-2 sm:py-3 z-40" role="region" aria-label="Lecteur audio">
|
||||
<!-- Mobile Compact View -->
|
||||
<div class="sm:hidden flex items-center gap-2">
|
||||
<!-- Track Info (Mobile) -->
|
||||
<div class="flex items-center gap-2 flex-1 min-w-0">
|
||||
<img id="player-cover" src="/static/img/default-cover.png" alt=""
|
||||
class="w-10 h-10 rounded-lg object-cover bg-gray-800 flex-shrink-0" aria-hidden="true">
|
||||
<button id="mobile-play-btn" class="p-2 bg-primary-600 rounded-full flex-shrink-0" aria-label="Lecture/Pause">
|
||||
<i class="fas fa-play text-xs"></i>
|
||||
</button>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div id="player-title" class="font-medium text-xs truncate" aria-live="polite">Aucun titre</div>
|
||||
<div id="player-artist" class="text-xs text-gray-400 truncate" aria-live="polite">-</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Actions (Mobile) -->
|
||||
<div class="flex items-center gap-1 flex-shrink-0">
|
||||
<button id="mobile-like-btn" class="p-2 text-gray-400 hover:text-accent-400 transition-all" aria-label="J'aime">
|
||||
<i class="far fa-heart text-sm"></i>
|
||||
</button>
|
||||
<button id="mobile-expand-btn" class="p-2 text-gray-400 hover:text-white transition-all" aria-label="Agrandir le player">
|
||||
<i class="fas fa-chevron-up text-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-controls">
|
||||
<button class="btn-control" id="shuffle-btn" title="Aléatoire">
|
||||
<i class="fas fa-random"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="prev-btn" title="Précédent">
|
||||
<i class="fas fa-step-backward"></i>
|
||||
</button>
|
||||
<button class="btn-control btn-play" id="play-btn" title="Play/Pause">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="next-btn" title="Suivant">
|
||||
<i class="fas fa-step-forward"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="repeat-btn" title="Répéter">
|
||||
<i class="fas fa-redo"></i>
|
||||
</button>
|
||||
<!-- Desktop Full View -->
|
||||
<div class="hidden sm:flex items-center gap-2 lg:gap-4 max-w-screen-2xl mx-auto">
|
||||
<!-- Track Info -->
|
||||
<div class="flex items-center gap-2 lg:gap-3 flex-shrink-0 w-32 lg:w-64">
|
||||
<img id="player-cover-desktop" src="/static/img/default-cover.png" alt=""
|
||||
class="w-10 h-10 lg:w-14 lg:h-14 rounded-lg object-cover bg-gray-800 flex-shrink-0" aria-hidden="true">
|
||||
<div class="min-w-0 flex-1 hidden sm:block">
|
||||
<div id="player-title-desktop" class="font-medium text-xs lg:text-sm truncate" aria-live="polite">Aucun titre</div>
|
||||
<div id="player-artist-desktop" class="text-xs text-gray-400 truncate" aria-live="polite">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="flex-1 flex flex-col items-center gap-1 lg:gap-2">
|
||||
<!-- Main Controls -->
|
||||
<div class="flex items-center gap-1 lg:gap-2">
|
||||
<button id="shuffle-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white hover:bg-gray-800/50 rounded-lg transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="Mode aléatoire" aria-pressed="false">
|
||||
<i class="fas fa-random text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="prev-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white hover:bg-gray-800/50 rounded-lg transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="Piste précédente">
|
||||
<i class="fas fa-step-backward text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="play-btn" class="p-2 lg:p-4 bg-primary-600 hover:bg-primary-500 rounded-full transition-all transform hover:scale-110 focus:outline-none focus:ring-4 focus:ring-primary-500/50 min-w-[40px] lg:min-w-[52px] min-h-[40px] lg:min-h-[52px] flex items-center justify-center" aria-label="Lecture" aria-pressed="false">
|
||||
<i class="fas fa-play text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="next-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white hover:bg-gray-800/50 rounded-lg transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="Piste suivante">
|
||||
<i class="fas fa-step-forward text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="repeat-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white hover:bg-gray-800/50 rounded-lg transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="Répéter" aria-pressed="false">
|
||||
<i class="fas fa-redo text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Progress -->
|
||||
<div class="flex items-center gap-2 lg:gap-3 w-full max-w-xl px-2">
|
||||
<span id="current-time" class="text-xs text-gray-400 w-8 lg:w-10 text-right flex-shrink-0" aria-live="off" aria-label="Temps écoulé">0:00</span>
|
||||
<label for="progress-bar" class="sr-only">Barre de progression</label>
|
||||
<input type="range" id="progress-bar" min="0" max="100" value="0"
|
||||
class="flex-1 h-1" aria-label="Progression de la lecture" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" aria-valuetext="0%">
|
||||
<span id="total-time" class="text-xs text-gray-400 w-8 lg:w-10 flex-shrink-0" aria-live="off" aria-label="Durée totale">0:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Volume & Actions -->
|
||||
<div class="flex items-center gap-1 lg:gap-2 flex-shrink-0">
|
||||
<button id="mute-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 rounded-lg min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="Couper le son" aria-pressed="false">
|
||||
<i class="fas fa-volume-up text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<label for="volume-bar" class="sr-only">Volume</label>
|
||||
<input type="range" id="volume-bar" min="0" max="100" value="100"
|
||||
class="w-12 lg:w-20 hidden md:block" aria-label="Volume" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" aria-valuetext="100%">
|
||||
<div class="w-px h-6 lg:h-8 bg-gray-700 mx-1 lg:mx-2 hidden md:block" aria-hidden="true"></div>
|
||||
<button id="like-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-accent-400 transition-all focus:outline-none focus:ring-2 focus:ring-accent-500 rounded-lg min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center" aria-label="J'aime" aria-pressed="false">
|
||||
<i class="far fa-heart text-sm lg:text-base" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="queue-open-btn" class="p-1.5 lg:p-3 text-gray-400 hover:text-white transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 rounded-lg min-w-[36px] lg:min-w-[44px] min-h-[36px] lg:min-h-[44px] flex items-center justify-center relative" aria-label="File d'attente" aria-expanded="false">
|
||||
<i class="fas fa-list-ul text-sm lg:text-base" aria-hidden="true"></i>
|
||||
<span id="queue-count" class="absolute -top-1 -right-1 bg-primary-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center font-bold">0</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-progress">
|
||||
<span id="current-time" class="time">0:00</span>
|
||||
<input type="range" id="progress-bar" min="0" max="100" value="0" class="progress-bar">
|
||||
<span id="total-time" class="time">0:00</span>
|
||||
<audio id="audio-player" preload="none" class="hidden"></audio>
|
||||
</div>
|
||||
|
||||
<!-- Create Playlist Modal -->
|
||||
<div id="create-playlist-modal" class="hidden fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" role="dialog" aria-labelledby="create-playlist-title" aria-modal="true" aria-hidden="true">
|
||||
<div class="glass-card rounded-2xl p-6 w-full max-w-md animate-fadeIn">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 id="create-playlist-title" class="text-xl font-bold">Créer une playlist</h2>
|
||||
<button id="close-create-playlist-modal" class="p-2 text-gray-400 hover:text-white transition-all rounded-lg hover:bg-gray-700/50 focus:outline-none focus:ring-2 focus:ring-primary-500" aria-label="Fermer">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="create-playlist-form" aria-label="Formulaire de création de playlist">
|
||||
<div class="mb-4">
|
||||
<label for="playlist-name" class="block text-sm font-medium text-gray-300 mb-2">Nom de la playlist *</label>
|
||||
<input type="text" id="playlist-name" required
|
||||
class="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all"
|
||||
placeholder="Ma nouvelle playlist" aria-describedby="playlist-name-hint">
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<label for="playlist-description" class="block text-sm font-medium text-gray-300 mb-2">Description (optionnel)</label>
|
||||
<textarea id="playlist-description" rows="3"
|
||||
class="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-transparent focus:outline-none transition-all resize-none"
|
||||
placeholder="Décrivez votre playlist..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button type="button" id="cancel-create-playlist"
|
||||
class="flex-1 px-4 py-3 bg-gray-700 hover:bg-gray-600 rounded-xl font-medium transition-all focus:outline-none focus:ring-2 focus:ring-gray-500">
|
||||
Annuler
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="flex-1 px-4 py-3 bg-gradient-to-r from-primary-600 to-primary-500 hover:from-primary-500 hover:to-primary-400 rounded-xl font-semibold transition-all transform hover:scale-[1.02] active:scale-[0.98] focus:outline-none focus:ring-2 focus:ring-primary-400 shadow-lg">
|
||||
Créer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Playlist Details Modal -->
|
||||
<div id="playlist-details-modal" class="hidden fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4" role="dialog" aria-labelledby="playlist-details-title" aria-modal="true" aria-hidden="true">
|
||||
<div class="glass-card rounded-2xl w-full max-w-3xl max-h-[90vh] overflow-hidden animate-fadeIn flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="p-6 border-b border-gray-800">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 id="playlist-details-title" class="text-xl font-bold truncate flex-1">Titre de la playlist</h2>
|
||||
<button id="close-playlist-details" class="p-2 text-gray-400 hover:text-white transition-all rounded-lg hover:bg-gray-700/50 focus:outline-none focus:ring-2 focus:ring-primary-500 ml-2" aria-label="Fermer">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p id="playlist-details-description" class="text-gray-400 text-sm mb-4"></p>
|
||||
<div class="flex items-center gap-3">
|
||||
<button id="play-playlist-btn" class="px-4 py-2 bg-primary-600 hover:bg-primary-500 rounded-lg font-medium transition-all flex items-center gap-2 focus:outline-none focus:ring-2 focus:ring-primary-400">
|
||||
<i class="fas fa-play"></i>
|
||||
Lecture
|
||||
</button>
|
||||
<button id="shuffle-playlist-btn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg font-medium transition-all flex items-center gap-2 focus:outline-none focus:ring-2 focus:ring-gray-500">
|
||||
<i class="fas fa-random"></i>
|
||||
Aléatoire
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tracks -->
|
||||
<div id="playlist-tracks" class="flex-1 overflow-y-auto p-4">
|
||||
<div class="flex flex-col items-center justify-center py-12 text-gray-400">
|
||||
<i class="fas fa-music text-4xl mb-4"></i>
|
||||
<p class="text-lg">Aucune piste</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Queue Panel -->
|
||||
<div id="queue-panel" class="fixed inset-y-0 right-0 w-full sm:w-96 glass border-l border-gray-800 z-50 transform translate-x-full transition-transform duration-300 ease-out" role="dialog" aria-labelledby="queue-title" aria-hidden="true">
|
||||
<!-- Header -->
|
||||
<div class="p-4 sm:p-6 border-b border-gray-800">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 id="queue-title" class="text-lg sm:text-xl font-bold flex items-center gap-2">
|
||||
<i class="fas fa-list-ul text-primary-400"></i>
|
||||
File d'attente
|
||||
<span id="queue-count-badge" class="text-sm font-normal text-gray-400">(0)</span>
|
||||
</h2>
|
||||
<button id="queue-close-btn" class="p-2 text-gray-400 hover:text-white transition-all focus:outline-none focus:ring-2 focus:ring-primary-500 rounded-lg" aria-label="Fermer la file d'attente">
|
||||
<i class="fas fa-times text-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Queue Actions -->
|
||||
<div class="flex items-center gap-2">
|
||||
<button id="queue-shuffle-btn" class="flex-1 px-4 py-2 bg-gray-800/50 hover:bg-gray-700/50 text-gray-300 hover:text-white rounded-lg transition-all text-sm font-medium focus:outline-none focus:ring-2 focus:ring-primary-500 flex items-center justify-center gap-2" aria-label="Mélanger la file d'attente">
|
||||
<i class="fas fa-random"></i>
|
||||
Mélanger
|
||||
</button>
|
||||
<button id="queue-clear-btn" class="flex-1 px-4 py-2 bg-gray-800/50 hover:bg-red-600/30 text-gray-300 hover:text-red-400 rounded-lg transition-all text-sm font-medium focus:outline-none focus:ring-2 focus:ring-red-500 flex items-center justify-center gap-2" aria-label="Vider la file d'attente">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
Vider
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-volume">
|
||||
<button class="btn-control" id="mute-btn" title="Muet">
|
||||
<i class="fas fa-volume-up"></i>
|
||||
</button>
|
||||
<input type="range" id="volume-bar" min="0" max="100" value="100" class="volume-bar">
|
||||
<!-- Queue List -->
|
||||
<div id="queue-list" class="p-4 overflow-y-auto" style="max-height: calc(100vh - 200px);" role="list" aria-label="Pistes dans la file d'attente">
|
||||
<!-- Queue items will be dynamically inserted here -->
|
||||
<div class="flex flex-col items-center justify-center py-12 text-gray-400">
|
||||
<i class="fas fa-list-ul text-4xl mb-4"></i>
|
||||
<p class="text-lg">File d'attente vide</p>
|
||||
<p class="text-sm mt-2">Cliquez sur une piste pour l'ajouter</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-actions">
|
||||
<button class="btn-control" id="like-btn" title="J'aime">
|
||||
<i class="far fa-heart"></i>
|
||||
</button>
|
||||
<button class="btn-control" id="playlist-btn" title="Ajouter à la playlist">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<audio id="audio-player" preload="none"></audio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Fallback: Hide loading screen after 5 seconds if JS fails
|
||||
setTimeout(function() {
|
||||
const loadingScreen = document.getElementById('loading-screen');
|
||||
if (loadingScreen) {
|
||||
console.error('Loading screen timeout - JS may have failed to load');
|
||||
loadingScreen.style.display = 'none';
|
||||
}
|
||||
}, 5000);
|
||||
</script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user