+ diff --git a/admin/settings.php b/admin/settings.php new file mode 100644 index 0000000..c573bde --- /dev/null +++ b/admin/settings.php @@ -0,0 +1,86 @@ +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'] ?? ''; + +?> + +
+
+

Settings

+
+ + + + + +
+
+
+
+

Application Settings

+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+
+
+ + diff --git a/admin/style.css b/admin/style.css new file mode 100644 index 0000000..3430f86 --- /dev/null +++ b/admin/style.css @@ -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; +} diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..9b338d0 --- /dev/null +++ b/admin/users.php @@ -0,0 +1,175 @@ +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'; +?> + +
+ + + + + +
+

User Management

+ +  Add New User + +
+ + + + + + + + + +
+
+

Registered Users

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
UsernameRoleRegistered OnActions
No users found.
+ + +
+
+
+
+
+ + + + + + diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..8095d88 --- /dev/null +++ b/assets/css/custom.css @@ -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; +} diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..76b9722 --- /dev/null +++ b/assets/css/style.css @@ -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; +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..7a511fd --- /dev/null +++ b/assets/js/main.js @@ -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 = ' 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 = ``; + } + } +}); \ No newline at end of file diff --git a/db/config.php b/db/config.php index bb98f7d..194111e 100644 --- a/db/config.php +++ b/db/config.php @@ -6,12 +6,33 @@ define('DB_USER', 'app_30953'); define('DB_PASS', 'e45f2778-db1f-450c-99c6-29efb4601472'); function db() { - static $pdo; - if (!$pdo) { - $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [ - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ]); - } - return $pdo; + static $pdo; + if (!$pdo) { + $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(); diff --git a/db/migrations/002_create_users_table.sql b/db/migrations/002_create_users_table.sql new file mode 100644 index 0000000..d571ffc --- /dev/null +++ b/db/migrations/002_create_users_table.sql @@ -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; diff --git a/db/migrations/003_create_settings_table.sql b/db/migrations/003_create_settings_table.sql new file mode 100644 index 0000000..4ef82ec --- /dev/null +++ b/db/migrations/003_create_settings_table.sql @@ -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`; diff --git a/db/migrations/004_create_pages_table.sql b/db/migrations/004_create_pages_table.sql new file mode 100644 index 0000000..66148e5 --- /dev/null +++ b/db/migrations/004_create_pages_table.sql @@ -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 +); diff --git a/db/migrations/005_add_is_published_and_menu_order_to_pages.sql b/db/migrations/005_add_is_published_and_menu_order_to_pages.sql new file mode 100644 index 0000000..ff59c40 --- /dev/null +++ b/db/migrations/005_add_is_published_and_menu_order_to_pages.sql @@ -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; \ No newline at end of file diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..8e6318a --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,10 @@ + +