diff --git a/admin/edit_question.php b/admin/edit_question.php new file mode 100644 index 0000000..7bf06bf --- /dev/null +++ b/admin/edit_question.php @@ -0,0 +1,99 @@ +beginTransaction(); + + // Update question text and score + $stmt = $pdo->prepare('UPDATE questions SET question_text = ?, score = ? WHERE id = ?'); + $stmt->execute([$_POST['question_text'], $_POST['score'], $question_id]); + + // Update answers + $correctAnswerIndex = $_POST['is_correct']; + foreach ($_POST['answers'] as $answer_id => $answerText) { + $isCorrect = ($answer_id == $_POST['correct_answer_id']); // This needs to be adjusted + + // Simplified logic: update text, and then separately update the correct flag + $stmt_update_text = $pdo->prepare('UPDATE answers SET answer_text = ? WHERE id = ? AND question_id = ?'); + $stmt_update_text->execute([$answerText, $answer_id, $question_id]); + } + + // Set all to incorrect first + $stmt_reset = $pdo->prepare('UPDATE answers SET is_correct = 0 WHERE question_id = ?'); + $stmt_reset->execute([$question_id]); + + // Set the new correct answer + $stmt_set_correct = $pdo->prepare('UPDATE answers SET is_correct = 1 WHERE id = ? AND question_id = ?'); + $stmt_set_correct->execute([$_POST['correct_answer_id'], $question_id]); + + $pdo->commit(); + $message = '
Domanda aggiornata con successo. Torna alla lista.
'; + + } catch (Exception $e) { + $pdo->rollBack(); + $message = '
Errore durante l\'aggiornamento: ' . $e->getMessage() . '
'; + } +} + + +// Fetch the question and its answers +$stmt = $pdo->prepare('SELECT * FROM questions WHERE id = ?'); +$stmt->execute([$question_id]); +$question = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$question) { + echo "

Domanda non trovata.

"; + require_once 'partials/footer.php'; + exit; +} + +$stmt = $pdo->prepare('SELECT * FROM answers WHERE question_id = ? ORDER BY id ASC'); +$stmt->execute([$question_id]); +$answers = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + + + +
+

Modifica Domanda

+
+ +
+ + +
+
+ + +
+
+ +
+ +
+ > + +
+ +
+
+ + Annulla +
+
+ + diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..c786209 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,5 @@ + + + + diff --git a/admin/partials/header.php b/admin/partials/header.php new file mode 100644 index 0000000..b2d8a35 --- /dev/null +++ b/admin/partials/header.php @@ -0,0 +1,22 @@ + + + + + + Backoffice Quiz + + + + + + +
+
+

Backoffice

+ +
+
diff --git a/admin/questions.php b/admin/questions.php new file mode 100644 index 0000000..1588839 --- /dev/null +++ b/admin/questions.php @@ -0,0 +1,149 @@ +beginTransaction(); + + // Insert question + $stmt = $pdo->prepare('INSERT INTO questions (question_text, score) VALUES (?, ?)'); + $stmt->execute([$_POST['question_text'], $_POST['score']]); + $questionId = $pdo->lastInsertId(); + + // Insert answers + $correctAnswerIndex = $_POST['is_correct']; + foreach ($_POST['answers'] as $index => $answerText) { + if (!empty($answerText)) { + $isCorrect = ($index == $correctAnswerIndex); + $stmt = $pdo->prepare('INSERT INTO answers (question_id, answer_text, is_correct) VALUES (?, ?, ?)'); + $stmt->execute([$questionId, $answerText, $isCorrect]); + } + } + $pdo->commit(); + $message = '
Domanda aggiunta con successo.
'; + } catch (Exception $e) { + $pdo->rollBack(); + $message = '
Errore: ' . $e->getMessage() . '
'; + } + break; + + case 'delete_question': + try { + $pdo->beginTransaction(); + // Note: foreign keys with ON DELETE CASCADE would simplify this + $stmt = $pdo->prepare('DELETE FROM answers WHERE question_id = ?'); + $stmt->execute([$_POST['question_id']]); + + $stmt = $pdo->prepare('DELETE FROM questions WHERE id = ?'); + $stmt->execute([$_POST['question_id']]); + + $pdo->commit(); + $message = '
Domanda eliminata con successo.
'; + } catch (Exception $e) { + $pdo->rollBack(); + $message = '
Errore: ' . $e->getMessage() . '
'; + } + break; + } + } +} + +// Fetch all questions with their answers +$stmt = $pdo->query('SELECT * FROM questions ORDER BY id DESC'); +$questions = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + + + +
+

Aggiungi Nuova Domanda

+
+ +
+ + +
+
+ + +
+
+ +
+ +
+ > + +
+ +
+
+ +
+
+ +
+

Elenco Domande

+ + + + + + + + + + + + + + + + + + + +
IDDomandaPunteggioAzioni
+ Modifica +
+ + + +
+
+
+ + + + diff --git a/admin/results.php b/admin/results.php new file mode 100644 index 0000000..f307a17 --- /dev/null +++ b/admin/results.php @@ -0,0 +1,192 @@ +beginTransaction(); + + $stmt = $pdo->prepare('DELETE FROM submissions WHERE participant_id = ?'); + $stmt->execute([$participant_id]); + + $stmt = $pdo->prepare('DELETE FROM participants WHERE id = ?'); + $stmt->execute([$participant_id]); + + $pdo->commit(); + $message = '
Partecipante eliminato con successo.
'; + } catch (Exception $e) { + $pdo->rollBack(); + $message = '
Errore: ' . $e->getMessage() . '
'; + } + break; + + case 'publish_leaderboard': + if (!$is_published) { + // Create flag file + file_put_contents('../leaderboard_published.flag', time()); + + // Send emails + try { + $participants_emails_stmt = $pdo->query("SELECT email FROM participants"); + $emails = $participants_emails_stmt->fetchAll(PDO::FETCH_COLUMN); + + $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http"; + $host = $_SERVER['HTTP_HOST']; + $leaderboard_url = $protocol . "://" . $host . "/leaderboard.php"; + + $subject = "La classifica del Quiz è online!"; + $body_html = "

Ciao!

La classifica finale del quiz è stata pubblicata. Clicca qui per vederla!

Grazie per aver partecipato.

"; + $body_text = "Ciao! La classifica finale del quiz è stata pubblicata. Vai su {$leaderboard_url} per vederla. Grazie per aver partecipato."; + + foreach ($emails as $email) { + MailService::sendMail($email, $subject, $body_html, $body_text); + } + + $message = '
Classifica pubblicata con successo! Email di notifica inviate a ' . count($emails) . ' partecipanti.
'; + $is_published = true; // Update status for the current page view + + } catch (Exception $e) { + $message = '
Errore durante l\'invio delle email: ' . $e->getMessage() . '
'; + } + } else { + $message = '
La classifica è già stata pubblicata.
'; + } + break; + } +} + try { + $participant_id = $_POST['participant_id']; + $pdo->beginTransaction(); + + // First delete submissions, then the participant + $stmt = $pdo->prepare('DELETE FROM submissions WHERE participant_id = ?'); + $stmt->execute([$participant_id]); + + $stmt = $pdo->prepare('DELETE FROM participants WHERE id = ?'); + $stmt->execute([$participant_id]); + + $pdo->commit(); + $message = '
Partecipante eliminato con successo.
'; + } catch (Exception $e) { + $pdo->rollBack(); + $message = '
Errore: ' . $e->getMessage() . '
'; + } +} + + +// Fetch all participants and their submission details +$participants_stmt = $pdo->query("SELECT * FROM participants ORDER BY created_at DESC"); +$participants = $participants_stmt->fetchAll(PDO::FETCH_ASSOC); + +// Function to calculate score for a participant +function calculate_score($participant_id, $pdo) { + $score_stmt = $pdo->prepare( + 'SELECT q.score, a.is_correct + FROM submissions s + JOIN answers a ON s.answer_id = a.id + JOIN questions q ON s.question_id = q.id + WHERE s.participant_id = ?' + ); + $score_stmt->execute([$participant_id]); + $results = $score_stmt->fetchAll(PDO::FETCH_ASSOC); + + $total_score = 0; + foreach ($results as $result) { + if ($result['is_correct']) { + $total_score += (int)$result['score']; + } else { + // As per requirements, wrong answer gives negative score + $total_score -= (int)$result['score']; + } + } + return $total_score; +} + +?> + + + +
+

Pubblica Classifica

+ +

La classifica è stata pubblicata. Vedi la classifica pubblica.

+ +

Una volta pubblicata la classifica, tutti i partecipanti riceveranno un'email con il link per visualizzarla. Questa azione è irreversibile.

+
+ + +
+ +
+ +
+

Risultati e Partecipanti

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NicknameEmailData InvioPunteggioAzioni
Nessun partecipante ha ancora completato il quiz.
+ Dettagli +
+ + + +
+
+
+ + + + diff --git a/admin/style.css b/admin/style.css new file mode 100644 index 0000000..21ebc8e --- /dev/null +++ b/admin/style.css @@ -0,0 +1,138 @@ + +body { + background-color: #1a1a2e; + color: #ffffff; + font-family: 'Roboto', sans-serif; + margin: 0; + padding: 20px; +} + +.container { + max-width: 1200px; + margin: 0 auto; +} + +.main-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 0; + border-bottom: 1px solid #0f3460; + margin-bottom: 20px; +} + +.main-header h1 { + font-family: 'Orbitron', sans-serif; + color: #e94560; + margin: 0; +} + +.main-header nav a { + color: #ffffff; + text-decoration: none; + margin-left: 20px; + font-weight: bold; + transition: color 0.3s; +} + +.main-header nav a:hover { + color: #e94560; +} + +h2 { + font-family: 'Orbitron', sans-serif; + color: #e94560; + border-bottom: 2px solid #0f3460; + padding-bottom: 10px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; +} + +th, td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #0f3460; +} + +th { + background-color: #16213e; + font-weight: bold; +} + +tr:hover { + background-color: #1f2a4a; +} + +.actions a { + color: #e94560; + text-decoration: none; + margin-right: 10px; +} + +.btn { + background-color: #e94560; + color: #ffffff; + padding: 10px 15px; + border: none; + border-radius: 5px; + text-decoration: none; + font-weight: bold; + cursor: pointer; + transition: background-color 0.3s; +} + +.btn:hover { + background-color: #c73c52; +} + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: bold; +} + +.form-group input[type="text"], +.form-group textarea { + width: 100%; + padding: 10px; + background-color: #16213e; + border: 1px solid #0f3460; + color: #ffffff; + border-radius: 5px; +} + +.form-group .answers-container .answer-group { + margin-bottom: 10px; + display: flex; + align-items: center; +} +.form-group .answers-container .answer-group input[type='radio'] { + margin-right: 10px; +} +.form-group .answers-container .answer-group input[type='text'] { + flex-grow: 1; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border-radius: 5px; +} + +.alert-success { + background-color: #28a745; + color: white; +} + +.alert-error { + background-color: #dc3545; + color: white; +} diff --git a/admin/view_submission.php b/admin/view_submission.php new file mode 100644 index 0000000..b045ee6 --- /dev/null +++ b/admin/view_submission.php @@ -0,0 +1,95 @@ +prepare('SELECT * FROM participants WHERE id = ?'); +$stmt->execute([$participant_id]); +$participant = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$participant) { + echo "

Partecipante non trovato.

"; + require_once 'partials/footer.php'; + exit; +} + +// Fetch submission details +$query = <<prepare($query); +$stmt->execute([$participant_id]); +$submission_details = $stmt->fetchAll(PDO::FETCH_ASSOC); + + +?> + +
+

Dettaglio Risposte -

+

Email:

+ « Torna alla lista dei risultati + +
+ +
+

+

+ Risposta data: + (Punti: ) +

+ +

Risposta corretta:

+ +
+ +
+
+ + + + + diff --git a/db/seed.php b/db/seed.php new file mode 100644 index 0000000..663f5a3 --- /dev/null +++ b/db/seed.php @@ -0,0 +1,97 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Clean up tables before seeding to make the script idempotent + $pdo->exec('SET FOREIGN_KEY_CHECKS=0;'); + $pdo->exec('TRUNCATE TABLE submissions;'); + $pdo->exec('TRUNCATE TABLE answers;'); + $pdo->exec('TRUNCATE TABLE questions;'); + $pdo->exec('TRUNCATE TABLE participants;'); + $pdo->exec('SET FOREIGN_KEY_CHECKS=1;'); + + $quiz_data = [ + [ + 'question' => "Quale sarà l'auto vincitrice del 'Best of Show' al Concorso d'Eleganza di Villa d'Este 2025?", + 'points' => 5, + 'answers' => [ + ['text' => "Un'auto d'epoca degli anni '30", 'correct' => true], + ['text' => "Una supercar moderna", 'correct' => false], + ['text' => "Un prototipo di design", 'correct' => false], + ] + ], + [ + 'question' => "Chi vincerà il premio 'FIVA World Motoring Heritage Year'?", + 'points' => 5, + 'answers' => [ + ['text' => "Un club di auto storiche italiano", 'correct' => false], + ['text' => "Un museo dell'automobile americano", 'correct' => true], + ['text' => "Una collezione privata giapponese", 'correct' => false], + ] + ], + [ + 'question' => "Quale brand presenterà il prototipo più audace?", + 'points' => 5, + 'answers' => [ + ['text' => "Un marchio di lusso europeo", 'correct' => false], + ['text' => "Una startup di veicoli elettrici", 'correct' => true], + ['text' => "Un produttore di hypercar di nicchia", 'correct' => false], + ] + ], + [ + 'question' => "DOMANDA JOLLY: Quale sarà il valore di aggiudicazione più alto all'asta RM Sotheby's di Villa Erba?", + 'points' => 45, + 'answers' => [ + ['text' => "Tra 5 e 10 milioni di euro", 'correct' => true], + ['text' => "Sotto i 5 milioni di euro", 'correct' => false], + ['text' => "Oltre 10 milioni di euro", 'correct' => false], + ] + ], + [ + 'question' => "ITALIAN TGA: Quale auto italiana vincerà il premio nella sua categoria?", + 'points' => 30, + 'answers' => [ + ['text' => "Una Ferrari classica", 'correct' => false], + ['text' => "Una Lamborghini rara", 'correct' => false], + ['text' => "Un'Alfa Romeo da competizione", 'correct' => true], + ] + ], + ]; + + $pdo->beginTransaction(); + + foreach ($quiz_data as $item) { + // Insert question + $sql_question = "INSERT INTO questions (question_text, points) VALUES (:question_text, :points)"; + $stmt_question = $pdo->prepare($sql_question); + $stmt_question->execute([ + ':question_text' => $item['question'], + ':points' => $item['points'] + ]); + $question_id = $pdo->lastInsertId(); + + // Insert answers + foreach ($item['answers'] as $answer) { + $sql_answer = "INSERT INTO answers (question_id, answer_text, is_correct) VALUES (:question_id, :answer_text, :is_correct)"; + $stmt_answer = $pdo->prepare($sql_answer); + $stmt_answer->execute([ + ':question_id' => $question_id, + ':answer_text' => $answer['text'], + ':is_correct' => (int)$answer['correct'] // Explicitly cast boolean to integer + ]); + } + } + + $pdo->commit(); + + echo "Database popolato con successo con le domande e le risposte del quiz." . PHP_EOL; + +} catch (PDOException $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + die("Errore durante il popolamento del database: " . $e->getMessage()); +} diff --git a/db/setup.php b/db/setup.php new file mode 100644 index 0000000..7af8513 --- /dev/null +++ b/db/setup.php @@ -0,0 +1,51 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // SQL statements for creating tables + $sql = <<exec($sql); + echo "Tabelle create con successo (o già esistenti)." . PHP_EOL; + +} catch (PDOException $e) { + die("Errore nella creazione delle tabelle: " . $e->getMessage()); +} diff --git a/index.php b/index.php index 7205f3d..4f410c9 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,205 @@ prepare("SELECT id FROM participants WHERE email = ?"); + $stmt->execute([$email]); + if ($stmt->fetch()) { + $error = 'Questo indirizzo email ha già partecipato. È consentito un solo invio per email.'; + } else { + $pdo->beginTransaction(); + + // Insert participant + $stmt = $pdo->prepare("INSERT INTO participants (email, nickname) VALUES (?, ?)"); + $stmt->execute([$email, $nickname]); + $participant_id = $pdo->lastInsertId(); + + // Insert submissions + $stmt_submission = $pdo->prepare("INSERT INTO submissions (participant_id, question_id, answer_id) VALUES (?, ?, ?)"); + foreach ($answers as $question_id => $answer_id) { + $stmt_submission->execute([$participant_id, $question_id, $answer_id]); + } + + $pdo->commit(); + + // Send confirmation email + $subject = 'Grazie per aver partecipato al nostro Quiz!'; + $body = "Ciao {$nickname},

Grazie per aver inviato le tue risposte. I risultati saranno pubblicati al termine del concorso.

In bocca al lupo!"; + MailService::sendMail($email, $subject, $body, strip_tags($body)); + + $message = 'Grazie per aver partecipato! Hai inviato con successo le tue risposte e riceverai una mail di conferma a breve.'; + $submitted_successfully = true; + } + } catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + $error = "Si è verificato un errore durante l'invio. Riprova. Dettagli: " . $e->getMessage(); + } + } +} + +// Fetch questions and answers for the form +$questions = []; +if (!$submitted_successfully) { + try { + $stmt = $pdo->query("SELECT id, question_text, points FROM questions ORDER BY id"); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $question_id = $row['id']; + $row['answers'] = []; + $stmt_answers = $pdo->prepare("SELECT id, answer_text FROM answers WHERE question_id = ? ORDER BY id"); + $stmt_answers->execute([$question_id]); + $row['answers'] = $stmt_answers->fetchAll(PDO::FETCH_ASSOC); + $questions[] = $row; + } + } catch (Exception $e) { + $error = "Impossibile caricare le domande del quiz. Dettagli: " . $e->getMessage(); + } +} -$phpVersion = PHP_VERSION; -$now = date('Y-m-d H:i:s'); ?> - - + + - - - New Style - - - - - - - - - - - - - - - - - - - + + + Quiz Eleganza Motoristica + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+
+
Spazio per logo e info
+

Quiz The Unheard Edition 2025

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

I tuoi dati

+
+ + +
+
+ + +
+
+ + $q): ?> +
+

( punti)

+ +
+ +
+ +
+ + + + + +
+
-
-
- Page updated: (UTC) -
- + \ No newline at end of file diff --git a/leaderboard.php b/leaderboard.php new file mode 100644 index 0000000..c3040eb --- /dev/null +++ b/leaderboard.php @@ -0,0 +1,155 @@ +prepare( + 'SELECT q.score, a.is_correct + FROM submissions s + JOIN answers a ON s.answer_id = a.id + JOIN questions q ON s.question_id = q.id + WHERE s.participant_id = ?' + ); + $score_stmt->execute([$participant_id]); + $results = $score_stmt->fetchAll(PDO::FETCH_ASSOC); + + $total_score = 0; + foreach ($results as $result) { + if ($result['is_correct']) { + $total_score += (int)$result['score']; + } else { + $total_score -= (int)$result['score']; + } + } + return $total_score; + } + + $participants_stmt = $pdo->query("SELECT id, nickname FROM participants"); + $participants = $participants_stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($participants as $participant) { + $leaderboard_data[] = [ + 'nickname' => $participant['nickname'], + 'score' => calculate_score($participant['id'], $pdo) + ]; + } + + // Sort by score descending + usort($leaderboard_data, function($a, $b) { + return $b['score'] <=> $a['score']; + }); +} + +// Using the same frontend style as index.php +// I will read the content of index.php and extract the header and footer +// For now I will just copy the style part +?> + + + + + + Classifica Quiz + + + + + + +
+

Classifica Finale

+ + + + + + + + + + + $row): ?> + + + + + + + +
RankNicknamePunteggio
+ +

La classifica non è stata ancora pubblicata. Torna più tardi!

+ + Torna al Quiz +
+ +