Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c26ea67559 |
32
admin/includes/leaderboard_functions.php
Normal file
32
admin/includes/leaderboard_functions.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
// admin/includes/leaderboard_functions.php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches leaderboard data by joining quiz_attempts and quiz_categories.
|
||||||
|
*
|
||||||
|
* @param int $limit The maximum number of records to return.
|
||||||
|
* @return array An array of associative arrays, each representing a leaderboard entry.
|
||||||
|
*/
|
||||||
|
function get_leaderboard_data($limit = 100) {
|
||||||
|
$db = db_connect();
|
||||||
|
$sql = "
|
||||||
|
SELECT
|
||||||
|
qa.player_name,
|
||||||
|
qc.name as category_name,
|
||||||
|
qa.score,
|
||||||
|
qa.total_questions,
|
||||||
|
qa.attempt_date
|
||||||
|
FROM
|
||||||
|
quiz_attempts qa
|
||||||
|
JOIN
|
||||||
|
quiz_categories qc ON qa.category_id = qc.id
|
||||||
|
ORDER BY
|
||||||
|
qa.score DESC, qa.attempt_date DESC
|
||||||
|
LIMIT :limit
|
||||||
|
";
|
||||||
|
$stmt = $db->prepare($sql);
|
||||||
|
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
?>
|
||||||
44
admin/includes/missions_functions.php
Normal file
44
admin/includes/missions_functions.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
// admin/includes/missions_functions.php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function get_all_missions() {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM missions ORDER BY id DESC");
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_mission_by_id($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM missions WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_mission($title, $description) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("INSERT INTO missions (title, description) VALUES (:title, :description)");
|
||||||
|
$stmt->bindParam(':title', $title, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_mission($id, $title, $description) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("UPDATE missions SET title = :title, description = :description WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':title', $title, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete_mission($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("DELETE FROM missions WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
151
admin/includes/questions_functions.php
Normal file
151
admin/includes/questions_functions.php
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
// admin/includes/questions_functions.php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single quiz category by its ID to get its name.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the category.
|
||||||
|
* @return array|false The category data or false if not found.
|
||||||
|
*/
|
||||||
|
function get_quiz_category_name($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT name FROM quiz_categories WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all questions for a given quiz ID.
|
||||||
|
*
|
||||||
|
* @param int $quiz_id The ID of the quiz category.
|
||||||
|
* @return array An array of questions.
|
||||||
|
*/
|
||||||
|
function get_questions_by_quiz_id($quiz_id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM questions WHERE category_id = :quiz_id ORDER BY id ASC");
|
||||||
|
$stmt->bindParam(':quiz_id', $quiz_id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all answers for a given question ID.
|
||||||
|
*
|
||||||
|
* @param int $question_id The ID of the question.
|
||||||
|
* @return array An array of answers.
|
||||||
|
*/
|
||||||
|
function get_answers_by_question_id($question_id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM answers WHERE question_id = :question_id ORDER BY id ASC");
|
||||||
|
$stmt->bindParam(':question_id', $question_id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a question and its associated answers.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the question to delete.
|
||||||
|
* @return bool True on success, false on failure.
|
||||||
|
*/
|
||||||
|
function delete_question($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
// The database should handle answer deletion via ON DELETE CASCADE foreign key
|
||||||
|
$stmt = $db->prepare("DELETE FROM questions WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new question and its answers to the database.
|
||||||
|
*
|
||||||
|
* @param int $quiz_id The ID of the quiz category.
|
||||||
|
* @param string $question_text The text of the question.
|
||||||
|
* @param array $answers An array of answers, where each answer is an array with 'text' and 'is_correct' keys.
|
||||||
|
* @return bool True on success, false on failure.
|
||||||
|
*/
|
||||||
|
function add_question($quiz_id, $question_text, $answers) {
|
||||||
|
$db = db_connect();
|
||||||
|
try {
|
||||||
|
$db->beginTransaction();
|
||||||
|
|
||||||
|
// Insert the question
|
||||||
|
$stmt = $db->prepare("INSERT INTO questions (category_id, question_text) VALUES (:quiz_id, :question_text)");
|
||||||
|
$stmt->bindParam(':quiz_id', $quiz_id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':question_text', $question_text, PDO::PARAM_STR);
|
||||||
|
$stmt->execute();
|
||||||
|
$question_id = $db->lastInsertId();
|
||||||
|
|
||||||
|
// Insert the answers
|
||||||
|
$stmt = $db->prepare("INSERT INTO answers (question_id, answer_text, is_correct) VALUES (:question_id, :answer_text, :is_correct)");
|
||||||
|
foreach ($answers as $answer) {
|
||||||
|
$stmt->bindParam(':question_id', $question_id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':answer_text', $answer['text'], PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':is_correct', $answer['is_correct'], PDO::PARAM_BOOL);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->commit();
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single question by its ID for editing.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the question.
|
||||||
|
* @return array|false The question data or false if not found.
|
||||||
|
*/
|
||||||
|
function get_question($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM questions WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a question and its answers.
|
||||||
|
*
|
||||||
|
* @param int $question_id The ID of the question to update.
|
||||||
|
* @param string $question_text The new text of the question.
|
||||||
|
* @param array $answers An array of answers to update/insert.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function update_question($question_id, $question_text, $answers) {
|
||||||
|
$db = db_connect();
|
||||||
|
try {
|
||||||
|
$db->beginTransaction();
|
||||||
|
|
||||||
|
// Update question text
|
||||||
|
$stmt = $db->prepare("UPDATE questions SET question_text = :question_text WHERE id = :id");
|
||||||
|
$stmt->bindParam(':question_text', $question_text, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':id', $question_id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// Delete old answers
|
||||||
|
$stmt = $db->prepare("DELETE FROM answers WHERE question_id = :question_id");
|
||||||
|
$stmt->bindParam(':question_id', $question_id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
// Insert new answers
|
||||||
|
$stmt = $db->prepare("INSERT INTO answers (question_id, answer_text, is_correct) VALUES (:question_id, :answer_text, :is_correct)");
|
||||||
|
foreach ($answers as $answer) {
|
||||||
|
$stmt->bindParam(':question_id', $question_id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':answer_text', $answer['text'], PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':is_correct', $answer['is_correct'], PDO::PARAM_BOOL);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->commit();
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
75
admin/includes/quizzes_functions.php
Normal file
75
admin/includes/quizzes_functions.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
// admin/includes/quizzes_functions.php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all quiz categories from the database.
|
||||||
|
*
|
||||||
|
* @return array An array of associative arrays, each representing a category.
|
||||||
|
*/
|
||||||
|
function get_all_quiz_categories() {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM quiz_categories ORDER BY name ASC");
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single quiz category by its ID.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the category.
|
||||||
|
* @return array|false The category data or false if not found.
|
||||||
|
*/
|
||||||
|
function get_quiz_category_by_id($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("SELECT * FROM quiz_categories WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new quiz category to the database.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the category.
|
||||||
|
* @param string $description The description of the category.
|
||||||
|
* @return bool True on success, false on failure.
|
||||||
|
*/
|
||||||
|
function add_quiz_category($name, $description) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("INSERT INTO quiz_categories (name, description) VALUES (:name, :description)");
|
||||||
|
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing quiz category.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the category to update.
|
||||||
|
* @param string $name The new name.
|
||||||
|
* @param string $description The new description.
|
||||||
|
* @return bool True on success, false on failure.
|
||||||
|
*/
|
||||||
|
function update_quiz_category($id, $name, $description) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("UPDATE quiz_categories SET name = :name, description = :description WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':description', $description, PDO::PARAM_STR);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a quiz category from the database.
|
||||||
|
*
|
||||||
|
* @param int $id The ID of the category to delete.
|
||||||
|
* @return bool True on success, false on failure.
|
||||||
|
*/
|
||||||
|
function delete_quiz_category($id) {
|
||||||
|
$db = db_connect();
|
||||||
|
$stmt = $db->prepare("DELETE FROM quiz_categories WHERE id = :id");
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
49
admin/index.php
Normal file
49
admin/index.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
// admin/index.php
|
||||||
|
|
||||||
|
// --- Basic Routing ---
|
||||||
|
$page = $_GET['page'] ?? 'missions';
|
||||||
|
$allowed_pages = ['missions', 'quizzes', 'leaderboard', 'questions'];
|
||||||
|
|
||||||
|
if (!in_array($page, $allowed_pages)) {
|
||||||
|
$page = 'missions'; // Default to missions if page is not allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Include necessary files ---
|
||||||
|
// General functions that are always needed
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
// Page-specific functions
|
||||||
|
if ($page === 'missions') {
|
||||||
|
require_once __DIR__ . '/includes/missions_functions.php';
|
||||||
|
} elseif ($page === 'quizzes') {
|
||||||
|
require_once __DIR__ . '/includes/quizzes_functions.php';
|
||||||
|
} elseif ($page === 'leaderboard') {
|
||||||
|
require_once __DIR__ . '/includes/leaderboard_functions.php';
|
||||||
|
} elseif ($page === 'questions') {
|
||||||
|
require_once __DIR__ . '/includes/questions_functions.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Set Page Title ---
|
||||||
|
$page_title = ucfirst($page) . ' Management';
|
||||||
|
|
||||||
|
|
||||||
|
// --- Include Header ---
|
||||||
|
include __DIR__ . '/partials/header.php';
|
||||||
|
|
||||||
|
|
||||||
|
// --- Include Page Content ---
|
||||||
|
$page_path = __DIR__ . '/pages/' . $page . '.php';
|
||||||
|
if (file_exists($page_path)) {
|
||||||
|
include $page_path;
|
||||||
|
} else {
|
||||||
|
// Fallback for missing page files
|
||||||
|
echo "<div class='alert alert-danger'>Content file not found for this page.</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Include Footer ---
|
||||||
|
include __DIR__ . '/partials/footer.php';
|
||||||
|
|
||||||
|
?>
|
||||||
39
admin/pages/leaderboard.php
Normal file
39
admin/pages/leaderboard.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
// admin/pages/leaderboard.php
|
||||||
|
|
||||||
|
$leaderboard_entries = get_leaderboard_data();
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">Leaderboard</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Giocatore</th>
|
||||||
|
<th>Categoria</th>
|
||||||
|
<th>Punteggio</th>
|
||||||
|
<th>Data</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($leaderboard_entries)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">Nessun risultato trovato.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($leaderboard_entries as $entry): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($entry['player_name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($entry['category_name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($entry['score']); ?> / <?php echo htmlspecialchars($entry['total_questions']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars(date('d/m/Y H:i', strtotime($entry['attempt_date']))); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
116
admin/pages/missions.php
Normal file
116
admin/pages/missions.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
// admin/pages/missions.php
|
||||||
|
|
||||||
|
$action = $_GET['action'] ?? 'list';
|
||||||
|
$id = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
// Handle POST requests for add/edit
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$title = $_POST['title'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$id = $_POST['id'] ?? null;
|
||||||
|
|
||||||
|
if ($action === 'add') {
|
||||||
|
add_mission($title, $description);
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
update_mission($id, $title, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to the list view after submission
|
||||||
|
header('Location: index.php?page=missions');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle delete action
|
||||||
|
if ($action === 'delete' && $id) {
|
||||||
|
delete_mission($id);
|
||||||
|
header('Location: index.php?page=missions');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2"><?php
|
||||||
|
if ($action === 'add') echo 'Aggiungi Nuova Missione';
|
||||||
|
elseif ($action === 'edit') echo 'Modifica Missione';
|
||||||
|
else echo 'Gestione Missioni';
|
||||||
|
?></h1>
|
||||||
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
|
<a href="index.php?page=missions" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="arrow-left"></span>
|
||||||
|
Torna alla Lista
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($action === 'add' || $action === 'edit'): ?>
|
||||||
|
<?php
|
||||||
|
$mission = null;
|
||||||
|
if ($action === 'edit' && $id) {
|
||||||
|
$mission = get_mission_by_id($id);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<form method="POST" action="index.php?page=missions&action=<?php echo $action; ?>">
|
||||||
|
<?php if ($id): ?>
|
||||||
|
<input type="hidden" name="id" value="<?php echo htmlspecialchars($id); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="title" class="form-label">Titolo Missione</label>
|
||||||
|
<input type="text" class="form-control" id="title" name="title" value="<?php echo htmlspecialchars($mission['title'] ?? ''); ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="description" class="form-label">Descrizione</label>
|
||||||
|
<textarea class="form-control" id="description" name="description" rows="3"><?php echo htmlspecialchars($mission['description'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<?php echo $action === 'edit' ? 'Salva Modifiche' : 'Aggiungi Missione'; ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php else: // 'list' action ?>
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<a href="index.php?page=missions&action=add" class="btn btn-sm btn-outline-primary">
|
||||||
|
<span data-feather="plus"></span>
|
||||||
|
Aggiungi Missione
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Titolo</th>
|
||||||
|
<th>Descrizione</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $missions = get_all_missions(); ?>
|
||||||
|
<?php if (empty($missions)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">Nessuna missione trovata.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($missions as $mission): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($mission['id']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($mission['title']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($mission['description']); ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="index.php?page=missions&action=edit&id=<?php echo $mission['id']; ?>" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="edit-2"></span>
|
||||||
|
Modifica
|
||||||
|
</a>
|
||||||
|
<a href="index.php?page=missions&action=delete&id=<?php echo $mission['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Sei sicuro di voler eliminare questa missione?');">
|
||||||
|
<span data-feather="trash-2"></span>
|
||||||
|
Elimina
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
153
admin/pages/questions.php
Normal file
153
admin/pages/questions.php
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
// admin/pages/questions.php
|
||||||
|
|
||||||
|
$quiz_id = $_GET['quiz_id'] ?? null;
|
||||||
|
$action = $_GET['action'] ?? 'list';
|
||||||
|
$id = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$quiz_id) {
|
||||||
|
echo "<div class='alert alert-danger'>ID Quiz non specificato.</div>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$quiz_category = get_quiz_category_name($quiz_id);
|
||||||
|
if (!$quiz_category) {
|
||||||
|
echo "<div class='alert alert-danger'>Categoria Quiz non trovata.</div>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle POST requests for add/edit
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$question_text = $_POST['question_text'] ?? '';
|
||||||
|
$answers_post = $_POST['answers'] ?? [];
|
||||||
|
$correct_answer_index = $_POST['correct_answer'] ?? -1;
|
||||||
|
$id = $_POST['id'] ?? null;
|
||||||
|
|
||||||
|
$answers = [];
|
||||||
|
foreach ($answers_post as $index => $text) {
|
||||||
|
if (!empty($text)) {
|
||||||
|
$answers[] = [
|
||||||
|
'text' => $text,
|
||||||
|
'is_correct' => ($index == $correct_answer_index)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'add') {
|
||||||
|
add_question($quiz_id, $question_text, $answers);
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
update_question($id, $question_text, $answers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to the list view after submission
|
||||||
|
header('Location: index.php?page=questions&quiz_id=' . $quiz_id);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle delete action
|
||||||
|
if ($action === 'delete' && $id) {
|
||||||
|
delete_question($id);
|
||||||
|
header('Location: index.php?page=questions&quiz_id=' . $quiz_id);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">Domande per: <?php echo htmlspecialchars($quiz_category['name']); ?></h1>
|
||||||
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
|
<a href="index.php?page=quizzes" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="arrow-left"></span>
|
||||||
|
Torna alle Categorie
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($action === 'add' || $action === 'edit'): ?>
|
||||||
|
<?php
|
||||||
|
$question = null;
|
||||||
|
$answers = [['text'=>'', 'is_correct'=>true], ['text'=>'', 'is_correct'=>false], ['text'=>'', 'is_correct'=>false], ['text'=>'', 'is_correct'=>false]];
|
||||||
|
if ($action === 'edit' && $id) {
|
||||||
|
$question = get_question($id);
|
||||||
|
$db_answers = get_answers_by_question_id($id);
|
||||||
|
$answers = [];
|
||||||
|
foreach($db_answers as $ans) {
|
||||||
|
$answers[] = ['text' => $ans['answer_text'], 'is_correct' => $ans['is_correct']];
|
||||||
|
}
|
||||||
|
// ensure we have 4 answers to show in the form
|
||||||
|
while(count($answers) < 4) {
|
||||||
|
$answers[] = ['text' => '', 'is_correct' => false];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<form method="POST" action="index.php?page=questions&quiz_id=<?php echo $quiz_id; ?>&action=<?php echo $action; ?>">
|
||||||
|
<?php if ($id): ?>
|
||||||
|
<input type="hidden" name="id" value="<?php echo htmlspecialchars($id); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="question_text" class="form-label">Testo della Domanda</label>
|
||||||
|
<textarea class="form-control" id="question_text" name="question_text" rows="3" required><?php echo htmlspecialchars($question['question_text'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Risposte (la prima selezionata è quella corretta)</label>
|
||||||
|
<?php for ($i = 0; $i < 4; $i++): ?>
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input class="form-check-input mt-0" type="radio" name="correct_answer" value="<?php echo $i; ?>" <?php if($answers[$i]['is_correct']) echo 'checked'; ?>>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control" name="answers[]" value="<?php echo htmlspecialchars($answers[$i]['text'] ?? ''); ?>" required>
|
||||||
|
</div>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<?php echo $action === 'edit' ? 'Salva Modifiche' : 'Aggiungi Domanda'; ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php else: // 'list' action ?>
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<a href="index.php?page=questions&quiz_id=<?php echo $quiz_id; ?>&action=add" class="btn btn-sm btn-outline-primary">
|
||||||
|
<span data-feather="plus"></span>
|
||||||
|
Aggiungi Domanda
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Testo della Domanda</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $questions = get_questions_by_quiz_id($quiz_id); ?>
|
||||||
|
<?php if (empty($questions)):
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">Nessuna domanda trovata per questo quiz.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($questions as $question): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($question['id']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($question['question_text']); ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="index.php?page=questions&quiz_id=<?php echo $quiz_id; ?>&action=edit&id=<?php echo $question['id']; ?>" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="edit-2"></span>
|
||||||
|
Modifica
|
||||||
|
</a>
|
||||||
|
<a href="index.php?page=questions&quiz_id=<?php echo $quiz_id; ?>&action=delete&id=<?php echo $question['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Sei sicuro di voler eliminare questa domanda?');">
|
||||||
|
<span data-feather="trash-2"></span>
|
||||||
|
Elimina
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
120
admin/pages/quizzes.php
Normal file
120
admin/pages/quizzes.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
// admin/pages/quizzes.php
|
||||||
|
|
||||||
|
$action = $_GET['action'] ?? 'list';
|
||||||
|
$id = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
// Handle POST requests for add/edit
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$name = $_POST['name'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$id = $_POST['id'] ?? null;
|
||||||
|
|
||||||
|
if ($action === 'add') {
|
||||||
|
add_quiz_category($name, $description);
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
update_quiz_category($id, $name, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to the list view after submission
|
||||||
|
header('Location: index.php?page=quizzes');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle delete action
|
||||||
|
if ($action === 'delete' && $id) {
|
||||||
|
delete_quiz_category($id);
|
||||||
|
header('Location: index.php?page=quizzes');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2"><?php
|
||||||
|
if ($action === 'add') echo 'Aggiungi Nuova Categoria';
|
||||||
|
elseif ($action === 'edit') echo 'Modifica Categoria';
|
||||||
|
else echo 'Gestione Quiz';
|
||||||
|
?></h1>
|
||||||
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
|
<a href="index.php?page=quizzes" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="arrow-left"></span>
|
||||||
|
Torna alla Lista
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($action === 'add' || $action === 'edit'): ?>
|
||||||
|
<?php
|
||||||
|
$category = null;
|
||||||
|
if ($action === 'edit' && $id) {
|
||||||
|
$category = get_quiz_category_by_id($id);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<form method="POST" action="index.php?page=quizzes&action=<?php echo $action; ?>">
|
||||||
|
<?php if ($id): ?>
|
||||||
|
<input type="hidden" name="id" value="<?php echo htmlspecialchars($id); ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Nome Categoria</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($category['name'] ?? ''); ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="description" class="form-label">Descrizione</label>
|
||||||
|
<textarea class="form-control" id="description" name="description" rows="3"><?php echo htmlspecialchars($category['description'] ?? ''); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<?php echo $action === 'edit' ? 'Salva Modifiche' : 'Aggiungi Categoria'; ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php else: // 'list' action ?>
|
||||||
|
<div class="d-flex justify-content-end mb-3">
|
||||||
|
<a href="index.php?page=quizzes&action=add" class="btn btn-sm btn-outline-primary">
|
||||||
|
<span data-feather="plus"></span>
|
||||||
|
Aggiungi Categoria
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Nome</th>
|
||||||
|
<th>Descrizione</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php $categories = get_all_quiz_categories(); ?>
|
||||||
|
<?php if (empty($categories)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">Nessuna categoria trovata.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($categories as $category): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($category['id']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($category['name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($category['description']); ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="index.php?page=questions&quiz_id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-info">
|
||||||
|
<span data-feather="help-circle"></span>
|
||||||
|
Domande
|
||||||
|
</a>
|
||||||
|
<a href="index.php?page=quizzes&action=edit&id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<span data-feather="edit-2"></span>
|
||||||
|
Modifica
|
||||||
|
</a>
|
||||||
|
<a href="index.php?page=quizzes&action=delete&id=<?php echo $category['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Sei sicuro di voler eliminare questa categoria?');">
|
||||||
|
<span data-feather="trash-2"></span>
|
||||||
|
Elimina
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
10
admin/partials/footer.php
Normal file
10
admin/partials/footer.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
// admin/partials/footer.php
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
45
admin/partials/header.php
Normal file
45
admin/partials/header.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
// admin/partials/header.php
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MAVI Admin</title>
|
||||||
|
<link href="../assets/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../assets/css/custom.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="admin-wrapper">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<nav id="sidebar">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h3>MAVI Admin</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="list-unstyled components">
|
||||||
|
<p>Content Management</p>
|
||||||
|
<li class="<?php echo ($page === 'missions') ? 'active' : ''; ?>">
|
||||||
|
<a href="index.php?page=missions">Missions</a>
|
||||||
|
</li>
|
||||||
|
<li class="<?php echo ($page === 'quizzes') ? 'active' : ''; ?>">
|
||||||
|
<a href="index.php?page=quizzes">Quizzes</a>
|
||||||
|
</li>
|
||||||
|
<li class="<?php echo ($page === 'leaderboard') ? 'active' : ''; ?>">
|
||||||
|
<a href="index.php?page=leaderboard">Leaderboard</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="list-unstyled ct-action">
|
||||||
|
<li><a href="/" class="article">Back to Public Site</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Page Content -->
|
||||||
|
<div id="content">
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<h4><?php echo $page_title ?? 'Dashboard'; ?></h4>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
6
assets/css/bootstrap.min.css
vendored
Normal file
6
assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
72
assets/css/custom.css
Normal file
72
assets/css/custom.css
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/* assets/css/custom.css */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-wrapper {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
min-width: 250px;
|
||||||
|
max-width: 250px;
|
||||||
|
background: #2E7D32;
|
||||||
|
color: #fff;
|
||||||
|
transition: all 0.3s;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar .sidebar-header {
|
||||||
|
padding: 20px;
|
||||||
|
background: #256828;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar ul.components {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar ul p {
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar ul li a {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
display: block;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar ul li a:hover {
|
||||||
|
background: #256828;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar ul li.active > a,
|
||||||
|
a[aria-expanded="true"] {
|
||||||
|
background: #256828;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #2E7D32;
|
||||||
|
border-color: #2E7D32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: #256828;
|
||||||
|
border-color: #256828;
|
||||||
|
}
|
||||||
BIN
assets/pasted-20250925-232847-81fff1ea.png
Normal file
BIN
assets/pasted-20250925-232847-81fff1ea.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/vm-shot-2025-09-25T23-30-09-002Z.jpg
Normal file
BIN
assets/vm-shot-2025-09-25T23-30-09-002Z.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@ -1,17 +1,48 @@
|
|||||||
<?php
|
<?php
|
||||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
// db/config.php
|
||||||
define('DB_HOST', '127.0.0.1');
|
|
||||||
define('DB_NAME', 'app_30903');
|
|
||||||
define('DB_USER', 'app_30903');
|
|
||||||
define('DB_PASS', '7d2d5a2d-6e5e-4580-a9b7-3f8e7288a494');
|
|
||||||
|
|
||||||
function db() {
|
function db_connect() {
|
||||||
static $pdo;
|
static $pdo;
|
||||||
if (!$pdo) {
|
|
||||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
if ($pdo) {
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = '127.0.0.1';
|
||||||
|
$db = 'app';
|
||||||
|
$user = 'root';
|
||||||
|
$pass = '';
|
||||||
|
$charset = 'utf8mb4';
|
||||||
|
|
||||||
|
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
|
||||||
|
$options = [
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
]);
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
}
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = new PDO($dsn, $user, $pass, $options);
|
||||||
return $pdo;
|
return $pdo;
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// In a real app, log this error and show a generic message
|
||||||
|
throw new \PDOException($e->getMessage(), (int)$e->getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_migrations() {
|
||||||
|
$pdo = db_connect();
|
||||||
|
$migration_dir = __DIR__ . '/migrations';
|
||||||
|
$files = glob($migration_dir . '/*.sql');
|
||||||
|
sort($files);
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
try {
|
||||||
|
$sql = file_get_contents($file);
|
||||||
|
$pdo->exec($sql);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// Log error if needed, but continue
|
||||||
|
// This might happen if the table already exists, which is fine for idempotent scripts
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
12
db/migrations/001_initial_schema.sql
Normal file
12
db/migrations/001_initial_schema.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `missions` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`title` VARCHAR(255) NOT NULL,
|
||||||
|
`description` TEXT,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert some dummy data for initial view
|
||||||
|
INSERT INTO `missions` (`title`, `description`) VALUES
|
||||||
|
('The Faces of Lacedonia', 'Explore the portraits of 1957 and learn the stories of the people.'),
|
||||||
|
('A Journey Through Time', 'Compare the Lacedonia of the past with the town of today.'),
|
||||||
|
('Artisans and Crafts', 'Discover the traditional crafts and tools used by the people of Irpinia.');
|
||||||
54
db/migrations/002_create_quiz_tables.sql
Normal file
54
db/migrations/002_create_quiz_tables.sql
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
-- Create categories table
|
||||||
|
CREATE TABLE IF NOT EXISTS `categories` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(255) NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create questions table
|
||||||
|
CREATE TABLE IF NOT EXISTS `questions` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`category_id` INT NOT NULL,
|
||||||
|
`question_text` TEXT NOT NULL,
|
||||||
|
FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create answers table
|
||||||
|
CREATE TABLE IF NOT EXISTS `answers` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`question_id` INT NOT NULL,
|
||||||
|
`answer_text` VARCHAR(255) NOT NULL,
|
||||||
|
`is_correct` BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
FOREIGN KEY (`question_id`) REFERENCES `questions`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert categories
|
||||||
|
INSERT INTO `categories` (`name`) VALUES ('Cultura'), ('Tradizione')
|
||||||
|
ON DUPLICATE KEY UPDATE `name` = `name`;
|
||||||
|
|
||||||
|
-- Insert questions and answers for "Cultura"
|
||||||
|
-- Question 1
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) SELECT id, 'Quale di questi monumenti si trova a Roma?' FROM `categories` WHERE `name` = 'Cultura';
|
||||||
|
SET @last_question_id = LAST_INSERT_ID();
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@last_question_id, 'La Torre di Pisa', 0),
|
||||||
|
(@last_question_id, 'Il Colosseo', 1),
|
||||||
|
(@last_question_id, 'Il Duomo di Milano', 0),
|
||||||
|
(@last_question_id, 'Il Ponte di Rialto', 0);
|
||||||
|
|
||||||
|
-- Question 2
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) SELECT id, 'Chi ha scritto "La Divina Commedia"?' FROM `categories` WHERE `name` = 'Cultura';
|
||||||
|
SET @last_question_id = LAST_INSERT_ID();
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@last_question_id, 'Alessandro Manzoni', 0),
|
||||||
|
(@last_question_id, 'Giacomo Leopardi', 0),
|
||||||
|
(@last_question_id, 'Dante Alighieri', 1),
|
||||||
|
(@last_question_id, 'Giovanni Boccaccio', 0);
|
||||||
|
|
||||||
|
-- Question 3
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) SELECT id, 'Qual è la capitale della Francia?' FROM `categories` WHERE `name` = 'Cultura';
|
||||||
|
SET @last_question_id = LAST_INSERT_ID();
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@last_question_id, 'Berlino', 0),
|
||||||
|
(@last_question_id, 'Madrid', 0),
|
||||||
|
(@last_question_id, 'Londra', 0),
|
||||||
|
(@last_question_id, 'Parigi', 1);
|
||||||
54
db/migrations/003_update_quiz_content.sql
Normal file
54
db/migrations/003_update_quiz_content.sql
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
-- Truncate tables to ensure a clean state
|
||||||
|
SET FOREIGN_KEY_CHECKS=0;
|
||||||
|
TRUNCATE TABLE `answers`;
|
||||||
|
TRUNCATE TABLE `questions`;
|
||||||
|
TRUNCATE TABLE `categories`;
|
||||||
|
SET FOREIGN_KEY_CHECKS=1;
|
||||||
|
|
||||||
|
-- Insert the new category
|
||||||
|
INSERT INTO `categories` (`id`, `name`, `description`) VALUES
|
||||||
|
(1, 'MAVI e Lacedonia', 'Domande sul Museo Antropologico Visivo Irpino e sul comune di Lacedonia');
|
||||||
|
|
||||||
|
-- Insert questions for the "MAVI e Lacedonia" category
|
||||||
|
INSERT INTO `questions` (`id`, `category_id`, `question_text`) VALUES
|
||||||
|
(1, 1, 'Dove si trova il MAVI?'),
|
||||||
|
(2, 1, 'Cosa significa l''acronimo MAVI?'),
|
||||||
|
(3, 1, 'In che anno è stato inaugurato il MAVI?'),
|
||||||
|
(4, 1, 'Quale importante fotografo americano ha documentato la vita di Lacedonia nel 1957?'),
|
||||||
|
(5, 1, 'A chi è dedicato il Museo di Lacedonia?');
|
||||||
|
|
||||||
|
-- Insert answers for the questions
|
||||||
|
-- Answers for question 1
|
||||||
|
INSERT INTO `answers` (`id`, `question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(1, 1, 'Lacedonia', 1),
|
||||||
|
(2, 1, 'Avellino', 0),
|
||||||
|
(3, 1, 'Napoli', 0),
|
||||||
|
(4, 1, 'Roma', 0);
|
||||||
|
|
||||||
|
-- Answers for question 2
|
||||||
|
INSERT INTO `answers` (`id`, `question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(5, 2, 'Museo Antropologico Visivo Irpino', 1),
|
||||||
|
(6, 2, 'Museo Archeologico Virtuale Italiano', 0),
|
||||||
|
(7, 2, 'Mostra Artistica Visiva Internazionale', 0),
|
||||||
|
(8, 2, 'Museo d''Arte Visiva Irpino', 0);
|
||||||
|
|
||||||
|
-- Answers for question 3
|
||||||
|
INSERT INTO `answers` (`id`, `question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(9, 3, '2008', 1),
|
||||||
|
(10, 3, '1998', 0),
|
||||||
|
(11, 3, '2018', 0),
|
||||||
|
(12, 3, '1980', 0);
|
||||||
|
|
||||||
|
-- Answers for question 4
|
||||||
|
INSERT INTO `answers` (`id`, `question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(13, 4, 'Frank Cancian', 1),
|
||||||
|
(14, 4, 'Robert Capa', 0),
|
||||||
|
(15, 4, 'Henri Cartier-Bresson', 0),
|
||||||
|
(16, 4, 'Ansel Adams', 0);
|
||||||
|
|
||||||
|
-- Answers for question 5
|
||||||
|
INSERT INTO `answers` (`id`, `question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(17, 5, 'A San Gerardo Maiella', 1),
|
||||||
|
(18, 5, 'A San Francesco d''Assisi', 0),
|
||||||
|
(19, 5, 'A Padre Pio', 0),
|
||||||
|
(20, 5, 'A San Gennaro', 0);
|
||||||
65
db/migrations/004_add_tradizioni_questions.sql
Normal file
65
db/migrations/004_add_tradizioni_questions.sql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
-- Inserimento della nuova categoria 'Tradizioni'
|
||||||
|
INSERT INTO `categories` (`name`) VALUES ('Tradizioni');
|
||||||
|
|
||||||
|
-- Inserimento delle domande per la categoria 'Tradizioni'
|
||||||
|
SET @category_id = (SELECT `id` FROM `categories` WHERE `name` = 'Tradizioni');
|
||||||
|
|
||||||
|
-- Domanda 1
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) VALUES
|
||||||
|
(@category_id, 'Come si chiamano i dolci tipici di Carnevale a Lacedonia?');
|
||||||
|
|
||||||
|
SET @question_id = LAST_INSERT_ID();
|
||||||
|
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@question_id, 'Chiacchiere', 1),
|
||||||
|
(@question_id, 'Struffoli', 0),
|
||||||
|
(@question_id, 'Cauzuncielli', 0),
|
||||||
|
(@question_id, 'Sfogliatelle', 0);
|
||||||
|
|
||||||
|
-- Domanda 2
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) VALUES
|
||||||
|
(@category_id, 'In che mese si svolge la sagra "cingule ricotta e vrasciol"?');
|
||||||
|
|
||||||
|
SET @question_id = LAST_INSERT_ID();
|
||||||
|
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@question_id, 'Agosto', 1),
|
||||||
|
(@question_id, 'Luglio', 0),
|
||||||
|
(@question_id, 'Settembre', 0),
|
||||||
|
(@question_id, 'Giugno', 0);
|
||||||
|
|
||||||
|
-- Domanda 3
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) VALUES
|
||||||
|
(@category_id, 'Quale evento storico viene rievocato a Lacedonia con un corteo in abiti d'epoca?');
|
||||||
|
|
||||||
|
SET @question_id = LAST_INSERT_ID();
|
||||||
|
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@question_id, 'La Congiura dei Baroni', 1),
|
||||||
|
(@question_id, 'La Battaglia di Lacedonia', 0),
|
||||||
|
(@question_id, 'L'arrivo di Garibaldi', 0),
|
||||||
|
(@question_id, 'La fondazione della città', 0);
|
||||||
|
|
||||||
|
-- Domanda 4
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) VALUES
|
||||||
|
(@category_id, 'Come si chiamano i ravioli dolci farciti con castagne e cioccolata?');
|
||||||
|
|
||||||
|
SET @question_id = LAST_INSERT_ID();
|
||||||
|
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@question_id, 'Cauzuncielli', 1),
|
||||||
|
(@question_id, 'Sfogliatelle', 0),
|
||||||
|
(@question_id, 'Chiacchiere', 0),
|
||||||
|
(@question_id, 'Struffoli', 0);
|
||||||
|
|
||||||
|
-- Domanda 5
|
||||||
|
INSERT INTO `questions` (`category_id`, `question_text`) VALUES
|
||||||
|
(@category_id, 'In onore di quale santo vengono allestiti grandi falò il 18 marzo?');
|
||||||
|
|
||||||
|
SET @question_id = LAST_INSERT_ID();
|
||||||
|
|
||||||
|
INSERT INTO `answers` (`question_id`, `answer_text`, `is_correct`) VALUES
|
||||||
|
(@question_id, 'San Giuseppe', 1),
|
||||||
|
(@question_id, 'San Gerardo', 0),
|
||||||
|
(@question_id, 'San Nicola', 0),
|
||||||
|
(@question_id, 'San Filippo Neri', 0);
|
||||||
5
db/migrations/005_add_description_to_categories.sql
Normal file
5
db/migrations/005_add_description_to_categories.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE `categories` ADD `description` TEXT NULL AFTER `name`;
|
||||||
|
|
||||||
|
UPDATE `categories` SET `description` = 'Domande sul Museo Antropologico Visivo Irpino e sul comune di Lacedonia.' WHERE `name` = 'MAVI e Lacedonia';
|
||||||
|
UPDATE `categories` SET `description` = 'Mettiti alla prova con domande sugli usi, i costumi e le tradizioni popolari.' WHERE `name` = 'Tradizioni';
|
||||||
|
UPDATE `categories` SET `description` = 'Domande sulla storia, l'arte e le curiosità legate al territorio di Lacedonia.' WHERE `name` = 'Cultura';
|
||||||
72
games.php
Normal file
72
games.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
// games.php
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Games - MAVI Project</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h1 style="font-family: 'Poppins', sans-serif;">Games Hub</h1>
|
||||||
|
<p class="lead">Select a game to play.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row gy-4">
|
||||||
|
<!-- Game 1: Cultural Quiz -->
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body d-flex flex-column text-center">
|
||||||
|
<h5 class="card-title">Quiz Culturale</h5>
|
||||||
|
<p class="card-text">Metti alla prova la tua conoscenza con domande sulla storia e la cultura locale.</p>
|
||||||
|
<a href="quiz.php" class="btn btn-primary mt-auto">Gioca</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Game 2: Memory Game -->
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body d-flex flex-column text-center">
|
||||||
|
<h5 class="card-title">Memory Game</h5>
|
||||||
|
<p class="card-text">Allena la tua memoria trovando le coppie di immagini a tema.</p>
|
||||||
|
<a href="#" class="btn btn-secondary disabled mt-auto">Gioca</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Game 3: Interactive Map -->
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body d-flex flex-column text-center">
|
||||||
|
<h5 class="card-title">Mappa Interattiva di Lacedonia</h5>
|
||||||
|
<p class="card-text">Esplora la Lacedonia del 1957 e scopri foto storiche nascoste.</p>
|
||||||
|
<a href="#" class="btn btn-secondary disabled mt-auto">Gioca</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Game 4: To Be Defined -->
|
||||||
|
<div class="col-lg-3 col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body d-flex flex-column text-center">
|
||||||
|
<h5 class="card-title">Gioco 4 (Da definire)</h5>
|
||||||
|
<p class="card-text">Un nuovo gioco in arrivo. Le idee sono benvenute!</p>
|
||||||
|
<a href="#" class="btn btn-secondary disabled mt-auto">Gioca</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-5">
|
||||||
|
<a href="/" class="btn btn-primary">Back to Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
135
index.php
135
index.php
@ -1,131 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
// index.php
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
<title>MAVI Landing Page</title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="stylesheet" href="assets/css/custom.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<div class="container d-flex justify-content-center align-items-center vh-100">
|
||||||
<div class="card">
|
<div class="text-center">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<h1 style="font-family: 'Poppins', sans-serif;">Welcome to the MAVI Project</h1>
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<p class="lead">The interactive learning platform for the MAVI museum.</p>
|
||||||
<span class="sr-only">Loading…</span>
|
<a href="/games.php" class="btn btn-success mt-3">Play Games</a>
|
||||||
|
<a href="/admin/" class="btn btn-primary mt-3">Go to Admin Panel</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWiZZy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
|
||||||
</footer>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
44
quiz.php
Normal file
44
quiz.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
$pdo = db_connect();
|
||||||
|
$stmt = $pdo->query("SELECT name, description FROM categories ORDER BY name");
|
||||||
|
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Quiz MAVI - Scegli una categoria</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<a href="games.php" class="btn btn-secondary mb-4">Torna a Giochi</a>
|
||||||
|
<h1 class="text-center mb-4">Scegli una categoria</h1>
|
||||||
|
|
||||||
|
<div class="row justify-content-center g-4">
|
||||||
|
<?php foreach ($categories as $category): ?>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="card h-100 text-center">
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h5 class="card-title"><?php echo htmlspecialchars($category['name']); ?></h5>
|
||||||
|
<p class="card-text"><?php echo htmlspecialchars($category['description'] ?? 'Descrizione non disponibile.'); ?></p>
|
||||||
|
<div class="mt-auto">
|
||||||
|
<a href="quiz_play.php?category=<?php echo urlencode($category['name']); ?>&action=start" class="btn btn-primary">Inizia il Quiz</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-5">
|
||||||
|
<a href="games.php" class="btn btn-secondary">← Torna alla lista dei giochi</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
218
quiz_play.php
Normal file
218
quiz_play.php
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// --- Configuration ---
|
||||||
|
$num_questions_per_quiz = 5;
|
||||||
|
|
||||||
|
// --- Helper Functions ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single question and its answers from the database.
|
||||||
|
* @param int $question_id
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
function get_question(int $question_id): ?array {
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT id, question_text FROM questions WHERE id = :id");
|
||||||
|
$stmt->execute(['id' => $question_id]);
|
||||||
|
$question = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$question) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = db()->prepare("SELECT id, answer_text FROM answers WHERE question_id = :id ORDER BY RAND()");
|
||||||
|
$stmt->execute(['id' => $question_id]);
|
||||||
|
$question['answers'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("Error in get_question: " . $e->getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a set of random question IDs for a given category.
|
||||||
|
* @param string $category_name
|
||||||
|
* @param int $limit
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function get_random_question_ids(string $category_name, int $limit): array {
|
||||||
|
try {
|
||||||
|
// First, get the category ID from the name
|
||||||
|
$cat_stmt = db()->prepare("SELECT id FROM categories WHERE name = :name");
|
||||||
|
$cat_stmt->execute(['name' => $category_name]);
|
||||||
|
$category = $cat_stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$category) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$category_id = $category['id'];
|
||||||
|
|
||||||
|
// Now, fetch the questions
|
||||||
|
$stmt = db()->prepare("SELECT id FROM questions WHERE category_id = :category_id ORDER BY RAND() LIMIT :limit");
|
||||||
|
$stmt->bindParam('category_id', $category_id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam('limit', $limit, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("Error in get_random_question_ids: " . $e->getMessage());
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided answer is correct.
|
||||||
|
* @param int $answer_id
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function is_answer_correct(int $answer_id): bool {
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT is_correct FROM answers WHERE id = :id");
|
||||||
|
$stmt->execute(['id' => $answer_id]);
|
||||||
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return $result && $result['is_correct'];
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("Error in is_answer_correct: " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- State Initialization ---
|
||||||
|
$question_ids_str = '';
|
||||||
|
$current_index = 0;
|
||||||
|
$score = 0;
|
||||||
|
$category_name = '';
|
||||||
|
$is_quiz_finished = false;
|
||||||
|
|
||||||
|
// --- Logic ---
|
||||||
|
|
||||||
|
// Is this a POST request (user answered a question)?
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Retrieve state from the form
|
||||||
|
$question_ids_str = $_POST['question_ids'] ?? '';
|
||||||
|
$current_index = isset($_POST['current_index']) ? (int)$_POST['current_index'] : 0;
|
||||||
|
$score = isset($_POST['score']) ? (int)$_POST['score'] : 0;
|
||||||
|
$submitted_answer_id = isset($_POST['answer_id']) ? (int)$_POST['answer_id'] : 0;
|
||||||
|
|
||||||
|
// Check if the answer was correct
|
||||||
|
if ($submitted_answer_id > 0 && is_answer_correct($submitted_answer_id)) {
|
||||||
|
$score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next question
|
||||||
|
$current_index++;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Or is this a GET request (start of the quiz)?
|
||||||
|
else if (isset($_GET['action']) && $_GET['action'] === 'start') {
|
||||||
|
$category_name = $_GET['category'] ?? '';
|
||||||
|
|
||||||
|
if (!empty($category_name)) {
|
||||||
|
$question_ids = get_random_question_ids($category_name, $num_questions_per_quiz);
|
||||||
|
if (count($question_ids) < $num_questions_per_quiz) {
|
||||||
|
$error_message = "Non ci sono abbastanza domande (ne servono {$num_questions_per_quiz}) in questa categoria per iniziare il quiz.";
|
||||||
|
$is_quiz_finished = true; // Stop the quiz
|
||||||
|
} else {
|
||||||
|
$question_ids_str = implode(',', $question_ids);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error_message = "Nome della categoria non valido.";
|
||||||
|
$is_quiz_finished = true; // Stop the quiz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$question_ids = !empty($question_ids_str) ? explode(',', $question_ids_str) : [];
|
||||||
|
$total_questions = count($question_ids);
|
||||||
|
|
||||||
|
// Check if the quiz is over
|
||||||
|
if ($current_index >= $total_questions && $total_questions > 0) {
|
||||||
|
$is_quiz_finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Quiz in Corso - MAVI</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<a href="quiz.php" class="btn btn-secondary mb-4">← Torna alle Categorie</a>
|
||||||
|
|
||||||
|
<?php if ($is_quiz_finished): ?>
|
||||||
|
<div class="card text-center">
|
||||||
|
<div class="card-body">
|
||||||
|
<h1 class="card-title">Quiz Terminato!</h1>
|
||||||
|
<?php if (isset($error_message)): ?>
|
||||||
|
<div class="alert alert-warning"><?php echo $error_message; ?></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="card-text fs-4">Il tuo punteggio finale è:</p>
|
||||||
|
<p class="display-3"><strong><?php echo $score; ?> su <?php echo $total_questions; ?></strong></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="quiz.php" class="btn btn-primary mt-3">Gioca Ancora</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php elseif (!empty($question_ids)): ?>
|
||||||
|
<?php
|
||||||
|
$question_id = (int)$question_ids[$current_index];
|
||||||
|
$question = get_question($question_id);
|
||||||
|
?>
|
||||||
|
<?php if ($question): ?>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Domanda <?php echo ($current_index + 1); ?> di <?php echo $total_questions; ?></h5>
|
||||||
|
<p class="card-text fs-4"><?php echo htmlspecialchars($question['question_text']); ?></p>
|
||||||
|
|
||||||
|
<form method="POST" action="quiz_play.php">
|
||||||
|
<?php foreach ($question['answers'] as $answer): ?>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="radio" name="answer_id" value="<?php echo $answer['id']; ?>" id="answer_<?php echo $answer['id']; ?>" required>
|
||||||
|
<label class="form-check-label" for="answer_<?php echo $answer['id']; ?>">
|
||||||
|
<?php echo htmlspecialchars($answer['answer_text']); ?>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<input type="hidden" name="question_ids" value="<?php echo htmlspecialchars($question_ids_str); ?>">
|
||||||
|
<input type="hidden" name="current_index" value="<?php echo $current_index; ?>">
|
||||||
|
<input type="hidden" name="score" value="<?php echo $score; ?>">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary mt-4">Rispondi</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- VISIBLE DEBUG INFO -->
|
||||||
|
<div class="card bg-light mt-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-title">Informazioni di Debug</h6>
|
||||||
|
<pre class="small"><?php echo htmlspecialchars(print_r([
|
||||||
|
'Question IDs' => $question_ids_str,
|
||||||
|
'Current Index' => $current_index,
|
||||||
|
'Score' => $score,
|
||||||
|
'Total Questions' => $total_questions
|
||||||
|
], true)); ?></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-danger">Errore: Impossibile caricare la domanda.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php elseif ($_SERVER['REQUEST_METHOD'] !== 'POST'): ?>
|
||||||
|
<div class="alert alert-info">Seleziona una categoria dalla <a href="quiz.php">pagina dei quiz</a> per iniziare.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user