Explorar o código

fx conflict deleted index.php

Vitalik hai 1 mes
pai
achega
a33d75e641

+ 0 - 11
bin/migrate.php

@@ -121,17 +121,6 @@ try {
         ['grupa' => 'styles', 'name' => 'burger-color2', 'value' => '#ffffff'],
 
         // SEO settings
-        ['grupa' => 'seo', 'name' => 'title', 'value' => 'Amazing Casino - Best Online Gaming Experience 2024'],
-        ['grupa' => 'seo', 'name' => 'description', 'value' => 'Join Amazing Casino for the ultimate online gaming experience. Enjoy 200+ games, generous bonuses, and secure payments. Licensed and trusted worldwide.'],
-        ['grupa' => 'seo', 'name' => 'keywords', 'value' => 'online casino, casino games, slots, blackjack, poker, roulette, bonuses, secure gaming'],
-        ['grupa' => 'seo', 'name' => 'canonical', 'value' => 'https://amazing-casino.com'],
-        ['grupa' => 'seo', 'name' => 'og-type', 'value' => 'website'],
-        ['grupa' => 'seo', 'name' => 'og-title', 'value' => 'Amazing Casino - Win Big with Our Casino Games'],
-        ['grupa' => 'seo', 'name' => 'og-description', 'value' => 'Experience the thrill of premium casino games with Amazing Casino. Get your 200% welcome bonus today!'],
-        ['grupa' => 'seo', 'name' => 'og-locale', 'value' => 'en_US'],
-        ['grupa' => 'seo', 'name' => 'og-image', 'value' => 'media/og-image.jpg'],
-        ['grupa' => 'seo', 'name' => 'lang', 'value' => 'en'],
-        ['grupa' => 'seo', 'name' => 'lang-page', 'value' => 'en-US'],
         ['grupa' => 'seo', 'name' => 'hreflang', 'value' => '[{"hreflang":"en","href":"https://amazing-casino.com/en/"},{"hreflang":"es","href":"https://amazing-casino.com/es/"},{"hreflang":"fr","href":"https://amazing-casino.com/fr/"},{"hreflang":"x-default","href":"https://amazing-casino.com/"}]'],
     ];
     

+ 2 - 1
public/index.php

@@ -4,7 +4,7 @@ require dirname(__FILE__, 2).'/vendor/autoload.php';
 //route
 $requestUri = $_GET['p'] ?? parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
 $page = match (true) {
-  ('/' === $requestUri) => 'home.php',
+  ('/' === $requestUri) => 'page.php',
   ('/admin/' === $requestUri) => 'admin/index.php',
   ('/admin/pages/' === $requestUri) => 'admin/pages.php',
   ('/admin/slots/' === $requestUri) => 'admin/slots.php',
@@ -12,6 +12,7 @@ $page = match (true) {
   ('/editor/uploads/' === $requestUri) => 'upload.php',
   ('/verstka/' === $requestUri) => 'verstka.php',
   (preg_match('#^/redirect/([\w-]+)/$#', $requestUri, $matches) === 1) => 'redirect.php',
+  (preg_match('#^/([\w-]+)/$#', $requestUri, $matches) === 1) => 'page.php',
   default => '404.php',
 };
 

+ 196 - 33
resources/view/admin/index_new.php → resources/view/admin/index.php

@@ -19,6 +19,10 @@ $tabs = [
     'seo' => [
         'label' => 'SEO & Meta',
         'icon' => 'M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z'
+    ],
+    'jsonld' => [
+        'label' => 'JSON-LD',
+        'icon' => 'M8 4a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1zm0 4a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1zm0 4a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z'
     ]
 ];
 
@@ -370,19 +374,6 @@ ob_start();
                                             </div>
                                         </div>
 
-                                        <!-- Акцентный цвет -->
-                                        <div>
-                                            <label class="block text-sm font-medium text-gray-700 mb-2">Акцентный цвет (для важных элементов)</label>
-                                            <div class="flex gap-3">
-                                                <input type="color" value="<?= $styles['accent_color'] ?? '#FF6B35' ?>"
-                                                       class="w-12 h-10 rounded border border-gray-300 cursor-pointer"
-                                                       onchange="this.nextElementSibling.value = this.value">
-                                                <input type="text" name='styles[accent_color]' value="<?= htmlspecialchars($styles['accent_color'] ?? '') ?>"
-                                                       placeholder="#FF6B35"
-                                                       class="flex-1 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>
-
                                         <!-- Цвет таблицы -->
                                         <div>
                                             <label class="block text-sm font-medium text-gray-700 mb-2">Цвет таблицы (фон)</label>
@@ -421,19 +412,6 @@ ob_start();
                                                        class="flex-1 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>
-
-                                        <!-- Цвет рамки контента -->
-                                        <div>
-                                            <label class="block text-sm font-medium text-gray-700 mb-2">Цвет рамки контента</label>
-                                            <div class="flex gap-3">
-                                                <input type="color" value="<?= $styles['content_border_color'] ?? '#E4E4E4' ?>"
-                                                       class="w-12 h-10 rounded border border-gray-300 cursor-pointer"
-                                                       onchange="this.nextElementSibling.value = this.value">
-                                                <input type="text" name='styles[content_border_color]' value="<?= htmlspecialchars($styles['content_border_color'] ?? '') ?>"
-                                                       placeholder="#E4E4E4"
-                                                       class="flex-1 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>
                                     </div>
                                 </div>
 
@@ -451,13 +429,198 @@ ob_start();
 
                         <!-- SEO Tab -->
                         <div x-show="activeTab === 'seo'" class="bg-white rounded-lg shadow-sm">
-                            <div class="p-6">
-                                <div class="text-center py-12">
-                                    <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
-                                    </svg>
-                                    <h3 class="mt-2 text-sm font-medium text-gray-900">SEO Coming Soon</h3>
-                                    <p class="mt-1 text-sm text-gray-500">SEO settings will be implemented in the next update.</p>
+                            <div class="p-6 space-y-8">
+
+                                <!-- Hreflang Settings -->
+                                <div x-data="{ 
+                                    hreflangs: <?= htmlspecialchars(json_encode(isset($seo['hreflang']) ? json_decode($seo['hreflang'], true) ?: [] : []), ENT_QUOTES, 'UTF-8') ?>,
+                                    addHreflang() {
+                                        this.hreflangs.push({hreflang: '', href: ''});
+                                    },
+                                    removeHreflang(index) {
+                                        this.hreflangs.splice(index, 1);
+                                    }
+                                }">
+                                    <h3 class="text-lg font-medium text-gray-900 mb-6 flex items-center">
+                                        <svg class="w-5 h-5 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M7 2a1 1 0 011 1v1h3a1 1 0 110 2H9.578a18.87 18.87 0 01-1.724 4.78c.29.354.596.696.914 1.026a1 1 0 11-1.44 1.389c-.188-.196-.373-.396-.554-.6a18.9 18.9 0 01-2.62 3.624 1 1 0 11-1.44-1.389A16.9 16.9 0 003.578 8H2a1 1 0 110-2h2.578c.15-.667.34-1.32.578-1.96A1 1 0 015.5 4c.76 0 1.5.11 2.5.342V3a1 1 0 011-1zm1.578 6H7.422c.12.456.264.903.428 1.34.164-.437.308-.884.428-1.34zM17 6a3 3 0 11-6 0 3 3 0 016 0z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Hreflang Settings
+                                    </h3>
+                                    
+                                    <!-- Hidden input to store JSON data -->
+                                    <input type="hidden" name="seo[hreflang]" x-bind:value="JSON.stringify(hreflangs)">
+                                    
+                                    <div class="space-y-4">
+                                        <template x-for="(item, index) in hreflangs" :key="index">
+                                            <div class="flex gap-4 items-end">
+                                                <div class="flex-1">
+                                                    <label class="block text-sm font-medium text-gray-700 mb-2">Language Code</label>
+                                                    <input type="text" x-model="item.hreflang" placeholder="en, ru, de, x-default" 
+                                                           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="flex-2">
+                                                    <label class="block text-sm font-medium text-gray-700 mb-2">URL</label>
+                                                    <input type="url" x-model="item.href" placeholder="https://example.com/en/" 
+                                                           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>
+                                                <button type="button" @click="removeHreflang(index)" 
+                                                        class="px-3 py-2 text-red-600 hover:text-red-800 border border-red-300 hover:border-red-500 rounded-md transition-colors">
+                                                    <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
+                                                        <path fill-rule="evenodd" d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" clip-rule="evenodd"/>
+                                                        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
+                                                    </svg>
+                                                </button>
+                                            </div>
+                                        </template>
+                                        
+                                        <button type="button" @click="addHreflang()" 
+                                                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 Hreflang
+                                        </button>
+                                    </div>
+                                </div>
+
+                                <!-- Save Button -->
+                                <div class="pt-6 border-t border-gray-200">
+                                    <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors">
+                                        <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Save Settings
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- JSON-LD Tab -->
+                        <div x-show="activeTab === 'jsonld'" class="bg-white rounded-lg shadow-sm">
+                            <div class="p-6 space-y-8">
+
+                                <!-- Organization Settings -->
+                                <div>
+                                    <h3 class="text-lg font-medium text-gray-900 mb-6 flex items-center">
+                                        <svg class="w-5 h-5 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 110 2h-3a1 1 0 01-1-1v-2a1 1 0 00-1-1H9a1 1 0 00-1 1v2a1 1 0 01-1 1H4a1 1 0 110-2V4z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Organization
+                                    </h3>
+                                    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
+                                        <div>
+                                            <label class="block text-sm font-medium text-gray-700 mb-2">Organization Name</label>
+                                            <input type="text" name='jsonld[organization_name]' value="<?= htmlspecialchars($jsonld['organization_name'] ?? '') ?>" 
+                                                   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">Organization URL</label>
+                                            <input type="url" name='jsonld[organization_url]' value="<?= htmlspecialchars($jsonld['organization_url'] ?? '') ?>" 
+                                                   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">Same As URLs (one per line)</label>
+                                            <textarea name='jsonld[organization_same_as]' rows="4" 
+                                                      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"><?= htmlspecialchars($jsonld['organization_same_as'] ?? '') ?></textarea>
+                                        </div>
+                                        <div>
+                                            <label class="block text-sm font-medium text-gray-700 mb-2">License URL</label>
+                                            <input type="url" name='jsonld[license_url]' value="<?= htmlspecialchars($jsonld['license_url'] ?? '') ?>" 
+                                                   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>
+                                </div>
+
+                                <!-- Breadcrumb Settings -->
+                                <div>
+                                    <h3 class="text-lg font-medium text-gray-900 mb-6 flex items-center">
+                                        <svg class="w-5 h-5 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Breadcrumbs
+                                    </h3>
+                                    <div class="grid grid-cols-1 gap-6">
+                                        <div>
+                                            <label class="block text-sm font-medium text-gray-700 mb-2">Breadcrumb Home Name</label>
+                                            <input type="text" name='jsonld[breadcrumb_home_name]' value="<?= htmlspecialchars($jsonld['breadcrumb_home_name'] ?? '') ?>" 
+                                                   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>
+                                </div>
+
+                                <!-- Rating & Review Settings -->
+                                <div>
+                                    <h3 class="text-lg font-medium text-gray-900 mb-6 flex items-center">
+                                        <svg class="w-5 h-5 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Reviews & Ratings
+                                    </h3>
+                                    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
+                                        <div>
+                                            <label class="block text-sm font-medium text-gray-700 mb-2">Rating Value</label>
+                                            <input type="number" step="0.1" min="1" max="5" name='jsonld[rating_value]' value="<?= htmlspecialchars($jsonld['rating_value'] ?? '') ?>" 
+                                                   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">Best Rating</label>
+                                            <input type="number" name='jsonld[best_rating]' value="<?= htmlspecialchars($jsonld['best_rating'] ?? '5') ?>" 
+                                                   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">Worst Rating</label>
+                                            <input type="number" name='jsonld[worst_rating]' value="<?= htmlspecialchars($jsonld['worst_rating'] ?? '1') ?>" 
+                                                   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">Review Count</label>
+                                            <input type="number" name='jsonld[review_count]' value="<?= htmlspecialchars($jsonld['review_count'] ?? '') ?>" 
+                                                   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>
+                                </div>
+
+                                <!-- Software Application Settings -->
+                                <div>
+                                    <h3 class="text-lg font-medium text-gray-900 mb-6 flex items-center">
+                                        <svg class="w-5 h-5 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Software Application
+                                    </h3>
+                                    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
+                                        <div>
+                                            <label class="block text-sm font-medium text-gray-700 mb-2">App Name</label>
+                                            <input type="text" name='jsonld[app_name]' value="<?= htmlspecialchars($jsonld['app_name'] ?? '') ?>" 
+                                                   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">Operating System</label>
+                                            <input type="text" name='jsonld[operating_system]' value="<?= htmlspecialchars($jsonld['operating_system'] ?? '') ?>" 
+                                                   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">Application Category</label>
+                                            <input type="text" name='jsonld[application_category]' value="<?= htmlspecialchars($jsonld['application_category'] ?? '') ?>" 
+                                                   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">Download URL</label>
+                                            <input type="url" name='jsonld[download_url]' value="<?= htmlspecialchars($jsonld['download_url'] ?? '') ?>" 
+                                                   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>
+                                </div>
+
+                                <!-- Save Button -->
+                                <div class="pt-6 border-t border-gray-200">
+                                    <button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors">
+                                        <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
+                                            <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
+                                        </svg>
+                                        Save JSON-LD Settings
+                                    </button>
                                 </div>
                             </div>
                         </div>

+ 1 - 77
resources/view/layouts/main.php

@@ -22,83 +22,7 @@ $settingsContent = settings('content')->getAll();
     <link rel="canonical" href="<?= $seo['canonical'] ?? '' ?>">
 
     <script type="application/ld+json">
-<?= json_encode([
-    "@context" => "https://schema.org",
-    "@graph" => [
-        [
-            "@type" => "WebPage",
-            "name" => $settingsContent['title'],
-            "description" => $settingsContent['description'],
-            "inLanguage" => $settingsContent['lang'],
-            "url" => $currentUrl,
-            "dateModified" => $settingsContent['modified-date'] ?? '' , // ISO формат
-            "author" => [
-                "@type" => "Person",
-                "name" => $settingsContent['author-name'] ?? '',
-                "image" => $currentDomain . '/' . htmlspecialchars($settingsContent['author-img'] ?? '')
-            ]
-        ],
-        [
-            "@type" => "Organization",
-            "name" => $domainName,
-            "url" => $currentDomain,
-            "logo" => $currentDomain . '/' . htmlspecialchars($settingsContent['uploaded_image'] ?? '')
-        ],
-        [
-            "@type" => "BreadcrumbList",
-            "itemListElement" => [
-                [
-                    "@type" => "ListItem",
-                    "position" => 1,
-                    "name" => $settingsContent['title-h1'],
-                    "item" => $currentUrl
-                ]
-            ]
-        ],
-        !empty($casinoItems) ? [
-            "@type" => "ItemList",
-            "name" => htmlspecialchars($settingsContent['listing']),
-            "itemListElement" => array_map(function ($item, $index) use ($currentDomain, $currentReviewCount) {
-                $data = json_decode($item['value'], true);
-                return [
-                    "@type" => "ListItem",
-                    "position" => $index + 1,
-                    "item" => [
-                        "@type" => "Offer",
-                        "name" => htmlspecialchars($data['heading']),
-                        "description" => htmlspecialchars($data['text']),
-                        "url" => $currentDomain . '/#' . getSlug($data['heading']),
-                        "offeredBy" => [
-                            "@type" => "Organization",
-                            "name" => htmlspecialchars($data['heading']),
-                            "logo" => $currentDomain . '/' . htmlspecialchars($data['image']),
-                            "aggregateRating" => [
-                                "@type" => "AggregateRating",
-                                "ratingValue" => "5",
-                                "reviewCount" => $currentReviewCount + ($index + 1),
-                                "bestRating" => "5",
-                                "worstRating" => "1"
-                            ]
-                        ]
-                    ]
-                ];
-            }, $casinoItems, array_keys($casinoItems))
-        ] : null,
-        [
-            "@type" => "FAQPage",
-            "mainEntity" => array_map(function ($item) {
-                return [
-                    "@type" => "Question",
-                    "name" => htmlspecialchars($item['question']),
-                    "acceptedAnswer" => [
-                        "@type" => "Answer",
-                        "text" => htmlspecialchars($item['answer'])
-                    ]
-                ];
-            }, $faqItems)
-        ]
-    ]
-], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); ?>
+        <?php include __DIR__ . '/../partials/schema.php'; ?>
     </script>
     <?php include __DIR__ . '/partials/styles.php'; ?>
 </head>

+ 71 - 0
resources/view/pages/default.php

@@ -0,0 +1,71 @@
+<?php
+$settingsContent = settings('content')->getAll();
+?>
+<!DOCTYPE html>
+<html lang="<?= $seo['extra_fields']['locale'] ?? 'en' ?>">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <base href="/">
+    <title><?= $seo['title'] ?></title>
+    <link rel="stylesheet" href="css/styles.css">
+    <meta name='robots' content='index, follow' />
+    <?php if (!empty($settingsContent['favicon'])): ?>
+        <link rel="icon" href="<?= htmlspecialchars($settingsContent['favicon']) ?>" type="image/x-icon">
+    <?php endif; ?>
+    <meta property="og:title" content="<?= $seo['title'] ?? '' ?>">
+    <meta property="og:description" content="<?= $seo['description'] ?? '' ?>">
+    <meta property="og:locale" content="<?= $seo['extra_fields']['locale'] ?? '' ?>">
+    <meta property="og:image" content="<?= htmlspecialchars($seo['image'] ?? '') ?>">
+    <meta name="description" content="<?= $seo['description'] ?? '' ?>">
+
+    <link rel="canonical" href="<?= $seo['canonical'] ?? '' ?>">
+
+    <script type="application/ld+json">
+        <?php include __DIR__ . '/../partials/schema.php'; ?>
+    </script>
+    <?php include __DIR__ . '/../partials/styles.php'; ?>
+</head>
+
+<body>
+    <?php include __DIR__ . '/../partials/header.php'; ?>
+    <main class="main">
+        <?php if (!empty($topContent)): ?>
+            <section class="top-content">
+                <div class="container">
+                    <?= $topContent ?>
+                </div>
+            </section>
+        <?php endif; ?>
+        
+        <section class="page-content">
+            <div class="container">
+                <h1><?= htmlspecialchars($pageData['name']) ?></h1>
+                <div class="content">
+                    <?= $pageContent ?>
+                </div>
+            </div>
+        </section>
+
+        <?php if (!empty($faqItems)): ?>
+            <section class="faq-section">
+                <div class="container">
+                    <h2>FAQ</h2>
+                    <div class="faq-list">
+                        <?php foreach ($faqItems as $faq): ?>
+                            <div class="faq-item">
+                                <h3><?= htmlspecialchars($faq['question']) ?></h3>
+                                <div class="faq-answer">
+                                    <?= $faq['answer'] ?>
+                                </div>
+                            </div>
+                        <?php endforeach; ?>
+                    </div>
+                </section>
+        <?php endif; ?>
+    </main>
+    <?php include __DIR__ . '/../partials/footer.php'; ?>
+    <script src="js/script.js"></script>
+</body>
+
+</html>

+ 211 - 0
resources/view/partials/schema.php

@@ -0,0 +1,211 @@
+<?php
+$settingsJsonld = settings('jsonld')->getAll();
+$schemaGraph = [];
+
+// 1. Organization
+$organizationSchema = [
+    "@type" => "Organization",
+    "@id" => $currentUrl . "#organization",
+    "name" => $settingsJsonld['organization_name'] ?? ($seo['title'] ?? $pageContent['title']),
+    "url" => $settingsJsonld['organization_url'] ?? $currentDomain,
+    "logo" => !empty($settingsContent['header-logo']) ? $currentDomain . '/' . json_decode($settingsContent['header-logo'], true)['src'] : ''
+];
+
+// Add sameAs if organization_same_as is filled
+if (!empty($settingsJsonld['organization_same_as'])) {
+    $sameAsUrls = array_filter(array_map('trim', explode("\n", $settingsJsonld['organization_same_as'])));
+    if (!empty($sameAsUrls)) {
+        $organizationSchema["sameAs"] = $sameAsUrls;
+    }
+}
+
+// Add license if filled
+if (!empty($settingsJsonld['license_url'])) {
+    $organizationSchema["license"] = $settingsJsonld['license_url'];
+}
+
+$schemaGraph[] = $organizationSchema;
+
+// 2. Casino Brand
+$casinoBrand = [
+    "@type" => ["Casino", "Brand"],
+    "@id" => $currentUrl . "#brand-casino-entity",
+    "name" => $seo['title'],
+    "url" => $seo['canonical'] ?? $currentUrl,
+    "image" => !empty($settingsContent['header-logo']) ? $currentDomain . '/' . json_decode($settingsContent['header-logo'], true)['src'] : '',
+    "aggregateRating" => [
+        "@type" => "AggregateRating",
+        "ratingValue" => $settingsJsonld['rating_value'] ?? "5",
+        "ratingCount" => $settingsJsonld['review_count'] ?? "150",
+        "worstRating" => $settingsJsonld['worst_rating'] ?? "1",
+        "bestRating" => $settingsJsonld['best_rating'] ?? "5"
+    ],
+    "priceRange" => "$",
+    "openingHoursSpecification" => [
+        [
+            "@type" => "OpeningHoursSpecification",
+            "dayOfWeek" => ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+            "opens" => "00:00",
+            "closes" => "23:59"
+        ]
+    ],
+    "parentOrganization" => [
+        "@id" => $currentUrl . "#organization"
+    ]
+];
+
+// Add address if any field is filled
+if (!empty($settingsContent['address-street']) || !empty($settingsContent['address-city']) || 
+    !empty($settingsContent['address-region']) || !empty($settingsContent['address-postal']) || 
+    !empty($settingsContent['address-country'])) {
+    $casinoBrand["address"] = [
+        "@type" => "PostalAddress",
+        "streetAddress" => $settingsContent['address-street'] ?? '',
+        "addressLocality" => $settingsContent['address-city'] ?? '',
+        "addressRegion" => $settingsContent['address-region'] ?? '',
+        "postalCode" => $settingsContent['address-postal'] ?? '',
+        "addressCountry" => $settingsContent['address-country'] ?? ''
+    ];
+}
+
+$schemaGraph[] = $casinoBrand;
+
+// 3. WebSite
+$schemaGraph[] = [
+    "@type" => "WebSite",
+    "url" => $currentDomain,
+    "name" => $settingsContent['title-h1'],
+    "publisher" => [
+        "@id" => $currentUrl . "#organization"
+    ]
+];
+
+// 4. WebPage
+$schemaGraph[] = [
+    "@type" => "WebPage",
+    "@id" => $currentUrl . "#webpage",
+    "name" => $seo['title'],
+    "description" => $seo['description'],
+    "inLanguage" => $seo['extra_fields']['locale'] ?? 'en',
+    "url" => $currentUrl,
+    "dateModified" => $settingsContent['modified-date'] ?? date('Y-m-d'),
+    "author" => [
+        "@type" => "Person",
+        "name" => $settingsContent['author-name'] ?? '',
+        "image" => !empty($settingsContent['author-img']) ? $currentDomain . '/' . $settingsContent['author-img'] : '',
+        "jobTitle" => "Casino Expert"
+    ],
+    "publisher" => [
+        "@id" => $currentUrl . "#organization"
+    ]
+];
+
+// 5. Review
+$schemaGraph[] = [
+    "@type" => "Review",
+    "itemReviewed" => [
+        "@id" => $currentUrl . "#brand-casino-entity"
+    ],
+    "reviewRating" => [
+        "@type" => "Rating",
+        "ratingValue" => "5",
+        "bestRating" => "5"
+    ],
+    "author" => [
+        "@id" => $currentUrl . "#organization"
+    ],
+    "name" => $seo['title'],
+    "reviewBody" => $seo['description'],
+    "datePublished" => $settingsContent['published-date'] ?? date('Y-m-d')
+];
+
+// 6. Offer (if bonus fields are filled)
+if (!empty($settingsContent['bonus-name']) || !empty($settingsContent['bonus-description'])) {
+    $schemaGraph[] = [
+        "@type" => "Offer",
+        "@id" => $currentUrl . "#offer",
+        "name" => $settingsContent['bonus-name'] ?? '',
+        "description" => $settingsContent['bonus-description'] ?? '',
+        "url" => $settingsContent['bonus-url'] ?? $currentUrl,
+        "price" => $settingsContent['bonus-price'] ?? '',
+        "priceCurrency" => $settingsContent['bonus-currency'] ?? 'EUR',
+        "availability" => "https://schema.org/InStock",
+        "eligibleCustomerType" => "https://schema.org/NewCustomer",
+        "offeredBy" => [
+            "@id" => $currentUrl . "#organization"
+        ]
+    ];
+}
+
+// 7. BreadcrumbList
+$schemaGraph[] = [
+    "@type" => "BreadcrumbList",
+    "itemListElement" => [
+        [
+            "@type" => "ListItem",
+            "position" => 1,
+            "name" => $settingsJsonld['breadcrumb_home_name'] ?? $settingsContent['title-h1'],
+            "item" => $currentUrl
+        ]
+    ]
+];
+
+// 8. FAQPage
+if (!empty($faqItems)) {
+    $schemaGraph[] = [
+        "@type" => "FAQPage",
+        "mainEntityOfPage" => [
+            "@id" => $currentUrl . "#webpage"
+        ],
+        "mainEntity" => array_map(function ($item) {
+            return [
+                "@type" => "Question",
+                "name" => htmlspecialchars($item['question']),
+                "acceptedAnswer" => [
+                    "@type" => "Answer",
+                    "text" => htmlspecialchars($item['answer'])
+                ]
+            ];
+        }, $faqItems)
+    ];
+}
+
+// 9. SoftwareApplication (if app fields are filled)
+if (!empty($settingsJsonld['download_url']) || !empty($settingsJsonld['app_name'])) {
+    $schemaGraph[] = [
+        "@type" => "SoftwareApplication",
+        "@id" => $currentUrl . "#app",
+        "name" => $settingsJsonld['app_name'] ?? ($settingsContent['title-h1'] . " App"),
+        "operatingSystem" => $settingsJsonld['operating_system'] ?? "Android, iOS",
+        "applicationCategory" => $settingsJsonld['application_category'] ?? "GameApplication",
+        "provider" => [
+            "@id" => $currentUrl . "#organization"
+        ],
+        "offers" => [
+            "@type" => "Offer",
+            "price" => "0",
+            "priceCurrency" => "EUR",
+            "availability" => "https://schema.org/InStock"
+        ],
+        "downloadUrl" => $settingsJsonld['download_url'] ?? '',
+        "aggregateRating" => [
+            "@type" => "AggregateRating",
+            "ratingValue" => $settingsJsonld['rating_value'] ?? "5",
+            "ratingCount" => $settingsJsonld['review_count'] ?? "99",
+            "worstRating" => $settingsJsonld['worst_rating'] ?? "1",
+            "bestRating" => $settingsJsonld['best_rating'] ?? "5"
+        ],
+        "author" => [
+            "@id" => $currentUrl . "#organization"
+        ]
+    ];
+}
+
+// Generate final JSON-LD
+$schemaMarkup = [
+    "@context" => "https://schema.org",
+    "@graph" => array_filter($schemaGraph) // Remove null entries
+];
+
+echo json_encode($schemaMarkup, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
+?>

+ 8 - 0
src/Enums/PageLayout.php

@@ -50,6 +50,14 @@ enum PageLayout: string
         };
     }
 
+    public function viewPath(): string
+    {
+        return match($this) {
+            self::Default => 'pages/default.php',
+            self::FrontPage => 'home.php',
+        };
+    }
+
     public function description(): string
     {
         return match($this) {

+ 1 - 1
src/Pages/admin/authors.php

@@ -4,7 +4,7 @@ use App\Models\Author;
 
 $authorModel = new Author();
 $action = $_GET['action'] ?? 'list';
-$id = $_GET['id'] ?? null;
+$id = $_GET['id'] ?? $_POST['id'] ?? null;
 
 // Handle form submissions
 if ($_SERVER['REQUEST_METHOD'] === 'POST') {

+ 10 - 1
src/Pages/admin/index.php

@@ -39,6 +39,14 @@ if (isset($_POST['form_submitted']) && $_POST['form_submitted'] == '1') {
         }
     }
     
+    // Сохранение jsonld данных
+    if (isset($_POST['jsonld'])) {
+        $jsonldSettings = settings('jsonld');
+        foreach ($_POST['jsonld'] as $key => $value) {
+            $jsonldSettings->set($key, $value);
+        }
+    }
+    
     // Обработка загрузки og-image
     if (isset($_FILES['seo'])) {
         $imagePath = uploadImage($_FILES['seo'], 'og-image');
@@ -127,10 +135,11 @@ if (isset($content['casino_list']) && !empty($content['casino_list'])) {
     $casinoItems = [];
 }
 
-return ViewRender('admin/index_new.php', [
+return ViewRender('admin/index.php', [
     'content' => $content,
     'styles' => $styles,
     'seo' => settings('seo')->getAll(),
+    'jsonld' => settings('jsonld')->getAll(),
     'faqItems' => $faqItems,
     'menuItems' => $menuItems,
     'casinoItems' => $casinoItems

+ 1 - 3
src/Pages/admin/pages.php

@@ -9,12 +9,11 @@ $pageModel = new Page();
 $seoMetaModel = new SeoMeta();
 $faqModel = new Faq();
 $action = $_GET['action'] ?? 'list';
-$id = $_GET['id'] ?? null;
+$id = $_GET['id'] ?? $_POST['id'] ?? null;
 
 // Handle form submissions
 if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     $data = $_POST;
-    
     // Generate slug from name if not provided
     if (empty($data['slug']) && !empty($data['name'])) {
         $data['slug'] = getSlug($data['name']);
@@ -30,7 +29,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     
     // Get action from form data
     $formAction = $data['action'] ?? $action;
-    
     try {
         switch ($formAction) {
             case 'create':

+ 1 - 0
src/Pages/home.php

@@ -81,6 +81,7 @@ return ViewRender('home.php', [
     'faqItems' => $faqItems,
     'faqTitle' => $faqs['title'] ?? '',
     'pageContent' => $pageContent,
+    'pageData' => $homepage,
     'topContent' => $topContent,
     'currentDomain' => $currentDomain,
     'currentUrl' => $currentUrl,

+ 77 - 0
src/Pages/page.php

@@ -0,0 +1,77 @@
+<?php
+
+use App\Settings;
+use App\Models\Page;
+use App\Models\SeoMeta;
+use App\Models\Faq;
+
+// Получение страницы по slug
+$pageModel = new Page();
+$seoMetaModel = new SeoMeta();
+$faqModel = new Faq();
+
+// Получаем slug из URL
+$slug = $slug ?? $_GET['slug'] ?? '';
+
+// Находим страницу по slug или главную страницу если slug пустой
+if (empty($slug)) {
+    // Главная страница
+    $currentPage = \db()->fetchOne("SELECT * FROM pages WHERE is_homepage = 1");
+} else {
+    // Обычная страница по slug
+    $currentPage = \db()->fetchOne("SELECT * FROM pages WHERE slug = ?", [$slug]);
+}
+
+// Если страница не найдена, возвращаем 404
+if (!$currentPage) {
+    http_response_code(404);
+    die('404 - Page not found');
+}
+
+// Получаем данные из настроек
+$content = (new Settings('content'))->getAll();
+
+// Получаем SEO данные для страницы
+$seo = $seoMetaModel->getForRecord('page', $currentPage['id']) ?: [];
+
+// Контент страницы
+$pageContent = $currentPage['content'] ?? '';
+
+// Получаем топ контент если есть
+$topContent = $currentPage['top_content'] ?? '';
+
+// Получаем FAQ для этой страницы
+$faqItems = $faqModel->getByMorphable('page', $currentPage['id']);
+
+// Данные для домена
+$currentDomain = getCurrentDomain();
+$currentUrl = getCurrentUrl();
+
+// Определение типа пользователя
+$ip = getUserIp();
+$isGoogleBot = (strpos(gethostbyaddr($ip), 'google') !== false)
+    ? true
+    : false;
+$isBingBot = (strpos(gethostbyaddr($ip), 'bing') !== false)
+    ? true
+    : false;
+
+$isSearchBot = $isGoogleBot || $isBingBot;
+
+// Определяем лейаут страницы
+$layout = \App\Enums\PageLayout::tryFrom($currentPage['layout'] ?? 'default') ?? \App\Enums\PageLayout::Default;
+$layoutFile = $layout->viewPath();
+
+return ViewRender($layoutFile, [
+    'content' => $content,
+    'seo' => $seo,
+    'faqItems' => $faqItems,
+    'pageContent' => $pageContent,
+    'pageData' => $currentPage,
+    'topContent' => $topContent,
+    'currentDomain' => $currentDomain,
+    'currentUrl' => $currentUrl,
+    'isHomepage' => !empty($currentPage['is_homepage']),
+    'isSearchBot' => $isSearchBot,
+    'userIp' => $ip,
+]);