sark 1 mês atrás
pai
commit
e7ec3a2dda

+ 19 - 0
bin/migrate.php

@@ -174,6 +174,25 @@ try {
     $db->execute($seoMetasIndexSQL);
     echo "✓ SEO Metas table index checked/created\n";
     
+    // Migration: Create faqs table
+    $faqsTableSQL = "
+        CREATE TABLE IF NOT EXISTS faqs (
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        faqable_type VARCHAR(255) NOT NULL,
+        faqable_id INTEGER NOT NULL,
+        title VARCHAR(255) NULL,
+        extra_fields JSON NULL,
+        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
+    )";
+    $db->execute($faqsTableSQL);
+    echo "✓ FAQs table checked/created\n";
+    
+    // Create indexes for faqs
+    $faqsIndexSQL = "CREATE INDEX IF NOT EXISTS idx_faqs_faqable ON faqs(faqable_type, faqable_id)";
+    $db->execute($faqsIndexSQL);
+    echo "✓ FAQs table index checked/created\n";
+    
     echo "Migration completed successfully!\n";
     
 } catch (Exception $e) {

BIN
database/database.db


+ 67 - 0
resources/view/admin/pages/form.php

@@ -111,6 +111,14 @@ ob_start();
                                         </svg>
                                         Content
                                     </button>
+                                    <button type="button" @click="activePageTab = 'faq'" 
+                                            :class="activePageTab === 'faq' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
+                                            class="py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap transition-colors">
+                                        <svg class="w-5 h-5 inline mr-2" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
+                                        </svg>
+                                        FAQ
+                                    </button>
                                     <button type="button" @click="activePageTab = 'seo'" 
                                             :class="activePageTab === 'seo' ? 'border-blue-500 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'"
                                             class="py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap transition-colors">
@@ -155,6 +163,65 @@ ob_start();
                             </div>
                         </div>
 
+                        <!-- FAQ Tab -->
+                        <?php 
+                        $faqItemsJson = !empty($faqs['extra_fields']) ? htmlspecialchars(json_encode($faqs['extra_fields']), ENT_QUOTES, 'UTF-8') : '[]';
+                        ?>
+                        <div x-show="activePageTab === 'faq'" 
+                             x-data='{ faqItems: <?= $faqItemsJson ?> }'
+                             x-transition:enter="transition ease-out duration-200"
+                             x-transition:enter-start="opacity-0"
+                             x-transition:enter-end="opacity-100"
+                             x-transition:leave="transition ease-in duration-150"
+                             x-transition:leave-start="opacity-100"
+                             x-transition:leave-end="opacity-0"
+                             class="bg-white rounded-lg shadow-sm mt-6">
+                            <div class="p-6">
+                                <h3 class="text-lg font-medium text-gray-900 mb-6">FAQ Management</h3>
+                                
+                                <div class="mb-6">
+                                    <label for="faq_title" class="block text-sm font-medium text-gray-700 mb-2">FAQ Section Title</label>
+                                    <input type="text" name="faq[title]" id="faq_title" 
+                                           value="<?= htmlspecialchars($faqs['title'] ?? '') ?>"
+                                           placeholder="Enter FAQ section title..." 
+                                           class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
+                                </div>
+                                
+                                <div class="space-y-4">
+                                    <template x-for="(item, index) in faqItems" :key="index">
+                                        <div class="faq-item bg-gray-50 rounded-lg p-4 border border-gray-200">
+                                            <div class="space-y-4">
+                                                <div>
+                                                    <label class="block text-sm font-medium text-gray-700 mb-2">Question</label>
+                                                    <input type="text" :name="`faq[items][${index}][question]`" x-model="item.question"
+                                                           placeholder="Enter FAQ question..." 
+                                                           class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
+                                                </div>
+                                                <div>
+                                                    <label class="block text-sm font-medium text-gray-700 mb-2">Answer</label>
+                                                    <textarea :name="`faq[items][${index}][answer]`" x-model="item.answer" rows="3"
+                                                              placeholder="Enter FAQ answer..."
+                                                              class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"></textarea>
+                                                </div>
+                                                <button type="button" @click="faqItems.splice(index, 1)"
+                                                        class="inline-flex items-center px-3 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
+                                                    Remove
+                                                </button>
+                                            </div>
+                                        </div>
+                                    </template>
+                                    
+                                    <button type="button" @click="faqItems.push({ question: '', answer: '' })"
+                                            class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
+                                        <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Add FAQ Item
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+
                         <!-- SEO Tab -->
                         <div x-show="activePageTab === 'seo'" 
                              x-transition:enter="transition ease-out duration-200"

+ 150 - 0
src/Models/Faq.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Models;
+
+use App\Database;
+
+class Faq
+{
+    private $db;
+    
+    public function __construct()
+    {
+        $this->db = Database::getInstance();
+    }
+    
+    public function getAll()
+    {
+        return $this->db->fetchAll("SELECT * FROM faqs ORDER BY created_at DESC");
+    }
+    
+    public function getById($id)
+    {
+        $result = $this->db->fetchOne("SELECT * FROM faqs WHERE id = ?", [$id]);
+        
+        // Decode extra_fields JSON
+        if ($result && !empty($result['extra_fields'])) {
+            $result['extra_fields'] = json_decode($result['extra_fields'], true) ?: [];
+        } else if ($result) {
+            $result['extra_fields'] = [];
+        }
+        
+        return $result;
+    }
+    
+    public function getByMorphable($faqableType, $faqableId)
+    {
+        $result = $this->db->fetchAll(
+            "SELECT * FROM faqs WHERE faqable_type = ? AND faqable_id = ? ORDER BY created_at ASC", 
+            [strtolower($faqableType), $faqableId]
+        );
+        
+        // Decode extra_fields JSON for each record
+        foreach ($result as &$item) {
+            if (!empty($item['extra_fields'])) {
+                $item['extra_fields'] = json_decode($item['extra_fields'], true) ?: [];
+            } else {
+                $item['extra_fields'] = [];
+            }
+        }
+        
+        return $result;
+    }
+    
+    public function create($data)
+    {
+        $sql = "INSERT INTO faqs (faqable_type, faqable_id, title, extra_fields) VALUES (?, ?, ?, ?)";
+        $params = [
+            strtolower($data['faqable_type']),
+            $data['faqable_id'],
+            $data['title'] ?? null,
+            $data['extra_fields'] ? json_encode($data['extra_fields']) : null
+        ];
+        
+        $this->db->execute($sql, $params);
+        return $this->db->lastInsertId();
+    }
+    
+    public function update($id, $data)
+    {
+        $sql = "UPDATE faqs SET faqable_type = ?, faqable_id = ?, title = ?, extra_fields = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?";
+        $params = [
+            strtolower($data['faqable_type']),
+            $data['faqable_id'],
+            $data['title'] ?? null,
+            $data['extra_fields'] ? json_encode($data['extra_fields']) : null,
+            $id
+        ];
+        
+        return $this->db->execute($sql, $params);
+    }
+    
+    public function updateOrCreate($faqableType, $faqableId, $data)
+    {
+        $faqableType = strtolower($faqableType);
+        $existing = $this->getByMorphable($faqableType, $faqableId);
+        
+        $data['faqable_type'] = $faqableType;
+        $data['faqable_id'] = $faqableId;
+        
+        if (!empty($existing)) {
+            return $this->update($existing[0]['id'], $data);
+        } else {
+            return $this->create($data);
+        }
+    }
+    
+    public function delete($id)
+    {
+        return $this->db->execute("DELETE FROM faqs WHERE id = ?", [$id]);
+    }
+    
+    public function deleteByMorphable($faqableType, $faqableId)
+    {
+        return $this->db->execute(
+            "DELETE FROM faqs WHERE faqable_type = ? AND faqable_id = ?", 
+            [strtolower($faqableType), $faqableId]
+        );
+    }
+    
+    public function exists($faqableType, $faqableId)
+    {
+        $result = $this->db->fetchOne(
+            "SELECT COUNT(*) as count FROM faqs WHERE faqable_type = ? AND faqable_id = ?", 
+            [strtolower($faqableType), $faqableId]
+        );
+        return $result['count'] > 0;
+    }
+    
+    // Helper methods for any entity type
+    public function getForRecord($entityType, $entityId)
+    {
+        return $this->getByMorphable($entityType, $entityId);
+    }
+    
+    public function createForRecord($entityType, $entityId, $data)
+    {
+        return $this->updateOrCreate($entityType, $entityId, $data);
+    }
+    
+    public function deleteForRecord($entityType, $entityId)
+    {
+        return $this->deleteByMorphable($entityType, $entityId);
+    }
+    
+    // Convenience methods for specific entity types
+    public function getForPage($pageId)
+    {
+        return $this->getForRecord('page', $pageId);
+    }
+    
+    public function createForPage($pageId, $data)
+    {
+        return $this->createForRecord('page', $pageId, $data);
+    }
+    
+    public function deleteForPage($pageId)
+    {
+        return $this->deleteForRecord('page', $pageId);
+    }
+}

+ 16 - 0
src/Pages/admin/pages.php

@@ -2,10 +2,12 @@
 
 use App\Models\Page;
 use App\Models\SeoMeta;
+use App\Models\Faq;
 use App\Enums\PageLayout;
 
 $pageModel = new Page();
 $seoMetaModel = new SeoMeta();
+$faqModel = new Faq();
 $action = $_GET['action'] ?? 'list';
 $id = $_GET['id'] ?? null;
 
@@ -39,6 +41,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
                 if (!empty($data['seo'])) {
                     $seoMetaModel->updateOrCreate('Page', $pageId, $data['seo']);
                 }
+                
+                // Save FAQ data if provided
+                if (!empty($data['faq'])) {
+                    $faqData = [
+                        'title' => $data['faq']['title'] ?? '',
+                        'extra_fields' => $data['faq']['items'] ?? []
+                    ];
+                    $faqModel->updateOrCreate('page', $pageId, $faqData);
+                }
                 header("Location: /admin/pages/?action=edit&id=" . $pageId);exit;
 
                 break;
@@ -89,6 +100,10 @@ switch ($action) {
         
         // Load SEO data
         $seoMeta = $seoMetaModel->getForRecord('page', $id) ?: [];
+        
+        // Load FAQ data
+        $faqData = $faqModel->getForRecord('page', $id);
+        $faqs = !empty($faqData) && is_array($faqData) ? $faqData[0] : [];
         break;
         
     case 'list':
@@ -113,6 +128,7 @@ return ViewRender($template, [
     'pages' => $pages ?? [],
     'page' => $page ?? null,
     'seoMeta' => $seoMeta ?? [],
+    'faqs' => $faqs ?? [],
     'layouts' => $layouts,
     'success' => $success ?? null,
     'error' => $error ?? null

+ 8 - 0
src/Pages/home.php

@@ -3,10 +3,12 @@
 use App\Settings;
 use App\Models\Page;
 use App\Models\SeoMeta;
+use App\Models\Faq;
 
 // Получение главной страницы
 $pageModel = new Page();
 $seoMetaModel = new SeoMeta();
+$faqModel = new Faq();
 
 // Находим главную страницу
 $homepage = \db()->fetchOne("SELECT * FROM pages WHERE is_homepage = 1");
@@ -30,6 +32,11 @@ $pageContent = $homepage['content'] ?? '';
 $extraFields = json_decode($homepage['extra_fields'] ?? '{}', true) ?: [];
 $topContent = $extraFields['top_content'] ?? '';
 
+// Загружаем FAQ для главной страницы
+$faqData = $faqModel->getForRecord('page', $homepage['id']);
+$faqs = !empty($faqData) && is_array($faqData) ? $faqData[0] : [];
+$faqItems = $faqs['extra_fields'] ?? [];
+
 // Parse JSON fields needed for view
 $bannerLogo = json_decode($content['banner-logo'] ?? '{}', true);
 $casinoItems = json_decode($content['casino_list'] ?? '[]', true);
@@ -72,6 +79,7 @@ return ViewRender('home.php', [
     'content' => $content,
     'seo' => $seo,
     'faqItems' => $faqItems,
+    'faqTitle' => $faqs['title'] ?? '',
     'pageContent' => $pageContent,
     'topContent' => $topContent,
     'currentDomain' => $currentDomain,