index.blade.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. {{--
  2. Вьюха: Настройки сайта — все секции сворачиваемые через AdminLTE card-widget="collapse"
  3. Partials (в admin/settings/partials/):
  4. _card_analytics — Яндекс.Метрика и Google Analytics
  5. _card_og — Open Graph метатеги
  6. _card_logos — Логотипы шапки и подвала
  7. _card_site_mode — Режим сайта (обслуживание + noindex)
  8. --}}
  9. @extends('admin.layout')
  10. @section('title', 'Настройки сайта')
  11. @section('content_header')
  12. <div class="d-flex justify-content-between align-items-center">
  13. <h1 class="m-0">Настройки сайта</h1>
  14. <button type="submit" form="settings-form" class="btn btn-primary">
  15. <i class="fas fa-save"></i> Сохранить настройки
  16. </button>
  17. </div>
  18. @stop
  19. @section('breadcrumb')
  20. <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Главная</a></li>
  21. <li class="breadcrumb-item active">Настройки</li>
  22. @stop
  23. @section('content')
  24. @if(session('success'))
  25. <div class="alert alert-success alert-dismissible">
  26. <button type="button" class="close" data-dismiss="alert">&times;</button>
  27. {{ session('success') }}
  28. </div>
  29. @endif
  30. <form id="settings-form" action="{{ route('admin.settings.update') }}" method="POST" enctype="multipart/form-data">
  31. @csrf
  32. <div class="row">
  33. {{-- Левая колонка --}}
  34. <div class="col-md-8">
  35. {{-- Контакты --}}
  36. <div class="card card-primary card-outline">
  37. <div class="card-header">
  38. <h3 class="card-title">Контакты</h3>
  39. <div class="card-tools">
  40. <button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
  41. </div>
  42. </div>
  43. <div class="card-body">
  44. <div class="form-group">
  45. <label>Номер телефона</label>
  46. <div class="input-group">
  47. <div class="input-group-prepend"><span class="input-group-text"><i class="fas fa-phone"></i></span></div>
  48. <input type="tel" name="phone" class="form-control @error('phone') is-invalid @enderror"
  49. value="{{ old('phone', $settings['phone'] ?? '') }}" placeholder="+7 (___) ___-__-__">
  50. </div>
  51. @error('phone')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  52. </div>
  53. <div class="form-group">
  54. <label>Email</label>
  55. <div class="input-group">
  56. <div class="input-group-prepend"><span class="input-group-text"><i class="fas fa-envelope"></i></span></div>
  57. <input type="email" name="email" class="form-control @error('email') is-invalid @enderror"
  58. value="{{ old('email', $settings['email'] ?? '') }}" placeholder="info@example.ru">
  59. </div>
  60. @error('email')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  61. </div>
  62. <div class="form-group mb-0">
  63. <label>Режим работы</label>
  64. <div class="input-group">
  65. <div class="input-group-prepend"><span class="input-group-text"><i class="fas fa-clock"></i></span></div>
  66. <input type="text" name="working_hours" class="form-control @error('working_hours') is-invalid @enderror"
  67. value="{{ old('working_hours', $settings['working_hours'] ?? '') }}"
  68. placeholder="Пн–Пт: 9:00 – 20:00 | Сб–Вс: 10:00 – 18:00">
  69. </div>
  70. <small class="text-muted">Разделитель строк: | (вертикальная черта)</small>
  71. @error('working_hours')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  72. </div>
  73. </div>
  74. </div>
  75. {{-- Социальные сети --}}
  76. <div class="card card-secondary card-outline">
  77. <div class="card-header">
  78. <h3 class="card-title">Социальные сети</h3>
  79. <div class="card-tools">
  80. <button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
  81. </div>
  82. </div>
  83. <div class="card-body">
  84. <div class="form-group">
  85. <label>Telegram</label>
  86. <div class="input-group">
  87. <div class="input-group-prepend"><span class="input-group-text" style="background:#229ED9;color:#fff;border-color:#229ED9">TG</span></div>
  88. <input type="url" name="telegram" class="form-control @error('telegram') is-invalid @enderror"
  89. value="{{ old('telegram', $settings['telegram'] ?? '') }}" placeholder="https://t.me/your_channel">
  90. </div>
  91. @error('telegram')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  92. </div>
  93. <div class="form-group">
  94. <label>YouTube</label>
  95. <div class="input-group">
  96. <div class="input-group-prepend"><span class="input-group-text" style="background:#FF0000;color:#fff;border-color:#FF0000">YT</span></div>
  97. <input type="url" name="youtube" class="form-control @error('youtube') is-invalid @enderror"
  98. value="{{ old('youtube', $settings['youtube'] ?? '') }}" placeholder="https://youtube.com/@channel">
  99. </div>
  100. @error('youtube')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  101. </div>
  102. <div class="form-group mb-0">
  103. <label>ВКонтакте</label>
  104. <div class="input-group">
  105. <div class="input-group-prepend"><span class="input-group-text" style="background:#0077FF;color:#fff;border-color:#0077FF">ВК</span></div>
  106. <input type="url" name="vk" class="form-control @error('vk') is-invalid @enderror"
  107. value="{{ old('vk', $settings['vk'] ?? '') }}" placeholder="https://vk.com/your_group">
  108. </div>
  109. @error('vk')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  110. </div>
  111. </div>
  112. </div>
  113. {{-- Подвал --}}
  114. <div class="card card-secondary card-outline">
  115. <div class="card-header">
  116. <h3 class="card-title">Подвал сайта</h3>
  117. <div class="card-tools">
  118. <button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
  119. </div>
  120. </div>
  121. <div class="card-body">
  122. <div class="form-group">
  123. <label>Слоган под логотипом в подвале</label>
  124. <input type="text" name="footer_slogan" class="form-control @error('footer_slogan') is-invalid @enderror"
  125. value="{{ old('footer_slogan', $settings['footer_slogan'] ?? '') }}"
  126. placeholder="Автоподбор по России и доставка авто из любой страны под ключ.">
  127. @error('footer_slogan')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  128. </div>
  129. <div class="form-group mb-0">
  130. <label>Копирайт (строка внизу страницы)</label>
  131. <input type="text" name="copyright" class="form-control @error('copyright') is-invalid @enderror"
  132. value="{{ old('copyright', $settings['copyright'] ?? '') }}"
  133. placeholder="© 2025 Компания. Все права защищены.">
  134. @error('copyright')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
  135. </div>
  136. </div>
  137. </div>
  138. @include('admin.settings.partials._card_analytics')
  139. @include('admin.settings.partials._card_og')
  140. @include('admin.settings.partials._card_logos')
  141. </div>
  142. {{-- Правая колонка --}}
  143. <div class="col-md-4">
  144. @include('admin.settings.partials._card_site_mode')
  145. <div class="card card-secondary card-outline">
  146. <div class="card-header">
  147. <h3 class="card-title">Где используются</h3>
  148. <div class="card-tools">
  149. <button type="button" class="btn btn-tool" data-card-widget="collapse"><i class="fas fa-minus"></i></button>
  150. </div>
  151. </div>
  152. <div class="card-body text-muted" style="font-size:13px;line-height:1.9">
  153. <p><strong>Телефон</strong> — topbar, кнопка «Позвонить», подвал, контакты</p>
  154. <p><strong>Email</strong> — topbar, подвал, контакты</p>
  155. <p><strong>Telegram</strong> — кнопка в шапке, подвал, плавающая кнопка, контакты</p>
  156. <p><strong>YouTube / ВК</strong> — topbar, подвал</p>
  157. <p><strong>Режим работы</strong> — страница контактов</p>
  158. <p><strong>Слоган / Копирайт</strong> — подвал сайта</p>
  159. <p><strong>Логотипы</strong> — шапка и подвал</p>
  160. <hr>
  161. <p><strong>Яндекс.Метрика / GA</strong> — &lt;head&gt; всех страниц</p>
  162. <p><strong>OG-изображение</strong> — превью в соцсетях (если нет фото авто)</p>
  163. <hr>
  164. <p><strong>Обслуживание</strong> — заглушка для гостей и редакторов</p>
  165. <p><strong>Noindex</strong> — запрет индексации поисковиками</p>
  166. <p><strong>Анимации</strong> — class="no-animations" на &lt;body&gt;, reveal-блоки видны сразу</p>
  167. <hr>
  168. <p class="mb-0 text-info">
  169. <i class="fas fa-info-circle"></i>
  170. Незаполненные поля скрываются на сайте автоматически.
  171. </p>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. </form>
  177. @stop
  178. @push('js')
  179. <script>
  180. // ── Аккордеон карточек настроек (только левая колонка) ──────────────────────
  181. $(function () {
  182. // Все карточки левой колонки у которых есть кнопка сворачивания
  183. var $cards = $('.col-md-8 [data-card-widget="collapse"]').closest('.card');
  184. // Сворачиваем все кроме первой (без анимации — чтобы не мигало при загрузке)
  185. $cards.each(function (i) {
  186. if (i > 0) {
  187. $(this).addClass('collapsed-card');
  188. $(this).find('[data-card-widget="collapse"] i')
  189. .removeClass('fa-minus').addClass('fa-plus');
  190. $(this).find('.card-body, .card-footer').hide();
  191. }
  192. });
  193. // При разворачивании карточки — сворачиваем все остальные в колонке
  194. $(document).on('expanded.lte.cardwidget', '.col-md-8 .card', function () {
  195. var $opened = $(this);
  196. $cards.each(function () {
  197. if (!$(this).is($opened) && !$(this).hasClass('collapsed-card')) {
  198. $(this).find('[data-card-widget="collapse"]').trigger('click');
  199. }
  200. });
  201. });
  202. });
  203. // Счётчик символов в поле og_description
  204. (function () {
  205. var ta = document.querySelector('textarea[name=og_description]');
  206. var counter = document.getElementById('og-desc-count');
  207. if (!ta || !counter) return;
  208. ta.addEventListener('input', function () {
  209. var len = this.value.length;
  210. counter.textContent = len + '/300';
  211. counter.className = len > 200 ? 'text-warning' : 'text-muted';
  212. });
  213. })();
  214. // Показываем имя выбранного файла в custom-file-input
  215. document.querySelectorAll('.custom-file-input').forEach(function(input) {
  216. input.addEventListener('change', function() {
  217. var label = this.nextElementSibling;
  218. label.textContent = this.files[0] ? this.files[0].name : 'Выбрать файл';
  219. });
  220. });
  221. // Маска телефона
  222. (function () {
  223. var el = document.querySelector('input[name=phone]');
  224. if (!el) return;
  225. el.setAttribute('maxlength', '18');
  226. function fmt(d) {
  227. var out = '+7';
  228. if (d.length > 0) out += ' (' + d.slice(0, 3);
  229. if (d.length >= 3) out += ')';
  230. if (d.length > 3) out += ' ' + d.slice(3, 6);
  231. if (d.length > 6) out += '-' + d.slice(6, 8);
  232. if (d.length > 8) out += '-' + d.slice(8, 10);
  233. return out;
  234. }
  235. function digits() { return el.value.replace(/\D/g, '').replace(/^[78]/, ''); }
  236. el.addEventListener('focus', function () { if (!this.value) this.value = '+7'; });
  237. el.addEventListener('blur', function () { if (digits().length === 0) this.value = ''; });
  238. el.addEventListener('keydown', function (e) {
  239. if (e.ctrlKey || e.metaKey) return;
  240. if (['Tab','Enter','ArrowLeft','ArrowRight','Home','End'].includes(e.key)) return;
  241. e.preventDefault();
  242. var d = digits();
  243. if (e.key === 'Backspace') { d = d.slice(0, -1); }
  244. else if (/^\d$/.test(e.key) && d.length < 10) { d += e.key; }
  245. else { return; }
  246. this.value = fmt(d);
  247. });
  248. el.addEventListener('paste', function (e) {
  249. e.preventDefault();
  250. var text = (e.clipboardData || window.clipboardData).getData('text');
  251. var d = (digits() + text.replace(/\D/g, '').replace(/^[78]/, '')).slice(0, 10);
  252. this.value = fmt(d);
  253. });
  254. })();
  255. </script>
  256. @endpush