From 4e41ca9bf7c8be95493389848945145c8f5bc02b Mon Sep 17 00:00:00 2001 From: Francesco Picone Date: Wed, 3 Dec 2025 18:35:21 +0100 Subject: [PATCH] fix --- README.md | 485 ++++++++++++++++++++++++++++ admin/dashboard.php | 203 ++++++++++++ admin/lesson_create.php | 286 +++++++++++++++++ admin/lessons.php | 148 +++++++++ admin/purchases.php | 165 ++++++++++ admin/users.php | 145 +++++++++ assets/css/style.css | 683 ++++++++++++++++++++++++++++++++++++++++ assets/js/main.js | 52 +++ database/schema.sql | 243 ++++++++++++++ forgot_password.php | 119 +++++++ includes/config.php | 172 ++++++++++ includes/functions.php | 564 +++++++++++++++++++++++++++++++++ includes/logout.php | 33 ++ index.php | 148 +++++++++ lesson.php | 262 +++++++++++++++ login.php | 148 +++++++++ process_payment.php | 104 ++++++ register.php | 187 +++++++++++ reset_password.php | 156 +++++++++ user/catalog.php | 192 +++++++++++ user/dashboard.php | 132 ++++++++ user/profile.php | 209 ++++++++++++ 22 files changed, 4836 insertions(+) create mode 100644 README.md create mode 100644 admin/dashboard.php create mode 100644 admin/lesson_create.php create mode 100644 admin/lessons.php create mode 100644 admin/purchases.php create mode 100644 admin/users.php create mode 100644 assets/css/style.css create mode 100644 assets/js/main.js create mode 100644 database/schema.sql create mode 100644 forgot_password.php create mode 100644 includes/config.php create mode 100644 includes/functions.php create mode 100644 includes/logout.php create mode 100644 index.php create mode 100644 lesson.php create mode 100644 login.php create mode 100644 process_payment.php create mode 100644 register.php create mode 100644 reset_password.php create mode 100644 user/catalog.php create mode 100644 user/dashboard.php create mode 100644 user/profile.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..b215b43 --- /dev/null +++ b/README.md @@ -0,0 +1,485 @@ +# 🧘‍♀️ Pilates Platform + +Piattaforma completa per vendere videolezioni e lezioni live di Pilates. Sviluppata in PHP puro con design minimale bianco e celeste. + +## 📋 Caratteristiche Principali + +### Area Pubblica +- **Homepage** con lezioni demo gratuite +- **Sistema di autenticazione** completo (login, registrazione, recupero password) +- **Visualizzazione lezioni** con integrazione video (YouTube, Vimeo, locale) +- **Pagamento sicuro** tramite PayPal + +### Area Utente +- **Dashboard personale** con lezioni acquistate +- **Catalogo completo** con filtri per tipo e livello +- **Gestione profilo** (dati personali e cambio password) +- **Accesso illimitato** alle lezioni acquistate + +### Area Amministratore +- **Dashboard** con statistiche in tempo reale +- **Gestione lezioni** (crea, modifica, elimina) +- **Gestione utenti** (visualizza, blocca/sblocca) +- **Storico acquisti** con dettagli pagamenti PayPal +- **Report e analisi** vendite + +### Funzionalità Tecniche +- ✅ **Sicurezza**: Password crittografate con bcrypt +- ✅ **Database**: MySQL con PDO per prevenire SQL injection +- ✅ **Sessioni**: Gestione sicura con timeout automatico +- ✅ **Responsive**: Design mobile-first +- ✅ **Codice commentato**: Ogni funzione è documentata per facilità di manutenzione +- ✅ **Log attività**: Tracciamento delle azioni importanti +- ✅ **Soft delete**: I dati eliminati non sono rimossi definitivamente + +--- + +## 🚀 Installazione + +### Requisiti +- **PHP** 7.4 o superiore +- **MySQL** 5.7 o superiore (o MariaDB 10.3+) +- **Web Server** (Apache, Nginx, o PHP built-in server per sviluppo) +- **Account PayPal** Sandbox (per test) o Business (per produzione) + +### Passo 1: Copia i File +```bash +# Copia tutti i file nella cartella del tuo web server +# Esempio per XAMPP/WAMP: +C:\xampp\htdocs\pilates-platform\ + +# Esempio per Linux: +/var/www/html/pilates-platform/ +``` + +### Passo 2: Crea il Database +1. Apri phpMyAdmin (o il tuo client MySQL preferito) +2. Esegui lo script `database/schema.sql` +3. Questo creerà: + - Il database `pilates_platform` + - Tutte le tabelle necessarie + - Un utente amministratore di default + - Alcune lezioni demo di esempio + +**Credenziali Admin di Default:** +- Email: `admin@pilatesstudio.com` +- Password: `admin123` +- ⚠️ **IMPORTANTE**: Cambia questa password subito dopo il primo accesso! + +### Passo 3: Configura l'Applicazione +Apri il file `includes/config.php` e modifica: + +#### Database +```php +define('DB_HOST', 'localhost'); // Host del database +define('DB_NAME', 'pilates_platform'); // Nome database +define('DB_USER', 'root'); // Username (es: root per localhost) +define('DB_PASS', ''); // Password database +``` + +#### Sito +```php +define('SITE_URL', 'http://localhost/pilates-platform'); // URL del sito +define('ADMIN_EMAIL', 'tua-email@esempio.com'); // Tua email +``` + +#### Sicurezza +```php +// Genera una chiave casuale sicura (es: usando password_hash) +define('SECRET_KEY', 'CAMBIA-QUESTA-CHIAVE-CON-STRINGA-CASUALE-LUNGA-E-SICURA'); +``` + +#### PayPal Sandbox (per Test) +1. Vai su [PayPal Developer](https://developer.paypal.com/) +2. Accedi con il tuo account PayPal +3. Vai su "My Apps & Credentials" +4. Crea un'app nella sezione Sandbox +5. Copia Client ID e Secret + +```php +define('PAYPAL_SANDBOX', true); // true per test, false per produzione +define('PAYPAL_CLIENT_ID', 'il-tuo-client-id-sandbox'); +define('PAYPAL_SECRET', 'il-tuo-secret-sandbox'); +``` + +### Passo 4: Configura i Permessi (Linux/Mac) +```bash +# Rendi scrivibile la cartella uploads +chmod 755 uploads/ +``` + +### Passo 5: Avvia il Server +#### Con PHP Built-in (per sviluppo) +```bash +cd pilates-platform +php -S localhost:8000 +``` +Poi apri: `http://localhost:8000` + +#### Con XAMPP/WAMP +1. Avvia Apache e MySQL +2. Apri: `http://localhost/pilates-platform` + +--- + +## 📖 Guida all'Uso + +### Per Amministratori + +#### Primo Accesso +1. Vai su `http://tuo-sito/login.php` +2. Accedi con le credenziali di default +3. Vai su "Area Admin" → "Profilo" e cambia la password + +#### Creare una Nuova Lezione +1. **Area Admin** → **Gestione Lezioni** → **Nuova Lezione** +2. Compila i campi: + - **Titolo**: Nome della lezione + - **Descrizione**: Descrizione dettagliata + - **Tipo**: Scegli Video o Live + - **Per Video**: + - Piattaforma: Local, YouTube, Vimeo, o S3 + - URL Video: Link al video + - Durata: Minuti + - **Per Live**: + - Piattaforma: Zoom, Google Meet, ecc. + - Link: URL della sessione + - Data e Ora: Quando si terrà + - **Livello**: Principiante, Intermedio, Avanzato + - **Categoria**: Es. Mat Work, Reformer, Stretching + - **Prezzo**: In euro + - **Opzioni**: + - ✓ Lezione Demo (gratuita per tutti) + - ✓ Lezione Attiva (visibile agli utenti) +3. Clicca **Crea Lezione** + +#### Gestire gli Utenti +1. **Area Admin** → **Gestione Utenti** +2. Puoi vedere: + - Tutti gli utenti registrati + - Numero acquisti per utente + - Totale speso + - Data ultimo accesso +3. Azioni disponibili: + - **Blocca/Sblocca**: Disabilita temporaneamente un account + +#### Visualizzare le Statistiche +La **Dashboard** mostra: +- 📊 Utenti registrati +- 🎥 Lezioni totali +- 💰 Acquisti completati +- 💵 Guadagni totali +- 📈 Lezioni più vendute +- 📋 Ultimi acquisti + +### Per Utenti + +#### Registrazione +1. Vai su `http://tuo-sito/register.php` +2. Compila il form con: + - Nome e Cognome + - Email + - Password (min 6 caratteri) +3. Verrai automaticamente loggato + +#### Navigare il Catalogo +1. **Dashboard Utente** → **Catalogo** +2. Usa i filtri per: + - Tipo (Video/Live) + - Livello (Principiante/Intermedio/Avanzato) +3. Clicca su una lezione per vedere i dettagli + +#### Acquistare una Lezione +1. Apri una lezione dal catalogo +2. Se non è demo, vedrai il prezzo +3. Clicca sul pulsante PayPal +4. Completa il pagamento +5. La lezione apparirà in "Le Mie Lezioni" + +#### Guardare una Lezione +1. **Dashboard** → **Le Mie Lezioni** +2. Clicca su **Guarda** (per video) o **Partecipa** (per live) +3. Accesso illimitato per sempre! + +#### Modificare il Profilo +1. **Dashboard** → **Profilo** +2. Puoi cambiare: + - Nome e Cognome + - Email + - Telefono + - Password + +--- + +## 🎨 Personalizzazione + +### Colori +Apri `assets/css/style.css` e modifica le variabili CSS: + +```css +:root { + --primary-color: #4A90E2; /* Celeste principale */ + --primary-light: #7AB8F5; /* Celeste chiaro */ + --primary-dark: #357ABD; /* Celeste scuro */ + --secondary-color: #E8F4F8; /* Celeste molto chiaro */ +} +``` + +### Logo +Sostituisci il testo "Pilates Studio" in `index.php` e negli altri file con: +```html + +``` + +### Email +Per usare SMTP invece di `mail()` PHP, decomenta in `includes/config.php`: +```php +define('SMTP_HOST', 'smtp.gmail.com'); +define('SMTP_PORT', 587); +define('SMTP_USERNAME', 'tua-email@gmail.com'); +define('SMTP_PASSWORD', 'tua-app-password'); +define('SMTP_ENCRYPTION', 'tls'); +``` + +--- + +## 🎥 Hosting Video + +### Opzione 1: YouTube (Consigliato per Iniziare) +1. Carica video su YouTube come **Non in elenco** +2. Copia l'URL (es: `https://youtube.com/watch?v=ABC123`) +3. Quando crei la lezione: + - Piattaforma: YouTube + - URL: Incolla il link + +### Opzione 2: Vimeo +1. Carica su Vimeo +2. Imposta privacy su "Nascosto" +3. Copia URL e usa come YouTube + +### Opzione 3: File Locali +1. Carica il file in `uploads/videos/` +2. URL Video: `/uploads/videos/nome-file.mp4` +3. ⚠️ Non consigliato per file grandi (limiti server) + +### Opzione 4: AWS S3 (Professionale) +Per grandi quantità di video, usa Amazon S3: +1. Crea un bucket S3 +2. Configura in `includes/config.php`: +```php +define('AWS_ACCESS_KEY', 'tua-key'); +define('AWS_SECRET_KEY', 'tua-secret'); +define('AWS_REGION', 'eu-west-1'); +define('AWS_BUCKET', 'pilates-videos'); +``` +3. Carica video su S3 +4. Usa URL S3 nelle lezioni + +--- + +## 💳 Configurare PayPal + +### Modalità Test (Sandbox) +1. Vai su [developer.paypal.com](https://developer.paypal.com) +2. Crea un'app Sandbox +3. Copia Client ID e Secret in `config.php` +4. Usa gli account test per simulare pagamenti + +### Modalità Produzione (Reale) +1. Vai su [paypal.com/businessmanage](https://www.paypal.com/businessmanage) +2. Crea app Live +3. Copia credenziali Live +4. In `config.php`: +```php +define('PAYPAL_SANDBOX', false); // Passa a false! +define('PAYPAL_LIVE_CLIENT_ID', 'tuo-client-id-live'); +define('PAYPAL_LIVE_SECRET', 'tuo-secret-live'); +``` + +--- + +## 🔒 Sicurezza + +### Checklist Produzione +Prima di mettere online: + +- [ ] Cambia password admin di default +- [ ] Cambia `SECRET_KEY` in `config.php` +- [ ] Imposta `DEBUG_MODE` a `false` +- [ ] Usa HTTPS (certificato SSL) +- [ ] Limita permessi cartelle (755 per cartelle, 644 per file) +- [ ] Backup regolari del database +- [ ] Configura SMTP per email +- [ ] Testa tutti i flussi di pagamento +- [ ] Verifica che `includes/config.php` NON sia accessibile via web + +### File .htaccess (per Apache) +Crea `.htaccess` nella cartella `includes/`: +```apache +# Nega accesso a config.php + + Require all denied + +``` + +--- + +## 🛠️ Risoluzione Problemi + +### Errore: "Errore connessione database" +- Verifica che MySQL sia avviato +- Controlla credenziali in `config.php` +- Assicurati che il database esista + +### Le email non vengono inviate +- Configura SMTP in `config.php` +- Verifica che `mail()` PHP funzioni sul tuo server +- Controlla la cartella spam + +### I video non si vedono +- Verifica che l'URL sia corretto +- Per YouTube/Vimeo: usa URL diretti al video +- Per file locali: controlla i permessi della cartella + +### PayPal non funziona +- Verifica Client ID e Secret +- In test, usa modalità Sandbox +- Controlla console browser per errori JavaScript + +### La pagina è bianca / Errore 500 +- Attiva `DEBUG_MODE` in `config.php` +- Controlla log errori PHP +- Verifica permessi file (644) e cartelle (755) + +--- + +## 📁 Struttura File + +``` +pilates-platform/ +│ +├── admin/ # Area amministratore +│ ├── dashboard.php # Dashboard admin +│ ├── lessons.php # Gestione lezioni +│ ├── lesson_create.php # Crea lezione +│ ├── lesson_edit.php # Modifica lezione (da creare se serve) +│ ├── users.php # Gestione utenti +│ └── purchases.php # Storico acquisti +│ +├── assets/ # Risorse statiche +│ ├── css/ +│ │ └── style.css # Stili principali +│ └── js/ +│ └── main.js # JavaScript +│ +├── database/ +│ └── schema.sql # Script creazione database +│ +├── includes/ # File PHP condivisi +│ ├── config.php # Configurazione +│ ├── functions.php # Funzioni comuni +│ └── logout.php # Script logout +│ +├── uploads/ # File caricati (da creare) +│ ├── thumbnails/ # Anteprime lezioni +│ └── videos/ # Video locali +│ +├── user/ # Area utente +│ ├── dashboard.php # Dashboard utente +│ ├── catalog.php # Catalogo lezioni +│ └── profile.php # Profilo utente +│ +├── index.php # Homepage +├── login.php # Login +├── register.php # Registrazione +├── forgot_password.php # Recupero password (step 1) +├── reset_password.php # Reset password (step 2) +├── lesson.php # Visualizza lezione +├── process_payment.php # Elabora pagamento PayPal +└── README.md # Questo file +``` + +--- + +## 🎓 Concetti per Neofiti + +### Cos'è PHP? +PHP è il linguaggio che "genera" le pagine HTML dinamicamente. Quando visiti `index.php`, il server: +1. Esegue il codice PHP +2. Genera l'HTML finale +3. Lo invia al browser + +### Cos'è il Database? +MySQL è dove vengono salvati i dati: +- Utenti registrati +- Lezioni create +- Acquisti effettuati + +### Come Funziona il Login? +1. Utente inserisce email e password +2. PHP cerca l'email nel database +3. Verifica la password (hashata) +4. Se corretta, crea una "sessione" +5. La sessione ricorda che sei loggato + +### Come Funziona PayPal? +1. Utente clicca "Acquista" +2. Si apre popup PayPal +3. Utente paga +4. PayPal conferma il pagamento +5. `process_payment.php` registra l'acquisto nel DB + +### Dove Modificare il Codice? +- **Aspetto grafico**: `assets/css/style.css` +- **Comportamento**: File `.php` specifici +- **Database**: Usa phpMyAdmin o client MySQL +- **Impostazioni**: `includes/config.php` + +--- + +## 📞 Supporto e Manutenzione + +### Backup Database +Esegui regolarmente (es: settimanalmente): +```bash +mysqldump -u root -p pilates_platform > backup_$(date +%Y%m%d).sql +``` + +### Aggiornare PHP +Quando aggiorni PHP, testa la piattaforma in locale prima di aggiornare in produzione. + +### Log Attività +La tabella `activity_log` traccia: +- Login/Logout +- Acquisti +- Modifiche importanti + +Usa per debug e sicurezza. + +--- + +## 📝 Licenza + +Questo progetto è stato creato come piattaforma custom. Sei libero di modificarlo e usarlo come preferisci. + +--- + +## ✅ Checklist Prossimi Passi + +Dopo l'installazione: +- [ ] Cambia password admin +- [ ] Configura PayPal Sandbox +- [ ] Carica almeno 3 lezioni demo +- [ ] Testa registrazione utente +- [ ] Testa acquisto lezione (in sandbox) +- [ ] Personalizza colori e logo +- [ ] Configura backup automatici +- [ ] Passa a produzione PayPal +- [ ] Attiva HTTPS +- [ ] Lancia! 🚀 + +--- + +**Buon lavoro con la tua piattaforma Pilates! 🧘‍♀️** + +Per domande o problemi, verifica sempre i log errori PHP e la console browser. diff --git a/admin/dashboard.php b/admin/dashboard.php new file mode 100644 index 0000000..692477b --- /dev/null +++ b/admin/dashboard.php @@ -0,0 +1,203 @@ +query("SELECT COUNT(*) as count FROM users WHERE deleted_at IS NULL AND is_admin = 0"); +$total_users = $stmt->fetch()['count']; + +// Conta lezioni totali +$stmt = $pdo->query("SELECT COUNT(*) as count FROM lessons WHERE deleted_at IS NULL"); +$total_lessons = $stmt->fetch()['count']; + +// Conta acquisti completati +$stmt = $pdo->query("SELECT COUNT(*) as count FROM purchases WHERE status = 'completed'"); +$total_purchases = $stmt->fetch()['count']; + +// Totale guadagni +$stmt = $pdo->query("SELECT SUM(amount) as total FROM purchases WHERE status = 'completed'"); +$total_revenue = $stmt->fetch()['total'] ?? 0; + +// Ultimi acquisti +$stmt = $pdo->query(" + SELECT p.*, u.first_name, u.last_name, u.email, l.title as lesson_title + FROM purchases p + INNER JOIN users u ON p.user_id = u.id + INNER JOIN lessons l ON p.lesson_id = l.id + WHERE p.status = 'completed' + ORDER BY p.purchased_at DESC + LIMIT 10 +"); +$recent_purchases = $stmt->fetchAll(); + +// Lezioni più vendute +$stmt = $pdo->query(" + SELECT l.*, COUNT(p.id) as sales_count + FROM lessons l + LEFT JOIN purchases p ON l.id = p.lesson_id AND p.status = 'completed' + WHERE l.deleted_at IS NULL AND l.is_demo = 0 + GROUP BY l.id + ORDER BY sales_count DESC + LIMIT 5 +"); +$top_lessons = $stmt->fetchAll(); + +?> + + + + + + Dashboard Admin - Pilates Platform + + + +
+
+
+

Pilates Studio - Admin

+ +
+
+
+ +
+
+ + + + +
+

Dashboard

+ + + + +
+
+
+
Utenti Registrati
+
+ +
+
+
Lezioni Totali
+
+ +
+
+
Acquisti
+
+ +
+
+
Guadagni Totali
+
+
+ + + + + +
+

Lezioni Più Vendute

+ + + + + + + + + + + + + + + + + + + + +
LezioneTipoPrezzoVendite
+ + + (Demo) + +
+ +

Nessuna lezione venduta ancora.

+ +
+ + +
+

Ultimi Acquisti

+ + + + + + + + + + + + + + + + + + + + +
DataUtenteLezioneImporto
+ +
+ +
+ +

Nessun acquisto ancora.

+ +
+
+
+
+ + + + diff --git a/admin/lesson_create.php b/admin/lesson_create.php new file mode 100644 index 0000000..f45a24d --- /dev/null +++ b/admin/lesson_create.php @@ -0,0 +1,286 @@ +prepare(" + INSERT INTO lessons ( + title, description, type, video_url, video_platform, + duration, live_platform, live_url, live_date, + level, category, price, is_demo, is_active, + created_by, created_at + ) VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW() + ) + "); + + $stmt->execute([ + $title, + $description, + $type, + $type === 'video' ? $video_url : null, + $type === 'video' ? $video_platform : null, + $duration, + $type === 'live' ? $live_platform : null, + $type === 'live' ? $live_url : null, + $live_date, + $level, + $category, + $price, + $is_demo, + $is_active, + $_SESSION['user_id'] + ]); + + set_flash_message('success', 'Lezione creata con successo!'); + header('Location: lessons.php'); + exit; + + } catch (PDOException $e) { + $error = 'Errore durante la creazione della lezione'; + } + } +} +?> + + + + + + Nuova Lezione - Admin + + + +
+
+
+

Pilates Studio - Admin

+ +
+
+
+ +
+
+ + +
+

Crea Nuova Lezione

+ + +
+ + +
+
+ +

Informazioni Base

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+

Dettagli Video

+ +
+ + +
+ +
+ + + Lascia vuoto se caricherai il video successivamente +
+ +
+ + +
+
+ + +
+

Dettagli Lezione Live

+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +

Classificazione

+ +
+ + +
+ +
+ + +
+ + +

Prezzo e Disponibilità

+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+ + Annulla +
+
+
+
+
+
+ + + + + diff --git a/admin/lessons.php b/admin/lessons.php new file mode 100644 index 0000000..6d8182a --- /dev/null +++ b/admin/lessons.php @@ -0,0 +1,148 @@ +prepare("UPDATE lessons SET deleted_at = NOW() WHERE id = ?"); + + if ($stmt->execute([$lesson_id])) { + set_flash_message('success', 'Lezione eliminata con successo'); + } else { + set_flash_message('error', 'Errore durante l\'eliminazione'); + } + + header('Location: lessons.php'); + exit; +} + +// Ottieni tutte le lezioni +$pdo = get_db_connection(); +$stmt = $pdo->query(" + SELECT l.*, + COUNT(DISTINCT p.id) as purchase_count, + SUM(CASE WHEN p.status = 'completed' THEN p.amount ELSE 0 END) as total_revenue + FROM lessons l + LEFT JOIN purchases p ON l.id = p.lesson_id + WHERE l.deleted_at IS NULL + GROUP BY l.id + ORDER BY l.created_at DESC +"); +$lessons = $stmt->fetchAll(); + +?> + + + + + + Gestione Lezioni - Admin + + + +
+
+
+

Pilates Studio - Admin

+ +
+
+
+ +
+
+ + + + +
+
+

Gestione Lezioni

+ ➕ Nuova Lezione +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TitoloTipoLivelloPrezzoVenditeGuadagnoStatusAzioni
+ + +
✓ Demo + +
+ + Attiva + + Disattiva + + + Modifica + Elimina +
+ +

Nessuna lezione disponibile. Creane una!

+ +
+
+
+
+ + + + diff --git a/admin/purchases.php b/admin/purchases.php new file mode 100644 index 0000000..a1119c6 --- /dev/null +++ b/admin/purchases.php @@ -0,0 +1,165 @@ +query(" + SELECT p.*, + u.first_name, u.last_name, u.email, + l.title as lesson_title, l.type as lesson_type + FROM purchases p + INNER JOIN users u ON p.user_id = u.id + INNER JOIN lessons l ON p.lesson_id = l.id + ORDER BY p.created_at DESC +"); +$purchases = $stmt->fetchAll(); + +// Statistiche +$stmt = $pdo->query(" + SELECT + COUNT(*) as total_purchases, + SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) as total_revenue, + SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count, + SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_count + FROM purchases +"); +$stats = $stmt->fetch(); + +?> + + + + + + Acquisti - Admin + + + +
+
+
+

Pilates Studio - Admin

+ +
+
+
+ +
+
+ + + + +
+

Storico Acquisti

+ + +
+
+
+
Totale Transazioni
+
+ +
+
+
Incassi Totali
+
+ +
+
+
In Attesa
+
+ +
+
+
Falliti
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
DataUtenteLezioneTipoImportoStatusPayPal ID
+ + + +
+ +
+ 'text-success', + 'pending' => 'text-warning', + 'failed' => 'text-danger', + 'refunded' => 'text-muted' + ]; + $color = $status_colors[$purchase['status']] ?? ''; + ?> + + + + + + + +
+ +

Nessun acquisto ancora.

+ +
+
+
+
+ + + + diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..9cbd990 --- /dev/null +++ b/admin/users.php @@ -0,0 +1,145 @@ +prepare("UPDATE users SET is_active = NOT is_active WHERE id = ?"); + + if ($stmt->execute([$user_id])) { + set_flash_message('success', 'Stato utente aggiornato'); + } else { + set_flash_message('error', 'Errore durante l\'aggiornamento'); + } + + header('Location: users.php'); + exit; +} + +// Ottieni tutti gli utenti non admin +$pdo = get_db_connection(); +$stmt = $pdo->query(" + SELECT u.*, + COUNT(DISTINCT p.id) as purchase_count, + SUM(CASE WHEN p.status = 'completed' THEN p.amount ELSE 0 END) as total_spent + FROM users u + LEFT JOIN purchases p ON u.id = p.user_id + WHERE u.is_admin = 0 AND u.deleted_at IS NULL + GROUP BY u.id + ORDER BY u.created_at DESC +"); +$users = $stmt->fetchAll(); + +?> + + + + + + Gestione Utenti - Admin + + + +
+
+
+

Pilates Studio - Admin

+ +
+
+
+ +
+
+ + + + +
+

Gestione Utenti

+ + + +
+

Totale utenti registrati:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NomeEmailRegistrato ilUltimo AccessoAcquistiSpesoStatusAzioni
+ + + + + + ✓ Attivo + + ✗ Bloccato + + + + + +
+ +

Nessun utente registrato ancora.

+ +
+
+
+
+ + + + diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..3853d44 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,683 @@ +/** + * PILATES PLATFORM - STILI PRINCIPALI + * + * Design minimale con palette bianco e celeste + * Responsive per tutti i dispositivi + */ + +/* ============================================ + VARIABILI CSS E RESET + ============================================ */ + +:root { + /* Colori principali - Tonalità celeste */ + --primary-color: #4A90E2; /* Celeste principale */ + --primary-light: #7AB8F5; /* Celeste chiaro */ + --primary-dark: #357ABD; /* Celeste scuro */ + + /* Colori secondari */ + --secondary-color: #E8F4F8; /* Celeste molto chiaro per sfondi */ + --accent-color: #2ECC71; /* Verde per successo */ + --warning-color: #F39C12; /* Arancione per warning */ + --danger-color: #E74C3C; /* Rosso per errori */ + + /* Scala di grigi */ + --white: #FFFFFF; + --gray-100: #F8F9FA; + --gray-200: #E9ECEF; + --gray-300: #DEE2E6; + --gray-400: #CED4DA; + --gray-500: #ADB5BD; + --gray-600: #6C757D; + --gray-700: #495057; + --gray-800: #343A40; + --gray-900: #212529; + + /* Tipografia */ + --font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + --font-size-base: 16px; + --line-height-base: 1.6; + + /* Spaziature */ + --spacing-xs: 0.25rem; /* 4px */ + --spacing-sm: 0.5rem; /* 8px */ + --spacing-md: 1rem; /* 16px */ + --spacing-lg: 1.5rem; /* 24px */ + --spacing-xl: 2rem; /* 32px */ + --spacing-2xl: 3rem; /* 48px */ + + /* Border radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-full: 9999px; + + /* Ombre */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.15); + + /* Transizioni */ + --transition-fast: 0.15s ease; + --transition-base: 0.3s ease; +} + +/* Reset e base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-family); + font-size: var(--font-size-base); + line-height: var(--line-height-base); + color: var(--gray-800); + background-color: var(--white); + overflow-x: hidden; +} + +img { + max-width: 100%; + height: auto; + display: block; +} + +a { + color: var(--primary-color); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--primary-dark); +} + +/* ============================================ + LAYOUT + ============================================ */ + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--spacing-lg); +} + +.container-sm { + max-width: 800px; + margin: 0 auto; + padding: 0 var(--spacing-lg); +} + +/* ============================================ + HEADER E NAVIGAZIONE + ============================================ */ + +.header { + background: var(--white); + box-shadow: var(--shadow-sm); + padding: var(--spacing-lg) 0; + position: sticky; + top: 0; + z-index: 100; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.75rem; + font-weight: 700; + color: var(--primary-color); + margin: 0; +} + +.nav { + display: flex; + gap: var(--spacing-md); + align-items: center; +} + +/* ============================================ + BOTTONI + ============================================ */ + +.btn { + display: inline-block; + padding: 0.625rem 1.25rem; + font-size: 1rem; + font-weight: 500; + text-align: center; + border: none; + border-radius: var(--radius-md); + cursor: pointer; + transition: all var(--transition-base); + text-decoration: none; + white-space: nowrap; +} + +.btn-primary { + background: var(--primary-color); + color: var(--white); +} + +.btn-primary:hover { + background: var(--primary-dark); + color: var(--white); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.btn-secondary { + background: var(--secondary-color); + color: var(--primary-color); +} + +.btn-secondary:hover { + background: var(--primary-light); + color: var(--white); +} + +.btn-outline { + background: transparent; + color: var(--primary-color); + border: 2px solid var(--primary-color); +} + +.btn-outline:hover { + background: var(--primary-color); + color: var(--white); +} + +.btn-success { + background: var(--accent-color); + color: var(--white); +} + +.btn-danger { + background: var(--danger-color); + color: var(--white); +} + +.btn-small { + padding: 0.5rem 1rem; + font-size: 0.875rem; +} + +.btn-large { + padding: 1rem 2rem; + font-size: 1.125rem; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ============================================ + HERO SECTION + ============================================ */ + +.hero { + background: linear-gradient(135deg, var(--secondary-color) 0%, var(--white) 100%); + padding: var(--spacing-2xl) 0; + text-align: center; +} + +.hero-content { + max-width: 700px; + margin: 0 auto; +} + +.hero-title { + font-size: 2.5rem; + font-weight: 700; + color: var(--gray-900); + margin-bottom: var(--spacing-md); +} + +.hero-subtitle { + font-size: 1.25rem; + color: var(--gray-600); + margin-bottom: var(--spacing-xl); +} + +/* ============================================ + SEZIONI + ============================================ */ + +.lessons-section, +.features-section { + padding: var(--spacing-2xl) 0; +} + +.section-title { + font-size: 2rem; + font-weight: 700; + text-align: center; + color: var(--gray-900); + margin-bottom: var(--spacing-md); +} + +.section-subtitle { + font-size: 1.125rem; + color: var(--gray-600); + text-align: center; + margin-bottom: var(--spacing-xl); +} + +/* ============================================ + GRIDS + ============================================ */ + +.lessons-grid, +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: var(--spacing-xl); + margin-top: var(--spacing-xl); +} + +/* ============================================ + LESSON CARD + ============================================ */ + +.lesson-card { + background: var(--white); + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: var(--shadow-md); + transition: transform var(--transition-base), box-shadow var(--transition-base); +} + +.lesson-card:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-lg); +} + +.lesson-thumbnail, +.lesson-thumbnail-placeholder { + width: 100%; + height: 200px; + object-fit: cover; + background: var(--secondary-color); +} + +.lesson-thumbnail-placeholder { + display: flex; + align-items: center; + justify-content: center; + font-size: 3rem; +} + +.lesson-content { + padding: var(--spacing-lg); +} + +.lesson-title { + font-size: 1.25rem; + font-weight: 600; + color: var(--gray-900); + margin-bottom: var(--spacing-sm); +} + +.lesson-description { + font-size: 0.95rem; + color: var(--gray-600); + margin-bottom: var(--spacing-md); + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.lesson-meta { + display: flex; + gap: var(--spacing-md); + margin-bottom: var(--spacing-lg); + font-size: 0.875rem; + color: var(--gray-600); +} + +.lesson-price { + font-size: 1.5rem; + font-weight: 700; + color: var(--primary-color); + margin-bottom: var(--spacing-md); +} + +/* ============================================ + FEATURE CARD + ============================================ */ + +.feature-card { + text-align: center; + padding: var(--spacing-xl); + background: var(--white); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + transition: box-shadow var(--transition-base); +} + +.feature-card:hover { + box-shadow: var(--shadow-md); +} + +.feature-icon { + font-size: 3rem; + margin-bottom: var(--spacing-md); +} + +.feature-card h3 { + font-size: 1.25rem; + font-weight: 600; + color: var(--gray-900); + margin-bottom: var(--spacing-sm); +} + +.feature-card p { + color: var(--gray-600); +} + +/* ============================================ + FORM + ============================================ */ + +.form-group { + margin-bottom: var(--spacing-lg); +} + +.form-label { + display: block; + margin-bottom: var(--spacing-sm); + font-weight: 500; + color: var(--gray-700); +} + +.form-control { + width: 100%; + padding: 0.75rem; + font-size: 1rem; + border: 2px solid var(--gray-300); + border-radius: var(--radius-md); + transition: border-color var(--transition-fast); +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); +} + +.form-control.error { + border-color: var(--danger-color); +} + +.form-error { + color: var(--danger-color); + font-size: 0.875rem; + margin-top: var(--spacing-xs); +} + +textarea.form-control { + min-height: 120px; + resize: vertical; +} + +select.form-control { + cursor: pointer; +} + +/* ============================================ + ALERTS + ============================================ */ + +.alert { + padding: var(--spacing-md) var(--spacing-lg); + border-radius: var(--radius-md); + margin-bottom: var(--spacing-lg); + font-weight: 500; +} + +.alert-success { + background: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; +} + +.alert-error { + background: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.alert-warning { + background: #fff3cd; + color: #856404; + border: 1px solid #ffeeba; +} + +.alert-info { + background: var(--secondary-color); + color: var(--primary-dark); + border: 1px solid var(--primary-light); +} + +/* ============================================ + TABELLE + ============================================ */ + +.table { + width: 100%; + border-collapse: collapse; + background: var(--white); + border-radius: var(--radius-md); + overflow: hidden; + box-shadow: var(--shadow-sm); +} + +.table thead { + background: var(--secondary-color); +} + +.table th, +.table td { + padding: var(--spacing-md); + text-align: left; + border-bottom: 1px solid var(--gray-200); +} + +.table th { + font-weight: 600; + color: var(--primary-color); +} + +.table tr:hover { + background: var(--gray-100); +} + +/* ============================================ + CARDS + ============================================ */ + +.card { + background: var(--white); + border-radius: var(--radius-lg); + padding: var(--spacing-xl); + box-shadow: var(--shadow-md); + margin-bottom: var(--spacing-xl); +} + +.card-header { + font-size: 1.5rem; + font-weight: 600; + color: var(--gray-900); + margin-bottom: var(--spacing-lg); + padding-bottom: var(--spacing-md); + border-bottom: 2px solid var(--secondary-color); +} + +/* ============================================ + DASHBOARD + ============================================ */ + +.dashboard { + display: grid; + grid-template-columns: 250px 1fr; + min-height: calc(100vh - 80px); + gap: var(--spacing-xl); +} + +.sidebar { + background: var(--gray-100); + padding: var(--spacing-xl); + border-radius: var(--radius-lg); +} + +.sidebar-menu { + list-style: none; +} + +.sidebar-menu li { + margin-bottom: var(--spacing-sm); +} + +.sidebar-menu a { + display: block; + padding: var(--spacing-md); + color: var(--gray-700); + border-radius: var(--radius-md); + transition: all var(--transition-fast); +} + +.sidebar-menu a:hover, +.sidebar-menu a.active { + background: var(--primary-color); + color: var(--white); +} + +.main-content { + padding: var(--spacing-xl) 0; +} + +/* ============================================ + STATISTICHE + ============================================ */ + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--spacing-lg); + margin-bottom: var(--spacing-xl); +} + +.stat-card { + background: linear-gradient(135deg, var(--primary-light), var(--primary-color)); + color: var(--white); + padding: var(--spacing-xl); + border-radius: var(--radius-lg); + text-align: center; +} + +.stat-value { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: var(--spacing-sm); +} + +.stat-label { + font-size: 0.875rem; + opacity: 0.9; +} + +/* ============================================ + FOOTER + ============================================ */ + +.footer { + background: var(--gray-900); + color: var(--white); + padding: var(--spacing-xl) 0; + text-align: center; + margin-top: var(--spacing-2xl); +} + +/* ============================================ + UTILITY CLASSES + ============================================ */ + +.text-center { text-align: center; } +.text-right { text-align: right; } +.text-muted { color: var(--gray-600); } +.text-primary { color: var(--primary-color); } +.text-danger { color: var(--danger-color); } +.text-success { color: var(--accent-color); } + +.mt-1 { margin-top: var(--spacing-md); } +.mt-2 { margin-top: var(--spacing-lg); } +.mt-3 { margin-top: var(--spacing-xl); } +.mb-1 { margin-bottom: var(--spacing-md); } +.mb-2 { margin-bottom: var(--spacing-lg); } +.mb-3 { margin-bottom: var(--spacing-xl); } + +.d-none { display: none; } +.d-block { display: block; } +.d-flex { display: flex; } +.justify-between { justify-content: space-between; } +.align-center { align-items: center; } +.gap-1 { gap: var(--spacing-md); } + +/* ============================================ + RESPONSIVE + ============================================ */ + +@media (max-width: 768px) { + .hero-title { + font-size: 2rem; + } + + .hero-subtitle { + font-size: 1rem; + } + + .header-content { + flex-direction: column; + gap: var(--spacing-md); + } + + .nav { + flex-wrap: wrap; + justify-content: center; + } + + .dashboard { + grid-template-columns: 1fr; + } + + .sidebar { + order: 2; + } + + .lessons-grid, + .features-grid { + grid-template-columns: 1fr; + } + + .stats-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 480px) { + .btn { + width: 100%; + display: block; + } + + .btn + .btn { + margin-top: var(--spacing-sm); + } +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..dad3e5e --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,52 @@ +/** + * JavaScript principale della piattaforma + */ + +// Esegui quando il DOM è caricato +document.addEventListener('DOMContentLoaded', function() { + + // Auto-chiudi gli alert dopo 5 secondi + const alerts = document.querySelectorAll('.alert'); + alerts.forEach(function(alert) { + setTimeout(function() { + alert.style.transition = 'opacity 0.3s ease'; + alert.style.opacity = '0'; + setTimeout(function() { + alert.remove(); + }, 300); + }, 5000); + }); + + // Conferma eliminazione + const deleteButtons = document.querySelectorAll('[data-confirm-delete]'); + deleteButtons.forEach(function(button) { + button.addEventListener('click', function(e) { + if (!confirm('Sei sicuro di voler eliminare questo elemento?')) { + e.preventDefault(); + } + }); + }); + +}); + +/** + * Valida un form prima dell'invio + */ +function validateForm(formId) { + const form = document.getElementById(formId); + if (!form) return false; + + let isValid = true; + const requiredFields = form.querySelectorAll('[required]'); + + requiredFields.forEach(function(field) { + if (!field.value.trim()) { + field.classList.add('error'); + isValid = false; + } else { + field.classList.remove('error'); + } + }); + + return isValid; +} diff --git a/database/schema.sql b/database/schema.sql new file mode 100644 index 0000000..84c5095 --- /dev/null +++ b/database/schema.sql @@ -0,0 +1,243 @@ +-- ============================================ +-- PILATES PLATFORM - DATABASE SCHEMA +-- ============================================ +-- Script per creare tutte le tabelle necessarie +-- Esegui questo script nel tuo database MySQL +-- ============================================ + +-- Crea il database (se non esiste) +CREATE DATABASE IF NOT EXISTS pilates_platform CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE pilates_platform; + +-- ============================================ +-- TABELLA UTENTI +-- ============================================ +-- Memorizza tutti gli utenti registrati (studenti e amministratori) + +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL COMMENT 'Password hashata con bcrypt', + first_name VARCHAR(100) NOT NULL COMMENT 'Nome', + last_name VARCHAR(100) NOT NULL COMMENT 'Cognome', + is_admin BOOLEAN DEFAULT FALSE COMMENT 'True se amministratore', + is_active BOOLEAN DEFAULT TRUE COMMENT 'Permette di disabilitare account', + profile_image VARCHAR(255) DEFAULT NULL COMMENT 'URL immagine profilo', + phone VARCHAR(20) DEFAULT NULL COMMENT 'Numero telefono opzionale', + created_at DATETIME NOT NULL COMMENT 'Data registrazione', + updated_at DATETIME DEFAULT NULL COMMENT 'Data ultimo aggiornamento', + last_login DATETIME DEFAULT NULL COMMENT 'Data ultimo accesso', + deleted_at DATETIME DEFAULT NULL COMMENT 'Soft delete - se valorizzato, utente cancellato', + + INDEX idx_email (email), + INDEX idx_is_admin (is_admin), + INDEX idx_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Utenti della piattaforma'; + +-- ============================================ +-- TABELLA LEZIONI +-- ============================================ +-- Memorizza sia videolezioni che lezioni live + +CREATE TABLE IF NOT EXISTS lessons ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL COMMENT 'Titolo della lezione', + description TEXT NOT NULL COMMENT 'Descrizione dettagliata', + type ENUM('video', 'live') NOT NULL COMMENT 'Tipo: video registrato o live', + + -- Informazioni video + video_url VARCHAR(500) DEFAULT NULL COMMENT 'URL video (YouTube, Vimeo, S3, o percorso locale)', + video_platform VARCHAR(50) DEFAULT 'local' COMMENT 'Piattaforma: local, youtube, vimeo, s3', + thumbnail VARCHAR(500) DEFAULT NULL COMMENT 'URL immagine anteprima', + duration INT DEFAULT NULL COMMENT 'Durata in minuti', + + -- Informazioni lezione live + live_platform VARCHAR(100) DEFAULT NULL COMMENT 'Piattaforma streaming (Zoom, Google Meet, ecc.)', + live_url VARCHAR(500) DEFAULT NULL COMMENT 'Link alla sessione live', + live_date DATETIME DEFAULT NULL COMMENT 'Data e ora della lezione live', + + -- Classificazione + level ENUM('principiante', 'intermedio', 'avanzato') NOT NULL COMMENT 'Livello difficoltà', + category VARCHAR(100) DEFAULT NULL COMMENT 'Categoria (es: mat, reformer, stretching)', + tags VARCHAR(255) DEFAULT NULL COMMENT 'Tag separati da virgola', + + -- Prezzo e disponibilità + price DECIMAL(10, 2) NOT NULL DEFAULT 0.00 COMMENT 'Prezzo in euro', + is_demo BOOLEAN DEFAULT FALSE COMMENT 'Se true, lezione gratuita', + is_active BOOLEAN DEFAULT TRUE COMMENT 'Se false, lezione non visibile', + + -- Statistiche + view_count INT DEFAULT 0 COMMENT 'Numero visualizzazioni', + purchase_count INT DEFAULT 0 COMMENT 'Numero acquisti', + + -- Metadata + created_by INT DEFAULT NULL COMMENT 'ID admin che ha creato', + created_at DATETIME NOT NULL COMMENT 'Data creazione', + updated_at DATETIME DEFAULT NULL COMMENT 'Data ultimo aggiornamento', + deleted_at DATETIME DEFAULT NULL COMMENT 'Soft delete', + + INDEX idx_type (type), + INDEX idx_is_demo (is_demo), + INDEX idx_is_active (is_active), + INDEX idx_level (level), + INDEX idx_live_date (live_date), + INDEX idx_deleted_at (deleted_at), + + FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Lezioni video e live'; + +-- ============================================ +-- TABELLA ACQUISTI +-- ============================================ +-- Traccia tutti gli acquisti effettuati dagli utenti + +CREATE TABLE IF NOT EXISTS purchases ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT 'Utente che ha acquistato', + lesson_id INT NOT NULL COMMENT 'Lezione acquistata', + + -- Informazioni pagamento + amount DECIMAL(10, 2) NOT NULL COMMENT 'Importo pagato', + currency VARCHAR(3) DEFAULT 'EUR' COMMENT 'Valuta', + payment_method VARCHAR(50) DEFAULT 'paypal' COMMENT 'Metodo pagamento', + + -- Dettagli transazione PayPal + paypal_order_id VARCHAR(100) DEFAULT NULL COMMENT 'ID ordine PayPal', + paypal_payer_id VARCHAR(100) DEFAULT NULL COMMENT 'ID pagatore PayPal', + paypal_payment_id VARCHAR(100) DEFAULT NULL COMMENT 'ID pagamento PayPal', + paypal_status VARCHAR(50) DEFAULT NULL COMMENT 'Status PayPal', + + -- Status acquisto + status ENUM('pending', 'completed', 'failed', 'refunded') DEFAULT 'pending' COMMENT 'Stato acquisto', + + -- Timestamp + purchased_at DATETIME DEFAULT NULL COMMENT 'Data completamento acquisto', + created_at DATETIME NOT NULL COMMENT 'Data creazione record', + + INDEX idx_user_id (user_id), + INDEX idx_lesson_id (lesson_id), + INDEX idx_status (status), + INDEX idx_purchased_at (purchased_at), + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (lesson_id) REFERENCES lessons(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Acquisti lezioni'; + +-- ============================================ +-- TABELLA RECUPERO PASSWORD +-- ============================================ +-- Gestisce i token per il reset della password + +CREATE TABLE IF NOT EXISTS password_resets ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL COMMENT 'Utente che ha richiesto reset', + token VARCHAR(255) NOT NULL UNIQUE COMMENT 'Token univoco per reset', + expires_at DATETIME NOT NULL COMMENT 'Scadenza token', + used_at DATETIME DEFAULT NULL COMMENT 'Quando il token è stato usato', + created_at DATETIME NOT NULL COMMENT 'Data creazione richiesta', + + INDEX idx_token (token), + INDEX idx_user_id (user_id), + INDEX idx_expires_at (expires_at), + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Token reset password'; + +-- ============================================ +-- TABELLA LOG ATTIVITÀ (opzionale ma utile) +-- ============================================ +-- Traccia le azioni importanti per debug e sicurezza + +CREATE TABLE IF NOT EXISTS activity_log ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT DEFAULT NULL COMMENT 'Utente che ha eseguito azione', + action VARCHAR(100) NOT NULL COMMENT 'Tipo azione (login, purchase, etc.)', + description TEXT DEFAULT NULL COMMENT 'Descrizione dettagliata', + ip_address VARCHAR(45) DEFAULT NULL COMMENT 'IP utente (supporta IPv6)', + user_agent TEXT DEFAULT NULL COMMENT 'Browser/dispositivo', + created_at DATETIME NOT NULL COMMENT 'Quando è avvenuta', + + INDEX idx_user_id (user_id), + INDEX idx_action (action), + INDEX idx_created_at (created_at), + + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Log attività utenti'; + +-- ============================================ +-- DATI DI ESEMPIO +-- ============================================ + +-- Inserisci un utente amministratore di default +-- Email: admin@pilatesstudio.com +-- Password: admin123 (CAMBIALA SUBITO dopo il primo accesso!) +INSERT INTO users (email, password, first_name, last_name, is_admin, created_at) +VALUES ( + 'admin@pilatesstudio.com', + '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', -- Hash di 'admin123' + 'Admin', + 'Pilates', + TRUE, + NOW() +); + +-- Inserisci alcune lezioni demo di esempio +INSERT INTO lessons (title, description, type, video_url, video_platform, thumbnail, duration, level, category, price, is_demo, is_active, created_at) VALUES +( + 'Introduzione al Pilates - Esercizi Base', + 'Una lezione introduttiva perfetta per chi inizia. Imparerai i principi fondamentali del Pilates e gli esercizi base per sviluppare forza e flessibilità.', + 'video', + NULL, + 'local', + NULL, + 30, + 'principiante', + 'Mat Work', + 0.00, + TRUE, + TRUE, + NOW() +), +( + 'Pilates Intermedio - Full Body Workout', + 'Sessione completa per lavorare su tutto il corpo. Include esercizi di tonificazione, equilibrio e controllo del core.', + 'video', + NULL, + 'local', + NULL, + 45, + 'intermedio', + 'Mat Work', + 9.99, + FALSE, + TRUE, + NOW() +), +( + 'Stretching e Mobilità', + 'Lezione dedicata allo stretching profondo e al miglioramento della mobilità articolare. Perfetta per recupero e rilassamento.', + 'video', + NULL, + 'local', + NULL, + 25, + 'principiante', + 'Stretching', + 0.00, + TRUE, + TRUE, + NOW() +); + +-- ============================================ +-- FINE SCRIPT +-- ============================================ + +-- Verifica che tutte le tabelle siano state create +SELECT + TABLE_NAME as 'Tabella', + TABLE_ROWS as 'Righe', + CREATE_TIME as 'Creata il' +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = 'pilates_platform' +ORDER BY TABLE_NAME; diff --git a/forgot_password.php b/forgot_password.php new file mode 100644 index 0000000..0d6a5e0 --- /dev/null +++ b/forgot_password.php @@ -0,0 +1,119 @@ + + + + + + + Recupero Password - Pilates Platform + + + +
+
+
+

Pilates Studio

+
+ +

Recupero Password

+ + +
+ Email inviata!
+ Se l'indirizzo email è registrato, riceverai un'email con le istruzioni per reimpostare la password. + Controlla anche la cartella spam. +
+ + + +
+ +
+ + +

+ Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la password. +

+ +
+
+ + +
+ + +
+ + + +
+
+ + diff --git a/includes/config.php b/includes/config.php new file mode 100644 index 0000000..5ab7354 --- /dev/null +++ b/includes/config.php @@ -0,0 +1,172 @@ + PDO::ERRMODE_EXCEPTION, // Lancia eccezioni per errori + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Fetch come array associativo + PDO::ATTR_EMULATE_PREPARES => false, // Usa prepared statements veri + PDO::ATTR_PERSISTENT => false, // Non usare connessioni persistenti + ]; + + // Crea connessione PDO + $pdo = new PDO($dsn, DB_USER, DB_PASS, $options); + + return $pdo; + + } catch (PDOException $e) { + // In caso di errore di connessione + if (DEBUG_MODE) { + die("Errore connessione database: " . $e->getMessage()); + } else { + die("Errore di connessione al database. Contatta l'amministratore."); + } + } +} + +?> diff --git a/includes/functions.php b/includes/functions.php new file mode 100644 index 0000000..ed19474 --- /dev/null +++ b/includes/functions.php @@ -0,0 +1,564 @@ + SESSION_LIFETIME) { + logout_user(); + return false; + } + } + + $_SESSION['last_activity'] = time(); + return true; +} + +// ============================================ +// FUNZIONI SICUREZZA +// ============================================ + +/** + * Hash sicuro di una password + * + * @param string $password Password in chiaro + * @return string Password hashata + */ +function hash_password($password) { + return password_hash($password, PASSWORD_DEFAULT); +} + +/** + * Verifica una password contro il suo hash + * + * @param string $password Password in chiaro + * @param string $hash Hash salvato nel database + * @return bool True se corrisponde, false altrimenti + */ +function verify_password($password, $hash) { + return password_verify($password, $hash); +} + +/** + * Genera un token casuale sicuro + * + * @param int $length Lunghezza del token + * @return string Token generato + */ +function generate_token($length = 32) { + return bin2hex(random_bytes($length)); +} + +/** + * Sanitizza input utente per prevenire XSS + * + * @param string $data Dati da sanitizzare + * @return string Dati sanitizzati + */ +function sanitize_input($data) { + $data = trim($data); + $data = stripslashes($data); + $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); + return $data; +} + +/** + * Valida un indirizzo email + * + * @param string $email Email da validare + * @return bool True se valida, false altrimenti + */ +function validate_email($email) { + return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; +} + +// ============================================ +// FUNZIONI DATABASE - UTENTI +// ============================================ + +/** + * Ottiene un utente dal database tramite email + * + * @param string $email Email dell'utente + * @return array|false Dati utente o false se non trovato + */ +function get_user_by_email($email) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? AND deleted_at IS NULL"); + $stmt->execute([$email]); + + return $stmt->fetch(); +} + +/** + * Ottiene un utente dal database tramite ID + * + * @param int $user_id ID dell'utente + * @return array|false Dati utente o false se non trovato + */ +function get_user_by_id($user_id) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ? AND deleted_at IS NULL"); + $stmt->execute([$user_id]); + + return $stmt->fetch(); +} + +/** + * Crea un nuovo utente nel database + * + * @param string $email Email + * @param string $password Password in chiaro (verrà hashata) + * @param string $first_name Nome + * @param string $last_name Cognome + * @param bool $is_admin Se è amministratore + * @return int|false ID del nuovo utente o false in caso di errore + */ +function create_user($email, $password, $first_name, $last_name, $is_admin = false) { + $pdo = get_db_connection(); + + $hashed_password = hash_password($password); + + try { + $stmt = $pdo->prepare(" + INSERT INTO users (email, password, first_name, last_name, is_admin, created_at) + VALUES (?, ?, ?, ?, ?, NOW()) + "); + + $stmt->execute([ + $email, + $hashed_password, + $first_name, + $last_name, + $is_admin ? 1 : 0 + ]); + + return $pdo->lastInsertId(); + + } catch (PDOException $e) { + return false; + } +} + +// ============================================ +// FUNZIONI DATABASE - LEZIONI +// ============================================ + +/** + * Ottiene tutte le lezioni demo (gratuite) + * + * @return array Array di lezioni demo + */ +function get_demo_lessons() { + $pdo = get_db_connection(); + + $stmt = $pdo->query(" + SELECT * FROM lessons + WHERE is_demo = 1 AND is_active = 1 + ORDER BY created_at DESC + "); + + return $stmt->fetchAll(); +} + +/** + * Ottiene tutte le lezioni attive + * + * @param string $type Tipo di lezione: 'all', 'video', 'live' + * @return array Array di lezioni + */ +function get_all_lessons($type = 'all') { + $pdo = get_db_connection(); + + $sql = "SELECT * FROM lessons WHERE is_active = 1"; + + if ($type !== 'all') { + $sql .= " AND type = :type"; + } + + $sql .= " ORDER BY created_at DESC"; + + $stmt = $pdo->prepare($sql); + + if ($type !== 'all') { + $stmt->execute(['type' => $type]); + } else { + $stmt->execute(); + } + + return $stmt->fetchAll(); +} + +/** + * Ottiene una lezione specifica tramite ID + * + * @param int $lesson_id ID della lezione + * @return array|false Dati lezione o false se non trovata + */ +function get_lesson_by_id($lesson_id) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare("SELECT * FROM lessons WHERE id = ?"); + $stmt->execute([$lesson_id]); + + return $stmt->fetch(); +} + +/** + * Verifica se un utente ha acquistato una lezione + * + * @param int $user_id ID utente + * @param int $lesson_id ID lezione + * @return bool True se acquistata, false altrimenti + */ +function user_owns_lesson($user_id, $lesson_id) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare(" + SELECT COUNT(*) as count + FROM purchases + WHERE user_id = ? AND lesson_id = ? AND status = 'completed' + "); + + $stmt->execute([$user_id, $lesson_id]); + $result = $stmt->fetch(); + + return $result['count'] > 0; +} + +/** + * Ottiene tutte le lezioni acquistate da un utente + * + * @param int $user_id ID utente + * @return array Array di lezioni acquistate + */ +function get_user_purchased_lessons($user_id) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare(" + SELECT l.*, p.purchased_at, p.amount as paid_amount + FROM lessons l + INNER JOIN purchases p ON l.id = p.lesson_id + WHERE p.user_id = ? AND p.status = 'completed' + ORDER BY p.purchased_at DESC + "); + + $stmt->execute([$user_id]); + + return $stmt->fetchAll(); +} + +// ============================================ +// FUNZIONI RECUPERO PASSWORD +// ============================================ + +/** + * Crea un token per il recupero password + * + * @param int $user_id ID utente + * @return string Token generato + */ +function create_password_reset_token($user_id) { + $pdo = get_db_connection(); + + $token = generate_token(32); + $expires_at = date('Y-m-d H:i:s', time() + PASSWORD_RESET_TOKEN_LIFETIME); + + // Cancella eventuali token precedenti per questo utente + $stmt = $pdo->prepare("DELETE FROM password_resets WHERE user_id = ?"); + $stmt->execute([$user_id]); + + // Crea nuovo token + $stmt = $pdo->prepare(" + INSERT INTO password_resets (user_id, token, expires_at, created_at) + VALUES (?, ?, ?, NOW()) + "); + + $stmt->execute([$user_id, $token, $expires_at]); + + return $token; +} + +/** + * Verifica la validità di un token di reset password + * + * @param string $token Token da verificare + * @return int|false User ID se valido, false altrimenti + */ +function verify_password_reset_token($token) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare(" + SELECT user_id FROM password_resets + WHERE token = ? AND expires_at > NOW() AND used_at IS NULL + "); + + $stmt->execute([$token]); + $result = $stmt->fetch(); + + return $result ? $result['user_id'] : false; +} + +/** + * Marca un token come utilizzato + * + * @param string $token Token da marcare + */ +function mark_token_as_used($token) { + $pdo = get_db_connection(); + + $stmt = $pdo->prepare("UPDATE password_resets SET used_at = NOW() WHERE token = ?"); + $stmt->execute([$token]); +} + +// ============================================ +// FUNZIONI EMAIL +// ============================================ + +/** + * Invia un'email (funzione base con mail() PHP) + * + * @param string $to Destinatario + * @param string $subject Oggetto + * @param string $message Messaggio HTML + * @return bool True se inviata, false altrimenti + */ +function send_email($to, $subject, $message) { + $headers = "MIME-Version: 1.0" . "\r\n"; + $headers .= "Content-type:text/html;charset=UTF-8" . "\r\n"; + $headers .= "From: " . MAIL_FROM_NAME . " <" . MAIL_FROM . ">" . "\r\n"; + + return mail($to, $subject, $message, $headers); +} + +/** + * Invia email di recupero password + * + * @param string $email Email destinatario + * @param string $token Token di reset + * @return bool True se inviata, false altrimenti + */ +function send_password_reset_email($email, $token) { + $reset_link = SITE_URL . "/reset_password.php?token=" . $token; + + $subject = "Recupero Password - " . SITE_NAME; + + $message = " + + + + + +
+

Recupero Password

+

Hai richiesto il recupero della password per il tuo account su " . SITE_NAME . ".

+

Clicca sul pulsante qui sotto per reimpostare la tua password:

+ Reimposta Password +

Oppure copia e incolla questo link nel tuo browser:

+

{$reset_link}

+

Questo link è valido per " . (PASSWORD_RESET_TOKEN_LIFETIME / 60) . " minuti.

+

Se non hai richiesto tu questo recupero, ignora questa email.

+
+

Questa è un'email automatica, non rispondere a questo messaggio.

+
+ + + "; + + return send_email($email, $subject, $message); +} + +// ============================================ +// FUNZIONI MESSAGGI FLASH +// ============================================ + +/** + * Imposta un messaggio flash da visualizzare nella prossima pagina + * + * @param string $type Tipo: 'success', 'error', 'warning', 'info' + * @param string $message Messaggio da visualizzare + */ +function set_flash_message($type, $message) { + $_SESSION['flash_message'] = [ + 'type' => $type, + 'message' => $message + ]; +} + +/** + * Ottiene e cancella il messaggio flash + * + * @return array|null Array con tipo e messaggio, o null se non presente + */ +function get_flash_message() { + if (isset($_SESSION['flash_message'])) { + $flash = $_SESSION['flash_message']; + unset($_SESSION['flash_message']); + return $flash; + } + return null; +} + +/** + * Visualizza il messaggio flash HTML + * + * @return string HTML del messaggio o stringa vuota + */ +function display_flash_message() { + $flash = get_flash_message(); + + if ($flash) { + $type = htmlspecialchars($flash['type']); + $message = htmlspecialchars($flash['message']); + + return "
{$message}
"; + } + + return ''; +} + +// ============================================ +// FUNZIONI UTILITY +// ============================================ + +/** + * Formatta una data in formato italiano + * + * @param string $date Data in formato SQL + * @return string Data formattata + */ +function format_date($date) { + return date('d/m/Y', strtotime($date)); +} + +/** + * Formatta data e ora in formato italiano + * + * @param string $datetime Datetime in formato SQL + * @return string Datetime formattata + */ +function format_datetime($datetime) { + return date('d/m/Y H:i', strtotime($datetime)); +} + +/** + * Formatta un prezzo in euro + * + * @param float $price Prezzo + * @return string Prezzo formattato + */ +function format_price($price) { + return '€ ' . number_format($price, 2, ',', '.'); +} + +/** + * Redirect a un'altra pagina + * + * @param string $url URL di destinazione + */ +function redirect($url) { + header("Location: $url"); + exit; +} + +?> diff --git a/includes/logout.php b/includes/logout.php new file mode 100644 index 0000000..0c22cf8 --- /dev/null +++ b/includes/logout.php @@ -0,0 +1,33 @@ +prepare(" + INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at) + VALUES (?, 'logout', 'Utente ha effettuato il logout', ?, ?, NOW()) + "); + $stmt->execute([ + $_SESSION['user_id'], + $_SERVER['REMOTE_ADDR'] ?? null, + $_SERVER['HTTP_USER_AGENT'] ?? null + ]); +} + +// Effettua il logout +logout_user(); + +// Reindirizza alla home +header('Location: ../index.php'); +exit; +?> diff --git a/index.php b/index.php new file mode 100644 index 0000000..5961d69 --- /dev/null +++ b/index.php @@ -0,0 +1,148 @@ + + + + + + + Pilates Platform - Videolezioni e Lezioni Live + + + + +
+
+
+

Pilates Studio

+ +
+
+
+ + +
+
+
+

Trasforma il tuo corpo con il Pilates

+

Videolezioni professionali e sessioni live per tutti i livelli

+ + Inizia Ora + +
+
+
+ + +
+
+

Lezioni Demo Gratuite

+

Prova gratuitamente alcune delle nostre lezioni migliori

+ +
+ + +
+ + <?php echo htmlspecialchars($lesson['title']); ?> + +
+ 📹 +
+ + +
+

+

+ +
+ ⏱️ min + 📊 +
+ + + Guarda Gratis + +
+
+ + +

Nessuna lezione demo disponibile al momento.

+ +
+
+
+ + +
+
+

Perché scegliere Pilates Studio?

+ +
+
+
🎥
+

Videolezioni HD

+

Contenuti di alta qualità disponibili 24/7

+
+ +
+
📅
+

Lezioni Live

+

Sessioni interattive con l'istruttrice

+
+ +
+
📱
+

Accessibile Ovunque

+

Su computer, tablet e smartphone

+
+ +
+
+

Tutti i Livelli

+

Principiante, intermedio e avanzato

+
+
+
+
+ + + + + + + diff --git a/lesson.php b/lesson.php new file mode 100644 index 0000000..de89556 --- /dev/null +++ b/lesson.php @@ -0,0 +1,262 @@ + + + + + + + <?php echo htmlspecialchars($lesson['title']); ?> - Pilates Platform + + + + + + + + +
+
+
+

Pilates Studio

+ +
+
+
+ +
+
+ + +

+ +
+ + + + + 📊 + + + ⏱️ min + + + 📂 + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ Video non ancora disponibile +
+ +
+ + +
+

📡 Lezione Live

+

Data e ora:

+ +

Piattaforma:

+ + + + Partecipa alla Lezione Live + + +
+ + + + +

Descrizione

+

+ +

+ + + +
+ ✓ Lezione Gratuita! Questa lezione è disponibile gratuitamente per tutti. +
+ +
+ ✓ Hai già acquistato questa lezione! +
+ + +
+

+ Prezzo: +

+

Acquista questa lezione e avrai accesso illimitato per sempre

+ + +
+ +

+ Pagamento sicuro tramite PayPal +

+
+ +
+

Accedi per acquistare questa lezione

+

Prezzo:

+ Accedi o Registrati +
+ +
+
+ + + + + + + + + + diff --git a/login.php b/login.php new file mode 100644 index 0000000..882c152 --- /dev/null +++ b/login.php @@ -0,0 +1,148 @@ +prepare("UPDATE users SET last_login = NOW() WHERE id = ?"); + $stmt->execute([$user['id']]); + + // Log attività + $stmt = $pdo->prepare(" + INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at) + VALUES (?, 'login', 'Utente ha effettuato il login', ?, ?, NOW()) + "); + $stmt->execute([ + $user['id'], + $_SERVER['REMOTE_ADDR'] ?? null, + $_SERVER['HTTP_USER_AGENT'] ?? null + ]); + + // Imposta la sessione + login_user($user['id'], $user['is_admin']); + + // Reindirizza alla pagina appropriata + if (isset($_SESSION['redirect_after_login'])) { + $redirect = $_SESSION['redirect_after_login']; + unset($_SESSION['redirect_after_login']); + header("Location: $redirect"); + } elseif ($user['is_admin']) { + header('Location: admin/dashboard.php'); + } else { + header('Location: user/dashboard.php'); + } + exit; + + } else { + $error = 'Email o password non corretti'; + } + } +} +?> + + + + + + Login - Pilates Platform + + + +
+
+
+

Pilates Studio

+
+ +

Accedi al tuo account

+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+ + +
+ +
+

+ Hai dimenticato la password? +

+

+ Non hai un account? Registrati +

+

+ ← Torna alla home +

+
+
+
+ + diff --git a/process_payment.php b/process_payment.php new file mode 100644 index 0000000..a6497cc --- /dev/null +++ b/process_payment.php @@ -0,0 +1,104 @@ + false, 'error' => 'Not logged in']); + exit; +} + +// Leggi i dati JSON +$json = file_get_contents('php://input'); +$data = json_decode($json, true); + +if (!$data) { + echo json_encode(['success' => false, 'error' => 'Invalid data']); + exit; +} + +$order_id = $data['orderID'] ?? ''; +$lesson_id = $data['lessonID'] ?? 0; +$payer_id = $data['payerID'] ?? ''; +$amount = $data['amount'] ?? 0; + +$user_id = $_SESSION['user_id']; + +// Validazione +if (empty($order_id) || !$lesson_id || !$amount) { + echo json_encode(['success' => false, 'error' => 'Missing required fields']); + exit; +} + +// Verifica che la lezione esista +$lesson = get_lesson_by_id($lesson_id); +if (!$lesson) { + echo json_encode(['success' => false, 'error' => 'Lesson not found']); + exit; +} + +// Verifica che l'utente non abbia già acquistato questa lezione +if (user_owns_lesson($user_id, $lesson_id)) { + echo json_encode(['success' => false, 'error' => 'Already purchased']); + exit; +} + +try { + $pdo = get_db_connection(); + + // Registra l'acquisto nel database + $stmt = $pdo->prepare(" + INSERT INTO purchases ( + user_id, lesson_id, amount, currency, payment_method, + paypal_order_id, paypal_payer_id, paypal_status, + status, purchased_at, created_at + ) VALUES ( + ?, ?, ?, 'EUR', 'paypal', + ?, ?, 'completed', + 'completed', NOW(), NOW() + ) + "); + + $stmt->execute([ + $user_id, + $lesson_id, + $amount, + $order_id, + $payer_id + ]); + + // Aggiorna il contatore acquisti della lezione + $stmt = $pdo->prepare(" + UPDATE lessons + SET purchase_count = purchase_count + 1 + WHERE id = ? + "); + $stmt->execute([$lesson_id]); + + // Log attività + $stmt = $pdo->prepare(" + INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at) + VALUES (?, 'purchase', ?, ?, ?, NOW()) + "); + $stmt->execute([ + $user_id, + "Acquisto lezione: {$lesson['title']}", + $_SERVER['REMOTE_ADDR'] ?? null, + $_SERVER['HTTP_USER_AGENT'] ?? null + ]); + + echo json_encode(['success' => true]); + +} catch (Exception $e) { + error_log("Payment processing error: " . $e->getMessage()); + echo json_encode(['success' => false, 'error' => 'Database error']); +} +?> diff --git a/register.php b/register.php new file mode 100644 index 0000000..ec1a6f2 --- /dev/null +++ b/register.php @@ -0,0 +1,187 @@ +prepare(" + INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at) + VALUES (?, 'register', 'Nuovo utente registrato', ?, ?, NOW()) + "); + $stmt->execute([ + $user_id, + $_SERVER['REMOTE_ADDR'] ?? null, + $_SERVER['HTTP_USER_AGENT'] ?? null + ]); + + // Login automatico dopo registrazione + login_user($user_id, false); + + // Reindirizza dopo 2 secondi + header("refresh:2;url=user/dashboard.php"); + } else { + $error = 'Errore durante la registrazione. Riprova più tardi.'; + } + } + } +} +?> + + + + + + Registrazione - Pilates Platform + + + +
+
+
+

Pilates Studio

+
+ +

Crea un nuovo account

+ + +
+ Registrazione completata! Verrai reindirizzato alla tua area personale... +
+ + + +
+ +
+ + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+

+ Hai già un account? Accedi +

+

+ ← Torna alla home +

+
+
+
+ + diff --git a/reset_password.php b/reset_password.php new file mode 100644 index 0000000..d49762d --- /dev/null +++ b/reset_password.php @@ -0,0 +1,156 @@ +prepare("UPDATE users SET password = ?, updated_at = NOW() WHERE id = ?"); + + if ($stmt->execute([$hashed_password, $user_id])) { + // Marca il token come usato + mark_token_as_used($token); + + // Log attività + $stmt = $pdo->prepare(" + INSERT INTO activity_log (user_id, action, description, ip_address, user_agent, created_at) + VALUES (?, 'password_reset', 'Password reimpostata', ?, ?, NOW()) + "); + $stmt->execute([ + $user_id, + $_SERVER['REMOTE_ADDR'] ?? null, + $_SERVER['HTTP_USER_AGENT'] ?? null + ]); + + $success = true; + } else { + $error = 'Errore durante il reset della password. Riprova più tardi.'; + } + } +} +?> + + + + + + Reimposta Password - Pilates Platform + + + +
+
+
+

Pilates Studio

+
+ +

Reimposta Password

+ + +
+ Password aggiornata!
+ La tua password è stata reimpostata con successo. Ora puoi effettuare il login. +
+ + + +
+ +
+ + + + +
+ +
+ + +

+ Inserisci la tua nuova password. +

+ +
+
+ + +
+ +
+ + +
+ + +
+ + + +
+
+ + diff --git a/user/catalog.php b/user/catalog.php new file mode 100644 index 0000000..3b3457b --- /dev/null +++ b/user/catalog.php @@ -0,0 +1,192 @@ +prepare(" + SELECT lesson_id FROM purchases + WHERE user_id = ? AND status = 'completed' +"); +$stmt->execute([$user_id]); +$owned_lesson_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + +?> + + + + + + Catalogo Lezioni - Pilates Platform + + + +
+
+
+

Pilates Studio

+ +
+
+
+ +
+
+ + + + +
+

Catalogo Lezioni

+ + + +
+ + +
+ + + <?php echo htmlspecialchars($lesson['title']); ?> + +
+ +
+ + +
+

+

+ +

+ +
+ + ⏱️ min + + 📊 +
+ + +

+ 📅 +

+ + + +
GRATIS
+ + Guarda Gratis + + +
✓ Già acquistata
+ + Vai alla Lezione + + +
+ + Vedi Dettagli + + +
+
+ +
+ + +
+

Nessuna lezione disponibile al momento.

+
+ +
+
+
+ + + + + diff --git a/user/dashboard.php b/user/dashboard.php new file mode 100644 index 0000000..2fd2c59 --- /dev/null +++ b/user/dashboard.php @@ -0,0 +1,132 @@ + + + + + + + Le Mie Lezioni - Pilates Platform + + + +
+
+
+

Pilates Studio

+ +
+
+
+ +
+
+ + + + +
+

Le Mie Lezioni

+ + + + +
+ +
+ + <?php echo htmlspecialchars($lesson['title']); ?> + +
+ +
+ + +
+

+

+ +

+ +
+ + ⏱️ min + + 📊 +
+ + +

+ 📅 +

+ + + + + + +

+ Acquistata il +

+
+
+ +
+ +
+

Non hai ancora acquistato nessuna lezione

+

Esplora il nostro catalogo e inizia il tuo percorso di Pilates!

+ Sfoglia il Catalogo +
+ +
+
+
+ + + + diff --git a/user/profile.php b/user/profile.php new file mode 100644 index 0000000..5f860cd --- /dev/null +++ b/user/profile.php @@ -0,0 +1,209 @@ +prepare("SELECT id FROM users WHERE email = ? AND id != ?"); + $stmt->execute([$email, $user_id]); + + if ($stmt->fetch()) { + $error = 'Questa email è già in uso'; + } else { + // Aggiorna profilo + $stmt = $pdo->prepare(" + UPDATE users + SET first_name = ?, last_name = ?, email = ?, phone = ?, updated_at = NOW() + WHERE id = ? + "); + + if ($stmt->execute([$first_name, $last_name, $email, $phone, $user_id])) { + $success = true; + $user = get_user_by_id($user_id); // Ricarica dati + set_flash_message('success', 'Profilo aggiornato con successo!'); + } else { + $error = 'Errore durante l\'aggiornamento'; + } + } + } +} + +// Processa cambio password +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['change_password'])) { + $current_password = $_POST['current_password'] ?? ''; + $new_password = $_POST['new_password'] ?? ''; + $confirm_password = $_POST['confirm_password'] ?? ''; + + if (empty($current_password) || empty($new_password)) { + $error = 'Inserisci la password attuale e la nuova password'; + } elseif (!verify_password($current_password, $user['password'])) { + $error = 'Password attuale non corretta'; + } elseif (strlen($new_password) < 6) { + $error = 'La nuova password deve essere di almeno 6 caratteri'; + } elseif ($new_password !== $confirm_password) { + $error = 'Le password non corrispondono'; + } else { + $pdo = get_db_connection(); + $hashed_password = hash_password($new_password); + + $stmt = $pdo->prepare("UPDATE users SET password = ?, updated_at = NOW() WHERE id = ?"); + + if ($stmt->execute([$hashed_password, $user_id])) { + set_flash_message('success', 'Password cambiata con successo!'); + header('Location: profile.php'); + exit; + } else { + $error = 'Errore durante il cambio password'; + } + } +} + +?> + + + + + + Profilo - Pilates Platform + + + +
+
+
+

Pilates Studio

+ +
+
+
+ +
+
+ + + + +
+

Il Mio Profilo

+ + + + +
+ + + +
+

Dati Personali

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

Cambia Password

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

Informazioni Account

+

Registrato il:

+ +

Ultimo accesso:

+ +
+
+
+
+ + + +