fix
This commit is contained in:
485
README.md
Normal file
485
README.md
Normal file
@@ -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
|
||||||
|
<img src="assets/images/logo.png" alt="Logo" class="logo">
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
<Files "config.php">
|
||||||
|
Require all denied
|
||||||
|
</Files>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 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.
|
||||||
203
admin/dashboard.php
Normal file
203
admin/dashboard.php
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Dashboard Amministratore
|
||||||
|
*
|
||||||
|
* Panoramica generale con statistiche e azioni rapide
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
// Ottieni statistiche
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
|
||||||
|
// Conta utenti totali
|
||||||
|
$stmt = $pdo->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();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Dashboard Admin - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Admin</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Vedi Sito</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php" class="active">📊 Dashboard</a></li>
|
||||||
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Dashboard</h2>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<!-- Statistiche -->
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-value"><?php echo $total_users; ?></div>
|
||||||
|
<div class="stat-label">Utenti Registrati</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-value"><?php echo $total_lessons; ?></div>
|
||||||
|
<div class="stat-label">Lezioni Totali</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-value"><?php echo $total_purchases; ?></div>
|
||||||
|
<div class="stat-label">Acquisti</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-value"><?php echo format_price($total_revenue); ?></div>
|
||||||
|
<div class="stat-label">Guadagni Totali</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Azioni rapide -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Azioni Rapide</h3>
|
||||||
|
<div class="d-flex gap-1">
|
||||||
|
<a href="lesson_create.php" class="btn btn-primary">➕ Nuova Lezione</a>
|
||||||
|
<a href="lessons.php" class="btn btn-secondary">📋 Vedi Tutte le Lezioni</a>
|
||||||
|
<a href="users.php" class="btn btn-secondary">👥 Gestisci Utenti</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lezioni più vendute -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Lezioni Più Vendute</h3>
|
||||||
|
<?php if (!empty($top_lessons)): ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Lezione</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Prezzo</th>
|
||||||
|
<th>Vendite</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($top_lessons as $lesson): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong><?php echo htmlspecialchars($lesson['title']); ?></strong>
|
||||||
|
<?php if ($lesson['is_demo']): ?>
|
||||||
|
<span class="text-success"> (Demo)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo ucfirst($lesson['type']); ?></td>
|
||||||
|
<td><?php echo format_price($lesson['price']); ?></td>
|
||||||
|
<td><strong><?php echo $lesson['sales_count']; ?></strong></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted">Nessuna lezione venduta ancora.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ultimi acquisti -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Ultimi Acquisti</h3>
|
||||||
|
<?php if (!empty($recent_purchases)): ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Data</th>
|
||||||
|
<th>Utente</th>
|
||||||
|
<th>Lezione</th>
|
||||||
|
<th>Importo</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($recent_purchases as $purchase): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo format_datetime($purchase['purchased_at']); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php echo htmlspecialchars($purchase['first_name'] . ' ' . $purchase['last_name']); ?>
|
||||||
|
<br>
|
||||||
|
<small class="text-muted"><?php echo htmlspecialchars($purchase['email']); ?></small>
|
||||||
|
</td>
|
||||||
|
<td><?php echo htmlspecialchars($purchase['lesson_title']); ?></td>
|
||||||
|
<td><strong><?php echo format_price($purchase['amount']); ?></strong></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted">Nessun acquisto ancora.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
286
admin/lesson_create.php
Normal file
286
admin/lesson_create.php
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Crea Nuova Lezione
|
||||||
|
*
|
||||||
|
* Form per creare una nuova videolezione o lezione live
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Processa il form
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$title = sanitize_input($_POST['title'] ?? '');
|
||||||
|
$description = sanitize_input($_POST['description'] ?? '');
|
||||||
|
$type = $_POST['type'] ?? 'video';
|
||||||
|
$level = $_POST['level'] ?? 'principiante';
|
||||||
|
$category = sanitize_input($_POST['category'] ?? '');
|
||||||
|
$price = floatval($_POST['price'] ?? 0);
|
||||||
|
$duration = !empty($_POST['duration']) ? intval($_POST['duration']) : null;
|
||||||
|
$video_url = sanitize_input($_POST['video_url'] ?? '');
|
||||||
|
$video_platform = $_POST['video_platform'] ?? 'local';
|
||||||
|
$live_url = sanitize_input($_POST['live_url'] ?? '');
|
||||||
|
$live_platform = sanitize_input($_POST['live_platform'] ?? '');
|
||||||
|
$live_date = !empty($_POST['live_date']) ? $_POST['live_date'] : null;
|
||||||
|
$is_demo = isset($_POST['is_demo']) ? 1 : 0;
|
||||||
|
$is_active = isset($_POST['is_active']) ? 1 : 0;
|
||||||
|
|
||||||
|
// Validazione
|
||||||
|
if (empty($title)) {
|
||||||
|
$error = 'Il titolo è obbligatorio';
|
||||||
|
} elseif (empty($description)) {
|
||||||
|
$error = 'La descrizione è obbligatoria';
|
||||||
|
} elseif ($type === 'live' && empty($live_date)) {
|
||||||
|
$error = 'Per le lezioni live, la data è obbligatoria';
|
||||||
|
} else {
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Nuova Lezione - Admin</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Admin</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="lessons.php" class="btn btn-outline">← Torna alle Lezioni</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php">📊 Dashboard</a></li>
|
||||||
|
<li><a href="lessons.php" class="active">🎥 Gestione Lezioni</a></li>
|
||||||
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Crea Nuova Lezione</h2>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error"><?php echo htmlspecialchars($error); ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<form method="POST" action="">
|
||||||
|
<!-- Informazioni Base -->
|
||||||
|
<h3 style="margin-bottom: 1rem; color: var(--primary-color);">Informazioni Base</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title" class="form-label">Titolo *</label>
|
||||||
|
<input type="text" id="title" name="title" class="form-control" required
|
||||||
|
value="<?php echo htmlspecialchars($_POST['title'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description" class="form-label">Descrizione *</label>
|
||||||
|
<textarea id="description" name="description" class="form-control" required
|
||||||
|
rows="4"><?php echo htmlspecialchars($_POST['description'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="type" class="form-label">Tipo Lezione *</label>
|
||||||
|
<select id="type" name="type" class="form-control" required onchange="toggleTypeFields()">
|
||||||
|
<option value="video" <?php echo ($_POST['type'] ?? 'video') === 'video' ? 'selected' : ''; ?>>
|
||||||
|
Videolezione Registrata
|
||||||
|
</option>
|
||||||
|
<option value="live" <?php echo ($_POST['type'] ?? '') === 'live' ? 'selected' : ''; ?>>
|
||||||
|
Lezione Live
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Campi Video -->
|
||||||
|
<div id="video-fields" style="display: <?php echo ($_POST['type'] ?? 'video') === 'video' ? 'block' : 'none'; ?>;">
|
||||||
|
<h3 style="margin: 2rem 0 1rem; color: var(--primary-color);">Dettagli Video</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="video_platform" class="form-label">Piattaforma Video</label>
|
||||||
|
<select id="video_platform" name="video_platform" class="form-control">
|
||||||
|
<option value="local">File Locale</option>
|
||||||
|
<option value="youtube">YouTube</option>
|
||||||
|
<option value="vimeo">Vimeo</option>
|
||||||
|
<option value="s3">AWS S3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="video_url" class="form-label">URL/Percorso Video</label>
|
||||||
|
<input type="text" id="video_url" name="video_url" class="form-control"
|
||||||
|
placeholder="es: https://youtube.com/watch?v=..."
|
||||||
|
value="<?php echo htmlspecialchars($_POST['video_url'] ?? ''); ?>">
|
||||||
|
<small class="text-muted">Lascia vuoto se caricherai il video successivamente</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="duration" class="form-label">Durata (minuti)</label>
|
||||||
|
<input type="number" id="duration" name="duration" class="form-control"
|
||||||
|
min="1" value="<?php echo htmlspecialchars($_POST['duration'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Campi Live -->
|
||||||
|
<div id="live-fields" style="display: <?php echo ($_POST['type'] ?? '') === 'live' ? 'block' : 'none'; ?>;">
|
||||||
|
<h3 style="margin: 2rem 0 1rem; color: var(--primary-color);">Dettagli Lezione Live</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="live_platform" class="form-label">Piattaforma Live</label>
|
||||||
|
<input type="text" id="live_platform" name="live_platform" class="form-control"
|
||||||
|
placeholder="es: Zoom, Google Meet, Teams..."
|
||||||
|
value="<?php echo htmlspecialchars($_POST['live_platform'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="live_url" class="form-label">Link Lezione Live</label>
|
||||||
|
<input type="text" id="live_url" name="live_url" class="form-control"
|
||||||
|
placeholder="es: https://zoom.us/j/..."
|
||||||
|
value="<?php echo htmlspecialchars($_POST['live_url'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="live_date" class="form-label">Data e Ora Lezione *</label>
|
||||||
|
<input type="datetime-local" id="live_date" name="live_date" class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($_POST['live_date'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Classificazione -->
|
||||||
|
<h3 style="margin: 2rem 0 1rem; color: var(--primary-color);">Classificazione</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="level" class="form-label">Livello *</label>
|
||||||
|
<select id="level" name="level" class="form-control" required>
|
||||||
|
<option value="principiante" <?php echo ($_POST['level'] ?? 'principiante') === 'principiante' ? 'selected' : ''; ?>>
|
||||||
|
Principiante
|
||||||
|
</option>
|
||||||
|
<option value="intermedio" <?php echo ($_POST['level'] ?? '') === 'intermedio' ? 'selected' : ''; ?>>
|
||||||
|
Intermedio
|
||||||
|
</option>
|
||||||
|
<option value="avanzato" <?php echo ($_POST['level'] ?? '') === 'avanzato' ? 'selected' : ''; ?>>
|
||||||
|
Avanzato
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="category" class="form-label">Categoria</label>
|
||||||
|
<input type="text" id="category" name="category" class="form-control"
|
||||||
|
placeholder="es: Mat Work, Reformer, Stretching..."
|
||||||
|
value="<?php echo htmlspecialchars($_POST['category'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Prezzo e Disponibilità -->
|
||||||
|
<h3 style="margin: 2rem 0 1rem; color: var(--primary-color);">Prezzo e Disponibilità</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="price" class="form-label">Prezzo (€) *</label>
|
||||||
|
<input type="number" id="price" name="price" class="form-control"
|
||||||
|
min="0" step="0.01" required
|
||||||
|
value="<?php echo htmlspecialchars($_POST['price'] ?? '0'); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
<input type="checkbox" name="is_demo" value="1"
|
||||||
|
<?php echo isset($_POST['is_demo']) ? 'checked' : ''; ?>>
|
||||||
|
Lezione Demo (gratuita per tutti)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
<input type="checkbox" name="is_active" value="1"
|
||||||
|
<?php echo !isset($_POST['is_active']) || $_POST['is_active'] ? 'checked' : ''; ?>>
|
||||||
|
Lezione attiva (visibile agli utenti)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-1 mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary">Crea Lezione</button>
|
||||||
|
<a href="lessons.php" class="btn btn-outline">Annulla</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Mostra/nascondi campi in base al tipo di lezione
|
||||||
|
function toggleTypeFields() {
|
||||||
|
const type = document.getElementById('type').value;
|
||||||
|
const videoFields = document.getElementById('video-fields');
|
||||||
|
const liveFields = document.getElementById('live-fields');
|
||||||
|
|
||||||
|
if (type === 'video') {
|
||||||
|
videoFields.style.display = 'block';
|
||||||
|
liveFields.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
videoFields.style.display = 'none';
|
||||||
|
liveFields.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
148
admin/lessons.php
Normal file
148
admin/lessons.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Gestione Lezioni - Lista tutte le lezioni
|
||||||
|
*
|
||||||
|
* Visualizza tutte le lezioni con opzioni per modificare ed eliminare
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
// Gestione eliminazione
|
||||||
|
if (isset($_GET['delete']) && is_numeric($_GET['delete'])) {
|
||||||
|
$lesson_id = (int)$_GET['delete'];
|
||||||
|
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
|
||||||
|
// Soft delete
|
||||||
|
$stmt = $pdo->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();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Gestione Lezioni - Admin</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Admin</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Vedi Sito</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php">📊 Dashboard</a></li>
|
||||||
|
<li><a href="lessons.php" class="active">🎥 Gestione Lezioni</a></li>
|
||||||
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<div class="d-flex justify-between align-center mb-2">
|
||||||
|
<h2 class="section-title" style="text-align: left; margin: 0;">Gestione Lezioni</h2>
|
||||||
|
<a href="lesson_create.php" class="btn btn-primary">➕ Nuova Lezione</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<?php if (!empty($lessons)): ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Titolo</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Livello</th>
|
||||||
|
<th>Prezzo</th>
|
||||||
|
<th>Vendite</th>
|
||||||
|
<th>Guadagno</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($lessons as $lesson): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong><?php echo htmlspecialchars($lesson['title']); ?></strong>
|
||||||
|
<?php if ($lesson['is_demo']): ?>
|
||||||
|
<br><span class="text-success"><small>✓ Demo</small></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo ucfirst($lesson['type']); ?></td>
|
||||||
|
<td><?php echo ucfirst($lesson['level']); ?></td>
|
||||||
|
<td><?php echo format_price($lesson['price']); ?></td>
|
||||||
|
<td><?php echo $lesson['purchase_count']; ?></td>
|
||||||
|
<td><strong><?php echo format_price($lesson['total_revenue'] ?? 0); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($lesson['is_active']): ?>
|
||||||
|
<span class="text-success">Attiva</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="text-muted">Disattiva</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="lesson_edit.php?id=<?php echo $lesson['id']; ?>"
|
||||||
|
class="btn btn-small btn-secondary">Modifica</a>
|
||||||
|
<a href="lessons.php?delete=<?php echo $lesson['id']; ?>"
|
||||||
|
class="btn btn-small btn-danger"
|
||||||
|
data-confirm-delete>Elimina</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted text-center">Nessuna lezione disponibile. <a href="lesson_create.php">Creane una!</a></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
165
admin/purchases.php
Normal file
165
admin/purchases.php
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Visualizza Acquisti
|
||||||
|
*
|
||||||
|
* Lista di tutti gli acquisti effettuati sulla piattaforma
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
// Ottieni tutti gli acquisti
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Acquisti - Admin</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Admin</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Vedi Sito</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php">📊 Dashboard</a></li>
|
||||||
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
|
<li><a href="users.php">👥 Gestione Utenti</a></li>
|
||||||
|
<li><a href="purchases.php" class="active">💰 Acquisti</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Storico Acquisti</h2>
|
||||||
|
|
||||||
|
<!-- Statistiche rapide -->
|
||||||
|
<div class="stats-grid" style="grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-value"><?php echo $stats['total_purchases']; ?></div>
|
||||||
|
<div class="stat-label">Totale Transazioni</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" style="background: linear-gradient(135deg, #2ECC71, #27AE60);">
|
||||||
|
<div class="stat-value"><?php echo format_price($stats['total_revenue']); ?></div>
|
||||||
|
<div class="stat-label">Incassi Totali</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" style="background: linear-gradient(135deg, #F39C12, #E67E22);">
|
||||||
|
<div class="stat-value"><?php echo $stats['pending_count']; ?></div>
|
||||||
|
<div class="stat-label">In Attesa</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-card" style="background: linear-gradient(135deg, #E74C3C, #C0392B);">
|
||||||
|
<div class="stat-value"><?php echo $stats['failed_count']; ?></div>
|
||||||
|
<div class="stat-label">Falliti</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<?php if (!empty($purchases)): ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Data</th>
|
||||||
|
<th>Utente</th>
|
||||||
|
<th>Lezione</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Importo</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>PayPal ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($purchases as $purchase): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
echo $purchase['purchased_at']
|
||||||
|
? format_datetime($purchase['purchased_at'])
|
||||||
|
: format_datetime($purchase['created_at']);
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo htmlspecialchars($purchase['first_name'] . ' ' . $purchase['last_name']); ?>
|
||||||
|
<br>
|
||||||
|
<small class="text-muted"><?php echo htmlspecialchars($purchase['email']); ?></small>
|
||||||
|
</td>
|
||||||
|
<td><?php echo htmlspecialchars($purchase['lesson_title']); ?></td>
|
||||||
|
<td><?php echo ucfirst($purchase['lesson_type']); ?></td>
|
||||||
|
<td><strong><?php echo format_price($purchase['amount']); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$status_colors = [
|
||||||
|
'completed' => 'text-success',
|
||||||
|
'pending' => 'text-warning',
|
||||||
|
'failed' => 'text-danger',
|
||||||
|
'refunded' => 'text-muted'
|
||||||
|
];
|
||||||
|
$color = $status_colors[$purchase['status']] ?? '';
|
||||||
|
?>
|
||||||
|
<span class="<?php echo $color; ?>">
|
||||||
|
<?php echo ucfirst($purchase['status']); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<small class="text-muted">
|
||||||
|
<?php echo $purchase['paypal_order_id'] ? htmlspecialchars($purchase['paypal_order_id']) : '-'; ?>
|
||||||
|
</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted text-center">Nessun acquisto ancora.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
145
admin/users.php
Normal file
145
admin/users.php
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Gestione Utenti
|
||||||
|
*
|
||||||
|
* Visualizza tutti gli utenti registrati con opzioni di gestione
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_admin();
|
||||||
|
|
||||||
|
// Gestione blocco/sblocco utente
|
||||||
|
if (isset($_GET['toggle_active']) && is_numeric($_GET['toggle_active'])) {
|
||||||
|
$user_id = (int)$_GET['toggle_active'];
|
||||||
|
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Gestione Utenti - Admin</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio - Admin</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Vedi Sito</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php">📊 Dashboard</a></li>
|
||||||
|
<li><a href="lessons.php">🎥 Gestione Lezioni</a></li>
|
||||||
|
<li><a href="users.php" class="active">👥 Gestione Utenti</a></li>
|
||||||
|
<li><a href="purchases.php">💰 Acquisti</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Gestione Utenti</h2>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="text-muted mb-2">Totale utenti registrati: <strong><?php echo count($users); ?></strong></p>
|
||||||
|
|
||||||
|
<?php if (!empty($users)): ?>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nome</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Registrato il</th>
|
||||||
|
<th>Ultimo Accesso</th>
|
||||||
|
<th>Acquisti</th>
|
||||||
|
<th>Speso</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($users as $user): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong><?php echo htmlspecialchars($user['first_name'] . ' ' . $user['last_name']); ?></strong>
|
||||||
|
</td>
|
||||||
|
<td><?php echo htmlspecialchars($user['email']); ?></td>
|
||||||
|
<td><?php echo format_date($user['created_at']); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
echo $user['last_login'] ? format_datetime($user['last_login']) : 'Mai';
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo $user['purchase_count']; ?></td>
|
||||||
|
<td><strong><?php echo format_price($user['total_spent'] ?? 0); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($user['is_active']): ?>
|
||||||
|
<span class="text-success">✓ Attivo</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="text-danger">✗ Bloccato</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="users.php?toggle_active=<?php echo $user['id']; ?>"
|
||||||
|
class="btn btn-small <?php echo $user['is_active'] ? 'btn-danger' : 'btn-success'; ?>">
|
||||||
|
<?php echo $user['is_active'] ? 'Blocca' : 'Sblocca'; ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-muted text-center">Nessun utente registrato ancora.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
683
assets/css/style.css
Normal file
683
assets/css/style.css
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
assets/js/main.js
Normal file
52
assets/js/main.js
Normal file
@@ -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;
|
||||||
|
}
|
||||||
243
database/schema.sql
Normal file
243
database/schema.sql
Normal file
@@ -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;
|
||||||
119
forgot_password.php
Normal file
119
forgot_password.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pagina Recupero Password - Step 1
|
||||||
|
*
|
||||||
|
* L'utente inserisce la sua email e riceve un link per resettare la password
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Se l'utente è già loggato, reindirizza
|
||||||
|
if (is_logged_in()) {
|
||||||
|
header('Location: user/dashboard.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = '';
|
||||||
|
$error = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Processa il form se inviato
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$email = sanitize_input($_POST['email'] ?? '');
|
||||||
|
|
||||||
|
// Validazione
|
||||||
|
if (empty($email)) {
|
||||||
|
$error = 'Inserisci il tuo indirizzo email';
|
||||||
|
} elseif (!validate_email($email)) {
|
||||||
|
$error = 'Email non valida';
|
||||||
|
} else {
|
||||||
|
// Cerca l'utente
|
||||||
|
$user = get_user_by_email($email);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
// Crea token di reset
|
||||||
|
$token = create_password_reset_token($user['id']);
|
||||||
|
|
||||||
|
// Invia email
|
||||||
|
if (send_password_reset_email($email, $token)) {
|
||||||
|
$success = true;
|
||||||
|
} else {
|
||||||
|
$error = 'Errore nell\'invio dell\'email. Riprova più tardi.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Per sicurezza, mostra lo stesso messaggio anche se l'email non esiste
|
||||||
|
// Questo previene di scoprire quali email sono registrate
|
||||||
|
$success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Recupero Password - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-sm" style="padding-top: 3rem;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="card-header text-center">Recupero Password</h2>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>Email inviata!</strong><br>
|
||||||
|
Se l'indirizzo email è registrato, riceverai un'email con le istruzioni per reimpostare la password.
|
||||||
|
Controlla anche la cartella spam.
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="login.php" class="btn btn-primary">Torna al Login</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p class="text-muted text-center mb-2">
|
||||||
|
Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la password.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($email); ?>"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-large" style="width: 100%;">
|
||||||
|
Invia Link di Reset
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<p class="mt-2">
|
||||||
|
<a href="login.php">← Torna al Login</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
172
includes/config.php
Normal file
172
includes/config.php
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* File di Configurazione
|
||||||
|
*
|
||||||
|
* Contiene tutte le impostazioni del sito: database, email, PayPal, ecc.
|
||||||
|
* IMPORTANTE: Non condividere mai questo file pubblicamente!
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE DATABASE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
define('DB_HOST', 'localhost'); // Host del database
|
||||||
|
define('DB_NAME', 'pilates_platform'); // Nome del database
|
||||||
|
define('DB_USER', 'root'); // Username database
|
||||||
|
define('DB_PASS', ''); // Password database
|
||||||
|
define('DB_CHARSET', 'utf8mb4'); // Charset per supportare tutti i caratteri
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE GENERALE SITO
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
define('SITE_NAME', 'Pilates Studio');
|
||||||
|
define('SITE_URL', 'http://localhost/pilates-platform'); // Modifica con il tuo dominio in produzione
|
||||||
|
define('ADMIN_EMAIL', 'admin@pilatesstudio.com'); // Email amministratore
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE SICUREZZA
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Chiave segreta per crittografia (CAMBIALA in produzione!)
|
||||||
|
define('SECRET_KEY', 'cambia-questa-chiave-con-una-stringa-casuale-sicura');
|
||||||
|
|
||||||
|
// Durata sessione in secondi (default: 2 ore)
|
||||||
|
define('SESSION_LIFETIME', 7200);
|
||||||
|
|
||||||
|
// Durata token recupero password (default: 1 ora)
|
||||||
|
define('PASSWORD_RESET_TOKEN_LIFETIME', 3600);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE PAYPAL
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Modalità sandbox per test (true) o produzione (false)
|
||||||
|
define('PAYPAL_SANDBOX', true);
|
||||||
|
|
||||||
|
// Credenziali PayPal Sandbox (per test)
|
||||||
|
define('PAYPAL_CLIENT_ID', 'inserisci-qui-il-tuo-client-id-sandbox');
|
||||||
|
define('PAYPAL_SECRET', 'inserisci-qui-il-tuo-secret-sandbox');
|
||||||
|
|
||||||
|
// Credenziali PayPal Live (per produzione - NON compilare finché non sei pronto!)
|
||||||
|
define('PAYPAL_LIVE_CLIENT_ID', '');
|
||||||
|
define('PAYPAL_LIVE_SECRET', '');
|
||||||
|
|
||||||
|
// URL API PayPal
|
||||||
|
define('PAYPAL_API_URL', PAYPAL_SANDBOX ?
|
||||||
|
'https://api-m.sandbox.paypal.com' :
|
||||||
|
'https://api-m.paypal.com'
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE EMAIL
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Configurazione per l'invio di email (usa SMTP per produzione)
|
||||||
|
define('MAIL_FROM', 'noreply@pilatesstudio.com');
|
||||||
|
define('MAIL_FROM_NAME', 'Pilates Studio');
|
||||||
|
|
||||||
|
// Impostazioni SMTP (opzionali - commentate se usi mail() PHP di base)
|
||||||
|
/*
|
||||||
|
define('SMTP_HOST', 'smtp.gmail.com');
|
||||||
|
define('SMTP_PORT', 587);
|
||||||
|
define('SMTP_USERNAME', 'tua-email@gmail.com');
|
||||||
|
define('SMTP_PASSWORD', 'tua-password-applicazione');
|
||||||
|
define('SMTP_ENCRYPTION', 'tls');
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE UPLOAD FILE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Cartella per upload file (thumbnails, video se locali)
|
||||||
|
define('UPLOAD_DIR', __DIR__ . '/../uploads/');
|
||||||
|
define('UPLOAD_URL', SITE_URL . '/uploads/');
|
||||||
|
|
||||||
|
// Dimensione massima file upload (in bytes - default 50MB)
|
||||||
|
define('MAX_UPLOAD_SIZE', 50 * 1024 * 1024);
|
||||||
|
|
||||||
|
// Estensioni permesse per upload
|
||||||
|
define('ALLOWED_IMAGE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'webp']);
|
||||||
|
define('ALLOWED_VIDEO_TYPES', ['mp4', 'mov', 'avi', 'webm']);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE VIDEO
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Tipo di hosting video: 'local', 'youtube', 'vimeo', 's3'
|
||||||
|
define('VIDEO_HOSTING_TYPE', 'local');
|
||||||
|
|
||||||
|
// Configurazione AWS S3 (se usi S3)
|
||||||
|
/*
|
||||||
|
define('AWS_ACCESS_KEY', 'tua-access-key');
|
||||||
|
define('AWS_SECRET_KEY', 'tua-secret-key');
|
||||||
|
define('AWS_REGION', 'eu-west-1');
|
||||||
|
define('AWS_BUCKET', 'pilates-videos');
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONFIGURAZIONE TIMEZONE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
date_default_timezone_set('Europe/Rome');
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// MODALITÀ DEBUG
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Attiva/disattiva visualizzazione errori (FALSE in produzione!)
|
||||||
|
define('DEBUG_MODE', true);
|
||||||
|
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
} else {
|
||||||
|
error_reporting(0);
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONNESSIONE DATABASE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crea e restituisce la connessione al database
|
||||||
|
* Usa PDO per sicurezza contro SQL injection
|
||||||
|
*/
|
||||||
|
function get_db_connection() {
|
||||||
|
static $pdo = null;
|
||||||
|
|
||||||
|
// Se la connessione esiste già, restituiscila
|
||||||
|
if ($pdo !== null) {
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Crea stringa DSN (Data Source Name)
|
||||||
|
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
|
||||||
|
|
||||||
|
// Opzioni PDO per sicurezza
|
||||||
|
$options = [
|
||||||
|
PDO::ATTR_ERRMODE => 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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
564
includes/functions.php
Normal file
564
includes/functions.php
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Funzioni Comuni
|
||||||
|
*
|
||||||
|
* Raccolta di funzioni utilizzate in tutta la piattaforma
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// FUNZIONI AUTENTICAZIONE E SESSIONE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se l'utente è loggato
|
||||||
|
*
|
||||||
|
* @return bool True se loggato, false altrimenti
|
||||||
|
*/
|
||||||
|
function is_logged_in() {
|
||||||
|
return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se l'utente è un amministratore
|
||||||
|
*
|
||||||
|
* @return bool True se admin, false altrimenti
|
||||||
|
*/
|
||||||
|
function is_admin() {
|
||||||
|
return isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Richiede che l'utente sia loggato, altrimenti reindirizza al login
|
||||||
|
*/
|
||||||
|
function require_login() {
|
||||||
|
if (!is_logged_in()) {
|
||||||
|
$_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI'];
|
||||||
|
header('Location: ' . SITE_URL . '/login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Richiede che l'utente sia amministratore, altrimenti reindirizza
|
||||||
|
*/
|
||||||
|
function require_admin() {
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
if (!is_admin()) {
|
||||||
|
header('Location: ' . SITE_URL . '/index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effettua il login dell'utente
|
||||||
|
*
|
||||||
|
* @param int $user_id ID dell'utente
|
||||||
|
* @param bool $is_admin Se l'utente è admin
|
||||||
|
*/
|
||||||
|
function login_user($user_id, $is_admin = false) {
|
||||||
|
// Rigenera l'ID di sessione per sicurezza
|
||||||
|
session_regenerate_id(true);
|
||||||
|
|
||||||
|
$_SESSION['user_id'] = $user_id;
|
||||||
|
$_SESSION['is_admin'] = $is_admin;
|
||||||
|
$_SESSION['last_activity'] = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effettua il logout dell'utente
|
||||||
|
*/
|
||||||
|
function logout_user() {
|
||||||
|
// Cancella tutte le variabili di sessione
|
||||||
|
$_SESSION = array();
|
||||||
|
|
||||||
|
// Distruggi il cookie di sessione se esiste
|
||||||
|
if (isset($_COOKIE[session_name()])) {
|
||||||
|
setcookie(session_name(), '', time() - 3600, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distruggi la sessione
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica la validità della sessione (timeout)
|
||||||
|
*
|
||||||
|
* @return bool True se sessione valida, false se scaduta
|
||||||
|
*/
|
||||||
|
function check_session_timeout() {
|
||||||
|
if (isset($_SESSION['last_activity'])) {
|
||||||
|
$elapsed = time() - $_SESSION['last_activity'];
|
||||||
|
|
||||||
|
if ($elapsed > 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 = "
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||||
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: #4A90E2;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class='container'>
|
||||||
|
<h2>Recupero Password</h2>
|
||||||
|
<p>Hai richiesto il recupero della password per il tuo account su " . SITE_NAME . ".</p>
|
||||||
|
<p>Clicca sul pulsante qui sotto per reimpostare la tua password:</p>
|
||||||
|
<a href='{$reset_link}' class='button'>Reimposta Password</a>
|
||||||
|
<p>Oppure copia e incolla questo link nel tuo browser:</p>
|
||||||
|
<p>{$reset_link}</p>
|
||||||
|
<p>Questo link è valido per " . (PASSWORD_RESET_TOKEN_LIFETIME / 60) . " minuti.</p>
|
||||||
|
<p>Se non hai richiesto tu questo recupero, ignora questa email.</p>
|
||||||
|
<hr>
|
||||||
|
<p><small>Questa è un'email automatica, non rispondere a questo messaggio.</small></p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
";
|
||||||
|
|
||||||
|
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 "<div class='alert alert-{$type}'>{$message}</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
33
includes/logout.php
Normal file
33
includes/logout.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Logout
|
||||||
|
*
|
||||||
|
* Termina la sessione utente e reindirizza alla home
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../config.php';
|
||||||
|
require_once 'functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Log attività prima di fare logout
|
||||||
|
if (is_logged_in()) {
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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;
|
||||||
|
?>
|
||||||
148
index.php
Normal file
148
index.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pilates Platform - Homepage
|
||||||
|
*
|
||||||
|
* Pagina principale della piattaforma. Mostra le lezioni demo e permette
|
||||||
|
* l'accesso per utenti registrati e amministratori.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Includi le configurazioni e funzioni comuni
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
// Avvia la sessione
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Recupera le lezioni demo dal database
|
||||||
|
$demo_lessons = get_demo_lessons();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Pilates Platform - Videolezioni e Lezioni Live</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Header con navigazione -->
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<?php if (is_logged_in()): ?>
|
||||||
|
<?php if (is_admin()): ?>
|
||||||
|
<a href="admin/dashboard.php" class="btn btn-secondary">Area Admin</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="user/dashboard.php" class="btn btn-secondary">Le Mie Lezioni</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="includes/logout.php" class="btn btn-outline">Logout</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php" class="btn btn-secondary">Accedi</a>
|
||||||
|
<a href="register.php" class="btn btn-primary">Registrati</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="hero">
|
||||||
|
<div class="container">
|
||||||
|
<div class="hero-content">
|
||||||
|
<h2 class="hero-title">Trasforma il tuo corpo con il Pilates</h2>
|
||||||
|
<p class="hero-subtitle">Videolezioni professionali e sessioni live per tutti i livelli</p>
|
||||||
|
<?php if (!is_logged_in()): ?>
|
||||||
|
<a href="register.php" class="btn btn-primary btn-large">Inizia Ora</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Sezione Lezioni Demo -->
|
||||||
|
<section class="lessons-section">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Lezioni Demo Gratuite</h2>
|
||||||
|
<p class="section-subtitle">Prova gratuitamente alcune delle nostre lezioni migliori</p>
|
||||||
|
|
||||||
|
<div class="lessons-grid">
|
||||||
|
<?php if (!empty($demo_lessons)): ?>
|
||||||
|
<?php foreach ($demo_lessons as $lesson): ?>
|
||||||
|
<div class="lesson-card">
|
||||||
|
<?php if ($lesson['thumbnail']): ?>
|
||||||
|
<img src="<?php echo htmlspecialchars($lesson['thumbnail']); ?>"
|
||||||
|
alt="<?php echo htmlspecialchars($lesson['title']); ?>"
|
||||||
|
class="lesson-thumbnail">
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="lesson-thumbnail-placeholder">
|
||||||
|
<span>📹</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="lesson-content">
|
||||||
|
<h3 class="lesson-title"><?php echo htmlspecialchars($lesson['title']); ?></h3>
|
||||||
|
<p class="lesson-description"><?php echo htmlspecialchars($lesson['description']); ?></p>
|
||||||
|
|
||||||
|
<div class="lesson-meta">
|
||||||
|
<span class="lesson-duration">⏱️ <?php echo $lesson['duration']; ?> min</span>
|
||||||
|
<span class="lesson-level">📊 <?php echo ucfirst($lesson['level']); ?></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="lesson.php?id=<?php echo $lesson['id']; ?>" class="btn btn-outline btn-small">
|
||||||
|
Guarda Gratis
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="no-lessons">Nessuna lezione demo disponibile al momento.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Sezione Vantaggi -->
|
||||||
|
<section class="features-section">
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="section-title">Perché scegliere Pilates Studio?</h2>
|
||||||
|
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">🎥</div>
|
||||||
|
<h3>Videolezioni HD</h3>
|
||||||
|
<p>Contenuti di alta qualità disponibili 24/7</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">📅</div>
|
||||||
|
<h3>Lezioni Live</h3>
|
||||||
|
<p>Sessioni interattive con l'istruttrice</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">📱</div>
|
||||||
|
<h3>Accessibile Ovunque</h3>
|
||||||
|
<p>Su computer, tablet e smartphone</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">⭐</div>
|
||||||
|
<h3>Tutti i Livelli</h3>
|
||||||
|
<p>Principiante, intermedio e avanzato</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<p>© <?php echo date('Y'); ?> Pilates Studio. Tutti i diritti riservati.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
262
lesson.php
Normal file
262
lesson.php
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Visualizza Lezione
|
||||||
|
*
|
||||||
|
* Mostra i dettagli di una lezione e permette di acquistarla o visualizzarla
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Ottieni ID lezione
|
||||||
|
$lesson_id = isset($_GET['id']) && is_numeric($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||||
|
|
||||||
|
if (!$lesson_id) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ottieni dati lezione
|
||||||
|
$lesson = get_lesson_by_id($lesson_id);
|
||||||
|
|
||||||
|
if (!$lesson || !$lesson['is_active']) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica se l'utente è loggato e possiede la lezione
|
||||||
|
$is_logged_in = is_logged_in();
|
||||||
|
$user_owns = false;
|
||||||
|
|
||||||
|
if ($is_logged_in) {
|
||||||
|
$user_owns = user_owns_lesson($_SESSION['user_id'], $lesson_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se è demo, tutti possono vedere
|
||||||
|
$can_view = $lesson['is_demo'] || $user_owns;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo htmlspecialchars($lesson['title']); ?> - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
|
||||||
|
<!-- PayPal SDK -->
|
||||||
|
<?php if (!$can_view && $is_logged_in): ?>
|
||||||
|
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo PAYPAL_CLIENT_ID; ?>¤cy=EUR"></script>
|
||||||
|
<?php endif; ?>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="index.php" class="btn btn-outline">Home</a>
|
||||||
|
<?php if ($is_logged_in): ?>
|
||||||
|
<?php if (is_admin()): ?>
|
||||||
|
<a href="admin/dashboard.php" class="btn btn-secondary">Area Admin</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="user/dashboard.php" class="btn btn-secondary">Le Mie Lezioni</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="includes/logout.php" class="btn btn-outline">Logout</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php" class="btn btn-secondary">Accedi</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container" style="padding: 2rem 0;">
|
||||||
|
<div class="card">
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<h1 style="margin-bottom: 1rem;"><?php echo htmlspecialchars($lesson['title']); ?></h1>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 1rem; margin-bottom: 1.5rem; flex-wrap: wrap;">
|
||||||
|
<span class="badge">
|
||||||
|
<?php echo $lesson['type'] === 'video' ? '📹 Video' : '📡 Live'; ?>
|
||||||
|
</span>
|
||||||
|
<span class="badge">
|
||||||
|
📊 <?php echo ucfirst($lesson['level']); ?>
|
||||||
|
</span>
|
||||||
|
<?php if ($lesson['duration']): ?>
|
||||||
|
<span class="badge">⏱️ <?php echo $lesson['duration']; ?> min</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($lesson['category']): ?>
|
||||||
|
<span class="badge">📂 <?php echo htmlspecialchars($lesson['category']); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Video Player o Info Live -->
|
||||||
|
<?php if ($can_view): ?>
|
||||||
|
<?php if ($lesson['type'] === 'video'): ?>
|
||||||
|
<div style="background: #000; border-radius: 8px; margin-bottom: 2rem; aspect-ratio: 16/9;">
|
||||||
|
<?php if ($lesson['video_url']): ?>
|
||||||
|
<?php if ($lesson['video_platform'] === 'youtube'): ?>
|
||||||
|
<!-- Embed YouTube -->
|
||||||
|
<?php
|
||||||
|
$video_id = '';
|
||||||
|
if (preg_match('/(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&\?\/]+)/', $lesson['video_url'], $matches)) {
|
||||||
|
$video_id = $matches[1];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php if ($video_id): ?>
|
||||||
|
<iframe width="100%" height="100%"
|
||||||
|
src="https://www.youtube.com/embed/<?php echo $video_id; ?>"
|
||||||
|
frameborder="0" allowfullscreen
|
||||||
|
style="border-radius: 8px;"></iframe>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php elseif ($lesson['video_platform'] === 'vimeo'): ?>
|
||||||
|
<!-- Embed Vimeo -->
|
||||||
|
<?php
|
||||||
|
$video_id = '';
|
||||||
|
if (preg_match('/vimeo\.com\/(\d+)/', $lesson['video_url'], $matches)) {
|
||||||
|
$video_id = $matches[1];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php if ($video_id): ?>
|
||||||
|
<iframe width="100%" height="100%"
|
||||||
|
src="https://player.vimeo.com/video/<?php echo $video_id; ?>"
|
||||||
|
frameborder="0" allowfullscreen
|
||||||
|
style="border-radius: 8px;"></iframe>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<!-- Video locale o altro -->
|
||||||
|
<video controls width="100%" height="100%" style="border-radius: 8px;">
|
||||||
|
<source src="<?php echo htmlspecialchars($lesson['video_url']); ?>">
|
||||||
|
Il tuo browser non supporta il tag video.
|
||||||
|
</video>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: white;">
|
||||||
|
Video non ancora disponibile
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<!-- Lezione Live -->
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<h3>📡 Lezione Live</h3>
|
||||||
|
<p><strong>Data e ora:</strong> <?php echo format_datetime($lesson['live_date']); ?></p>
|
||||||
|
<?php if ($lesson['live_platform']): ?>
|
||||||
|
<p><strong>Piattaforma:</strong> <?php echo htmlspecialchars($lesson['live_platform']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($lesson['live_url']): ?>
|
||||||
|
<a href="<?php echo htmlspecialchars($lesson['live_url']); ?>"
|
||||||
|
target="_blank" class="btn btn-primary" style="margin-top: 1rem;">
|
||||||
|
Partecipa alla Lezione Live
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Descrizione -->
|
||||||
|
<h3>Descrizione</h3>
|
||||||
|
<p style="line-height: 1.8; margin-bottom: 2rem;">
|
||||||
|
<?php echo nl2br(htmlspecialchars($lesson['description'])); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Acquisto o Accesso -->
|
||||||
|
<?php if ($lesson['is_demo']): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>✓ Lezione Gratuita!</strong> Questa lezione è disponibile gratuitamente per tutti.
|
||||||
|
</div>
|
||||||
|
<?php elseif ($user_owns): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>✓ Hai già acquistato questa lezione!</strong>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($is_logged_in): ?>
|
||||||
|
<!-- Form Acquisto PayPal -->
|
||||||
|
<div style="border: 2px solid var(--primary-color); border-radius: 12px; padding: 2rem; text-align: center;">
|
||||||
|
<h2 style="color: var(--primary-color); margin-bottom: 1rem;">
|
||||||
|
Prezzo: <?php echo format_price($lesson['price']); ?>
|
||||||
|
</h2>
|
||||||
|
<p class="text-muted mb-2">Acquista questa lezione e avrai accesso illimitato per sempre</p>
|
||||||
|
|
||||||
|
<!-- PayPal Button Container -->
|
||||||
|
<div id="paypal-button-container" style="max-width: 400px; margin: 0 auto;"></div>
|
||||||
|
|
||||||
|
<p class="text-muted" style="font-size: 0.875rem; margin-top: 1rem;">
|
||||||
|
Pagamento sicuro tramite PayPal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-warning text-center">
|
||||||
|
<h3>Accedi per acquistare questa lezione</h3>
|
||||||
|
<p>Prezzo: <strong><?php echo format_price($lesson['price']); ?></strong></p>
|
||||||
|
<a href="login.php" class="btn btn-primary" style="margin-top: 1rem;">Accedi o Registrati</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!$can_view && $is_logged_in): ?>
|
||||||
|
<script>
|
||||||
|
// Integrazione PayPal
|
||||||
|
paypal.Buttons({
|
||||||
|
createOrder: function(data, actions) {
|
||||||
|
return actions.order.create({
|
||||||
|
purchase_units: [{
|
||||||
|
description: '<?php echo addslashes($lesson['title']); ?>',
|
||||||
|
amount: {
|
||||||
|
value: '<?php echo number_format($lesson['price'], 2, '.', ''); ?>'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onApprove: function(data, actions) {
|
||||||
|
return actions.order.capture().then(function(details) {
|
||||||
|
// Invia i dettagli al server per registrare l'acquisto
|
||||||
|
fetch('process_payment.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
orderID: data.orderID,
|
||||||
|
lessonID: <?php echo $lesson_id; ?>,
|
||||||
|
payerID: details.payer.payer_id,
|
||||||
|
amount: '<?php echo number_format($lesson['price'], 2, '.', ''); ?>'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
window.location.href = 'lesson.php?id=<?php echo $lesson_id; ?>&payment=success';
|
||||||
|
} else {
|
||||||
|
alert('Errore durante la registrazione del pagamento. Contatta il supporto.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: function(err) {
|
||||||
|
console.error(err);
|
||||||
|
alert('Si è verificato un errore durante il pagamento. Riprova.');
|
||||||
|
}
|
||||||
|
}).render('#paypal-button-container');
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: var(--secondary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
148
login.php
Normal file
148
login.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pagina di Login
|
||||||
|
*
|
||||||
|
* Permette agli utenti di accedere alla piattaforma
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Se l'utente è già loggato, reindirizza
|
||||||
|
if (is_logged_in()) {
|
||||||
|
if (is_admin()) {
|
||||||
|
header('Location: admin/dashboard.php');
|
||||||
|
} else {
|
||||||
|
header('Location: user/dashboard.php');
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variabili per il form
|
||||||
|
$email = '';
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
// Processa il form se inviato
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$email = sanitize_input($_POST['email'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
// Validazione
|
||||||
|
if (empty($email) || empty($password)) {
|
||||||
|
$error = 'Inserisci email e password';
|
||||||
|
} elseif (!validate_email($email)) {
|
||||||
|
$error = 'Email non valida';
|
||||||
|
} else {
|
||||||
|
// Cerca l'utente nel database
|
||||||
|
$user = get_user_by_email($email);
|
||||||
|
|
||||||
|
if ($user && verify_password($password, $user['password'])) {
|
||||||
|
// Password corretta - effettua il login
|
||||||
|
|
||||||
|
// Aggiorna data ultimo accesso
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-sm" style="padding-top: 3rem;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="card-header text-center">Accedi al tuo account</h2>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($email); ?>"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-large" style="width: 100%;">
|
||||||
|
Accedi
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<p class="text-muted">
|
||||||
|
<a href="forgot_password.php">Hai dimenticato la password?</a>
|
||||||
|
</p>
|
||||||
|
<p class="text-muted">
|
||||||
|
Non hai un account? <a href="register.php"><strong>Registrati</strong></a>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2">
|
||||||
|
<a href="index.php">← Torna alla home</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
104
process_payment.php
Normal file
104
process_payment.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Process Payment
|
||||||
|
*
|
||||||
|
* Riceve la conferma del pagamento da PayPal e registra l'acquisto nel database
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Verifica che l'utente sia loggato
|
||||||
|
if (!is_logged_in()) {
|
||||||
|
echo json_encode(['success' => 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']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
187
register.php
Normal file
187
register.php
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pagina di Registrazione
|
||||||
|
*
|
||||||
|
* Permette ai nuovi utenti di creare un account
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Se l'utente è già loggato, reindirizza
|
||||||
|
if (is_logged_in()) {
|
||||||
|
header('Location: user/dashboard.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variabili per il form
|
||||||
|
$first_name = '';
|
||||||
|
$last_name = '';
|
||||||
|
$email = '';
|
||||||
|
$error = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Processa il form se inviato
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$first_name = sanitize_input($_POST['first_name'] ?? '');
|
||||||
|
$last_name = sanitize_input($_POST['last_name'] ?? '');
|
||||||
|
$email = sanitize_input($_POST['email'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$password_confirm = $_POST['password_confirm'] ?? '';
|
||||||
|
|
||||||
|
// Validazione
|
||||||
|
if (empty($first_name) || empty($last_name) || empty($email) || empty($password)) {
|
||||||
|
$error = 'Tutti i campi sono obbligatori';
|
||||||
|
} elseif (!validate_email($email)) {
|
||||||
|
$error = 'Email non valida';
|
||||||
|
} elseif (strlen($password) < 6) {
|
||||||
|
$error = 'La password deve essere di almeno 6 caratteri';
|
||||||
|
} elseif ($password !== $password_confirm) {
|
||||||
|
$error = 'Le password non corrispondono';
|
||||||
|
} else {
|
||||||
|
// Verifica se l'email esiste già
|
||||||
|
if (get_user_by_email($email)) {
|
||||||
|
$error = 'Questa email è già registrata';
|
||||||
|
} else {
|
||||||
|
// Crea il nuovo utente
|
||||||
|
$user_id = create_user($email, $password, $first_name, $last_name);
|
||||||
|
|
||||||
|
if ($user_id) {
|
||||||
|
$success = true;
|
||||||
|
|
||||||
|
// Log attività
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Registrazione - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-sm" style="padding-top: 3rem; padding-bottom: 3rem;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="card-header text-center">Crea un nuovo account</h2>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>Registrazione completata!</strong> Verrai reindirizzato alla tua area personale...
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!$success): ?>
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="first_name" class="form-label">Nome</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="first_name"
|
||||||
|
name="first_name"
|
||||||
|
class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($first_name); ?>"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="last_name" class="form-label">Cognome</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="last_name"
|
||||||
|
name="last_name"
|
||||||
|
class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($last_name); ?>"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($email); ?>"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="form-label">Password (minimo 6 caratteri)</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
minlength="6"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password_confirm" class="form-label">Conferma Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password_confirm"
|
||||||
|
name="password_confirm"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
minlength="6"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-large" style="width: 100%;">
|
||||||
|
Registrati
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<p class="text-muted">
|
||||||
|
Hai già un account? <a href="login.php"><strong>Accedi</strong></a>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2">
|
||||||
|
<a href="index.php">← Torna alla home</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
156
reset_password.php
Normal file
156
reset_password.php
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Pagina Reset Password - Step 2
|
||||||
|
*
|
||||||
|
* L'utente arriva qui cliccando il link nell'email e può impostare una nuova password
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'includes/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Se l'utente è già loggato, reindirizza
|
||||||
|
if (is_logged_in()) {
|
||||||
|
header('Location: user/dashboard.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $_GET['token'] ?? '';
|
||||||
|
$error = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Verifica che il token esista e sia valido
|
||||||
|
$user_id = verify_password_reset_token($token);
|
||||||
|
|
||||||
|
if (!$user_id) {
|
||||||
|
$error = 'Link non valido o scaduto. Richiedi un nuovo link di reset.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processa il form se inviato
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $user_id) {
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$password_confirm = $_POST['password_confirm'] ?? '';
|
||||||
|
|
||||||
|
// Validazione
|
||||||
|
if (empty($password)) {
|
||||||
|
$error = 'Inserisci la nuova password';
|
||||||
|
} elseif (strlen($password) < 6) {
|
||||||
|
$error = 'La password deve essere di almeno 6 caratteri';
|
||||||
|
} elseif ($password !== $password_confirm) {
|
||||||
|
$error = 'Le password non corrispondono';
|
||||||
|
} else {
|
||||||
|
// Aggiorna la password
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$hashed_password = hash_password($password);
|
||||||
|
|
||||||
|
$stmt = $pdo->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.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Reimposta Password - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-sm" style="padding-top: 3rem;">
|
||||||
|
<div class="card">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="card-header text-center">Reimposta Password</h2>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>Password aggiornata!</strong><br>
|
||||||
|
La tua password è stata reimpostata con successo. Ora puoi effettuare il login.
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="login.php" class="btn btn-primary btn-large">Vai al Login</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php elseif (!$user_id): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="forgot_password.php" class="btn btn-primary">Richiedi Nuovo Link</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p class="text-muted text-center mb-2">
|
||||||
|
Inserisci la tua nuova password.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="form-label">Nuova Password (minimo 6 caratteri)</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
minlength="6"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password_confirm" class="form-label">Conferma Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password_confirm"
|
||||||
|
name="password_confirm"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
minlength="6"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-large" style="width: 100%;">
|
||||||
|
Reimposta Password
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<p class="mt-2">
|
||||||
|
<a href="login.php">← Torna al Login</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
192
user/catalog.php
Normal file
192
user/catalog.php
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Catalogo Lezioni
|
||||||
|
*
|
||||||
|
* Mostra tutte le lezioni disponibili per l'acquisto
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Ottieni tutte le lezioni attive
|
||||||
|
$all_lessons = get_all_lessons('all');
|
||||||
|
|
||||||
|
// Ottieni le lezioni già acquistate dall'utente
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT lesson_id FROM purchases
|
||||||
|
WHERE user_id = ? AND status = 'completed'
|
||||||
|
");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$owned_lesson_ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Catalogo Lezioni - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Home</a>
|
||||||
|
<a href="dashboard.php" class="btn btn-secondary">Le Mie Lezioni</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-outline">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar con filtri -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<h3 style="margin-bottom: 1rem;">Filtri</h3>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 1.5rem;">
|
||||||
|
<label style="font-weight: 500; display: block; margin-bottom: 0.5rem;">Tipo</label>
|
||||||
|
<label style="display: block; margin-bottom: 0.25rem;">
|
||||||
|
<input type="checkbox" class="filter-type" value="video" checked> Videolezioni
|
||||||
|
</label>
|
||||||
|
<label style="display: block;">
|
||||||
|
<input type="checkbox" class="filter-type" value="live" checked> Lezioni Live
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 1.5rem;">
|
||||||
|
<label style="font-weight: 500; display: block; margin-bottom: 0.5rem;">Livello</label>
|
||||||
|
<label style="display: block; margin-bottom: 0.25rem;">
|
||||||
|
<input type="checkbox" class="filter-level" value="principiante" checked> Principiante
|
||||||
|
</label>
|
||||||
|
<label style="display: block; margin-bottom: 0.25rem;">
|
||||||
|
<input type="checkbox" class="filter-level" value="intermedio" checked> Intermedio
|
||||||
|
</label>
|
||||||
|
<label style="display: block;">
|
||||||
|
<input type="checkbox" class="filter-level" value="avanzato" checked> Avanzato
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="applyFilters()" class="btn btn-primary" style="width: 100%;">
|
||||||
|
Applica Filtri
|
||||||
|
</button>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Catalogo Lezioni</h2>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<div class="lessons-grid" id="lessons-container">
|
||||||
|
<?php foreach ($all_lessons as $lesson): ?>
|
||||||
|
<?php
|
||||||
|
$is_owned = in_array($lesson['id'], $owned_lesson_ids);
|
||||||
|
$is_demo = $lesson['is_demo'];
|
||||||
|
?>
|
||||||
|
<div class="lesson-card"
|
||||||
|
data-type="<?php echo $lesson['type']; ?>"
|
||||||
|
data-level="<?php echo $lesson['level']; ?>">
|
||||||
|
|
||||||
|
<?php if ($lesson['thumbnail']): ?>
|
||||||
|
<img src="<?php echo htmlspecialchars($lesson['thumbnail']); ?>"
|
||||||
|
alt="<?php echo htmlspecialchars($lesson['title']); ?>"
|
||||||
|
class="lesson-thumbnail">
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="lesson-thumbnail-placeholder">
|
||||||
|
<span><?php echo $lesson['type'] === 'video' ? '📹' : '📡'; ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="lesson-content">
|
||||||
|
<h3 class="lesson-title"><?php echo htmlspecialchars($lesson['title']); ?></h3>
|
||||||
|
<p class="lesson-description">
|
||||||
|
<?php echo htmlspecialchars(substr($lesson['description'], 0, 100)) . '...'; ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="lesson-meta">
|
||||||
|
<?php if ($lesson['duration']): ?>
|
||||||
|
<span>⏱️ <?php echo $lesson['duration']; ?> min</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<span>📊 <?php echo ucfirst($lesson['level']); ?></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($lesson['type'] === 'live' && $lesson['live_date']): ?>
|
||||||
|
<p class="text-muted" style="font-size: 0.875rem; margin: 0.5rem 0;">
|
||||||
|
📅 <?php echo format_datetime($lesson['live_date']); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($is_demo): ?>
|
||||||
|
<div class="lesson-price text-success">GRATIS</div>
|
||||||
|
<a href="../lesson.php?id=<?php echo $lesson['id']; ?>" class="btn btn-success btn-small">
|
||||||
|
Guarda Gratis
|
||||||
|
</a>
|
||||||
|
<?php elseif ($is_owned): ?>
|
||||||
|
<div class="text-success" style="margin-bottom: 0.5rem;">✓ Già acquistata</div>
|
||||||
|
<a href="../lesson.php?id=<?php echo $lesson['id']; ?>" class="btn btn-primary btn-small">
|
||||||
|
Vai alla Lezione
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="lesson-price"><?php echo format_price($lesson['price']); ?></div>
|
||||||
|
<a href="../lesson.php?id=<?php echo $lesson['id']; ?>" class="btn btn-primary btn-small">
|
||||||
|
Vedi Dettagli
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($all_lessons)): ?>
|
||||||
|
<div class="card text-center">
|
||||||
|
<p class="text-muted">Nessuna lezione disponibile al momento.</p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Funzione per applicare i filtri
|
||||||
|
function applyFilters() {
|
||||||
|
// Ottieni i tipi selezionati
|
||||||
|
const selectedTypes = Array.from(document.querySelectorAll('.filter-type:checked'))
|
||||||
|
.map(cb => cb.value);
|
||||||
|
|
||||||
|
// Ottieni i livelli selezionati
|
||||||
|
const selectedLevels = Array.from(document.querySelectorAll('.filter-level:checked'))
|
||||||
|
.map(cb => cb.value);
|
||||||
|
|
||||||
|
// Filtra le lezioni
|
||||||
|
const lessonCards = document.querySelectorAll('.lesson-card');
|
||||||
|
|
||||||
|
lessonCards.forEach(card => {
|
||||||
|
const cardType = card.dataset.type;
|
||||||
|
const cardLevel = card.dataset.level;
|
||||||
|
|
||||||
|
const typeMatch = selectedTypes.length === 0 || selectedTypes.includes(cardType);
|
||||||
|
const levelMatch = selectedLevels.length === 0 || selectedLevels.includes(cardLevel);
|
||||||
|
|
||||||
|
if (typeMatch && levelMatch) {
|
||||||
|
card.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
132
user/dashboard.php
Normal file
132
user/dashboard.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Dashboard Utente
|
||||||
|
*
|
||||||
|
* Area personale dell'utente con le sue lezioni acquistate
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
// Ottieni le lezioni acquistate dall'utente
|
||||||
|
$purchased_lessons = get_user_purchased_lessons($user_id);
|
||||||
|
|
||||||
|
// Ottieni info utente
|
||||||
|
$user = get_user_by_id($user_id);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Le Mie Lezioni - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Home</a>
|
||||||
|
<a href="catalog.php" class="btn btn-secondary">Catalogo Lezioni</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-outline">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div style="text-align: center; margin-bottom: 1.5rem;">
|
||||||
|
<div style="width: 80px; height: 80px; border-radius: 50%; background: var(--primary-light);
|
||||||
|
margin: 0 auto 1rem; display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 2rem; color: white;">
|
||||||
|
<?php echo strtoupper(substr($user['first_name'], 0, 1)); ?>
|
||||||
|
</div>
|
||||||
|
<strong><?php echo htmlspecialchars($user['first_name'] . ' ' . $user['last_name']); ?></strong>
|
||||||
|
<p class="text-muted" style="font-size: 0.875rem; margin-top: 0.25rem;">
|
||||||
|
<?php echo htmlspecialchars($user['email']); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php" class="active">📚 Le Mie Lezioni</a></li>
|
||||||
|
<li><a href="catalog.php">🔍 Catalogo</a></li>
|
||||||
|
<li><a href="profile.php">⚙️ Profilo</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Le Mie Lezioni</h2>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<?php if (!empty($purchased_lessons)): ?>
|
||||||
|
<div class="lessons-grid">
|
||||||
|
<?php foreach ($purchased_lessons as $lesson): ?>
|
||||||
|
<div class="lesson-card">
|
||||||
|
<?php if ($lesson['thumbnail']): ?>
|
||||||
|
<img src="<?php echo htmlspecialchars($lesson['thumbnail']); ?>"
|
||||||
|
alt="<?php echo htmlspecialchars($lesson['title']); ?>"
|
||||||
|
class="lesson-thumbnail">
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="lesson-thumbnail-placeholder">
|
||||||
|
<span><?php echo $lesson['type'] === 'video' ? '📹' : '📡'; ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="lesson-content">
|
||||||
|
<h3 class="lesson-title"><?php echo htmlspecialchars($lesson['title']); ?></h3>
|
||||||
|
<p class="lesson-description">
|
||||||
|
<?php echo htmlspecialchars(substr($lesson['description'], 0, 100)) . '...'; ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="lesson-meta">
|
||||||
|
<?php if ($lesson['duration']): ?>
|
||||||
|
<span class="lesson-duration">⏱️ <?php echo $lesson['duration']; ?> min</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<span class="lesson-level">📊 <?php echo ucfirst($lesson['level']); ?></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($lesson['type'] === 'live'): ?>
|
||||||
|
<p class="text-muted" style="font-size: 0.875rem; margin: 0.5rem 0;">
|
||||||
|
📅 <?php echo format_datetime($lesson['live_date']); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<a href="../lesson.php?id=<?php echo $lesson['id']; ?>" class="btn btn-primary btn-small">
|
||||||
|
<?php echo $lesson['type'] === 'video' ? 'Guarda' : 'Partecipa'; ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p class="text-muted" style="font-size: 0.75rem; margin-top: 0.5rem;">
|
||||||
|
Acquistata il <?php echo format_date($lesson['purchased_at']); ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="card text-center">
|
||||||
|
<h3>Non hai ancora acquistato nessuna lezione</h3>
|
||||||
|
<p class="text-muted mb-2">Esplora il nostro catalogo e inizia il tuo percorso di Pilates!</p>
|
||||||
|
<a href="catalog.php" class="btn btn-primary">Sfoglia il Catalogo</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
209
user/profile.php
Normal file
209
user/profile.php
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Profilo Utente
|
||||||
|
*
|
||||||
|
* Permette all'utente di visualizzare e modificare i propri dati
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once '../includes/config.php';
|
||||||
|
require_once '../includes/functions.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
check_session_timeout();
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
$user = get_user_by_id($user_id);
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = false;
|
||||||
|
|
||||||
|
// Processa aggiornamento profilo
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_profile'])) {
|
||||||
|
$first_name = sanitize_input($_POST['first_name'] ?? '');
|
||||||
|
$last_name = sanitize_input($_POST['last_name'] ?? '');
|
||||||
|
$email = sanitize_input($_POST['email'] ?? '');
|
||||||
|
$phone = sanitize_input($_POST['phone'] ?? '');
|
||||||
|
|
||||||
|
if (empty($first_name) || empty($last_name) || empty($email)) {
|
||||||
|
$error = 'Nome, cognome ed email sono obbligatori';
|
||||||
|
} elseif (!validate_email($email)) {
|
||||||
|
$error = 'Email non valida';
|
||||||
|
} else {
|
||||||
|
// Verifica se l'email è già usata da un altro utente
|
||||||
|
$pdo = get_db_connection();
|
||||||
|
$stmt = $pdo->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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Profilo - Pilates Platform</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header-content">
|
||||||
|
<h1 class="logo">Pilates Studio</h1>
|
||||||
|
<nav class="nav">
|
||||||
|
<a href="../index.php" class="btn btn-outline">Home</a>
|
||||||
|
<a href="dashboard.php" class="btn btn-secondary">Le Mie Lezioni</a>
|
||||||
|
<a href="../includes/logout.php" class="btn btn-outline">Logout</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<aside class="sidebar">
|
||||||
|
<ul class="sidebar-menu">
|
||||||
|
<li><a href="dashboard.php">📚 Le Mie Lezioni</a></li>
|
||||||
|
<li><a href="catalog.php">🔍 Catalogo</a></li>
|
||||||
|
<li><a href="profile.php" class="active">⚙️ Profilo</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="main-content">
|
||||||
|
<h2 class="section-title" style="text-align: left;">Il Mio Profilo</h2>
|
||||||
|
|
||||||
|
<?php echo display_flash_message(); ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-error"><?php echo htmlspecialchars($error); ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Form Dati Personali -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Dati Personali</h3>
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="first_name" class="form-label">Nome</label>
|
||||||
|
<input type="text" id="first_name" name="first_name" class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($user['first_name']); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="last_name" class="form-label">Cognome</label>
|
||||||
|
<input type="text" id="last_name" name="last_name" class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($user['last_name']); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" id="email" name="email" class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($user['email']); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="phone" class="form-label">Telefono (opzionale)</label>
|
||||||
|
<input type="tel" id="phone" name="phone" class="form-control"
|
||||||
|
value="<?php echo htmlspecialchars($user['phone'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" name="update_profile" class="btn btn-primary">
|
||||||
|
Aggiorna Profilo
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Form Cambio Password -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Cambia Password</h3>
|
||||||
|
<form method="POST" action="">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="current_password" class="form-label">Password Attuale</label>
|
||||||
|
<input type="password" id="current_password" name="current_password"
|
||||||
|
class="form-control" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new_password" class="form-label">Nuova Password</label>
|
||||||
|
<input type="password" id="new_password" name="new_password"
|
||||||
|
class="form-control" required minlength="6">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirm_password" class="form-label">Conferma Nuova Password</label>
|
||||||
|
<input type="password" id="confirm_password" name="confirm_password"
|
||||||
|
class="form-control" required minlength="6">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" name="change_password" class="btn btn-primary">
|
||||||
|
Cambia Password
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Info Account -->
|
||||||
|
<div class="card">
|
||||||
|
<h3 class="card-header">Informazioni Account</h3>
|
||||||
|
<p><strong>Registrato il:</strong> <?php echo format_date($user['created_at']); ?></p>
|
||||||
|
<?php if ($user['last_login']): ?>
|
||||||
|
<p><strong>Ultimo accesso:</strong> <?php echo format_datetime($user['last_login']); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../assets/js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user