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