diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..fbf5a7e --- /dev/null +++ b/admin.php @@ -0,0 +1,99 @@ +query("SELECT COUNT(*) FROM feedback_submissions"); +$total_records = $total_stmt->fetchColumn(); +$total_pages = ceil($total_records / $records_per_page); + +// Fetch submissions for the current page +$submissions_stmt = db()->prepare("SELECT s.id, s.name, s.email, s.created_at, sv.title as survey_title, sv.id as survey_id FROM feedback_submissions s JOIN surveys sv ON s.survey_id = sv.id ORDER BY s.created_at DESC LIMIT :limit OFFSET :offset"); +$submissions_stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT); +$submissions_stmt->bindValue(':offset', $offset, PDO::PARAM_INT); +$submissions_stmt->execute(); +$submissions = $submissions_stmt->fetchAll(PDO::FETCH_ASSOC); + +$pageTitle = "Admin - Feedback Submissions"; +require_once 'templates/header.php'; +?> +
+
+
+
+

Feedback Submissions

+
+ Dashboard + Manage Surveys + Export to CSV + Logout +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
SurveySubmitterEmailSubmitted AtAnswers
No feedback submissions yet.
+ View Answers +
+
+ + + + +
+
+
+ diff --git a/api.php b/api.php new file mode 100644 index 0000000..6fdecd6 --- /dev/null +++ b/api.php @@ -0,0 +1,63 @@ + 'Unauthorized']); + exit; +} + +$action = $_GET['action'] ?? ''; + +switch ($action) { + case 'submissions_per_survey': + $stmt = db()->query("SELECT s.title, COUNT(fs.id) as submission_count FROM surveys s LEFT JOIN feedback_submissions fs ON s.id = fs.survey_id GROUP BY s.id"); + $results = $stmt->fetchAll(PDO::FETCH_ASSOC); + $labels = array_column($results, 'title'); + $values = array_column($results, 'submission_count'); + echo json_encode(['labels' => $labels, 'values' => $values]); + break; + + case 'survey_question_analytics': + $surveys_stmt = db()->query("SELECT * FROM surveys"); + $surveys = $surveys_stmt->fetchAll(PDO::FETCH_ASSOC); + $response = []; + + foreach ($surveys as $survey) { + $questions_stmt = db()->prepare("SELECT * FROM survey_questions WHERE survey_id = ? AND (question_type = 'rating' OR question_type = 'multiple-choice')"); + $questions_stmt->execute([$survey['id']]); + $questions = $questions_stmt->fetchAll(PDO::FETCH_ASSOC); + + $survey_data = [ + 'id' => $survey['id'], + 'title' => $survey['title'], + 'questions' => [] + ]; + + foreach ($questions as $question) { + $answers_stmt = db()->prepare("SELECT answer_text, COUNT(*) as count FROM survey_answers WHERE question_id = ? GROUP BY answer_text"); + $answers_stmt->execute([$question['id']]); + $answers = $answers_stmt->fetchAll(PDO::FETCH_ASSOC); + + $question_data = [ + 'id' => $question['id'], + 'question_text' => $question['question_text'], + 'type' => $question['question_type'], + 'answers' => [ + 'labels' => array_column($answers, 'answer_text'), + 'values' => array_column($answers, 'count') + ] + ]; + $survey_data['questions'][] = $question_data; + } + $response[] = $survey_data; + } + echo json_encode($response); + break; + + default: + echo json_encode(['error' => 'Invalid action']); + break; +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..a3336f7 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,174 @@ + +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Georgia&display=swap'); + +:root { + --primary-color: #1a1a1a; + --secondary-color: #198754; /* Changed from #4f46e5 */ + --background-color: #f8f9fa; + --surface-color: #ffffff; + --text-color: #212529; + --border-radius: 0.5rem; + --border-radius-full: 9999px; + --spacing-1: 0.5rem; + --spacing-2: 1rem; + --spacing-3: 1.5rem; + --spacing-4: 2rem; +} + +body { + background-color: var(--background-color); + color: var(--text-color); + font-family: 'Inter', sans-serif; + line-height: 1.6; + margin: 0; + padding: 0; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Georgia', serif; + color: var(--primary-color); +} + +.header { + padding: var(--spacing-2) 0; + border-bottom: 1px solid #dee2e6; +} + +.logo { + font-family: 'Georgia', serif; + font-weight: bold; + font-size: 1.5rem; + color: var(--primary-color); + text-decoration: none; +} + +.hero { + color: white; + padding: 6rem 0; + text-align: center; + background: linear-gradient(135deg, #198754, #20c997); + position: relative; + overflow: hidden; +} + +.hero::before { + content: ''; + position: absolute; + bottom: -150px; + left: -50px; + width: 300px; + height: 300px; + background: white; + opacity: 0.1; + border-radius: 50%; + transform: rotate(45deg); +} + +.hero::after { + content: ''; + position: absolute; + top: -100px; + right: -100px; + width: 400px; + height: 400px; + background: white; + opacity: 0.1; + border-radius: 50%; +} + + +.hero h1 { + font-size: 3.5rem; + font-weight: 700; + margin-bottom: var(--spacing-2); + color: white; +} + +.hero p { + font-size: 1.25rem; + max-width: 600px; + margin: 0 auto; +} + +.survey-section { + padding: 4rem 0; +} + +.form-container { + background: var(--surface-color); + padding: var(--spacing-4); + border-radius: var(--border-radius); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05); + max-width: 600px; + margin: 0 auto; +} + +.form-group { + margin-bottom: var(--spacing-3); +} + +.form-label { + font-weight: bold; + display: block; + margin-bottom: var(--spacing-1); +} + +.form-control { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid #ced4da; + border-radius: var(--border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +.form-control:focus { + border-color: var(--secondary-color); + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); +} + +.btn-primary { + background-color: var(--secondary-color); + border-color: var(--secondary-color); + color: white; + padding: 0.75rem 1.5rem; + font-size: 1rem; + font-weight: bold; + border-radius: var(--border-radius); + cursor: pointer; + transition: background-color 0.2s; + width: 100%; +} + +.btn-primary:hover { + background-color: #157347; +} + +.footer { + padding: var(--spacing-3) 0; + border-top: 1px solid #dee2e6; + text-align: center; + font-size: 0.9rem; + color: #6c757d; +} + +.footer a { + color: var(--secondary-color); + text-decoration: none; +} + +.footer a:hover { + text-decoration: underline; +} + +#success-message { + text-align: center; + padding: 2rem; + background-color: #e8f5e9; + color: #2e7d32; + border-radius: var(--border-radius); +} + +.hidden { + display: none; +} diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js new file mode 100644 index 0000000..8c3ff03 --- /dev/null +++ b/assets/js/dashboard.js @@ -0,0 +1,78 @@ +document.addEventListener('DOMContentLoaded', function () { + // Chart for submissions per survey + fetch('api.php?action=submissions_per_survey') + .then(response => response.json()) + .then(data => { + const ctx = document.getElementById('submissions-chart').getContext('2d'); + new Chart(ctx, { + type: 'bar', + data: { + labels: data.labels, + datasets: [{ + label: '# of Submissions', + data: data.values, + backgroundColor: 'rgba(75, 192, 192, 0.2)', + borderColor: 'rgba(75, 192, 192, 1)', + borderWidth: 1 + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + } + } + }); + }); + + // Charts for each survey's questions + fetch('api.php?action=survey_question_analytics') + .then(response => response.json()) + .then(surveys => { + const container = document.getElementById('survey-charts-container'); + surveys.forEach(survey => { + const surveyRow = document.createElement('div'); + surveyRow.className = 'row'; + + const surveyTitle = document.createElement('h2'); + surveyTitle.innerText = `Survey: ${survey.title}`; + container.appendChild(surveyTitle); + container.appendChild(surveyRow); + + survey.questions.forEach(question => { + if (question.type === 'rating' || question.type === 'multiple-choice') { + const col = document.createElement('div'); + col.className = 'col-md-6'; + + const card = document.createElement('div'); + card.className = 'card mb-4'; + + const cardHeader = document.createElement('div'); + cardHeader.className = 'card-header'; + cardHeader.innerHTML = `

${question.question_text}

`; + + const cardBody = document.createElement('div'); + cardBody.className = 'card-body'; + + const canvas = document.createElement('canvas'); + cardBody.appendChild(canvas); + card.appendChild(cardHeader); + card.appendChild(cardBody); + col.appendChild(card); + surveyRow.appendChild(col); + + new Chart(canvas.getContext('2d'), { + type: 'pie', + data: { + labels: question.answers.labels, + datasets: [{ + data: question.answers.values + }] + } + }); + } + }); + }); + }); +}); diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..03850b0 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,50 @@ +document.addEventListener('DOMContentLoaded', function () { + const surveyForm = document.getElementById('survey-form'); + if (!surveyForm) return; + + const successMessage = document.getElementById('success-message'); + const formContainer = document.querySelector('.form-container'); + + surveyForm.addEventListener('submit', function (e) { + e.preventDefault(); + + const name = document.getElementById('name').value.trim(); + const email = document.getElementById('email').value.trim(); + const message = document.getElementById('message').value.trim(); + + if (name === '' || email === '' || message === '') { + alert('Please fill out all fields.'); + return; + } + + if (!validateEmail(email)) { + alert('Please enter a valid email address.'); + return; + } + + const formData = new FormData(this); + + fetch('submit_feedback.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + formContainer.classList.add('hidden'); + successMessage.classList.remove('hidden'); + } else { + alert('An error occurred: ' + data.error); + } + }) + .catch(error => { + console.error('Error:', error); + alert('A server error occurred. Please try again later.'); + }); + }); + + function validateEmail(email) { + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); + } +}); diff --git a/dashboard.php b/dashboard.php new file mode 100644 index 0000000..8fddaa8 --- /dev/null +++ b/dashboard.php @@ -0,0 +1,47 @@ + + +
+
+
+
+

Analytics Dashboard

+ Back to Submissions +
+ +
+
+
+
+

Submissions per Survey

+
+
+ +
+
+
+
+ +
+ +
+
+
+ + + + + diff --git a/db/config.php b/db/config.php index bb98f7d..bc6f7cb 100644 --- a/db/config.php +++ b/db/config.php @@ -6,12 +6,52 @@ define('DB_USER', 'app_30953'); define('DB_PASS', 'e45f2778-db1f-450c-99c6-29efb4601472'); function db() { - static $pdo; - if (!$pdo) { - $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; + static $pdo; + if (!$pdo) { + try { + // Connect to MySQL server without specifying a database + $pdo = new PDO('mysql:host='.DB_HOST.';charset=utf8mb4', DB_USER, DB_PASS, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + + // Create the database if it doesn't exist + $pdo->exec('CREATE DATABASE IF NOT EXISTS `'.DB_NAME.'`'); + + // Re-connect to the specific database + $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + + } catch (PDOException $e) { + // If database creation fails, it's a critical error. + error_log('Database connection or creation failed: ' . $e->getMessage()); + // Exit or handle the error gracefully. For a web request, you might show a generic error page. + http_response_code(500); + echo "Database connection failed. Please check the server logs."; + exit; + } + } + return $pdo; } + +function run_migrations() { + $pdo = db(); + $migrations_dir = __DIR__ . '/migrations'; + if (!is_dir($migrations_dir)) { + return; + } + $files = glob($migrations_dir . '/*.sql'); + foreach ($files as $file) { + $sql = file_get_contents($file); + try { + $pdo->exec($sql); + } catch (PDOException $e) { + // Log error or handle it as needed + error_log("Migration failed for file: " . basename($file) . " with error: " . $e->getMessage()); + } + } +} + +run_migrations(); \ No newline at end of file diff --git a/db/migrations/001_create_feedback_submissions_table.sql b/db/migrations/001_create_feedback_submissions_table.sql new file mode 100644 index 0000000..9a7d885 --- /dev/null +++ b/db/migrations/001_create_feedback_submissions_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS feedback_submissions ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/db/migrations/002_create_surveys_table.sql b/db/migrations/002_create_surveys_table.sql new file mode 100644 index 0000000..64ad19d --- /dev/null +++ b/db/migrations/002_create_surveys_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS `surveys` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `title` VARCHAR(255) NOT NULL, + `description` TEXT, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/003_create_survey_questions_table.sql b/db/migrations/003_create_survey_questions_table.sql new file mode 100644 index 0000000..65a3ad4 --- /dev/null +++ b/db/migrations/003_create_survey_questions_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS `survey_questions` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `survey_id` INT NOT NULL, + `question_text` TEXT NOT NULL, + `question_type` VARCHAR(50) NOT NULL, -- e.g., 'text', 'textarea', 'rating', 'multiple-choice' + `options` TEXT, -- For multiple-choice questions, store options as a JSON array or comma-separated values + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`survey_id`) REFERENCES `surveys`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/004_add_survey_id_to_feedback_submissions.sql b/db/migrations/004_add_survey_id_to_feedback_submissions.sql new file mode 100644 index 0000000..3a9e84f --- /dev/null +++ b/db/migrations/004_add_survey_id_to_feedback_submissions.sql @@ -0,0 +1 @@ +ALTER TABLE `feedback_submissions` ADD COLUMN `survey_id` INT NULL AFTER `id`; \ No newline at end of file diff --git a/db/migrations/005_create_survey_answers_table.sql b/db/migrations/005_create_survey_answers_table.sql new file mode 100644 index 0000000..925c314 --- /dev/null +++ b/db/migrations/005_create_survey_answers_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS `survey_answers` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `submission_id` INT NOT NULL, + `question_id` INT NOT NULL, + `answer_text` TEXT, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`submission_id`) REFERENCES `feedback_submissions`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`question_id`) REFERENCES `survey_questions`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/006_create_users_table.sql b/db/migrations/006_create_users_table.sql new file mode 100644 index 0000000..195077e --- /dev/null +++ b/db/migrations/006_create_users_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `username` VARCHAR(50) NOT NULL UNIQUE, + `email` VARCHAR(100) NOT NULL UNIQUE, + `password` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/007_create_roles_table.sql b/db/migrations/007_create_roles_table.sql new file mode 100644 index 0000000..df1956e --- /dev/null +++ b/db/migrations/007_create_roles_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS `roles` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `role_name` VARCHAR(50) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/008_create_user_roles_table.sql b/db/migrations/008_create_user_roles_table.sql new file mode 100644 index 0000000..6bdb97d --- /dev/null +++ b/db/migrations/008_create_user_roles_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS `user_roles` ( + `user_id` INT NOT NULL, + `role_id` INT NOT NULL, + PRIMARY KEY (`user_id`, `role_id`), + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/009_insert_default_roles.sql b/db/migrations/009_insert_default_roles.sql new file mode 100644 index 0000000..6438db4 --- /dev/null +++ b/db/migrations/009_insert_default_roles.sql @@ -0,0 +1 @@ +INSERT INTO `roles` (`role_name`) VALUES ('Admin'), ('Respondent'); \ No newline at end of file diff --git a/edit_survey.php b/edit_survey.php new file mode 100644 index 0000000..78600ca --- /dev/null +++ b/edit_survey.php @@ -0,0 +1,72 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$survey_stmt->execute([$survey_id]); +$survey = $survey_stmt->fetch(); +if (!$survey) { + header('Location: surveys.php'); + exit; +} + +// Handle form submission for updating survey +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['title'])) { + $title = trim($_POST['title']); + $description = trim($_POST['description']); + + if (!empty($title)) { + $stmt = db()->prepare("UPDATE surveys SET title = ?, description = ? WHERE id = ?"); + $stmt->execute([$title, $description, $survey_id]); + header("Location: surveys.php"); // Redirect to the survey list + exit; + } +} + +$pageTitle = "Edit Survey"; +require_once 'templates/header.php'; +?> + +
+
+
+
+

Edit Survey

+ Back to Surveys +
+ + +
+
+
+ + +
+
+ + +
+ +
+
+
+
+
+ + diff --git a/export.php b/export.php new file mode 100644 index 0000000..e5d8396 --- /dev/null +++ b/export.php @@ -0,0 +1,66 @@ +query("SELECT + s.id as submission_id, + s.name as submitter_name, + s.email as submitter_email, + s.created_at as submission_date, + sv.title as survey_title, + q.question_text, + a.answer_text +FROM feedback_submissions s +JOIN surveys sv ON s.survey_id = sv.id +LEFT JOIN survey_answers a ON s.id = a.submission_id +LEFT JOIN survey_questions q ON a.question_id = q.id +ORDER BY s.created_at DESC, q.created_at ASC"); + +$results = $stmt->fetchAll(PDO::FETCH_ASSOC); + +// Set headers for CSV download +header('Content-Type: text/csv; charset=utf-8'); +header('Content-Disposition: attachment; filename=feedback_submissions_' . date('Y-m-d') . '.csv'); + +$output = fopen('php://output', 'w'); + +// Get all unique questions in order +$questions_stmt = db()->query("SELECT DISTINCT question_text FROM survey_questions ORDER BY created_at ASC"); +$question_headers = $questions_stmt->fetchAll(PDO::FETCH_COLUMN); + +// Output the column headings +$headers = array_merge(['Submission ID', 'Submitter', 'Email', 'Date', 'Survey'], $question_headers); +fputcsv($output, $headers); + +// Process the results +$submissions = []; +foreach ($results as $row) { + $submissions[$row['submission_id']]['details'] = [ + 'submission_id' => $row['submission_id'], + 'submitter_name' => $row['submitter_name'], + 'submitter_email' => $row['submitter_email'], + 'submission_date' => $row['submission_date'], + 'survey_title' => $row['survey_title'] + ]; + $submissions[$row['submission_id']]['answers'][$row['question_text']] = $row['answer_text']; +} + +// Write rows to CSV +foreach ($submissions as $submission_id => $data) { + $row = $data['details']; + foreach ($question_headers as $question) { + $row[] = $data['answers'][$question] ?? ''; + } + fputcsv($output, $row); +} + +fclose($output); +exit; +?> \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..4580a5c 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,74 @@ query("SELECT COUNT(*) FROM surveys"); +$total_records = $total_stmt->fetchColumn(); +$total_pages = ceil($total_records / $records_per_page); + +// Fetch surveys for the current page +$surveys_stmt = db()->prepare("SELECT * FROM surveys ORDER BY created_at DESC LIMIT :limit OFFSET :offset"); +$surveys_stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT); +$surveys_stmt->bindValue(':offset', $offset, PDO::PARAM_INT); +$surveys_stmt->execute(); +$surveys = $surveys_stmt->fetchAll(); + +$pageTitle = "Available Surveys"; +$description = "Choose a survey to provide your feedback."; + +require_once 'templates/header.php'; ?> - - - - - - New Style + +
+
+
+

Available Surveys

+

Please choose one of the surveys below to provide your feedback.

+
+
+ +
+
+
+ +

No surveys available at the moment.

+ + + +
+
+ +
+

+
+ + +
+ + + + +
+
+
+ - - - - - - - - - - - - - - - - - - - - -
-
-

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

-
-
- - - diff --git a/login.php b/login.php new file mode 100644 index 0000000..74fc091 --- /dev/null +++ b/login.php @@ -0,0 +1,85 @@ +prepare("SELECT * FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password'])) { + // Get user roles + $roles_stmt = $pdo->prepare("SELECT r.role_name FROM user_roles ur JOIN roles r ON ur.role_id = r.id WHERE ur.user_id = ?"); + $roles_stmt->execute([$user['id']]); + $roles = $roles_stmt->fetchAll(PDO::FETCH_COLUMN); + + // Store user info in session + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + $_SESSION['email'] = $user['email']; + $_SESSION['user_roles'] = $roles; + + // Redirect based on role + if (in_array('Admin', $roles)) { + header('Location: admin.php'); + } else { + header('Location: index.php'); + } + exit; + } else { + $error = 'Invalid username or password.'; + } + } +} +?> + +
+
+
+
+

Login

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

Don't have an account? Register here.

+
+
+
+
+ + diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..4e97304 --- /dev/null +++ b/logout.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/manage_questions.php b/manage_questions.php new file mode 100644 index 0000000..364470e --- /dev/null +++ b/manage_questions.php @@ -0,0 +1,136 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$survey_stmt->execute([$survey_id]); +$survey = $survey_stmt->fetch(); +if (!$survey) { + header('Location: surveys.php'); + exit; +} + +// Handle form submission for new question +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question_text'])) { + $question_text = trim($_POST['question_text']); + $question_type = trim($_POST['question_type']); + $options = ($question_type === 'multiple-choice') ? trim($_POST['options']) : null; + + if (!empty($question_text) && !empty($question_type)) { + $stmt = db()->prepare("INSERT INTO survey_questions (survey_id, question_text, question_type, options) VALUES (?, ?, ?, ?)"); + $stmt->execute([$survey_id, $question_text, $question_type, $options]); + header("Location: manage_questions.php?survey_id=" . $survey_id); + exit; + } +} + +// Handle question deletion +if (isset($_GET['delete_question'])) { + $question_id = $_GET['delete_question']; + $stmt = db()->prepare("DELETE FROM survey_questions WHERE id = ? AND survey_id = ?"); + $stmt->execute([$question_id, $survey_id]); + header("Location: manage_questions.php?survey_id=" . $survey_id); + exit; +} + +// Fetch all questions for the survey +$questions_stmt = db()->prepare("SELECT * FROM survey_questions WHERE survey_id = ? ORDER BY created_at ASC"); +$questions_stmt->execute([$survey_id]); +$questions = $questions_stmt->fetchAll(); + +$pageTitle = "Manage Questions for " . htmlspecialchars($survey['title']); +require_once 'templates/header.php'; +?> + +
+
+
+
+

Manage Questions for ""

+ Back to Surveys +
+ + +
+

Add New Question

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

Existing Questions

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Question TextTypeOptionsActions
No questions added to this survey yet.
+ Delete +
+
+
+
+
+ + + + diff --git a/privacy.php b/privacy.php new file mode 100644 index 0000000..24a30ca --- /dev/null +++ b/privacy.php @@ -0,0 +1,37 @@ + + + + + + + <?php echo htmlspecialchars($pageTitle); ?> - Your Real Feedback + + + + + +
+
+ +
+
+ +
+

Privacy Policy

+

This is a placeholder for your privacy policy. You should replace this with your own terms.

+

Information we collect and why we collect it...

+

How we use that information...

+

etc...

+
+ + + + + diff --git a/register.php b/register.php new file mode 100644 index 0000000..4719df0 --- /dev/null +++ b/register.php @@ -0,0 +1,89 @@ +prepare("SELECT * FROM users WHERE username = ? OR email = ?"); + $stmt->execute([$username, $email]); + if ($stmt->fetch()) { + $error = 'Username or email already exists.'; + } else { + // Insert new user + $hashed_password = password_hash($password, PASSWORD_DEFAULT); + $user_stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)"); + $user_stmt->execute([$username, $email, $hashed_password]); + $user_id = $pdo->lastInsertId(); + + // Assign default role (Respondent) + $role_stmt = $pdo->prepare("SELECT id FROM roles WHERE role_name = ?"); + $role_stmt->execute(['Respondent']); + $role_id = $role_stmt->fetchColumn(); + if ($role_id) { + $user_role_stmt = $pdo->prepare("INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)"); + $user_role_stmt->execute([$user_id, $role_id]); + } + + $success = 'Registration successful! You can now login.'; + } + } +} +?> + +
+
+
+
+

Register

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
+
+ + diff --git a/submit_feedback.php b/submit_feedback.php new file mode 100644 index 0000000..9f3146c --- /dev/null +++ b/submit_feedback.php @@ -0,0 +1,60 @@ + false, 'error' => 'Invalid request']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $name = trim($_POST['name'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $survey_id = trim($_POST['survey_id'] ?? ''); + $answers = $_POST['answers'] ?? []; + + if (empty($name) || empty($email) || empty($survey_id) || empty($answers)) { + $response['error'] = 'Please fill out all fields.'; + } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $response['error'] = 'Invalid email format.'; + } else { + $pdo = db(); + try { + $pdo->beginTransaction(); + + // Insert into feedback_submissions + $stmt = $pdo->prepare("INSERT INTO feedback_submissions (name, email, survey_id) VALUES (?, ?, ?)"); + $stmt->execute([$name, $email, $survey_id]); + $submission_id = $pdo->lastInsertId(); + + // Insert into survey_answers + $answer_stmt = $pdo->prepare("INSERT INTO survey_answers (submission_id, question_id, answer_text) VALUES (?, ?, ?)"); + foreach ($answers as $question_id => $answer_text) { + if (!empty($answer_text)) { + $answer_stmt->execute([$submission_id, $question_id, $answer_text]); + } + } + + $pdo->commit(); + $response['success'] = true; + unset($response['error']); + + // Send email notification + require_once __DIR__ . '/mail/MailService.php'; + $survey_stmt = $pdo->prepare("SELECT title FROM surveys WHERE id = ?"); + $survey_stmt->execute([$survey_id]); + $survey_title = $survey_stmt->fetchColumn(); + + $subject = "New Submission for Survey: " . $survey_title; + $submission_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . '/view_submission.php?id=' . $submission_id; + $htmlBody = "

A new submission has been received for the survey: {$survey_title}

\n

Submitter: {$name} ({$email})

\n

Click here to view the full submission.

"; + $textBody = "A new submission has been received for the survey: {$survey_title}. Submitter: {$name} ({$email}). View the submission here: {$submission_url}"; + + MailService::sendMail(null, $subject, $htmlBody, $textBody); + + } catch (PDOException $e) { + $pdo->rollBack(); + error_log($e->getMessage()); + $response['error'] = 'Database error. Could not submit feedback.'; + } + } +} + +echo json_encode($response); diff --git a/survey.php b/survey.php new file mode 100644 index 0000000..392a8c3 --- /dev/null +++ b/survey.php @@ -0,0 +1,100 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$survey_stmt->execute([$survey_id]); +$survey = $survey_stmt->fetch(); + +// If survey doesn't exist, show an error or redirect +if (!$survey) { + $pageTitle = "Survey Not Found"; + require_once 'templates/header.php'; + echo "

Survey not found.

"; + require_once 'templates/footer.php'; + exit; +} + +// Fetch questions for the survey +$questions_stmt = db()->prepare("SELECT * FROM survey_questions WHERE survey_id = ? ORDER BY created_at ASC"); +$questions_stmt->execute([$survey_id]); +$questions = $questions_stmt->fetchAll(); + +$pageTitle = htmlspecialchars($survey['title']); +$description = htmlspecialchars($survey['description']); + +require_once 'templates/header.php'; +?> + +
+
+
+

+

+
+
+ +
+
+ + +
+
+ + +
+ + +
+
+ + +
+ + +
+ + + + + + +
+ + + + +
+ + + + +
+ + + +
+
+
+
+
+ + diff --git a/surveys.php b/surveys.php new file mode 100644 index 0000000..910aa93 --- /dev/null +++ b/surveys.php @@ -0,0 +1,124 @@ +prepare("INSERT INTO surveys (title, description) VALUES (?, ?)"); + $stmt->execute([$title, $description]); + header("Location: surveys.php"); // Redirect to avoid form resubmission + exit; + } +} + +// Pagination +$page = isset($_GET['page']) ? (int)$_GET['page'] : 1; +$records_per_page = 10; +$offset = ($page - 1) * $records_per_page; + +// Get total number of surveys +$total_stmt = db()->query("SELECT COUNT(*) FROM surveys"); +$total_records = $total_stmt->fetchColumn(); +$total_pages = ceil($total_records / $records_per_page); + +// Fetch surveys for the current page +$surveys_stmt = db()->prepare("SELECT * FROM surveys ORDER BY created_at DESC LIMIT :limit OFFSET :offset"); +$surveys_stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT); +$surveys_stmt->bindValue(':offset', $offset, PDO::PARAM_INT); +$surveys_stmt->execute(); +$surveys = $surveys_stmt->fetchAll(); + +$pageTitle = "Manage Surveys"; +require_once 'templates/header.php'; +?> + +
+
+
+
+

Manage Surveys

+ Back to Submissions +
+ + +
+

Create New Survey

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

Existing Surveys

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
TitleDescriptionCreated AtActions
No surveys created yet.
+ Manage Questions + Edit +
+
+ + + + +
+
+
+ + diff --git a/templates/footer.php b/templates/footer.php new file mode 100644 index 0000000..f15c979 --- /dev/null +++ b/templates/footer.php @@ -0,0 +1,10 @@ + + + + + diff --git a/templates/header.php b/templates/header.php new file mode 100644 index 0000000..1632c9c --- /dev/null +++ b/templates/header.php @@ -0,0 +1,47 @@ + + + + + + + <?php echo htmlspecialchars($pageTitle); ?> + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
diff --git a/view_submission.php b/view_submission.php new file mode 100644 index 0000000..8112e6e --- /dev/null +++ b/view_submission.php @@ -0,0 +1,79 @@ +prepare("SELECT s.name, s.email, s.created_at, sv.title as survey_title FROM feedback_submissions s JOIN surveys sv ON s.survey_id = sv.id WHERE s.id = ?"); +$submission_stmt->execute([$submission_id]); +$submission = $submission_stmt->fetch(); +if (!$submission) { + header('Location: admin.php'); + exit; +} + +// Fetch answers for the submission +$answers_stmt = db()->prepare("SELECT q.question_text, a.answer_text FROM survey_answers a JOIN survey_questions q ON a.question_id = q.id WHERE a.submission_id = ? ORDER BY q.created_at ASC"); +$answers_stmt->execute([$submission_id]); +$answers = $answers_stmt->fetchAll(); + +$pageTitle = "View Submission"; +require_once 'templates/header.php'; +?> + +
+
+
+
+

Submission Details

+ Back to All Submissions +
+ +
+
+

Survey:

+
+
+

Submitter:

+

Email:

+

Submitted At:

+
+
+ +
+
+

Answers

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