surveymonkey software
This commit is contained in:
parent
c004a42556
commit
fd6d509567
190
admin/analytics.php
Normal file
190
admin/analytics.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
// Fetch all survey responses
|
||||
$stmt = $pdo->query("SELECT * FROM survey_responses ORDER BY created_at DESC");
|
||||
$responses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Calculate basic statistics
|
||||
$total_responses = count($responses);
|
||||
$satisfaction_counts = [
|
||||
'very_satisfied' => 0,
|
||||
'satisfied' => 0,
|
||||
'neutral' => 0,
|
||||
'unsatisfied' => 0,
|
||||
'very_unsatisfied' => 0,
|
||||
];
|
||||
|
||||
$how_heard_counts = [
|
||||
'social_media' => 0,
|
||||
'friend' => 0,
|
||||
'search_engine' => 0,
|
||||
'advertisement' => 0,
|
||||
'other' => 0,
|
||||
];
|
||||
|
||||
foreach ($responses as $response) {
|
||||
if (isset($satisfaction_counts[$response['satisfaction']])) {
|
||||
$satisfaction_counts[$response['satisfaction']]++;
|
||||
}
|
||||
if (isset($how_heard_counts[$response['how_heard']])) {
|
||||
$how_heard_counts[$response['how_heard']]++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$error = "Database error: " . $e->getMessage();
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="d-sm-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="text-dark mb-0">Analytics & Reporting</h3>
|
||||
</div>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php else: ?>
|
||||
<div class="row">
|
||||
<!-- Total Responses Card -->
|
||||
<div class="col-md-6 col-xl-3 mb-4">
|
||||
<div class="card shadow border-start-primary py-2">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center no-gutters">
|
||||
<div class="col me-2">
|
||||
<div class="text-uppercase text-primary fw-bold text-xs mb-1"><span>Total Responses</span></div>
|
||||
<div class="text-dark fw-bold h5 mb-0"><span><?php echo $total_responses; ?></span></div>
|
||||
</div>
|
||||
<div class="col-auto"><i class="fas fa-comments fa-2x text-gray-300"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Satisfaction Distribution Chart -->
|
||||
<div class="row">
|
||||
<div class="col-lg-7 col-xl-8">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="text-primary fw-bold m-0">Satisfaction Distribution</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container" style="position: relative; height:40vh; width:80vw">
|
||||
<canvas id="satisfactionChart"></canvas>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const ctx = document.getElementById('satisfactionChart').getContext('2d');
|
||||
const satisfactionData = <?php echo json_encode(array_values($satisfaction_counts)); ?>;
|
||||
const satisfactionLabels = <?php echo json_encode(array_map(function($label) {
|
||||
return ucwords(str_replace('_', ' ', $label));
|
||||
}, array_keys($satisfaction_counts))); ?>;
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: satisfactionLabels,
|
||||
datasets: [{
|
||||
label: 'Number of Responses',
|
||||
data: satisfactionData,
|
||||
backgroundColor: [
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(255, 159, 64, 0.2)',
|
||||
'rgba(255, 99, 132, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(255, 159, 64, 1)',
|
||||
'rgba(255, 99, 132, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 col-xl-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h6 class="text-primary fw-bold m-0">Referral Sources</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container" style="position: relative; height:40vh; width:80vw">
|
||||
<canvas id="howHeardChart"></canvas>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const ctx = document.getElementById('howHeardChart').getContext('2d');
|
||||
const howHeardData = <?php echo json_encode(array_values($how_heard_counts)); ?>;
|
||||
const howHeardLabels = <?php echo json_encode(array_map(function($label) {
|
||||
return ucwords(str_replace('_', ' ', $label));
|
||||
}, array_keys($how_heard_counts))); ?>;
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: howHeardLabels,
|
||||
datasets: [{
|
||||
label: 'Number of Responses',
|
||||
data: howHeardData,
|
||||
backgroundColor: [
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(255, 159, 64, 0.2)',
|
||||
'rgba(255, 99, 132, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(255, 159, 64, 1)',
|
||||
'rgba(255, 99, 132, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/partials/footer.php'; ?>
|
||||
94
admin/edit_page.php
Normal file
94
admin/edit_page.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
$page_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$page_id) {
|
||||
header('Location: pages.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Handle form submission for updating the page
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_page'])) {
|
||||
$title = trim($_POST['title']);
|
||||
$slug = trim($_POST['slug']);
|
||||
$content = trim($_POST['content']);
|
||||
$is_published = isset($_POST['is_published']) ? 1 : 0;
|
||||
$menu_order = (int)($_POST['menu_order'] ?? 0);
|
||||
|
||||
if (empty($title) || empty($slug)) {
|
||||
$error = "Title and slug are required.";
|
||||
} else {
|
||||
// Check if slug already exists for another page
|
||||
$stmt = db()->prepare("SELECT id FROM pages WHERE slug = ? AND id != ?");
|
||||
$stmt->execute([$slug, $page_id]);
|
||||
if ($stmt->fetch()) {
|
||||
$error = "Slug already exists. Please choose a unique one.";
|
||||
} else {
|
||||
$stmt = db()->prepare("UPDATE pages SET title = ?, slug = ?, content = ?, is_published = ?, menu_order = ? WHERE id = ?");
|
||||
if ($stmt->execute([$title, $slug, $content, $is_published, $menu_order, $page_id])) {
|
||||
$success = "Page updated successfully!";
|
||||
} else {
|
||||
$error = "Failed to update page.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the page data
|
||||
$stmt = db()->prepare("SELECT * FROM pages WHERE id = ?");
|
||||
$stmt->execute([$page_id]);
|
||||
$page = $stmt->fetch();
|
||||
|
||||
if (!$page) {
|
||||
header('Location: pages.php');
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Edit Page</h1>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($success); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Editing '<?php echo htmlspecialchars($page['title']); ?>'</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="edit_page.php?id=<?php echo $page['id']; ?>" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" class="form-control" value="<?php echo htmlspecialchars($page['title']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="slug">Slug</label>
|
||||
<input type="text" id="slug" name="slug" class="form-control" value="<?php echo htmlspecialchars($page['slug']); ?>" required>
|
||||
<small>The slug is the URL-friendly version of the name. E.g., "about-us"</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="content">Content</label>
|
||||
<textarea id="content" name="content" class="form-control" rows="10"><?php echo htmlspecialchars($page['content']); ?></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="menu_order">Menu Order</label>
|
||||
<input type="number" id="menu_order" name="menu_order" class="form-control" value="<?php echo htmlspecialchars($page['menu_order']); ?>">
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="is_published" name="is_published" class="form-check-input" value="1" <?php echo $page['is_published'] ? 'checked' : ''; ?>>
|
||||
<label for="is_published" class="form-check-label">Publish?</label>
|
||||
</div>
|
||||
<button type="submit" name="update_page" class="btn btn-primary mt-3">Update Page</button>
|
||||
<a href="pages.php" class="btn btn-secondary mt-3">Back to Pages</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/partials/footer.php'; ?>
|
||||
92
admin/edit_user.php
Normal file
92
admin/edit_user.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
$user_id = $_GET['id'] ?? null;
|
||||
if (!$user_id) {
|
||||
header('Location: users.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle form submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username']);
|
||||
$role = $_POST['role'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
try {
|
||||
if (!empty($password)) {
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare('UPDATE users SET username = ?, role = ?, password = ? WHERE id = ?');
|
||||
$stmt->execute([$username, $role, $hashed_password, $user_id]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare('UPDATE users SET username = ?, role = ? WHERE id = ?');
|
||||
$stmt->execute([$username, $role, $user_id]);
|
||||
}
|
||||
header('Location: users.php?updated=true');
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
$error_message = "Error updating user: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch user data
|
||||
try {
|
||||
$stmt = $pdo->prepare('SELECT username, role FROM users WHERE id = ?');
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$user) {
|
||||
header('Location: users.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
die("Error fetching user data: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$page_title = "Edit User";
|
||||
include 'partials/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h3 class="text-dark mb-4">Edit User</h3>
|
||||
|
||||
<?php if (!empty($error_message)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error_message; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<p class="text-primary m-0 fw-bold">Edit User Details</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="edit_user.php?id=<?php echo $user_id; ?>" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($user['username']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">New Password (leave blank to keep current password)</label>
|
||||
<input type="password" class="form-control" id="password" name="password">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Role</label>
|
||||
<select class="form-select" id="role" name="role">
|
||||
<option value="admin" <?php echo ($user['role'] === 'admin') ? 'selected' : ''; ?>>Admin</option>
|
||||
<option value="editor" <?php echo ($user['role'] === 'editor') ? 'selected' : ''; ?>>Editor</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
<a href="users.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'partials/footer.php'; ?>
|
||||
35
admin/export.php
Normal file
35
admin/export.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT id, name, email, satisfaction, message, created_at FROM survey_responses ORDER BY created_at DESC");
|
||||
$responses = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="survey_responses.csv"');
|
||||
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
// Add header row
|
||||
fputcsv($output, array('ID', 'Name', 'Email', 'Satisfaction', 'Message', 'Submitted At'));
|
||||
|
||||
// Add data rows
|
||||
foreach ($responses as $response) {
|
||||
fputcsv($output, $response);
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
exit;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, you'd log this error.
|
||||
die("Error: Could not export data.");
|
||||
}
|
||||
?>
|
||||
88
admin/index.php
Normal file
88
admin/index.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Get total users
|
||||
$stmt_users = $pdo->query("SELECT COUNT(*) FROM users");
|
||||
$total_users = $stmt_users->fetchColumn();
|
||||
|
||||
// Get total pages
|
||||
$stmt_pages = $pdo->query("SELECT COUNT(*) FROM pages");
|
||||
$total_pages = $stmt_pages->fetchColumn();
|
||||
|
||||
// Get recent pages
|
||||
$stmt_recent_pages = $pdo->query("SELECT * FROM pages ORDER BY created_at DESC LIMIT 5");
|
||||
$recent_pages = $stmt_recent_pages->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="mt-4">Admin Dashboard</h1>
|
||||
<p>Welcome to the admin dashboard. Here is a summary of your application.</p>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card text-white bg-primary">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total Users</h5>
|
||||
<p class="card-text fs-2"><?php echo $total_users; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card text-white bg-success">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Total Pages</h5>
|
||||
<p class="card-text fs-2"><?php echo $total_pages; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<a href="users.php" class="btn btn-secondary">Manage Users</a>
|
||||
<a href="pages.php" class="btn btn-secondary">Manage Pages</a>
|
||||
<a href="settings.php" class="btn btn-secondary">Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Recent Pages
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (empty($recent_pages)): ?>
|
||||
<p>No pages created yet.</p>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Slug</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($recent_pages as $page): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($page['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($page['title']); ?></td>
|
||||
<td><?php echo htmlspecialchars($page['slug']); ?></td>
|
||||
<td><?php echo htmlspecialchars($page['created_at']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/partials/footer.php';
|
||||
?>
|
||||
52
admin/login.php
Normal file
52
admin/login.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
$error_message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$error_message = 'Please enter both username and password.';
|
||||
} else {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
} else {
|
||||
$error_message = 'Invalid username or password.';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="login-container">
|
||||
<form method="POST" action="login.php">
|
||||
<h2 style="text-align: center; margin-bottom: 2rem;">Admin Login</h2>
|
||||
<?php if ($error_message): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/partials/footer.php';
|
||||
?>
|
||||
6
admin/logout.php
Normal file
6
admin/logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
131
admin/pages.php
Normal file
131
admin/pages.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
// Handle form submission for creating a new page
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['create_page'])) {
|
||||
$title = trim($_POST['title']);
|
||||
$slug = trim($_POST['slug']);
|
||||
$content = trim($_POST['content']);
|
||||
$is_published = isset($_POST['is_published']) ? 1 : 0;
|
||||
$menu_order = (int)($_POST['menu_order'] ?? 0);
|
||||
|
||||
|
||||
if (empty($title) || empty($slug)) {
|
||||
$error = "Title and slug are required.";
|
||||
} else {
|
||||
// Check if slug already exists
|
||||
$stmt = db()->prepare("SELECT id FROM pages WHERE slug = ?");
|
||||
$stmt->execute([$slug]);
|
||||
if ($stmt->fetch()) {
|
||||
$error = "Slug already exists. Please choose a unique one.";
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO pages (title, slug, content, is_published, menu_order) VALUES (?, ?, ?, ?, ?)");
|
||||
if ($stmt->execute([$title, $slug, $content, $is_published, $menu_order])) {
|
||||
$success = "Page created successfully!";
|
||||
} else {
|
||||
$error = "Failed to create page.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle page deletion
|
||||
if (isset($_GET['delete'])) {
|
||||
$id = $_GET['delete'];
|
||||
$stmt = db()->prepare("DELETE FROM pages WHERE id = ?");
|
||||
if ($stmt->execute([$id])) {
|
||||
$success = "Page deleted successfully!";
|
||||
} else {
|
||||
$error = "Failed to delete page.";
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all pages
|
||||
$stmt = db()->query("SELECT * FROM pages ORDER BY menu_order ASC");
|
||||
$pages = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1>Manage Pages</h1>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($success); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Create New Page</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="pages.php" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="slug">Slug</label>
|
||||
<input type="text" id="slug" name="slug" class="form-control" required>
|
||||
<small>The slug is the URL-friendly version of the name. E.g., "about-us"</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="content">Content</label>
|
||||
<textarea id="content" name="content" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="menu_order">Menu Order</label>
|
||||
<input type="number" id="menu_order" name="menu_order" class="form-control" value="0">
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="is_published" name="is_published" class="form-check-input" value="1">
|
||||
<label for="is_published" class="form-check-label">Publish?</label>
|
||||
</div>
|
||||
<button type="submit" name="create_page" class="btn btn-primary mt-3">Create Page</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h2>Existing Pages</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order</th>
|
||||
<th>Title</th>
|
||||
<th>Slug</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($pages)): ?>
|
||||
<tr>
|
||||
<td colspan="5">No pages found.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($pages as $page): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($page['menu_order']); ?></td>
|
||||
<td><?php echo htmlspecialchars($page['title']); ?></td>
|
||||
<td>/<?php echo htmlspecialchars($page['slug']); ?></td>
|
||||
<td><?php echo $page['is_published'] ? '<span class="badge badge-success">Published</span>' : '<span class="badge badge-secondary">Draft</span>'; ?></td>
|
||||
<td>
|
||||
<a href="edit_page.php?id=<?php echo $page['id']; ?>" class="btn btn-sm btn-secondary">Edit</a>
|
||||
<a href="pages.php?delete=<?php echo $page['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to delete this page?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/partials/footer.php'; ?>
|
||||
7
admin/partials/footer.php
Normal file
7
admin/partials/footer.php
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
</div> <!-- .main-content -->
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
46
admin/partials/header.php
Normal file
46
admin/partials/header.php
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Protect admin pages
|
||||
if (basename($_SERVER['PHP_SELF']) != 'login.php') {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
$current_page = basename($_SERVER['PHP_SELF']);
|
||||
|
||||
// Fetch site title from settings
|
||||
require_once __DIR__ . '/../../db/config.php';
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT setting_value FROM `settings` WHERE setting_key = 'site_title'");
|
||||
$site_title = $stmt->fetchColumn();
|
||||
if (!$site_title) {
|
||||
$site_title = 'Admin Panel';
|
||||
}
|
||||
|
||||
?>
|
||||
<!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($site_title); ?></title>
|
||||
<link rel="stylesheet" href="style.css?v=<?php echo time(); ?>">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<div class="sidebar">
|
||||
<a href="index.php" class="<?php echo ($current_page == 'index.php') ? 'active' : ''; ?>">Dashboard</a>
|
||||
<a href="users.php" class="<?php echo ($current_page == 'users.php') ? 'active' : ''; ?>">User Management</a>
|
||||
<a href="analytics.php" class="<?php echo ($current_page == 'analytics.php') ? 'active' : ''; ?>">Analytics</a>
|
||||
<a href="pages.php" class="<?php echo ($current_page == 'pages.php' || $current_page == 'edit_page.php') ? 'active' : ''; ?>">Pages</a>
|
||||
<a href="settings.php" class="<?php echo ($current_page == 'settings.php') ? 'active' : ''; ?>">Settings</a>
|
||||
<a href="logout.php">Logout</a>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<?php endif; ?>
|
||||
86
admin/settings.php
Normal file
86
admin/settings.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/partials/header.php';
|
||||
|
||||
// Handle form submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$site_title = $_POST['site_title'] ?? '';
|
||||
$responses_per_page = $_POST['responses_per_page'] ?? '10';
|
||||
$contact_email = $_POST['contact_email'] ?? '';
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO `settings` (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)");
|
||||
$stmt->execute(['site_title', $site_title]);
|
||||
$stmt->execute(['responses_per_page', $responses_per_page]);
|
||||
$stmt->execute(['contact_email', $contact_email]);
|
||||
|
||||
$success_message = 'Settings saved successfully!';
|
||||
}
|
||||
|
||||
// Fetch current settings
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM `settings`");
|
||||
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
$site_title = $settings['site_title'] ?? 'My Awesome App';
|
||||
$responses_per_page = $settings['responses_per_page'] ?? '10';
|
||||
$contact_email = $settings['contact_email'] ?? '';
|
||||
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="d-sm-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="text-dark mb-0">Settings</h3>
|
||||
</div>
|
||||
|
||||
<?php if (isset($success_message)): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?php echo $success_message; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card shadow mb-3">
|
||||
<div class="card-header py-3">
|
||||
<p class="text-primary m-0 fw-bold">Application Settings</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="site_title"><strong>Site Title</strong></label>
|
||||
<input class="form-control" type="text" id="site_title" value="<?php echo htmlspecialchars($site_title); ?>" name="site_title">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="responses_per_page"><strong>Responses Per Page</strong></label>
|
||||
<input class="form-control" type="number" id="responses_per_page" value="<?php echo htmlspecialchars($responses_per_page); ?>" name="responses_per_page">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="contact_email"><strong>Contact Email</strong></label>
|
||||
<input class="form-control" type="email" id="contact_email" value="<?php echo htmlspecialchars($contact_email); ?>" name="contact_email">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Save Settings</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once __DIR__ . '/partials/footer.php';
|
||||
?>
|
||||
144
admin/style.css
Normal file
144
admin/style.css
Normal file
@ -0,0 +1,144 @@
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background-color: #F9FAFB;
|
||||
color: #111827;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 4rem auto;
|
||||
padding: 2rem;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid #D1D5DB;
|
||||
border-radius: 0.375rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #4F46E5;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background-color: #FEE2E2;
|
||||
color: #B91C1C;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #FFFFFF;
|
||||
padding: 1rem 2rem;
|
||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header .logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header a {
|
||||
text-decoration: none;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #1F2937;
|
||||
color: #F9FAFB;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
display: block;
|
||||
padding: 1rem 1.5rem;
|
||||
color: #D1D5DB;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar a:hover, .sidebar a.active {
|
||||
background-color: #374151;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid #E5E7EB;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #E5E7EB;
|
||||
}
|
||||
|
||||
.table th {
|
||||
font-weight: 600;
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
175
admin/users.php
Normal file
175
admin/users.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Handle Delete User
|
||||
if (isset($_GET['delete_id'])) {
|
||||
$delete_id = $_GET['delete_id'];
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
|
||||
$stmt->execute([$delete_id]);
|
||||
header("Location: users.php?deleted=true");
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
$error_message = "Error deleting user: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Add User Form Submission
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['username'])) {
|
||||
$username = trim($_POST['username']);
|
||||
$password = $_POST['password'];
|
||||
$role = $_POST['role'];
|
||||
|
||||
if (!empty($username) && !empty($password) && !empty($role)) {
|
||||
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $hashed_password, $role]);
|
||||
|
||||
// Redirect to avoid form resubmission
|
||||
header("Location: users.php");
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
// Check for duplicate entry
|
||||
if ($e->errorInfo[1] == 1062) {
|
||||
$error_message = "Error: This username is already taken.";
|
||||
} else {
|
||||
$error_message = "Database error: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$error_message = "All fields are required.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fetch all users
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SELECT id, username, role, created_at FROM users ORDER BY created_at DESC');
|
||||
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
// For now, we'll just die on error. In a real app, handle this gracefully.
|
||||
die("Error fetching users: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$page_title = "User Management";
|
||||
include 'partials/header.php';
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<?php if (!empty($error_message)): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?php echo $error_message; ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-sm-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="text-dark mb-0">User Management</h3>
|
||||
<a class="btn btn-primary btn-sm d-none d-sm-inline-block" role="button" href="#addUserModal" data-bs-toggle="modal">
|
||||
<i class="fas fa-plus fa-sm text-white-50"></i> Add New User
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
User successfully deleted.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
User successfully updated.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<p class="text-primary m-0 fw-bold">Registered Users</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive table mt-2" id="dataTable" role="grid" aria-describedby="dataTable_info">
|
||||
<table class="table my-0" id="dataTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Role</th>
|
||||
<th>Registered On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($users)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No users found.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($user['username']); ?></td>
|
||||
<td><?php echo htmlspecialchars(ucfirst($user['role'])); ?></td>
|
||||
<td><?php echo date("F j, Y, g:i a", strtotime($user['created_at'])); ?></td>
|
||||
<td>
|
||||
<a href="edit_user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-warning me-2" title="Edit"><i class="fas fa-pencil-alt"></i></a>
|
||||
<a href="users.php?delete_id=<?php echo $user['id']; ?>" class="btn btn-sm btn-danger" title="Delete" onclick="return confirm('Are you sure you want to delete this user?');"><i class="fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add User Modal -->
|
||||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addUserModalLabel">Add New User</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addUserForm" action="users.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Role</label>
|
||||
<select class="form-select" id="role" name="role">
|
||||
<option value="admin">Admin</option>
|
||||
<option value="editor">Editor</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" form="addUserForm" class="btn btn-primary">Save User</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php include 'partials/footer.php'; ?>
|
||||
54
assets/css/custom.css
Normal file
54
assets/css/custom.css
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background-color: #F9FAFB;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.hero {
|
||||
background: linear-gradient(135deg, #4F46E5, #2563EB);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #4F46E5;
|
||||
border-color: #4F46E5;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #4338CA;
|
||||
border-color: #4338CA;
|
||||
}
|
||||
|
||||
.survey-card {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid #D1D5DB;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #4F46E5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
|
||||
}
|
||||
|
||||
.form-check-input:checked {
|
||||
background-color: #4F46E5;
|
||||
border-color: #4F46E5;
|
||||
}
|
||||
84
assets/css/style.css
Normal file
84
assets/css/style.css
Normal file
@ -0,0 +1,84 @@
|
||||
/* General Body Styling */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Main Container */
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
/* Header and Navigation */
|
||||
header {
|
||||
background-color: #ffffff;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
header .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
header .logo {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
header nav ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
header nav a {
|
||||
text-decoration: none;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
header nav a:hover,
|
||||
header nav a.active {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
/* Page Content */
|
||||
main {
|
||||
background-color: #ffffff;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
padding: 1.5rem 0;
|
||||
color: #6c757d;
|
||||
font-size: 0.9rem;
|
||||
border-top: 1px solid #e9ecef;
|
||||
}
|
||||
53
assets/js/main.js
Normal file
53
assets/js/main.js
Normal file
@ -0,0 +1,53 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const surveyForm = document.getElementById('surveyForm');
|
||||
const formMessages = document.getElementById('form-messages');
|
||||
|
||||
if (surveyForm) {
|
||||
surveyForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
// --- Client-side validation ---
|
||||
const satisfaction = surveyForm.querySelector('input[name="satisfaction"]:checked');
|
||||
if (!satisfaction) {
|
||||
displayMessage('Please select a satisfaction level.', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(surveyForm);
|
||||
const submitButton = surveyForm.querySelector('button[type="submit"]');
|
||||
submitButton.disabled = true;
|
||||
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Submitting...';
|
||||
|
||||
fetch('submit.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
displayMessage(data.message, 'success');
|
||||
// Redirect to thank-you page after a short delay
|
||||
setTimeout(() => {
|
||||
window.location.href = 'thank-you.php';
|
||||
}, 1500);
|
||||
} else {
|
||||
displayMessage(data.message || 'An unexpected error occurred.', 'danger');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'Submit Feedback';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
displayMessage('A network error occurred. Please try again.', 'danger');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'Submit Feedback';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function displayMessage(message, type) {
|
||||
if (formMessages) {
|
||||
formMessages.innerHTML = `<div class="alert alert-${type}" role="alert">${message}</div>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -8,10 +8,31 @@ 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 = 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;
|
||||
}
|
||||
|
||||
function run_migrations() {
|
||||
$pdo = db();
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS `migrations` (`migration` VARCHAR(255) PRIMARY KEY, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
|
||||
|
||||
$stmt = $pdo->query("SELECT `migration` FROM `migrations`");
|
||||
$run_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$migration_files = glob(__DIR__ . '/migrations/*.sql');
|
||||
foreach ($migration_files as $file) {
|
||||
$migration_name = basename($file);
|
||||
if (!in_array($migration_name, $run_migrations)) {
|
||||
$sql = file_get_contents($file);
|
||||
$pdo->exec($sql);
|
||||
$stmt = $pdo->prepare("INSERT INTO `migrations` (`migration`) VALUES (?)");
|
||||
$stmt->execute([$migration_name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run_migrations();
|
||||
|
||||
8
db/migrations/002_create_users_table.sql
Normal file
8
db/migrations/002_create_users_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`username` VARCHAR(255) NOT NULL UNIQUE,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`role` VARCHAR(50) NOT NULL DEFAULT 'admin',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
15
db/migrations/003_create_settings_table.sql
Normal file
15
db/migrations/003_create_settings_table.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- Create settings table
|
||||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`setting_key` varchar(255) NOT NULL,
|
||||
`setting_value` text NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `setting_key` (`setting_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Insert default settings
|
||||
INSERT INTO `settings` (`setting_key`, `setting_value`) VALUES
|
||||
('site_title', 'My Awesome App'),
|
||||
('responses_per_page', '10'),
|
||||
('contact_email', 'admin@example.com')
|
||||
ON DUPLICATE KEY UPDATE `setting_key` = `setting_key`;
|
||||
8
db/migrations/004_create_pages_table.sql
Normal file
8
db/migrations/004_create_pages_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS pages (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
slug VARCHAR(255) NOT NULL UNIQUE,
|
||||
content TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
@ -0,0 +1,3 @@
|
||||
ALTER TABLE `pages`
|
||||
ADD COLUMN `is_published` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN `menu_order` INT(11) NOT NULL DEFAULT 0;
|
||||
10
includes/footer.php
Normal file
10
includes/footer.php
Normal file
@ -0,0 +1,10 @@
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© <?php echo date('Y'); ?> <?php echo htmlspecialchars($site_title); ?>. All Rights Reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
44
includes/header.php
Normal file
44
includes/header.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Fetch site title from settings
|
||||
$stmt = db()->query("SELECT value FROM settings WHERE name = 'site_title'");
|
||||
$site_title_setting = $stmt->fetchColumn();
|
||||
$site_title = $site_title_setting ?: 'My Awesome Website';
|
||||
|
||||
// Fetch pages for navigation
|
||||
$pages_stmt = db()->query("SELECT title, slug FROM pages WHERE is_published = 1 ORDER BY menu_order ASC");
|
||||
$nav_pages = $pages_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$current_slug = basename($_SERVER['REQUEST_URI']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo isset($page_title) ? htmlspecialchars($page_title) . ' | ' . htmlspecialchars($site_title) : htmlspecialchars($site_title); ?></title>
|
||||
<meta name="description" content="<?php echo isset($meta_description) ? htmlspecialchars($meta_description) : 'Welcome to our website'; ?>">
|
||||
<link rel="stylesheet" href="/assets/css/style.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<a href="/" class="logo"><?php echo htmlspecialchars($site_title); ?></a>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/" class="<?php echo ($current_slug == 'index.php' || $current_slug == '') ? 'active' : ''; ?>">Home</a></li>
|
||||
<?php foreach ($nav_pages as $p): ?>
|
||||
<li>
|
||||
<a href="/page.php?slug=<?php echo htmlspecialchars($p['slug']); ?>"
|
||||
class="<?php echo (isset($_GET['slug']) && $_GET['slug'] == $p['slug']) ? 'active' : ''; ?>">
|
||||
<?php echo htmlspecialchars($p['title']); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main>
|
||||
205
index.php
205
index.php
@ -1,150 +1,77 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once 'db/config.php';
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
// Fetch settings
|
||||
$settings_stmt = db()->query("SELECT * FROM settings");
|
||||
$settings = $settings_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
$site_title = $settings['site_title'] ?? 'Survey App';
|
||||
|
||||
// Fetch total responses
|
||||
$responses_stmt = db()->query("SELECT COUNT(*) FROM survey_responses");
|
||||
$total_responses = $responses_stmt->fetchColumn();
|
||||
?>
|
||||
<!doctype html>
|
||||
<!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>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($site_title); ?> - Create & Share Surveys</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?v=<?php echo time(); ?>">
|
||||
<meta name="description" content="Create and share surveys effortlessly. Gather feedback, analyze results, and make better decisions.">
|
||||
<meta name="keywords" content="survey, feedback, questionnaire, form builder, online survey, poll, data collection, research, customer feedback, employee engagement, market research, Built with Flatlogic Generator">
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($site_title); ?>">
|
||||
<meta property="og:description" content="Built with Flatlogic Generator">
|
||||
<meta property="og:image" content="">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<body class="bg-light">
|
||||
|
||||
<div class="hero text-center py-5 bg-white">
|
||||
<div class="container py-5">
|
||||
<h1 class="display-4 fw-bold"><?php echo htmlspecialchars($site_title); ?></h1>
|
||||
<p class="lead col-lg-8 mx-auto">
|
||||
Build beautiful, intuitive surveys in minutes. Gather feedback, analyze data, and gain insights to drive your business forward.
|
||||
</p>
|
||||
<a href="survey.php" class="btn btn-primary btn-lg mt-4">Take a Sample Survey</a>
|
||||
</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>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row text-center">
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body p-4">
|
||||
<h3 class="h4">Easy to Use</h3>
|
||||
<p>An intuitive interface makes survey creation a breeze.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body p-4">
|
||||
<h3 class="h4">Powerful Insights</h3>
|
||||
<p>Analyze responses with real-time charts and data exports.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body p-4">
|
||||
<h3 class="h4">Total Responses</h3>
|
||||
<p class="display-6 fw-bold"><?php echo $total_responses; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-center py-4 text-muted">
|
||||
<p>© <?php echo date("Y"); ?> <?php echo htmlspecialchars($site_title); ?>. All rights reserved. | <a href="/admin">Admin Login</a></p>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
41
page.php
Normal file
41
page.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$slug = $_GET['slug'] ?? null;
|
||||
|
||||
if (!$slug) {
|
||||
http_response_code(404);
|
||||
echo "Page not found.";
|
||||
exit();
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("SELECT * FROM pages WHERE slug = ?");
|
||||
$stmt->execute([$slug]);
|
||||
$page = $stmt->fetch();
|
||||
|
||||
if (!$page) {
|
||||
http_response_code(404);
|
||||
// You can create a more sophisticated 404 page
|
||||
include 'includes/header.php';
|
||||
echo "<div class=\"container\"><p>Sorry, the page you are looking for does not exist.</p></div>";
|
||||
include 'includes/footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
// SEO and page metadata
|
||||
$page_title = htmlspecialchars($page['title']);
|
||||
$meta_description = htmlspecialchars(substr(strip_tags($page['content']), 0, 160));
|
||||
|
||||
include 'includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="container page-content">
|
||||
<h1><?php echo htmlspecialchars($page['title']); ?></h1>
|
||||
<hr>
|
||||
<div>
|
||||
<?php echo nl2br(htmlspecialchars($page['content'])); // Using nl2br to respect line breaks, and htmlspecialchars for security ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
include 'includes/footer.php';
|
||||
45
submit.php
Normal file
45
submit.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$response = ['success' => false, 'message' => 'An unexpected error occurred.'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$satisfaction = filter_input(INPUT_POST, 'satisfaction', FILTER_SANITIZE_STRING);
|
||||
$comments = filter_input(INPUT_POST, 'comments', FILTER_SANITIZE_STRING);
|
||||
$how_heard = filter_input(INPUT_POST, 'how_heard', FILTER_SANITIZE_STRING);
|
||||
|
||||
if (empty($satisfaction)) {
|
||||
$response['message'] = 'Satisfaction level is a required field.';
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$sql = "INSERT INTO survey_responses (satisfaction, comments, how_heard) VALUES (:satisfaction, :comments, :how_heard)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
$stmt->bindParam(':satisfaction', $satisfaction);
|
||||
$stmt->bindParam(':comments', $comments);
|
||||
$stmt->bindParam(':how_heard', $how_heard);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$response['success'] = true;
|
||||
$response['message'] = 'Thank you! Your feedback has been submitted successfully.';
|
||||
} else {
|
||||
$response['message'] = 'Failed to save your response. Please try again.';
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, you'd log this error. For now, send a generic message.
|
||||
// error_log('PDOException: ' . $e->getMessage());
|
||||
$response['message'] = 'Database error: Could not save your response.';
|
||||
}
|
||||
|
||||
} else {
|
||||
$response['message'] = 'Invalid request method.';
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
88
survey.php
Normal file
88
survey.php
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sample Survey - Survey App</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?v=<?php echo time(); ?>">
|
||||
<meta name="description" content="Built with Flatlogic Generator">
|
||||
<meta name="keywords" content="survey, feedback, questionnaire, form builder, online survey, poll, data collection, research, customer feedback, employee engagement, market research, Built with Flatlogic Generator">
|
||||
<meta property="og:title" content="clone surveymonkey software">
|
||||
<meta property="og:description" content="Built with Flatlogic Generator">
|
||||
<meta property="og:image" content="">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="text-center mb-5">
|
||||
<h1 class="display-5 fw-bold">Customer Feedback Survey</h1>
|
||||
<p class="lead text-muted">We value your feedback! Please take a moment to share your thoughts.</p>
|
||||
</div>
|
||||
|
||||
<div class="survey-card p-4 p-md-5">
|
||||
<form action="submit.php" method="POST" id="surveyForm">
|
||||
|
||||
<!-- Question 1: Multiple Choice -->
|
||||
<div class="mb-5">
|
||||
<label class="form-label">1. How satisfied are you with our product?</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="satisfaction" id="verySatisfied" value="very_satisfied" required>
|
||||
<label class="form-check-label" for="verySatisfied">Very Satisfied</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="satisfaction" id="satisfied" value="satisfied">
|
||||
<label class="form-check-label" for="satisfied">Satisfied</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="satisfaction" id="neutral" value="neutral">
|
||||
<label class="form-check-label" for="neutral">Neutral</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="satisfaction" id="unsatisfied" value="unsatisfied">
|
||||
<label class="form-check-label" for="unsatisfied">Unsatisfied</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="satisfaction" id="veryUnsatisfied" value="very_unsatisfied">
|
||||
<label class="form-check-label" for="veryUnsatisfied">Very Unsatisfied</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Question 2: Text Area -->
|
||||
<div class="mb-5">
|
||||
<label for="comments" class="form-label">2. Do you have any comments or suggestions?</label>
|
||||
<textarea class="form-control" id="comments" name="comments" rows="5" placeholder="Your feedback helps us improve..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Question 3: Select -->
|
||||
<div class="mb-5">
|
||||
<label for="howHeard" class="form-label">3. How did you hear about us?</label>
|
||||
<select class="form-select" id="howHeard" name="how_heard">
|
||||
<option selected disabled value="">Choose an option...</option>
|
||||
<option value="social_media">Social Media</option>
|
||||
<option value="friend">From a friend</option>
|
||||
<option value="search_engine">Search Engine (Google, etc.)</option>
|
||||
<option value="advertisement">Advertisement</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="form-messages" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
36
thank-you.php
Normal file
36
thank-you.php
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="refresh" content="5;url=index.php">
|
||||
<title>Thank You - Survey App</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?v=<?php echo time(); ?>">
|
||||
<meta name="description" content="Built with Flatlogic Generator">
|
||||
<meta name="keywords" content="survey, feedback, questionnaire, form builder, online survey, poll, data collection, research, customer feedback, employee engagement, market research, Built with Flatlogic Generator">
|
||||
<meta property="og:title" content="clone surveymonkey software">
|
||||
<meta property="og:description" content="Built with Flatlogic Generator">
|
||||
<meta property="og:image" content="">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container d-flex flex-column justify-content-center align-items-center vh-100">
|
||||
<div class="text-center survey-card p-5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-success mb-4" width="80" height="80" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||
</svg>
|
||||
<h1 class="display-4">Thank You!</h1>
|
||||
<p class="lead">Your submission has been received.</p>
|
||||
<hr class="my-4">
|
||||
<p>You can now safely close this page.</p>
|
||||
<a class="btn btn-primary btn-lg mt-3" href="index.php" role="button">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>
|
||||
Loading…
x
Reference in New Issue
Block a user