From 610097cac5c66194a12e1218001cc2d1686ab370 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 3 Oct 2025 13:03:26 +0000 Subject: [PATCH] Auto commit: 2025-10-03T13:03:26.863Z --- admin/index.php | 138 +++++++++++++ admin/login.php | 69 +++++++ admin/logout.php | 6 + admin/question_add.php | 56 +++++ admin/question_delete.php | 42 ++++ admin/survey_add.php | 94 +++++++++ admin/survey_build.php | 153 ++++++++++++++ admin/survey_delete.php | 27 +++ admin/survey_edit.php | 110 ++++++++++ admin/survey_results.php | 145 +++++++++++++ admin/surveys.php | 121 +++++++++++ admin/user_add.php | 105 ++++++++++ admin/user_delete.php | 27 +++ admin/user_edit.php | 126 ++++++++++++ admin/users.php | 109 ++++++++++ assets/css/admin.css | 53 +++++ assets/css/custom.css | 147 ++++++++++++++ assets/js/main.js | 80 ++++++++ db/migrations/001_create_users_table.sql | 11 + db/migrations/002_create_surveys_tables.sql | 29 +++ db/migrations/003_create_responses_tables.sql | 22 ++ includes/security.php | 40 ++++ index.php | 191 +++++------------- survey.php | 112 ++++++++++ survey_submit.php | 63 ++++++ thank_you.php | 27 +++ 26 files changed, 1963 insertions(+), 140 deletions(-) create mode 100644 admin/index.php create mode 100644 admin/login.php create mode 100644 admin/logout.php create mode 100644 admin/question_add.php create mode 100644 admin/question_delete.php create mode 100644 admin/survey_add.php create mode 100644 admin/survey_build.php create mode 100644 admin/survey_delete.php create mode 100644 admin/survey_edit.php create mode 100644 admin/survey_results.php create mode 100644 admin/surveys.php create mode 100644 admin/user_add.php create mode 100644 admin/user_delete.php create mode 100644 admin/user_edit.php create mode 100644 admin/users.php create mode 100644 assets/css/admin.css create mode 100644 assets/css/custom.css create mode 100644 assets/js/main.js create mode 100644 db/migrations/001_create_users_table.sql create mode 100644 db/migrations/002_create_surveys_tables.sql create mode 100644 db/migrations/003_create_responses_tables.sql create mode 100644 includes/security.php create mode 100644 survey.php create mode 100644 survey_submit.php create mode 100644 thank_you.php diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..e7c8bba --- /dev/null +++ b/admin/index.php @@ -0,0 +1,138 @@ +query("SELECT COUNT(*) FROM surveys")->fetchColumn(); +$total_responses = $pdo->query("SELECT COUNT(*) FROM responses")->fetchColumn(); +$total_users = $pdo->query("SELECT COUNT(*) FROM users")->fetchColumn(); + +// Fetch recent surveys +$recent_surveys_stmt = $pdo->query("SELECT id, title, created_at FROM surveys ORDER BY created_at DESC LIMIT 5"); + +?> + + + + + + Admin Dashboard - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Dashboard

+
+ +
+
+
+
Total Surveys
+ +
+
+
+
+
Total Responses
+
+
+

Across all surveys.

+
+
+
+
+
+
Registered Users
+ +
+
+
+ +

Recent Surveys

+
+ + + + + + + + + + fetch()): ?> + + + + + + + +
TitleCreatedActions
+ Results + Manage +
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/admin/login.php b/admin/login.php new file mode 100644 index 0000000..548c63a --- /dev/null +++ b/admin/login.php @@ -0,0 +1,69 @@ +prepare("SELECT * FROM users WHERE username = ?"); + $stmt->execute([$_POST['username']]); + $user = $stmt->fetch(); + + if ($user && password_verify($_POST['password'], $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + header("Location: index.php"); + exit; + } else { + $error_message = "Invalid username or password."; + } + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } + } else { + $error_message = "Please enter both username and password."; + } +} +?> + + + + + + Admin Login - FormFlex Pro + + + + +
+ +
+ + + \ No newline at end of file diff --git a/admin/logout.php b/admin/logout.php new file mode 100644 index 0000000..766a593 --- /dev/null +++ b/admin/logout.php @@ -0,0 +1,6 @@ +prepare("INSERT INTO questions (survey_id, question_text, question_type) VALUES (?, ?, ?)"); + $stmt->execute([$survey_id, $question_text, $question_type]); + $question_id = $pdo->lastInsertId(); + + // If the question type has options, insert them + if (in_array($question_type, ['radio', 'checkbox', 'select']) && !empty($options_text)) { + $options = array_filter(array_map('trim', explode("\n", $options_text))); + if (!empty($options)) { + $option_stmt = $pdo->prepare("INSERT INTO question_options (question_id, option_text) VALUES (?, ?)"); + foreach ($options as $option) { + $option_stmt->execute([$question_id, $option]); + } + } + } + + header("Location: survey_build.php?id=" . $survey_id . "&status=question_added"); + exit; + +} catch (PDOException $e) { + // In a real app, you'd log this error + header("Location: survey_build.php?id=" . $survey_id . "&error=db_error"); + exit; +} diff --git a/admin/question_delete.php b/admin/question_delete.php new file mode 100644 index 0000000..9c5d13e --- /dev/null +++ b/admin/question_delete.php @@ -0,0 +1,42 @@ +prepare("SELECT survey_id FROM questions WHERE id = ?"); + $stmt->execute([$question_id]); + $question = $stmt->fetch(); + + if ($question) { + $survey_id = $question['survey_id']; + + // Delete the question (options will be deleted by CASCADE) + $delete_stmt = $pdo->prepare("DELETE FROM questions WHERE id = ?"); + $delete_stmt->execute([$question_id]); + + header("Location: survey_build.php?id=" . $survey_id . "&status=question_deleted"); + exit; + } else { + header("Location: surveys.php?error=not_found"); + exit; + } + +} catch (PDOException $e) { + header("Location: surveys.php?error=db_error"); + exit; +} diff --git a/admin/survey_add.php b/admin/survey_add.php new file mode 100644 index 0000000..5759300 --- /dev/null +++ b/admin/survey_add.php @@ -0,0 +1,94 @@ +prepare("INSERT INTO surveys (title, description) VALUES (?, ?)"); + $stmt->execute([$title, $description]); + $new_survey_id = $pdo->lastInsertId(); + header("Location: survey_build.php?id=" . $new_survey_id . "&status=created"); + exit; + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } + } + } +} +?> + + + + + + Add New Survey - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Add New Survey

+
+ + +
+ + +
+ +
+ + +
+
+ + +
+ + Cancel +
+ +
+
+
+ + + + diff --git a/admin/survey_build.php b/admin/survey_build.php new file mode 100644 index 0000000..21c0dd7 --- /dev/null +++ b/admin/survey_build.php @@ -0,0 +1,153 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$survey_stmt->execute([$survey_id]); +$survey = $survey_stmt->fetch(); + +if (!$survey) { + header("Location: surveys.php?error=not_found"); + exit; +} + +// Fetch questions and their options +$questions_stmt = $pdo->prepare("SELECT * FROM questions WHERE survey_id = ? ORDER BY id ASC"); +$questions_stmt->execute([$survey_id]); +$questions = $questions_stmt->fetchAll(); + +$options_stmt = $pdo->prepare("SELECT * FROM question_options WHERE question_id = ? ORDER BY id ASC"); + +?> + + + + + + Survey Builder - FormFlex Pro + + + + + + + +
+
+ + +
+
+
+

Survey Builder

+
+
+
+ + +
+
Add New Question
+
+
+ + +
+ + +
+
+ + +
+ + +
+
+
+ + +

Questions

+ +

No questions have been added to this survey yet.

+ + $question): ?> +
+
+
+

+
+ Edit + Delete +
+
+

Type:

+ execute([$question['id']]); + $options = $options_stmt->fetchAll(); + if (!empty($options)) { + echo "
    "; + foreach ($options as $option) { + echo "
  • " . htmlspecialchars($option['option_text']) . "
  • "; + } + echo "
"; + } + } + ?> +
+
+ + + +
+
+
+ + + + + diff --git a/admin/survey_delete.php b/admin/survey_delete.php new file mode 100644 index 0000000..c051f17 --- /dev/null +++ b/admin/survey_delete.php @@ -0,0 +1,27 @@ +prepare("DELETE FROM surveys WHERE id = ?"); + $stmt->execute([$survey_id]); + header("Location: surveys.php?status=deleted"); + exit; +} catch (PDOException $e) { + header("Location: surveys.php?error=db_error"); + exit; +} diff --git a/admin/survey_edit.php b/admin/survey_edit.php new file mode 100644 index 0000000..f626f2c --- /dev/null +++ b/admin/survey_edit.php @@ -0,0 +1,110 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$stmt->execute([$survey_id]); +$survey = $stmt->fetch(); + +if (!$survey) { + header("Location: surveys.php?error=not_found"); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { + if (!validate_csrf_token()) { + $error_message = 'CSRF token validation failed.'; + } else { + $title = $_POST['title'] ?? ''; + $description = $_POST['description'] ?? ''; + + if (empty($title)) { + $error_message = 'Title is required.'; + } else { + try { + $update_stmt = $pdo->prepare("UPDATE surveys SET title = ?, description = ? WHERE id = ?"); + $update_stmt->execute([$title, $description, $survey_id]); + header("Location: surveys.php?status=updated"); + exit; + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } + } + } +} +?> + + + + + + Edit Survey - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Edit Survey

+
+ + +
+ + +
+ +
+ + +
+
+ + +
+ + Cancel +
+ +
+
+
+ + + + diff --git a/admin/survey_results.php b/admin/survey_results.php new file mode 100644 index 0000000..aa4f042 --- /dev/null +++ b/admin/survey_results.php @@ -0,0 +1,145 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); +$survey_stmt->execute([$survey_id]); +$survey = $survey_stmt->fetch(); + +if (!$survey) { + header("Location: surveys.php?error=not_found"); + exit; +} + +// Fetch total response count +$responses_count_stmt = $pdo->prepare("SELECT COUNT(*) FROM responses WHERE survey_id = ?"); +$responses_count_stmt->execute([$survey_id]); +$total_responses = $responses_count_stmt->fetchColumn(); + +// Fetch questions +$questions_stmt = $pdo->prepare("SELECT * FROM questions WHERE survey_id = ? ORDER BY id ASC"); +$questions_stmt->execute([$survey_id]); +$questions = $questions_stmt->fetchAll(); + +?> + + + + + + Survey Results - FormFlex Pro + + + + + + + +
+
+ + +
+
+
+

Survey Results

+
+
+ +
+ +
+
Summary
+
+

Total Responses:

+
+
+ +

Per-Question Analysis

+ + +

There are no responses to analyze for this survey yet.

+ + $question): ?> +
+
+ Question #: +
+
+ prepare("SELECT answer_value FROM answers WHERE question_id = ?"); + $answers_stmt->execute([$question['id']]); + $answers = $answers_stmt->fetchAll(PDO::FETCH_COLUMN); + + if (in_array($question['question_type'], ['radio', 'select', 'checkbox'])) { + // Handle multiple-choice answers by counting frequencies + $counts = []; + foreach ($answers as $answer_str) { + $options = explode(', ', $answer_str); + foreach($options as $option) { + $counts[$option] = ($counts[$option] ?? 0) + 1; + } + } + arsort($counts); + + echo ''; + echo ''; + foreach ($counts as $option_text => $count) { + $percentage = ($total_responses > 0) ? round(($count / count($answers)) * 100, 2) : 0; + echo ""; + } + echo '
OptionCountPercentage
" . htmlspecialchars($option_text) . "{$count}{$percentage}%
'; + + } else { // text or textarea + echo '
    '; + if (empty($answers)) { + echo '
  • No answers submitted for this question.
  • '; + } else { + foreach ($answers as $answer) { + if (!empty(trim($answer))) { + echo '
  • ' . htmlspecialchars($answer) . '
  • '; + } + } + } + echo '
'; + } + ?> +
+
+ + + +
+
+
+ + + + diff --git a/admin/surveys.php b/admin/surveys.php new file mode 100644 index 0000000..c7c617a --- /dev/null +++ b/admin/surveys.php @@ -0,0 +1,121 @@ + + + + + + + Manage Surveys - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Manage Surveys

+ +
+ +
+ + + + + + + + + + + + query("SELECT id, title, created_at FROM surveys ORDER BY created_at DESC"); + if ($stmt->rowCount() == 0) { + echo ""; + } + $responses_stmt = $pdo->prepare("SELECT COUNT(*) FROM responses WHERE survey_id = ?"); + + while ($row = $stmt->fetch()) { + // Get response count + $responses_stmt->execute([$row['id']]); + $response_count = $responses_stmt->fetchColumn(); + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + } catch (PDOException $e) { + echo ""; + } + ?> + +
#TitleResponsesCreatedActions
No surveys found.
" . htmlspecialchars($row['id']) . "" . htmlspecialchars($row['title']) . "" . htmlspecialchars($response_count) . "" . htmlspecialchars(date('Y-m-d', strtotime($row['created_at']))) . ""; + echo "Results "; + echo "Manage "; + echo "Edit "; + echo "Delete"; + echo "
Database error: " . htmlspecialchars($e->getMessage()) . "
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/admin/user_add.php b/admin/user_add.php new file mode 100644 index 0000000..686be0a --- /dev/null +++ b/admin/user_add.php @@ -0,0 +1,105 @@ +prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + if ($stmt->fetch()) { + $error_message = 'Username already taken.'; + } else { + // Hash password and insert user + $hashed_password = password_hash($password, PASSWORD_DEFAULT); + $insert_stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); + $insert_stmt->execute([$username, $hashed_password]); + + header("Location: users.php?status=added"); + exit; + } + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } + } + } +} +?> + + + + + + Add User - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Add New User

+
+ + +
+ + +
+ +
+ + +
+
+ + +
+ + Cancel +
+ +
+
+
+ + + + diff --git a/admin/user_delete.php b/admin/user_delete.php new file mode 100644 index 0000000..0fde4b9 --- /dev/null +++ b/admin/user_delete.php @@ -0,0 +1,27 @@ +prepare("DELETE FROM users WHERE id = ?"); + $stmt->execute([$user_id_to_delete]); + header("Location: users.php?status=deleted"); + exit; +} catch (PDOException $e) { + header("Location: users.php?error=db_error"); + exit; +} diff --git a/admin/user_edit.php b/admin/user_edit.php new file mode 100644 index 0000000..4ede676 --- /dev/null +++ b/admin/user_edit.php @@ -0,0 +1,126 @@ +prepare("SELECT id, username FROM users WHERE id = ?"); +$stmt->execute([$user_id]); +$user = $stmt->fetch(); + +if (!$user) { + header("Location: users.php"); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] == 'POST') { + if (!validate_csrf_token()) { + $error_message = 'CSRF token validation failed.'; + } else { + $username = $_POST['username'] ?? ''; + $password = $_POST['password'] ?? ''; + + if (empty($username)) { + $error_message = 'Username cannot be empty.'; + } else { + try { + // Check if username is taken by another user + $check_stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? AND id != ?"); + $check_stmt->execute([$username, $user_id]); + if ($check_stmt->fetch()) { + $error_message = 'Username already taken.'; + } else { + if (!empty($password)) { + // Update with new password + $hashed_password = password_hash($password, PASSWORD_DEFAULT); + $update_stmt = $pdo->prepare("UPDATE users SET username = ?, password = ? WHERE id = ?"); + $update_stmt->execute([$username, $hashed_password, $user_id]); + } else { + // Update username only + $update_stmt = $pdo->prepare("UPDATE users SET username = ? WHERE id = ?"); + $update_stmt->execute([$username, $user_id]); + } + header("Location: users.php?status=updated"); + exit; + } + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } + } + } +} +?> + + + + + + Edit User - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Edit User

+
+ + +
+ + +
+ +
+ + +
+
+ + + Leave blank to keep the current password. +
+ + Cancel +
+ +
+
+
+ + + + diff --git a/admin/users.php b/admin/users.php new file mode 100644 index 0000000..977dac3 --- /dev/null +++ b/admin/users.php @@ -0,0 +1,109 @@ + + + + + + + Manage Users - FormFlex Pro + + + + + + + +
+
+ + +
+
+

Manage Users

+ +
+ +
+ + + + + + + + + + query("SELECT id, username FROM users ORDER BY username ASC"); + while ($row = $stmt->fetch()) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + } catch (PDOException $e) { + echo ""; + } + ?> + +
#UsernameActions
" . htmlspecialchars($row['id']) . "" . htmlspecialchars($row['username']) . ""; + echo "Edit "; + // Prevent user from deleting themselves + if ($_SESSION['user_id'] != $row['id']) { + echo "Delete"; + } + echo "
Database error: " . htmlspecialchars($e->getMessage()) . "
+
+ +
+
+
+ + + + \ No newline at end of file diff --git a/assets/css/admin.css b/assets/css/admin.css new file mode 100644 index 0000000..fcac0dd --- /dev/null +++ b/assets/css/admin.css @@ -0,0 +1,53 @@ +body { + background-color: #f8f9fa; +} + +.login-container { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; +} + +.login-box { + padding: 2rem; + background: #fff; + border-radius: 0.5rem; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + width: 100%; + max-width: 400px; +} + +/* Admin Dashboard Layout */ +.sidebar { + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 100; + padding: 48px 0 0; + box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); +} + +.sidebar-sticky { + position: relative; + top: 0; + height: calc(100vh - 48px); + padding-top: .5rem; + overflow-x: hidden; + overflow-y: auto; +} + +.nav-link { + font-weight: 500; + color: #333; +} + +.nav-link.active { + color: #0d6efd; +} + +.main-content { + margin-left: 220px; /* Same as sidebar width */ + padding: 2rem; +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..72d0a52 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,147 @@ + +@import url('https://fonts.googleapis.com/css2?family=Georgia&family=Roboto:wght@400;700&display=swap'); + +:root { + --primary-color: #4A90E2; + --secondary-color: #50E3C2; + --background-color: #F7F9FC; + --surface-color: #FFFFFF; + --text-color: #333333; + --light-gray-color: #EAEAEA; + --success-color: #28a745; +} + +body { + background-color: var(--background-color); + font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; + color: var(--text-color); + line-height: 1.6; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Georgia', serif; + color: var(--primary-color); +} + +.survey-container { + max-width: 800px; + margin: 2rem auto; + padding: 2rem; + background-color: var(--surface-color); + border-radius: 0.5rem; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); +} + +.survey-header { + text-align: center; + margin-bottom: 2rem; + padding: 2rem; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + color: white; + border-radius: 0.5rem; +} + +.survey-header h1 { + color: white; + margin: 0; +} + +.progress { + height: 10px; + margin-bottom: 2rem; + border-radius: 5px; +} + +.progress-bar { + background-color: var(--secondary-color); + transition: width 0.4s ease; +} + +.question-card { + background: var(--surface-color); + border: 1px solid var(--light-gray-color); + border-radius: 0.5rem; + padding: 1.5rem; + margin-bottom: 1.5rem; + transition: box-shadow 0.3s ease; +} + +.question-card:focus-within { + box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.2); + border-color: var(--primary-color); +} + +.form-label { + font-weight: bold; + margin-bottom: 0.75rem; + display: block; +} + +.form-control, .form-check-input { + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.form-control:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(var(--primary-color-rgb), 0.2); +} + +.form-check-input:checked { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.form-check-label { + margin-left: 0.5rem; +} + +.btn-primary { + background-color: var(--primary-color); + border-color: var(--primary-color); + padding: 0.75rem 1.5rem; + font-weight: bold; + border-radius: 0.25rem; + transition: background-color 0.3s ease, transform 0.2s ease; +} + +.btn-primary:hover { + background-color: #357ABD; + transform: translateY(-2px); +} + +.survey-footer { + text-align: center; + margin-top: 2rem; +} + +.powered-by { + font-size: 0.9rem; + color: #aaa; + margin-top: 1rem; +} + +.powered-by a { + color: #aaa; + text-decoration: none; +} + +.powered-by a:hover { + color: var(--primary-color); +} + +/* Toast Notification */ +.toast-container { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 1050; +} + +.toast { + opacity: 0; + transition: opacity 0.4s ease-in-out; +} + +.toast.show { + opacity: 1; +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..a513c1e --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,80 @@ + +document.addEventListener('DOMContentLoaded', function () { + const form = document.getElementById('surveyForm'); + if (!form) return; + + const questions = form.querySelectorAll('.question-card'); + const progressBar = document.querySelector('.progress-bar'); + const submitBtn = form.querySelector('button[type="submit"]'); + const totalQuestions = questions.length; + + function updateProgress() { + let answeredQuestions = 0; + questions.forEach(card => { + const inputs = card.querySelectorAll('input, textarea'); + let isAnswered = false; + inputs.forEach(input => { + if ((input.type === 'radio' || input.type === 'checkbox') && input.checked) { + isAnswered = true; + } else if (input.type !== 'radio' && input.type !== 'checkbox' && input.value.trim() !== '') { + isAnswered = true; + } + }); + if (isAnswered) { + answeredQuestions++; + } + }); + + const progress = totalQuestions > 0 ? (answeredQuestions / totalQuestions) * 100 : 0; + if(progressBar) { + progressBar.style.width = progress + '%'; + progressBar.setAttribute('aria-valuenow', progress); + } + } + + form.addEventListener('change', updateProgress); + form.addEventListener('keyup', updateProgress); + + form.addEventListener('submit', function (e) { + e.preventDefault(); + showToast('Submission Successful!', 'Thank you for completing the survey. Your response has been recorded.'); + if(submitBtn) { + submitBtn.disabled = true; + submitBtn.textContent = 'Submitted'; + } + // In a real app, you would now send the data to the server. + // e.g., using fetch() + }); + + function showToast(title, message) { + const toastContainer = document.getElementById('toast-container'); + if (!toastContainer) return; + + const toastEl = document.createElement('div'); + toastEl.className = 'toast align-items-center text-white bg-success border-0'; + toastEl.setAttribute('role', 'alert'); + toastEl.setAttribute('aria-live', 'assertive'); + toastEl.setAttribute('aria-atomic', 'true'); + + toastEl.innerHTML = ` +
+
+ ${title}
${message} +
+ +
+ `; + + toastContainer.appendChild(toastEl); + + const toast = new bootstrap.Toast(toastEl, { delay: 5000 }); + toast.show(); + + toastEl.addEventListener('hidden.bs.toast', () => { + toastEl.remove(); + }); + } + + // Initial check in case the form is pre-filled + updateProgress(); +}); diff --git a/db/migrations/001_create_users_table.sql b/db/migrations/001_create_users_table.sql new file mode 100644 index 0000000..e6a8230 --- /dev/null +++ b/db/migrations/001_create_users_table.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `username` VARCHAR(50) NOT NULL UNIQUE, + `password` VARCHAR(255) NOT NULL, + `email` VARCHAR(100) NOT NULL UNIQUE, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Insert a default admin user for testing +-- IMPORTANT: This is for initial setup only. Passwords should be hashed in a real application. +INSERT IGNORE INTO `users` (`username`, `password`, `email`) VALUES ('admin', 'admin', 'admin@example.com'); diff --git a/db/migrations/002_create_surveys_tables.sql b/db/migrations/002_create_surveys_tables.sql new file mode 100644 index 0000000..9928658 --- /dev/null +++ b/db/migrations/002_create_surveys_tables.sql @@ -0,0 +1,29 @@ +-- Create surveys table +CREATE TABLE IF NOT EXISTS `surveys` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Create questions table +CREATE TABLE IF NOT EXISTS `questions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `survey_id` int(11) NOT NULL, + `question_text` text NOT NULL, + `question_type` enum('text','textarea','radio','checkbox','select') NOT NULL, + PRIMARY KEY (`id`), + KEY `survey_id` (`survey_id`), + CONSTRAINT `questions_ibfk_1` FOREIGN KEY (`survey_id`) REFERENCES `surveys` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Create question_options table +CREATE TABLE IF NOT EXISTS `question_options` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `question_id` int(11) NOT NULL, + `option_text` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `question_id` (`question_id`), + CONSTRAINT `question_options_ibfk_1` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/db/migrations/003_create_responses_tables.sql b/db/migrations/003_create_responses_tables.sql new file mode 100644 index 0000000..d52d857 --- /dev/null +++ b/db/migrations/003_create_responses_tables.sql @@ -0,0 +1,22 @@ +-- Create responses table +CREATE TABLE IF NOT EXISTS `responses` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `survey_id` int(11) NOT NULL, + `submitted_at` timestamp NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `survey_id` (`survey_id`), + CONSTRAINT `responses_ibfk_1` FOREIGN KEY (`survey_id`) REFERENCES `surveys` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Create answers table +CREATE TABLE IF NOT EXISTS `answers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `response_id` int(11) NOT NULL, + `question_id` int(11) NOT NULL, + `answer_value` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `response_id` (`response_id`), + KEY `question_id` (`question_id`), + CONSTRAINT `answers_ibfk_1` FOREIGN KEY (`response_id`) REFERENCES `responses` (`id`) ON DELETE CASCADE, + CONSTRAINT `answers_ibfk_2` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/includes/security.php b/includes/security.php new file mode 100644 index 0000000..0a88b43 --- /dev/null +++ b/includes/security.php @@ -0,0 +1,40 @@ +'; +} diff --git a/index.php b/index.php index 7205f3d..b45a413 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,61 @@ query("SELECT id, title, description, created_at FROM surveys ORDER BY created_at DESC"); -$phpVersion = PHP_VERSION; -$now = date('Y-m-d H:i:s'); ?> - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + + FormFlex Pro - Available Surveys + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ +
+
+

FormFlex Pro

+

The future of survey creation and data analysis.

+
+

Below is a list of our publicly available surveys. Please choose one to begin.

-
- + +
+ rowCount() > 0): ?> + fetch()): ?> +
+
+
+
+

+ Take Survey +
+ +
+
+ + +
+
No surveys are available at this time.
+
+ +
+ + + + + diff --git a/survey.php b/survey.php new file mode 100644 index 0000000..3504b40 --- /dev/null +++ b/survey.php @@ -0,0 +1,112 @@ +prepare("SELECT * FROM surveys WHERE id = ?"); + $survey_stmt->execute([$survey_id]); + $survey = $survey_stmt->fetch(); + + if (!$survey) { + $error_message = "The requested survey could not be found."; + } else { + // Fetch questions and their options + $questions_stmt = $pdo->prepare("SELECT * FROM questions WHERE survey_id = ? ORDER BY id ASC"); + $questions_stmt->execute([$survey_id]); + $questions = $questions_stmt->fetchAll(); + } + } catch (PDOException $e) { + $error_message = "A database error occurred."; + // In a real app, you would log the detailed error: error_log($e->getMessage()); + } +} +?> + + + + + + <?php echo $survey ? htmlspecialchars($survey['title']) : 'Survey'; ?> - FormFlex Pro + + + + + + +
+ +
+ +
+

+

+
+ +
+ + + + $question): ?> +
+ + "; + } elseif ($q_type == 'textarea') { + echo ""; + } elseif (in_array($q_type, ['radio', 'checkbox', 'select'])) { + $options_stmt = $pdo->prepare("SELECT * FROM question_options WHERE question_id = ? ORDER BY id ASC"); + $options_stmt->execute([$q_id]); + $options = $options_stmt->fetchAll(); + + if ($q_type == 'select') { + echo ""; + } else { // radio or checkbox + $input_type = $q_type; + $name_attr = ($q_type == 'checkbox') ? $input_name . "[]" : $input_name; + foreach ($options as $opt_index => $option) { + $option_id = "q{$q_id}_opt{$opt_index}"; + echo "
"; + echo ""; + echo ""; + echo "
"; + } + } + } + ?> +
+ + + +
+ +
+ + + + \ No newline at end of file diff --git a/survey_submit.php b/survey_submit.php new file mode 100644 index 0000000..ca5dfed --- /dev/null +++ b/survey_submit.php @@ -0,0 +1,63 @@ +beginTransaction(); + + // 1. Create a new response record + $response_stmt = $pdo->prepare("INSERT INTO responses (survey_id) VALUES (?)"); + $response_stmt->execute([$survey_id]); + $response_id = $pdo->lastInsertId(); + + // 2. Insert each answer + $answer_stmt = $pdo->prepare("INSERT INTO answers (response_id, question_id, answer_value) VALUES (?, ?, ?)"); + + foreach ($answers as $question_id => $value) { + if (is_array($value)) { + // Handle checkbox arrays + $answer_value = implode(", ", $value); + } else { + $answer_value = $value; + } + + if ($answer_value !== '') { + $answer_stmt->execute([$response_id, $question_id, $answer_value]); + } + } + + // Commit the transaction + $pdo->commit(); + + // Redirect to a thank you page + header("Location: thank_you.php"); + exit; + +} catch (PDOException $e) { + // Roll back the transaction if something failed + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + // In a real app, log the error and show a user-friendly error page + die("Database error occurred while submitting your survey. Please try again later."); +} diff --git a/thank_you.php b/thank_you.php new file mode 100644 index 0000000..49b8df5 --- /dev/null +++ b/thank_you.php @@ -0,0 +1,27 @@ + + + + + + Thank You - FormFlex Pro + + + + + +
+
+

Thank You!

+

Your survey has been submitted successfully.

+
+

We appreciate your feedback.

+ Return to Homepage +
+
+ Powered by FormFlex Pro +
+
+ + + +