From 3c0bd5adf27aa9d8bd26684d793646146ef57280 Mon Sep 17 00:00:00 2001 From: "[dyad]" Date: Thu, 6 Nov 2025 13:31:30 +0100 Subject: [PATCH] [dyad] wrote 3 file(s) --- public/css/styles.css | 170 ++++++++++++++++++++++++++++++++++++++++++ public/index.html | 132 ++++++++++++++++++++++++++++++++ public/js/auth.js | 90 ++++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 public/css/styles.css create mode 100644 public/index.html create mode 100644 public/js/auth.js diff --git a/public/css/styles.css b/public/css/styles.css new file mode 100644 index 0000000..8eaab9b --- /dev/null +++ b/public/css/styles.css @@ -0,0 +1,170 @@ +/* Base styles */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + +:root { + --primary-color: #3B82F6; + --background-color: #FAFAFA; + --card-background: #FFFFFF; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + line-height: 1.5; + color: #111827; + background-color: var(--background-color); +} + +/* Utility classes */ +.hidden { + display: none !important; +} + +/* Transitions */ +.transition { + transition: all 200ms ease-in-out; +} + +/* Form elements */ +input[type="text"], +input[type="password"], +textarea { + width: 100%; + padding: 0.5rem 0.75rem; + border: 1px solid #E5E7EB; + border-radius: 0.375rem; + background-color: white; +} + +input[type="text"]:focus, +input[type="password"]:focus, +textarea:focus { + outline: none; + border-color: var(--primary-color); + ring: 2px var(--primary-color); +} + +/* Buttons */ +.btn { + padding: 0.5rem 1rem; + border-radius: 0.375rem; + font-weight: 500; + cursor: pointer; + transition: all 200ms; +} + +.btn-primary { + background-color: var(--primary-color); + color: white; +} + +.btn-primary:hover { + background-color: #2563EB; +} + +/* Note item in sidebar */ +.note-item { + padding: 0.75rem; + border-radius: 0.375rem; + cursor: pointer; + transition: background-color 200ms; +} + +.note-item:hover { + background-color: #F3F4F6; +} + +.note-item.active { + background-color: #EFF6FF; + border-left: 3px solid var(--primary-color); +} + +/* Todo items */ +.todo-item { + display: flex; + align-items: center; + padding: 0.5rem; + border-radius: 0.375rem; + background-color: #F9FAFB; +} + +.todo-item input[type="checkbox"] { + margin-right: 0.75rem; +} + +.todo-item.completed { + text-decoration: line-through; + color: #6B7280; +} + +/* Image thumbnails */ +.image-thumbnail { + position: relative; + aspect-ratio: 1; + border-radius: 0.375rem; + overflow: hidden; +} + +.image-thumbnail img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.image-thumbnail .delete-btn { + position: absolute; + top: 0.25rem; + right: 0.25rem; + padding: 0.25rem; + background-color: rgba(0, 0, 0, 0.5); + border-radius: 9999px; + color: white; + cursor: pointer; +} + +/* Admin panel styles */ +.user-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem; + background-color: #F9FAFB; + border-radius: 0.375rem; +} + +.user-item .actions { + display: flex; + gap: 0.5rem; +} + +/* Animations */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.fade-in { + animation: fadeIn 200ms ease-in-out; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + aside { + width: 100%; + height: auto; + position: relative; + } + + main { + margin-left: 0; + } + + .note-editor { + margin: 1rem; + } +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..d9dcfff --- /dev/null +++ b/public/index.html @@ -0,0 +1,132 @@ + + + + + + Notes & Todos + + + + + + +
+
+

Connexion

+
+
+ + +
+
+ + +
+ +
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/public/js/auth.js b/public/js/auth.js new file mode 100644 index 0000000..c5b2733 --- /dev/null +++ b/public/js/auth.js @@ -0,0 +1,90 @@ +class AuthService { + constructor() { + this.token = localStorage.getItem('token'); + this.user = JSON.parse(localStorage.getItem('user')); + this.loginForm = document.getElementById('login-form'); + this.logoutBtn = document.getElementById('logout-btn'); + this.userInfo = document.getElementById('user-info'); + + this.setupEventListeners(); + this.updateUI(); + } + + setupEventListeners() { + this.loginForm.addEventListener('submit', (e) => this.handleLogin(e)); + this.logoutBtn.addEventListener('click', () => this.handleLogout()); + } + + async handleLogin(e) { + e.preventDefault(); + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + try { + const response = await fetch('/api/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, password }) + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || 'Erreur de connexion'); + } + + this.token = data.token; + this.user = data.user; + localStorage.setItem('token', this.token); + localStorage.setItem('user', JSON.stringify(this.user)); + + this.updateUI(); + window.app.init(); // Initialize main app after login + } catch (error) { + alert(error.message); + } + } + + handleLogout() { + localStorage.removeItem('token'); + localStorage.removeItem('user'); + this.token = null; + this.user = null; + this.updateUI(); + window.location.reload(); + } + + updateUI() { + const authContainer = document.getElementById('auth-container'); + const appContainer = document.getElementById('app-container'); + const adminPanel = document.getElementById('admin-panel'); + + if (this.token && this.user) { + authContainer.classList.add('hidden'); + appContainer.classList.remove('hidden'); + this.userInfo.textContent = this.user.username; + + // Show admin panel button if user is admin + if (this.user.is_admin) { + const adminBtn = document.createElement('button'); + adminBtn.className = 'ml-4 text-sm text-gray-600 hover:text-gray-900'; + adminBtn.textContent = 'Admin'; + adminBtn.onclick = () => adminPanel.classList.remove('hidden'); + this.userInfo.parentNode.insertBefore(adminBtn, this.logoutBtn); + } + } else { + authContainer.classList.remove('hidden'); + appContainer.classList.add('hidden'); + } + } + + getHeaders() { + return { + 'Authorization': `Bearer ${this.token}`, + 'Content-Type': 'application/json' + }; + } +} + +// Initialize auth service +window.auth = new AuthService(); \ No newline at end of file