This commit is contained in:
Flatlogic Bot 2025-10-07 16:17:37 +00:00
parent 7b5bbd6d4d
commit d47e350516
30 changed files with 1591 additions and 153 deletions

99
admin.php Normal file
View File

@ -0,0 +1,99 @@
<?php
session_start();
// Check if user is logged in and is an Admin
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
// If logged in, show the admin content
require_once 'db/config.php';
// Pagination
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$records_per_page = 10;
$offset = ($page - 1) * $records_per_page;
// Get total number of submissions
$total_stmt = db()->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';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Feedback Submissions</h1>
<div>
<a href="dashboard.php" class="btn btn-info">Dashboard</a>
<a href="surveys.php" class="btn btn-success">Manage Surveys</a>
<a href="export.php" class="btn btn-primary">Export to CSV</a>
<a href="?logout=true" class="btn btn-secondary">Logout</a>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>Survey</th>
<th>Submitter</th>
<th>Email</th>
<th>Submitted At</th>
<th>Answers</th>
</tr>
</thead>
<tbody>
<?php if (empty($submissions)): ?>
<tr>
<td colspan="5">No feedback submissions yet.</td>
</tr>
<?php else: ?>
<?php foreach ($submissions as $submission): ?>
<tr>
<td><?= htmlspecialchars($submission['survey_title']) ?></td>
<td><?= htmlspecialchars($submission['name']) ?></td>
<td><?= htmlspecialchars($submission['email']) ?></td>
<td><?= $submission['created_at'] ?></td>
<td>
<a href="view_submission.php?id=<?= $submission['id'] ?>" class="btn btn-sm btn-info">View Answers</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination Links -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<?php if ($page > 1): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page - 1 ?>">Previous</a></li>
<?php endif; ?>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?= ($i == $page) ? 'active' : '' ?>"><a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a></li>
<?php endfor; ?>
<?php if ($page < $total_pages): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page + 1 ?>">Next</a></li>
<?php endif; ?>
</ul>
</nav>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

63
api.php Normal file
View File

@ -0,0 +1,63 @@
<?php
header('Content-Type: application/json');
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
echo json_encode(['error' => '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;
}

174
assets/css/custom.css Normal file
View File

@ -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;
}

78
assets/js/dashboard.js Normal file
View File

@ -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 = `<h3>${question.question_text}</h3>`;
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
}]
}
});
}
});
});
});
});

50
assets/js/main.js Normal file
View File

@ -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());
}
});

47
dashboard.php Normal file
View File

@ -0,0 +1,47 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
$pageTitle = "Analytics Dashboard";
require_once 'templates/header.php';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Analytics Dashboard</h1>
<a href="admin.php" class="btn btn-secondary">Back to Submissions</a>
</div>
<div class="row">
<div class="col-md-12">
<div class="card mb-4">
<div class="card-header">
<h3>Submissions per Survey</h3>
</div>
<div class="card-body">
<canvas id="submissions-chart"></canvas>
</div>
</div>
</div>
</div>
<div id="survey-charts-container"></div>
</div>
</section>
</main>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="assets/js/dashboard.js?v=<?php echo time(); ?>"></script>
<?php
require_once 'templates/footer.php';
?>

View File

@ -8,10 +8,50 @@ define('DB_PASS', 'e45f2778-db1f-450c-99c6-29efb4601472');
function db() { function db() {
static $pdo; static $pdo;
if (!$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 = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
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,
]); ]);
} 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; 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();

View File

@ -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
);

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1 @@
ALTER TABLE `feedback_submissions` ADD COLUMN `survey_id` INT NULL AFTER `id`;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1 @@
INSERT INTO `roles` (`role_name`) VALUES ('Admin'), ('Respondent');

72
edit_survey.php Normal file
View File

@ -0,0 +1,72 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
// Check for Survey ID
if (!isset($_GET['id'])) {
header('Location: surveys.php');
exit;
}
$survey_id = $_GET['id'];
// Fetch survey details
$survey_stmt = db()->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';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Edit Survey</h1>
<a href="surveys.php" class="btn btn-secondary">Back to Surveys</a>
</div>
<!-- Edit Survey Form -->
<div class="form-container">
<form method="POST">
<div class="form-group">
<label for="title" class="form-label">Survey Title</label>
<input type="text" id="title" name="title" class="form-control" value="<?= htmlspecialchars($survey['title']) ?>" required>
</div>
<div class="form-group">
<label for="description" class="form-label">Description (Optional)</label>
<textarea id="description" name="description" rows="3" class="form-control"><?= htmlspecialchars($survey['description']) ?></textarea>
</div>
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

66
export.php Normal file
View File

@ -0,0 +1,66 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
// Fetch all submissions with their answers
$stmt = db()->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;
?>

210
index.php
View File

@ -1,150 +1,74 @@
<?php <?php
declare(strict_types=1); require_once __DIR__ . '/db/config.php';
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; // Pagination
$now = date('Y-m-d H:i:s'); $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 = "Available Surveys";
$description = "Choose a survey to provide your feedback.";
require_once 'templates/header.php';
?> ?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<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>
<body>
<main> <main>
<div class="card"> <section class="hero">
<h1>Analyzing your requirements and generating your website…</h1> <div class="container">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <h1>Available Surveys</h1>
<span class="sr-only">Loading…</span> <p>Please choose one of the surveys below to provide your feedback.</p>
</div> </div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> </section>
<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> <section class="survey-section">
<div class="container">
<div class="list-group">
<?php if (empty($surveys)): ?>
<p class="list-group-item">No surveys available at the moment.</p>
<?php else: ?>
<?php foreach ($surveys as $survey): ?>
<a href="survey.php?id=<?= $survey['id'] ?>" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1"><?= htmlspecialchars($survey['title']) ?></h5>
<small><?= date('M j, Y', strtotime($survey['created_at'])) ?></small>
</div> </div>
<p class="mb-1"><?= htmlspecialchars($survey['description']) ?></p>
</a>
<?php endforeach; ?>
<?php endif; ?>
</div>
<!-- Pagination Links -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center mt-4">
<?php if ($page > 1): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page - 1 ?>">Previous</a></li>
<?php endif; ?>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?= ($i == $page) ? 'active' : '' ?>"><a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a></li>
<?php endfor; ?>
<?php if ($page < $total_pages): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page + 1 ?>">Next</a></li>
<?php endif; ?>
</ul>
</nav>
</div>
</section>
</main> </main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <?php
</footer> require_once 'templates/footer.php';
</body> ?>
</html>

85
login.php Normal file
View File

@ -0,0 +1,85 @@
<?php
session_start();
require_once 'db/config.php';
// If already logged in, redirect to the appropriate page
if (isset($_SESSION['user_id'])) {
if (in_array('Admin', $_SESSION['user_roles'])) {
header('Location: admin.php');
} else {
header('Location: index.php');
}
exit;
}
$pageTitle = "Login";
require_once 'templates/header.php';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
if (empty($username) || empty($password)) {
$error = 'Please fill out all fields.';
} else {
$pdo = db();
$stmt = $pdo->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.';
}
}
}
?>
<main>
<section class="survey-section">
<div class="container">
<div class="form-container">
<h1>Login</h1>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<form method="POST">
<div class="form-group">
<label for="username" class="form-label">Username</label>
<input type="text" id="username" name="username" class="form-control" required>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
<p class="mt-3">Don't have an account? <a href="register.php">Register here</a>.</p>
</div>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

7
logout.php Normal file
View File

@ -0,0 +1,7 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;
?>

136
manage_questions.php Normal file
View File

@ -0,0 +1,136 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
// Check for Survey ID
if (!isset($_GET['survey_id'])) {
header('Location: surveys.php');
exit;
}
$survey_id = $_GET['survey_id'];
// Fetch survey details
$survey_stmt = db()->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';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Manage Questions for "<?= htmlspecialchars($survey['title']) ?>"</h1>
<a href="surveys.php" class="btn btn-secondary">Back to Surveys</a>
</div>
<!-- Add New Question Form -->
<div class="form-container mb-5">
<h2>Add New Question</h2>
<form method="POST">
<div class="form-group">
<label for="question_text" class="form-label">Question Text</label>
<input type="text" id="question_text" name="question_text" class="form-control" required>
</div>
<div class="form-group">
<label for="question_type" class="form-label">Question Type</label>
<select id="question_type" name="question_type" class="form-control" required onchange="toggleOptions(this.value)">
<option value="text">Text (Single Line)</option>
<option value="textarea">Textarea (Multi-line)</option>
<option value="rating">Rating (1-5)</option>
<option value="multiple-choice">Multiple Choice</option>
</select>
</div>
<div class="form-group" id="options-container" style="display: none;">
<label for="options" class="form-label">Options (comma-separated)</label>
<input type="text" id="options" name="options" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Add Question</button>
</form>
</div>
<!-- List of Existing Questions -->
<h2>Existing Questions</h2>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>Question Text</th>
<th>Type</th>
<th>Options</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($questions)): ?>
<tr>
<td colspan="4">No questions added to this survey yet.</td>
</tr>
<?php else: ?>
<?php foreach ($questions as $question): ?>
<tr>
<td><?= htmlspecialchars($question['question_text']) ?></td>
<td><?= htmlspecialchars($question['question_type']) ?></td>
<td><?= htmlspecialchars($question['options'] ?? '') ?></td>
<td>
<a href="?survey_id=<?= $survey_id ?>&delete_question=<?= $question['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to delete this question?')">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</section>
</main>
<script>
function toggleOptions(type) {
document.getElementById('options-container').style.display = (type === 'multiple-choice') ? 'block' : 'none';
}
</script>
<?php
require_once 'templates/footer.php';
?>

37
privacy.php Normal file
View File

@ -0,0 +1,37 @@
<?php
$pageTitle = "Privacy Policy";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?> - Your Real Feedback</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<header class="header">
<div class="container">
<a href="/" class="logo">Your Real Feedback</a>
</div>
</header>
<main class="container py-5">
<h1>Privacy Policy</h1>
<p>This is a placeholder for your privacy policy. You should replace this with your own terms.</p>
<p>Information we collect and why we collect it...</p>
<p>How we use that information...</p>
<p>etc...</p>
</main>
<footer class="footer">
<div class="container">
<p>&copy; <?php echo date('Y'); ?> Your Real Feedback. All Rights Reserved.</p>
<p><a href="/privacy.php">Privacy Policy</a></p>
</div>
</footer>
</body>
</html>

89
register.php Normal file
View File

@ -0,0 +1,89 @@
<?php
require_once 'db/config.php';
$pageTitle = "Register";
require_once 'templates/header.php';
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
if (empty($username) || empty($email) || empty($password)) {
$error = 'Please fill out all fields.';
} elseif ($password !== $password_confirm) {
$error = 'Passwords do not match.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = 'Invalid email format.';
} else {
$pdo = db();
// Check if username or email already exists
$stmt = $pdo->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 <a href="login.php">login</a>.';
}
}
}
?>
<main>
<section class="survey-section">
<div class="container">
<div class="form-container">
<h1>Register</h1>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?= $success ?></div>
<?php else: ?>
<form method="POST">
<div class="form-group">
<label for="username" class="form-label">Username</label>
<input type="text" id="username" name="username" class="form-control" required>
</div>
<div class="form-group">
<label for="email" class="form-label">Email Address</label>
<input type="email" id="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label for="password_confirm" class="form-label">Confirm Password</label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
<?php endif; ?>
</div>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

60
submit_feedback.php Normal file
View File

@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/db/config.php';
$response = ['success' => 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 = "<p>A new submission has been received for the survey: <strong>{$survey_title}</strong></p>\n <p><strong>Submitter:</strong> {$name} ({$email})</p>\n <p><a href=\"{$submission_url}\">Click here to view the full submission.</a></p>";
$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);

100
survey.php Normal file
View File

@ -0,0 +1,100 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
// Get survey ID from the URL
if (!isset($_GET['id'])) {
header("Location: index.php");
exit;
}
$survey_id = $_GET['id'];
// Fetch survey details
$survey_stmt = db()->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 "<main><section class='survey-section'><div class='container'><h2>Survey not found.</h2></div></section></main>";
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';
?>
<main>
<section class="hero">
<div class="container">
<h1><?= htmlspecialchars($survey['title']) ?></h1>
<p><?= htmlspecialchars($survey['description']) ?></p>
</div>
</section>
<section class="survey-section">
<div class="container">
<div id="success-message" class="hidden">
<h2>Thank you!</h2>
<p>Your feedback has been submitted successfully. We appreciate you taking the time to help us improve.</p>
</div>
<div class="form-container">
<form id="survey-form">
<input type="hidden" name="survey_id" value="<?= $survey_id ?>">
<div class="form-group">
<label for="name" class="form-label">Full Name</label>
<input type="text" id="name" name="name" class="form-control" value="<?= isset($_SESSION['username']) ? htmlspecialchars($_SESSION['username']) : '' ?>" required>
</div>
<div class="form-group">
<label for="email" class="form-label">Email Address</label>
<input type="email" id="email" name="email" class="form-control" value="<?= isset($_SESSION['email']) ? htmlspecialchars($_SESSION['email']) : '' ?>" required>
</div>
<?php foreach ($questions as $question): ?>
<div class="form-group">
<label class="form-label"><?= htmlspecialchars($question['question_text']) ?></label>
<?php if ($question['question_type'] === 'text'): ?>
<input type="text" name="answers[<?= $question['id'] ?>]" class="form-control" required>
<?php elseif ($question['question_type'] === 'textarea'): ?>
<textarea name="answers[<?= $question['id'] ?>]" rows="5" class="form-control" required></textarea>
<?php elseif ($question['question_type'] === 'rating'): ?>
<div class="rating">
<?php for ($i = 1; $i <= 5; $i++): ?>
<input type="radio" id="rating-<?= $question['id'] ?>-<?= $i ?>" name="answers[<?= $question['id'] ?>]" value="<?= $i ?>" required>
<label for="rating-<?= $question['id'] ?>-<?= $i ?>"><?= $i ?></label>
<?php endfor; ?>
</div>
<?php elseif ($question['question_type'] === 'multiple-choice'): ?>
<?php $options = explode(',', $question['options']); ?>
<select name="answers[<?= $question['id'] ?>]" class="form-control" required>
<option value="">Select an option</option>
<?php foreach ($options as $option): ?>
<option value="<?= htmlspecialchars(trim($option)) ?>"><?= htmlspecialchars(trim($option)) ?></option>
<?php endforeach; ?>
</select>
<?php endif; ?>
</div>
<?php endforeach; ?>
<button type="submit" class="btn btn-primary">Submit Feedback</button>
</form>
</div>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

124
surveys.php Normal file
View File

@ -0,0 +1,124 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['user_id']) || !in_array('Admin', $_SESSION['user_roles'])) {
header('Location: login.php');
exit;
}
// Handle form submission for new survey
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['title'])) {
$title = trim($_POST['title']);
$description = trim($_POST['description']);
if (!empty($title)) {
$stmt = db()->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';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Manage Surveys</h1>
<a href="admin.php" class="btn btn-secondary">Back to Submissions</a>
</div>
<!-- Create New Survey Form -->
<div class="form-container mb-5">
<h2>Create New Survey</h2>
<form method="POST">
<div class="form-group">
<label for="title" class="form-label">Survey Title</label>
<input type="text" id="title" name="title" class="form-control" required>
</div>
<div class="form-group">
<label for="description" class="form-label">Description (Optional)</label>
<textarea id="description" name="description" rows="3" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Create Survey</button>
</form>
</div>
<!-- List of Existing Surveys -->
<h2>Existing Surveys</h2>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>Title</th>
<th>Description</th>
<th>Created At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($surveys)): ?>
<tr>
<td colspan="4">No surveys created yet.</td>
</tr>
<?php else: ?>
<?php foreach ($surveys as $survey): ?>
<tr>
<td><?= htmlspecialchars($survey['title']) ?></td>
<td><?= htmlspecialchars($survey['description']) ?></td>
<td><?= $survey['created_at'] ?></td>
<td>
<a href="manage_questions.php?survey_id=<?= $survey['id'] ?>" class="btn btn-sm btn-primary">Manage Questions</a>
<a href="edit_survey.php?id=<?= $survey['id'] ?>" class="btn btn-sm btn-secondary">Edit</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Pagination Links -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<?php if ($page > 1): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page - 1 ?>">Previous</a></li>
<?php endif; ?>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?= ($i == $page) ? 'active' : '' ?>"><a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a></li>
<?php endfor; ?>
<?php if ($page < $total_pages): ?>
<li class="page-item"><a class="page-link" href="?page=<?= $page + 1 ?>">Next</a></li>
<?php endif; ?>
</ul>
</nav>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>

10
templates/footer.php Normal file
View File

@ -0,0 +1,10 @@
<footer class="footer">
<div class="container">
<p>&copy; <?php echo date('Y'); ?> Your Real Feedback. All Rights Reserved.</p>
<p><a href="/privacy.php">Privacy Policy</a></p>
</div>
</footer>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

47
templates/header.php Normal file
View File

@ -0,0 +1,47 @@
<?php
$pageTitle = isset($pageTitle) ? $pageTitle : "Your Real Feedback";
$description = isset($description) ? $description : "Provide your feedback to help us improve.";
$keywords = isset($keywords) ? $keywords : "feedback, survey, customer feedback, product feedback, flatlogic, user survey, feedback app";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($description); ?>">
<meta name="keywords" content="<?php echo htmlspecialchars($keywords); ?>">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
<meta property="og:description" content="<?php echo htmlspecialchars($description); ?>">
<meta property="og:image" content="<?php echo isset($_SERVER['PROJECT_IMAGE_URL']) ? htmlspecialchars($_SERVER['PROJECT_IMAGE_URL']) : ''; ?>">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:title" content="<?php echo htmlspecialchars($pageTitle); ?>">
<meta property="twitter:description" content="<?php echo htmlspecialchars($description); ?>">
<meta property="twitter:image" content="<?php echo isset($_SERVER['PROJECT_IMAGE_URL']) ? htmlspecialchars($_SERVER['PROJECT_IMAGE_URL']) : ''; ?>">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<header class="header">
<div class="container d-flex justify-content-between align-items-center">
<a href="/" class="logo">Your Real Feedback</a>
<nav>
<?php if (isset($_SESSION['user_id'])): ?>
<?php if (in_array('Admin', $_SESSION['user_roles'])):
<a href="admin.php" class="btn btn-sm btn-outline-secondary">Admin</a>
<?php endif; ?>
<a href="logout.php" class="btn btn-sm btn-outline-secondary">Logout</a>
<?php else: ?>
<a href="login.php" class="btn btn-sm btn-outline-secondary">Login</a>
<a href="register.php" class="btn btn-sm btn-primary">Register</a>
<?php endif; ?>
</nav>
</div>
</header>

79
view_submission.php Normal file
View File

@ -0,0 +1,79 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure admin is logged in
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
header('Location: admin.php');
exit;
}
// Check for Submission ID
if (!isset($_GET['id'])) {
header('Location: admin.php');
exit;
}
$submission_id = $_GET['id'];
// Fetch submission details
$submission_stmt = db()->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';
?>
<main>
<section class="survey-section">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Submission Details</h1>
<a href="admin.php" class="btn btn-secondary">Back to All Submissions</a>
</div>
<div class="card">
<div class="card-header">
<h3>Survey: <?= htmlspecialchars($submission['survey_title']) ?></h3>
</div>
<div class="card-body">
<p><strong>Submitter:</strong> <?= htmlspecialchars($submission['name']) ?></p>
<p><strong>Email:</strong> <?= htmlspecialchars($submission['email']) ?></p>
<p><strong>Submitted At:</strong> <?= $submission['created_at'] ?></p>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
<h3>Answers</h3>
</div>
<div class="card-body">
<table class="table table-striped">
<tbody>
<?php foreach ($answers as $answer): ?>
<tr>
<th style="width: 30%;"><?= htmlspecialchars($answer['question_text']) ?></th>
<td><?= nl2br(htmlspecialchars($answer['answer_text'])) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</section>
</main>
<?php
require_once 'templates/footer.php';
?>