From 3689691baba8c864c10fb81e97e56666ed1886f7 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 6 Mar 2026 17:54:56 +0000 Subject: [PATCH] Auto commit: 2026-03-06T17:54:56.191Z --- assets/css/custom.css | 578 +++++++++------------------- assets/js/main.js | 48 +-- build.php | 153 ++++++++ builds.php | 126 ++++++ create_build.php | 114 ++++++ create_thread.php | 95 +++++ db/migrations/001_builds_forums.sql | 43 +++ forums.php | 108 ++++++ includes/app.php | 67 ++++ includes/footer.php | 14 + includes/header.php | 48 +++ index.php | 250 +++++------- thread.php | 146 +++++++ 13 files changed, 1203 insertions(+), 587 deletions(-) create mode 100644 build.php create mode 100644 builds.php create mode 100644 create_build.php create mode 100644 create_thread.php create mode 100644 db/migrations/001_builds_forums.sql create mode 100644 forums.php create mode 100644 includes/app.php create mode 100644 includes/footer.php create mode 100644 includes/header.php create mode 100644 thread.php diff --git a/assets/css/custom.css b/assets/css/custom.css index 789132e..9582814 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,403 +1,175 @@ -body { - background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); - background-size: 400% 400%; - animation: gradient 15s ease infinite; - color: #212529; - font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - font-size: 14px; - margin: 0; - min-height: 100vh; -} - -.main-wrapper { - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; - width: 100%; - padding: 20px; - box-sizing: border-box; - position: relative; - z-index: 1; -} - -@keyframes gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} - -.chat-container { - width: 100%; - max-width: 600px; - background: rgba(255, 255, 255, 0.85); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 20px; - display: flex; - flex-direction: column; - height: 85vh; - box-shadow: 0 20px 40px rgba(0,0,0,0.2); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - overflow: hidden; -} - -.chat-header { - padding: 1.5rem; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); - background: rgba(255, 255, 255, 0.5); - font-weight: 700; - font-size: 1.1rem; - display: flex; - justify-content: space-between; - align-items: center; -} - -.chat-messages { - flex: 1; - overflow-y: auto; - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.25rem; -} - -/* Custom Scrollbar */ -::-webkit-scrollbar { - width: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 10px; -} - -::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); -} - -.message { - max-width: 85%; - padding: 0.85rem 1.1rem; - border-radius: 16px; - line-height: 1.5; - font-size: 0.95rem; - box-shadow: 0 4px 15px rgba(0,0,0,0.05); - animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(20px) scale(0.95); } - to { opacity: 1; transform: translateY(0) scale(1); } -} - -.message.visitor { - align-self: flex-end; - background: linear-gradient(135deg, #212529 0%, #343a40 100%); - color: #fff; - border-bottom-right-radius: 4px; -} - -.message.bot { - align-self: flex-start; - background: #ffffff; - color: #212529; - border-bottom-left-radius: 4px; -} - -.chat-input-area { - padding: 1.25rem; - background: rgba(255, 255, 255, 0.5); - border-top: 1px solid rgba(0, 0, 0, 0.05); -} - -.chat-input-area form { - display: flex; - gap: 0.75rem; -} - -.chat-input-area input { - flex: 1; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - padding: 0.75rem 1rem; - outline: none; - background: rgba(255, 255, 255, 0.9); - transition: all 0.3s ease; -} - -.chat-input-area input:focus { - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2); -} - -.chat-input-area button { - background: #212529; - color: #fff; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - transition: all 0.3s ease; -} - -.chat-input-area button:hover { - background: #000; - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0,0,0,0.2); -} - -/* Background Animations */ -.bg-animations { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - overflow: hidden; - pointer-events: none; -} - -.blob { - position: absolute; - width: 500px; - height: 500px; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; - filter: blur(80px); - animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1); -} - -.blob-1 { - top: -10%; - left: -10%; - background: rgba(238, 119, 82, 0.4); -} - -.blob-2 { - bottom: -10%; - right: -10%; - background: rgba(35, 166, 213, 0.4); - animation-delay: -7s; - width: 600px; - height: 600px; -} - -.blob-3 { - top: 40%; - left: 30%; - background: rgba(231, 60, 126, 0.3); - animation-delay: -14s; - width: 450px; - height: 450px; -} - -@keyframes move { - 0% { transform: translate(0, 0) rotate(0deg) scale(1); } - 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); } - 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); } - 100% { transform: translate(0, 0) rotate(360deg) scale(1); } -} - -.header-link { - font-size: 14px; - color: #fff; - text-decoration: none; - background: rgba(0, 0, 0, 0.2); - padding: 0.5rem 1rem; - border-radius: 8px; - transition: all 0.3s ease; -} - -.header-link:hover { - background: rgba(0, 0, 0, 0.4); - text-decoration: none; -} - -/* Admin Styles */ -.admin-container { - max-width: 900px; - margin: 3rem auto; - padding: 2.5rem; - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border-radius: 24px; - box-shadow: 0 20px 50px rgba(0,0,0,0.15); - border: 1px solid rgba(255, 255, 255, 0.4); - position: relative; - z-index: 1; -} - -.admin-container h1 { - margin-top: 0; - color: #212529; - font-weight: 800; -} - -.table { - width: 100%; - border-collapse: separate; - border-spacing: 0 8px; - margin-top: 1.5rem; -} - -.table th { - background: transparent; - border: none; - padding: 1rem; - color: #6c757d; - font-weight: 600; - text-transform: uppercase; - font-size: 0.75rem; - letter-spacing: 1px; -} - -.table td { - background: #fff; - padding: 1rem; - border: none; -} - -.table tr td:first-child { border-radius: 12px 0 0 12px; } -.table tr td:last-child { border-radius: 0 12px 12px 0; } - -.form-group { - margin-bottom: 1.25rem; -} - -.form-group label { - display: block; - margin-bottom: 0.5rem; - font-weight: 600; - font-size: 0.9rem; -} - -.form-control { - width: 100%; - padding: 0.75rem 1rem; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 12px; - background: #fff; - transition: all 0.3s ease; - box-sizing: border-box; -} - -.form-control:focus { - outline: none; - border-color: #23a6d5; - box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1); -} - -.header-container { - display: flex; - justify-content: space-between; - align-items: center; -} - -.header-links { - display: flex; - gap: 1rem; -} - -.admin-card { - background: rgba(255, 255, 255, 0.6); - padding: 2rem; - border-radius: 20px; - border: 1px solid rgba(255, 255, 255, 0.5); - margin-bottom: 2.5rem; - box-shadow: 0 10px 30px rgba(0,0,0,0.05); -} - -.admin-card h3 { - margin-top: 0; - margin-bottom: 1.5rem; - font-weight: 700; -} - -.btn-delete { - background: #dc3545; - color: white; - border: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - cursor: pointer; -} - -.btn-add { - background: #212529; - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 4px; - cursor: pointer; - margin-top: 1rem; -} - -.btn-save { - background: #0088cc; - color: white; - border: none; - padding: 0.8rem 1.5rem; - border-radius: 12px; - cursor: pointer; - font-weight: 600; - width: 100%; - transition: all 0.3s ease; -} - -.webhook-url { - font-size: 0.85em; - color: #555; - margin-top: 0.5rem; -} - -.history-table-container { - overflow-x: auto; - background: rgba(255, 255, 255, 0.4); - padding: 1rem; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.3); -} - -.history-table { - width: 100%; -} - -.history-table-time { - width: 15%; - white-space: nowrap; - font-size: 0.85em; - color: #555; -} - -.history-table-user { - width: 35%; - background: rgba(255, 255, 255, 0.3); - border-radius: 8px; - padding: 8px; -} - -.history-table-ai { - width: 50%; - background: rgba(255, 255, 255, 0.5); - border-radius: 8px; - padding: 8px; -} - -.no-messages { - text-align: center; - color: #777; -} \ No newline at end of file + :root { + --bg: #0d0f12; + --surface: #14181f; + --surface-2: #1b2029; + --border: #232a34; + --text: #e6e9ef; + --muted: #9aa3b2; + --accent: #3ea6ff; + --accent-2: #7dd3fc; + --success: #2dd4bf; + --warning: #fbbf24; + } + + * { + box-sizing: border-box; + } + + body.app-body { + margin: 0; + min-height: 100vh; + background: var(--bg); + color: var(--text); + font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 15px; + line-height: 1.6; + } + + a { + color: var(--accent); + text-decoration: none; + } + + a:hover { + color: var(--accent-2); + } + + .navbar { + background: rgba(13, 15, 18, 0.96); + border-bottom: 1px solid var(--border); + } + + .navbar-brand { + font-weight: 600; + letter-spacing: 0.3px; + } + + .nav-link { + color: var(--muted); + } + + .nav-link.active, + .nav-link:hover { + color: var(--text); + } + + .hero { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + padding: 2.5rem; + } + + .stat-card, + .app-card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 10px; + padding: 1.5rem; + } + + .stat-card h3 { + font-size: 1.3rem; + margin: 0 0 0.5rem; + } + + .badge-soft { + background: var(--surface-2); + color: var(--muted); + border: 1px solid var(--border); + font-weight: 500; + } + + .table-dark { + --bs-table-bg: var(--surface); + --bs-table-border-color: var(--border); + --bs-table-color: var(--text); + } + + .form-control, + .form-select, + .form-check-input { + background: var(--surface); + border: 1px solid var(--border); + color: var(--text); + border-radius: 8px; + } + + .form-control:focus, + .form-select:focus, + .form-check-input:focus { + border-color: var(--accent); + box-shadow: 0 0 0 0.2rem rgba(62, 166, 255, 0.15); + } + + .form-text { + color: var(--muted); + } + + .btn-primary { + background: var(--accent); + border-color: var(--accent); + color: #0b1117; + font-weight: 600; + border-radius: 8px; + } + + .btn-primary:hover { + background: #62b6ff; + border-color: #62b6ff; + color: #0b1117; + } + + .btn-outline-light { + border-color: var(--border); + color: var(--text); + border-radius: 8px; + } + + .btn-outline-light:hover { + background: var(--surface-2); + color: var(--text); + } + + .alert { + border-radius: 10px; + border: 1px solid var(--border); + background: var(--surface-2); + color: var(--text); + } + + .alert-success { + border-color: rgba(45, 212, 191, 0.4); + } + + .alert-warning { + border-color: rgba(251, 191, 36, 0.4); + } + + .tag { + background: rgba(62, 166, 255, 0.12); + color: var(--accent); + border: 1px solid rgba(62, 166, 255, 0.25); + border-radius: 999px; + padding: 0.2rem 0.6rem; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.2px; + } + + .footer { + border-top: 1px solid var(--border); + background: var(--surface); + color: var(--muted); + } + + .muted { + color: var(--muted); + } + + .section-title { + font-size: 1.1rem; + letter-spacing: 0.4px; + text-transform: uppercase; + color: var(--muted); + } diff --git a/assets/js/main.js b/assets/js/main.js index d349598..16175d4 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,39 +1,9 @@ -document.addEventListener('DOMContentLoaded', () => { - const chatForm = document.getElementById('chat-form'); - const chatInput = document.getElementById('chat-input'); - const chatMessages = document.getElementById('chat-messages'); - - const appendMessage = (text, sender) => { - const msgDiv = document.createElement('div'); - msgDiv.classList.add('message', sender); - msgDiv.textContent = text; - chatMessages.appendChild(msgDiv); - chatMessages.scrollTop = chatMessages.scrollHeight; - }; - - chatForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const message = chatInput.value.trim(); - if (!message) return; - - appendMessage(message, 'visitor'); - chatInput.value = ''; - - try { - const response = await fetch('api/chat.php', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ message }) - }); - const data = await response.json(); - - // Artificial delay for realism - setTimeout(() => { - appendMessage(data.reply, 'bot'); - }, 500); - } catch (error) { - console.error('Error:', error); - appendMessage("Sorry, something went wrong. Please try again.", 'bot'); - } - }); -}); + document.addEventListener('DOMContentLoaded', () => { + const alerts = document.querySelectorAll('.alert[data-autohide="true"]'); + alerts.forEach((alert) => { + setTimeout(() => { + alert.classList.add('fade'); + alert.classList.remove('show'); + }, 4000); + }); + }); diff --git a/build.php b/build.php new file mode 100644 index 0000000..ae42c37 --- /dev/null +++ b/build.php @@ -0,0 +1,153 @@ +prepare("INSERT INTO build_comments (build_id, author, body) VALUES (:build_id, :author, :body)"); + $stmt->execute([ + ':build_id' => $id, + ':author' => $author, + ':body' => $body, + ]); + header('Location: build.php?id=' . $id . '&commented=1'); + exit; + } +} + +$stmt = $pdo->prepare("SELECT * FROM builds WHERE id = :id"); +$stmt->execute([':id' => $id]); +$build = $stmt->fetch(); + +if (!$build) { + $pageTitle = 'Build Not Found'; +} + +$comments = []; +if ($build) { + $stmt = $pdo->prepare("SELECT * FROM build_comments WHERE build_id = :id ORDER BY created_at DESC"); + $stmt->execute([':id' => $id]); + $comments = $stmt->fetchAll(); +} + +include __DIR__ . '/includes/header.php'; +?> + + +
+

Build not found

+

The build you requested does not exist. Try browsing the build library.

+ Back to builds +
+ +
+
+ + + Patch + +

+

· Authored by ·

+
+ Publish new build +
+ + + + + + + + + + + +
+
+
+

Build summary

+

+
+

Skill priorities

+

+

Gear & stats

+

+
+
+
+
+

Patch impact notes

+

Track changes and explain why this build remains optimal.

+
    +
  • Meta Recommended for current season.
  • +
  • Stats Focus on cooldown + crit.
  • +
  • Tips Positioning advice included in skills block.
  • +
+
+
+
+ +
+
+

Community comments

+ comments +
+
+
+
+ + +
+
+ + +
+
+ +
+ + +

No comments yet. Share first impressions.

+ +
+ +
+
+ + +
+

+
+ +
+ +
+ + + diff --git a/builds.php b/builds.php new file mode 100644 index 0000000..af103b9 --- /dev/null +++ b/builds.php @@ -0,0 +1,126 @@ + trim($_GET['search'] ?? ''), + 'game' => trim($_GET['game'] ?? ''), + 'class' => trim($_GET['class'] ?? ''), + 'patch' => trim($_GET['patch'] ?? ''), +]; + +$sql = "SELECT id, title, game, class_name, patch, summary, author, created_at FROM builds WHERE 1=1"; +$params = []; + +if ($filters['search']) { + $sql .= " AND (title LIKE :search OR summary LIKE :search)"; + $params[':search'] = '%' . $filters['search'] . '%'; +} +if ($filters['game']) { + $sql .= " AND game = :game"; + $params[':game'] = $filters['game']; +} +if ($filters['class']) { + $sql .= " AND class_name = :class"; + $params[':class'] = $filters['class']; +} +if ($filters['patch']) { + $sql .= " AND patch = :patch"; + $params[':patch'] = $filters['patch']; +} + +$sql .= " ORDER BY created_at DESC"; +$stmt = $pdo->prepare($sql); +$stmt->execute($params); +$builds = $stmt->fetchAll(); + +$games = $pdo->query("SELECT DISTINCT game FROM builds ORDER BY game")->fetchAll(PDO::FETCH_COLUMN); +$classes = $pdo->query("SELECT DISTINCT class_name FROM builds ORDER BY class_name")->fetchAll(PDO::FETCH_COLUMN); +$patches = $pdo->query("SELECT DISTINCT patch FROM builds WHERE patch IS NOT NULL AND patch != '' ORDER BY patch")->fetchAll(PDO::FETCH_COLUMN); + +include __DIR__ . '/includes/header.php'; +?> + +
+
+

Build Library

+

Filter by game, class, and patch for fast comparisons.

+
+ Publish build +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+ +
+
+

No matches yet

+

Try broadening filters or publish a new build to seed the library.

+ Publish build +
+
+ + +
+
+
+ + + Patch + +
+

+

+
+ · + View +
+
+
+ + +
+ + diff --git a/create_build.php b/create_build.php new file mode 100644 index 0000000..e2c429e --- /dev/null +++ b/create_build.php @@ -0,0 +1,114 @@ + '', + 'game' => '', + 'class_name' => '', + 'patch' => '', + 'summary' => '', + 'skills' => '', + 'gear' => '', + 'author' => '', +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + foreach ($values as $key => $value) { + $values[$key] = trim($_POST[$key] ?? ''); + } + + if ($values['title'] === '') { + $errors[] = 'Title is required.'; + } + if ($values['game'] === '') { + $errors[] = 'Game is required.'; + } + if ($values['class_name'] === '') { + $errors[] = 'Class is required.'; + } + if ($values['author'] === '') { + $errors[] = 'Author name is required.'; + } + + if (!$errors) { + $stmt = db()->prepare( + "INSERT INTO builds (title, game, class_name, patch, summary, skills, gear, author) + VALUES (:title, :game, :class_name, :patch, :summary, :skills, :gear, :author)" + ); + $stmt->execute([ + ':title' => $values['title'], + ':game' => $values['game'], + ':class_name' => $values['class_name'], + ':patch' => $values['patch'] ?: null, + ':summary' => $values['summary'] ?: null, + ':skills' => $values['skills'] ?: null, + ':gear' => $values['gear'] ?: null, + ':author' => $values['author'], + ]); + $id = (int)db()->lastInsertId(); + header('Location: build.php?id=' . $id . '&created=1'); + exit; + } +} + +include __DIR__ . '/includes/header.php'; +?> + +
+
+

Publish a Build

+

Share your strategy with a concise overview, skills, and gear notes.

+
+ Back to builds +
+ + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Keep it short: 1-2 sentences.
+
+
+ + +
+
+ + +
+
+ +
+ + diff --git a/create_thread.php b/create_thread.php new file mode 100644 index 0000000..830dea3 --- /dev/null +++ b/create_thread.php @@ -0,0 +1,95 @@ + '', + 'game' => '', + 'tag' => '', + 'body' => '', + 'author' => '', +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + foreach ($values as $key => $value) { + $values[$key] = trim($_POST[$key] ?? ''); + } + + if ($values['title'] === '') { + $errors[] = 'Title is required.'; + } + if ($values['game'] === '') { + $errors[] = 'Game is required.'; + } + if ($values['body'] === '' || strlen($values['body']) < 10) { + $errors[] = 'Opening post must be at least 10 characters.'; + } + if ($values['author'] === '') { + $errors[] = 'Author name is required.'; + } + + if (!$errors) { + $stmt = db()->prepare( + "INSERT INTO forum_threads (title, game, tag, body, author) + VALUES (:title, :game, :tag, :body, :author)" + ); + $stmt->execute([ + ':title' => $values['title'], + ':game' => $values['game'], + ':tag' => $values['tag'] ?: null, + ':body' => $values['body'], + ':author' => $values['author'], + ]); + $id = (int)db()->lastInsertId(); + header('Location: thread.php?id=' . $id . '&created=1'); + exit; + } +} + +include __DIR__ . '/includes/header.php'; +?> + +
+
+

Start a Thread

+

Lead the conversation on builds, patches, or loot.

+
+ Back to forums +
+ + + + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + diff --git a/db/migrations/001_builds_forums.sql b/db/migrations/001_builds_forums.sql new file mode 100644 index 0000000..3d319b9 --- /dev/null +++ b/db/migrations/001_builds_forums.sql @@ -0,0 +1,43 @@ +-- Initial builds + forums tables +CREATE TABLE IF NOT EXISTS builds ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(160) NOT NULL, + game VARCHAR(80) NOT NULL, + class_name VARCHAR(80) NOT NULL, + patch VARCHAR(40) DEFAULT NULL, + summary VARCHAR(240) DEFAULT NULL, + skills TEXT, + gear TEXT, + author VARCHAR(80) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS build_comments ( + id INT AUTO_INCREMENT PRIMARY KEY, + build_id INT NOT NULL, + author VARCHAR(80) NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX (build_id), + CONSTRAINT fk_build_comment FOREIGN KEY (build_id) REFERENCES builds(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS forum_threads ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(160) NOT NULL, + game VARCHAR(80) NOT NULL, + tag VARCHAR(40) DEFAULT NULL, + body TEXT NOT NULL, + author VARCHAR(80) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS forum_posts ( + id INT AUTO_INCREMENT PRIMARY KEY, + thread_id INT NOT NULL, + author VARCHAR(80) NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX (thread_id), + CONSTRAINT fk_thread_post FOREIGN KEY (thread_id) REFERENCES forum_threads(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/forums.php b/forums.php new file mode 100644 index 0000000..c49a5ee --- /dev/null +++ b/forums.php @@ -0,0 +1,108 @@ + trim($_GET['search'] ?? ''), + 'game' => trim($_GET['game'] ?? ''), + 'tag' => trim($_GET['tag'] ?? ''), +]; + +$sql = "SELECT id, title, game, tag, author, created_at FROM forum_threads WHERE 1=1"; +$params = []; + +if ($filters['search']) { + $sql .= " AND (title LIKE :search OR body LIKE :search)"; + $params[':search'] = '%' . $filters['search'] . '%'; +} +if ($filters['game']) { + $sql .= " AND game = :game"; + $params[':game'] = $filters['game']; +} +if ($filters['tag']) { + $sql .= " AND tag = :tag"; + $params[':tag'] = $filters['tag']; +} + +$sql .= " ORDER BY created_at DESC"; +$stmt = $pdo->prepare($sql); +$stmt->execute($params); +$threads = $stmt->fetchAll(); + +$games = $pdo->query("SELECT DISTINCT game FROM forum_threads ORDER BY game")->fetchAll(PDO::FETCH_COLUMN); +$tags = $pdo->query("SELECT DISTINCT tag FROM forum_threads WHERE tag IS NOT NULL AND tag != '' ORDER BY tag")->fetchAll(PDO::FETCH_COLUMN); + +include __DIR__ . '/includes/header.php'; +?> + +
+
+

Community Forums

+

Patch reaction, class strategy, and loot discussions.

+
+ Start thread +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+ +
+ +
+
+

No threads yet

+

Start a discussion about the latest patch or class strategy.

+ Start thread +
+
+ + +
+
+
+ + + + +
+

+

By ·

+ Open thread +
+
+ + +
+ + diff --git a/includes/app.php b/includes/app.php new file mode 100644 index 0000000..d842a8a --- /dev/null +++ b/includes/app.php @@ -0,0 +1,67 @@ +exec( + "CREATE TABLE IF NOT EXISTS builds ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(160) NOT NULL, + game VARCHAR(80) NOT NULL, + class_name VARCHAR(80) NOT NULL, + patch VARCHAR(40) DEFAULT NULL, + summary VARCHAR(240) DEFAULT NULL, + skills TEXT, + gear TEXT, + author VARCHAR(80) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" + ); + + $pdo->exec( + "CREATE TABLE IF NOT EXISTS build_comments ( + id INT AUTO_INCREMENT PRIMARY KEY, + build_id INT NOT NULL, + author VARCHAR(80) NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX (build_id), + CONSTRAINT fk_build_comment FOREIGN KEY (build_id) REFERENCES builds(id) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" + ); + + $pdo->exec( + "CREATE TABLE IF NOT EXISTS forum_threads ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(160) NOT NULL, + game VARCHAR(80) NOT NULL, + tag VARCHAR(40) DEFAULT NULL, + body TEXT NOT NULL, + author VARCHAR(80) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" + ); + + $pdo->exec( + "CREATE TABLE IF NOT EXISTS forum_posts ( + id INT AUTO_INCREMENT PRIMARY KEY, + thread_id INT NOT NULL, + author VARCHAR(80) NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX (thread_id), + CONSTRAINT fk_thread_post FOREIGN KEY (thread_id) REFERENCES forum_threads(id) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" + ); +} + +function format_date(string $value): string { + return date('M j, Y H:i', strtotime($value)); +} diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..5197bb3 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,14 @@ + + + + + + diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..8f7782f --- /dev/null +++ b/includes/header.php @@ -0,0 +1,48 @@ + + + + + + + <?= h($fullTitle) ?> + + + + + + + + + + + + + + + +
diff --git a/index.php b/index.php index 7205f3d..c7a17a2 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,110 @@ query("SELECT id, title, game, class_name, patch, summary, author, created_at FROM builds ORDER BY created_at DESC LIMIT 3")->fetchAll(); +$threads = $pdo->query("SELECT id, title, game, tag, author, created_at FROM forum_threads ORDER BY created_at DESC LIMIT 3")->fetchAll(); + +include __DIR__ . '/includes/header.php'; ?> - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… + +
+
+
+ Patch-ready community hub +

Track the best builds and keep the meta in focus.

+

Publish build guides, track patch notes, and keep tactical discussions organized. This MVP delivers searchable builds, build detail pages with comments, and live forum threads.

+ -

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- Page updated: (UTC) -
- - +
+
+

Live pulse

+
    +
  • Active buildsquery("SELECT COUNT(*) FROM builds")->fetchColumn()) ?>
  • +
  • Forum threadsquery("SELECT COUNT(*) FROM forum_threads")->fetchColumn()) ?>
  • +
  • Latest update
  • +
+
+
+ + + +
+
+

Featured builds

+ View all → +
+
+ +
+
+

No builds yet

+

Start by publishing the first build so the community can discuss skills, gear, and patch impact.

+ Publish first build +
+
+ + +
+
+
+ + + Patch + +
+

+

+
+ · + Open +
+
+
+ + +
+
+ +
+
+

Latest forum threads

+ View all → +
+
+ +
+
+

No threads yet

+

Kick off the first discussion with patch feedback or class strategy.

+ Start thread +
+
+ + +
+
+
+ + + + +
+

+

Started by ·

+ Open thread +
+
+ + +
+
+ + diff --git a/thread.php b/thread.php new file mode 100644 index 0000000..f10bd32 --- /dev/null +++ b/thread.php @@ -0,0 +1,146 @@ +prepare("INSERT INTO forum_posts (thread_id, author, body) VALUES (:thread_id, :author, :body)"); + $stmt->execute([ + ':thread_id' => $id, + ':author' => $author, + ':body' => $body, + ]); + header('Location: thread.php?id=' . $id . '&posted=1'); + exit; + } +} + +$stmt = $pdo->prepare("SELECT * FROM forum_threads WHERE id = :id"); +$stmt->execute([':id' => $id]); +$thread = $stmt->fetch(); + +if (!$thread) { + $pageTitle = 'Thread Not Found'; +} + +$posts = []; +if ($thread) { + $stmt = $pdo->prepare("SELECT * FROM forum_posts WHERE thread_id = :id ORDER BY created_at DESC"); + $stmt->execute([':id' => $id]); + $posts = $stmt->fetchAll(); +} + +include __DIR__ . '/includes/header.php'; +?> + + +
+

Thread not found

+

This thread may have been removed or never existed.

+ Back to forums +
+ +
+
+ + + + +

+

Started by ·

+
+ Start new thread +
+ + + + + + + + + + + +
+
+
+

Opening post

+

+
+
+
+
+

Thread focus

+
    +
  • Game:
  • +
  • Tag:
  • +
  • Replies:
  • +
+
+
+
+ +
+
+

Replies

+ replies +
+
+
+
+ + +
+
+ + +
+
+ +
+ + +

No replies yet. Be the first to respond.

+ +
+ +
+
+ + +
+

+
+ +
+ +
+ + +