edit.blade.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. @extends('layouts.app')
  2. @section('content')
  3. @php
  4. $commonCatalogReadableFields = $commonCatalogReadableFields ?? [];
  5. $commonCatalogWritableFields = $commonCatalogWritableFields ?? [];
  6. $canView = static fn (string $field): bool => (bool) ($commonCatalogReadableFields[$field] ?? false);
  7. $canUpdate = static fn (string $field): bool => (bool) ($commonCatalogWritableFields[$field] ?? false);
  8. $calculatorEnabledValue = old(
  9. 'calculator_enabled',
  10. $item === null || $item->calculator_enabled === null ? '' : (string) (int) $item->calculator_enabled,
  11. );
  12. $priceFields = \App\Models\CommonCatalogItem::PRICE_FIELDS;
  13. $technicalTextFields = [
  14. 'characteristics' => ['Характеристики', 4],
  15. 'technical_description' => ['Техническое описание', 6],
  16. 'technical_description_short' => ['Краткое техническое описание', 5],
  17. ];
  18. @endphp
  19. <div class="px-3">
  20. <div class="row mb-3">
  21. <div class="col-md-7 d-flex align-items-center">
  22. <h3 class="mb-0">
  23. {{ $item ? 'Позиция '.($canView('article') ? $item->article : '') : 'Новая позиция общего каталога' }}
  24. </h3>
  25. </div>
  26. @if($item)
  27. <div class="col-md-5 d-flex flex-wrap align-items-center justify-content-end gap-2 action-toolbar">
  28. @if(hasPermission('common-catalog.image.upload'))
  29. <button type="button" class="btn btn-sm btn-outline-success"
  30. onclick="document.getElementById('common-catalog-image-input').click()">
  31. <i class="bi bi-image"></i> Загрузить фото
  32. </button>
  33. <form action="{{ route('common-catalog.image.upload', ['commonCatalogItem' => $item, 'nav' => $nav ?? null]) }}"
  34. method="POST" enctype="multipart/form-data" class="visually-hidden">
  35. @csrf
  36. <input id="common-catalog-image-input" type="file" name="image"
  37. accept=".jpg,.jpeg,.png,.webp" required onchange="this.form.submit()">
  38. </form>
  39. @endif
  40. <button type="button" class="btn btn-sm btn-outline-secondary" disabled
  41. title="Будет подключено после получения контракта API">
  42. <i class="bi bi-calculator"></i> Калькуляция
  43. </button>
  44. </div>
  45. @endif
  46. </div>
  47. @if($item?->imageFile && $canView('image'))
  48. <div class="card mb-3">
  49. <div class="card-body d-flex align-items-start gap-3">
  50. <a href="{{ $item->imageFile->link }}" data-toggle="lightbox" data-gallery="common-catalog">
  51. <img src="{{ $item->imageFile->thumbnail_link }}" alt="{{ $item->article }}"
  52. class="img-thumbnail" style="max-width: 180px; max-height: 180px">
  53. </a>
  54. @if(hasPermission('common-catalog.image.delete'))
  55. <form action="{{ route('common-catalog.image.delete', ['commonCatalogItem' => $item, 'nav' => $nav ?? null]) }}"
  56. method="POST" onsubmit="return confirm('Удалить изображение?')">
  57. @csrf
  58. @method('DELETE')
  59. <button type="submit" class="btn btn-sm btn-outline-danger">Удалить фото</button>
  60. </form>
  61. @endif
  62. </div>
  63. </div>
  64. @endif
  65. <form action="{{ $item ? route('common-catalog.update', $item) : route('common-catalog.store') }}" method="POST">
  66. @csrf
  67. @if($item) @method('PUT') @endif
  68. <input type="hidden" name="nav" value="{{ $nav ?? '' }}">
  69. <div class="row">
  70. <div class="col-xl-6">
  71. @if($canView('article'))
  72. @include('partials.input', ['name' => 'article', 'title' => 'Артикул', 'required' => true, 'value' => $item?->article, 'disabled' => !$canUpdate('article')])
  73. @endif
  74. @if($canView('calculator_name'))
  75. <div class="row mb-2">
  76. <label for="calculator_name" class="col-form-label small col-md-4 text-md-end">
  77. Наименование <sup>*</sup>
  78. </label>
  79. <div class="col-md-8">
  80. <textarea name="calculator_name" id="calculator_name" rows="4" required
  81. @disabled(!$canUpdate('calculator_name'))
  82. class="form-control form-control-sm @error('calculator_name') is-invalid @enderror">{{ old('calculator_name', $item?->calculator_name) }}</textarea>
  83. @error('calculator_name')
  84. <div class="invalid-feedback"><strong>{{ $message }}</strong></div>
  85. @enderror
  86. </div>
  87. </div>
  88. @endif
  89. @if($canView('kind')) @include('partials.input', ['name' => 'kind', 'title' => 'Вид', 'value' => $item?->kind, 'disabled' => !$canUpdate('kind')]) @endif
  90. @if($canView('dimension_length')) @include('partials.input', ['name' => 'dimension_length', 'title' => 'Габариты: длина', 'value' => $item?->dimension_length, 'disabled' => !$canUpdate('dimension_length')]) @endif
  91. @if($canView('dimension_width')) @include('partials.input', ['name' => 'dimension_width', 'title' => 'Габариты: ширина', 'value' => $item?->dimension_width, 'disabled' => !$canUpdate('dimension_width')]) @endif
  92. @if($canView('dimension_height')) @include('partials.input', ['name' => 'dimension_height', 'title' => 'Габариты: высота', 'value' => $item?->dimension_height, 'disabled' => !$canUpdate('dimension_height')]) @endif
  93. @if($canView('site_length')) @include('partials.input', ['name' => 'site_length', 'title' => 'Размер участка: длина', 'value' => $item?->site_length, 'disabled' => !$canUpdate('site_length')]) @endif
  94. @if($canView('site_width')) @include('partials.input', ['name' => 'site_width', 'title' => 'Размер участка: ширина', 'value' => $item?->site_width, 'disabled' => !$canUpdate('site_width')]) @endif
  95. @if($canView('fall_height')) @include('partials.input', ['name' => 'fall_height', 'title' => 'Высота падения', 'type' => 'number', 'min' => 0, 'step' => '0.001', 'value' => $item?->fall_height, 'disabled' => !$canUpdate('fall_height')]) @endif
  96. @if($canView('additional_info'))
  97. <div class="row mb-2">
  98. <label for="additional_info" class="col-form-label small col-md-4 text-md-end">Дополнительные сведения</label>
  99. <div class="col-md-8">
  100. <textarea name="additional_info" id="additional_info" rows="4"
  101. @disabled(!$canUpdate('additional_info'))
  102. class="form-control form-control-sm @error('additional_info') is-invalid @enderror">{{ old('additional_info', $item?->additional_info) }}</textarea>
  103. @error('additional_info')
  104. <div class="invalid-feedback"><strong>{{ $message }}</strong></div>
  105. @enderror
  106. </div>
  107. </div>
  108. @endif
  109. </div>
  110. <div class="col-xl-6">
  111. @if($canView('dimension_unit')) @include('partials.input', ['name' => 'dimension_unit', 'title' => 'Единица измерения габаритов', 'value' => $item?->dimension_unit, 'disabled' => !$canUpdate('dimension_unit')]) @endif
  112. @if($canView('weight')) @include('partials.input', ['name' => 'weight', 'title' => 'Вес, кг.', 'type' => 'number', 'min' => 0, 'step' => '0.001', 'value' => $item?->weight, 'disabled' => !$canUpdate('weight')]) @endif
  113. @if($canView('volume')) @include('partials.input', ['name' => 'volume', 'title' => 'Объем, м3', 'type' => 'number', 'min' => 0, 'step' => '0.001', 'value' => $item?->volume, 'disabled' => !$canUpdate('volume')]) @endif
  114. @if($canView('places')) @include('partials.input', ['name' => 'places', 'title' => 'Места', 'type' => 'number', 'min' => 0, 'step' => 1, 'value' => $item?->places, 'disabled' => !$canUpdate('places')]) @endif
  115. @if($canView('composition'))
  116. <div class="row mb-2">
  117. <label for="composition" class="col-form-label small col-md-4 text-md-end">Состав</label>
  118. <div class="col-md-8">
  119. <textarea name="composition" id="composition" rows="4"
  120. @disabled(!$canUpdate('composition'))
  121. class="form-control form-control-sm @error('composition') is-invalid @enderror">{{ old('composition', $item?->composition) }}</textarea>
  122. @error('composition')
  123. <div class="invalid-feedback"><strong>{{ $message }}</strong></div>
  124. @enderror
  125. </div>
  126. </div>
  127. @endif
  128. @if($canView('age_group')) @include('partials.input', ['name' => 'age_group', 'title' => 'Возрастная группа', 'value' => $item?->age_group, 'disabled' => !$canUpdate('age_group')]) @endif
  129. @if($canView('max_users')) @include('partials.input', ['name' => 'max_users', 'title' => 'Макс. кол-во пользователей', 'type' => 'number', 'min' => 0, 'step' => 1, 'value' => $item?->max_users, 'disabled' => !$canUpdate('max_users')]) @endif
  130. @if($canView('unit')) @include('partials.input', ['name' => 'unit', 'title' => 'Ед.', 'value' => $item?->unit, 'disabled' => !$canUpdate('unit')]) @endif
  131. @if($canView('series')) @include('partials.input', ['name' => 'series', 'title' => 'Серия', 'value' => $item?->series, 'disabled' => !$canUpdate('series')]) @endif
  132. @if($canView('trademark')) @include('partials.input', ['name' => 'trademark', 'title' => 'ТМ', 'value' => $item?->trademark, 'disabled' => !$canUpdate('trademark')]) @endif
  133. @if($canView('calculator_enabled'))
  134. <div class="row mb-2">
  135. <label for="calculator_enabled" class="col-form-label small col-md-4 text-md-end">Калькулятор</label>
  136. <div class="col-md-8">
  137. <select name="calculator_enabled" id="calculator_enabled"
  138. @disabled(!$canUpdate('calculator_enabled'))
  139. class="form-select form-select-sm @error('calculator_enabled') is-invalid @enderror">
  140. <option value="">—</option>
  141. <option value="1" @selected((string) $calculatorEnabledValue === '1')>да</option>
  142. <option value="0" @selected((string) $calculatorEnabledValue === '0')>нет</option>
  143. </select>
  144. @error('calculator_enabled')
  145. <div class="invalid-feedback"><strong>{{ $message }}</strong></div>
  146. @enderror
  147. </div>
  148. </div>
  149. @endif
  150. </div>
  151. </div>
  152. @if(collect(array_keys($priceFields))->contains(fn (string $field): bool => $canView($field)))
  153. <div class="card mt-3">
  154. <div class="card-header"><strong>Цены</strong></div>
  155. <div class="card-body row">
  156. @foreach($priceFields as $field => $label)
  157. @if($canView($field))
  158. <div class="col-xl-6">
  159. @include('partials.input', [
  160. 'name' => $field,
  161. 'title' => $label,
  162. 'type' => 'number',
  163. 'min' => 0,
  164. 'step' => '0.01',
  165. 'value' => $item?->{$field},
  166. 'disabled' => !$canUpdate($field),
  167. ])
  168. </div>
  169. @endif
  170. @endforeach
  171. </div>
  172. </div>
  173. @endif
  174. @if(collect(\App\Models\CommonCatalogItem::TECHNICAL_DESCRIPTION_FIELDS)->contains(fn (string $field): bool => $canView($field)))
  175. <div class="card mt-3">
  176. <div class="card-header"><strong>Техническое описание</strong></div>
  177. <div class="card-body">
  178. <div class="row">
  179. @if($canView('print_name'))
  180. <div class="col-xl-6">
  181. @include('partials.input', [
  182. 'name' => 'print_name',
  183. 'title' => 'Наименование для формы',
  184. 'value' => $item?->print_name,
  185. 'disabled' => !$canUpdate('print_name'),
  186. ])
  187. </div>
  188. @endif
  189. @if($canView('product_group'))
  190. <div class="col-xl-6">
  191. @include('partials.input', [
  192. 'name' => 'product_group',
  193. 'title' => 'Группа',
  194. 'value' => $item?->product_group,
  195. 'disabled' => !$canUpdate('product_group'),
  196. ])
  197. </div>
  198. @endif
  199. </div>
  200. @foreach($technicalTextFields as $field => [$label, $rows])
  201. @if($canView($field))
  202. <div class="row mb-2">
  203. <label for="{{ $field }}" class="col-form-label small col-md-4 text-md-end">{{ $label }}</label>
  204. <div class="col-md-8">
  205. <textarea name="{{ $field }}" id="{{ $field }}" rows="{{ $rows }}"
  206. @disabled(!$canUpdate($field))
  207. class="form-control form-control-sm @error($field) is-invalid @enderror">{{ old($field, $item?->{$field}) }}</textarea>
  208. @error($field)
  209. <div class="invalid-feedback"><strong>{{ $message }}</strong></div>
  210. @enderror
  211. </div>
  212. </div>
  213. @endif
  214. @endforeach
  215. @if($item)
  216. <div class="alert alert-light border mb-0 mt-3 py-2">
  217. @if($canViewTechnicalDescriptionPrice)
  218. Цена для DOCX: <strong>{{ $technicalDescriptionPriceLabel }}</strong> —
  219. <strong>{{ $item->{$technicalDescriptionPriceField.'_txt'} }}</strong>
  220. @else
  221. Цена для DOCX скрыта вашими правами доступа.
  222. @endif
  223. </div>
  224. @endif
  225. </div>
  226. </div>
  227. @endif
  228. <div class="d-flex flex-wrap gap-2 mt-3">
  229. @if(($item && hasPermission('common-catalog.update')) || (!$item && hasPermission('common-catalog.create')))
  230. <button type="submit" class="btn btn-sm btn-primary">Сохранить</button>
  231. @endif
  232. <a href="{{ $back_url ?? route('common-catalog.index') }}" class="btn btn-sm btn-outline-secondary">Назад</a>
  233. </div>
  234. </form>
  235. @if($item)
  236. @if($canViewTechnicalDescriptionPrice)
  237. <div class="card mt-4">
  238. <div class="card-header"><strong>Экспорт технического описания</strong></div>
  239. <div class="card-body">
  240. <form action="{{ route('common-catalog.technical-description.export') }}" method="POST">
  241. @csrf
  242. <input type="hidden" name="item_ids[]" value="{{ $item->id }}">
  243. @include('partials.select', [
  244. 'name' => 'description_mode',
  245. 'title' => 'Содержимое описания',
  246. 'options' => $technicalDescriptionModes,
  247. 'value' => \App\Services\TechnicalDescriptionService::MODE_CHARACTERISTICS_SHORT,
  248. ])
  249. <div class="row">
  250. <div class="offset-md-4 col-md-8">
  251. <button type="submit" class="btn btn-sm btn-outline-primary">
  252. <i class="bi bi-file-earmark-word"></i> Сформировать DOCX
  253. </button>
  254. </div>
  255. </div>
  256. </form>
  257. </div>
  258. </div>
  259. @endif
  260. <div class="card mt-4">
  261. <div class="card-header d-flex justify-content-between align-items-center">
  262. <strong>Документы</strong>
  263. @if(hasPermission('common-catalog.documents.upload'))
  264. <form action="{{ route('common-catalog.documents.upload', ['commonCatalogItem' => $item, 'nav' => $nav ?? null]) }}"
  265. method="POST" enctype="multipart/form-data" class="d-flex gap-2">
  266. @csrf
  267. <input type="file" name="document" class="form-control form-control-sm" required>
  268. <button type="submit" class="btn btn-sm btn-outline-primary">Загрузить</button>
  269. </form>
  270. @endif
  271. </div>
  272. <div class="card-body">
  273. @forelse($item->documents as $document)
  274. <div class="d-flex justify-content-between align-items-center border-bottom py-2">
  275. <a href="{{ $document->link }}" target="_blank" rel="noopener">{{ $document->original_name }}</a>
  276. @if(hasPermission('common-catalog.documents.delete'))
  277. <form action="{{ route('common-catalog.documents.delete', ['commonCatalogItem' => $item, 'file' => $document, 'nav' => $nav ?? null]) }}"
  278. method="POST" onsubmit="return confirm('Удалить документ?')">
  279. @csrf
  280. @method('DELETE')
  281. <button type="submit" class="btn btn-sm btn-outline-danger">Удалить</button>
  282. </form>
  283. @endif
  284. </div>
  285. @empty
  286. <span class="text-muted">Документы не загружены.</span>
  287. @endforelse
  288. </div>
  289. </div>
  290. @if(hasPermission('common-catalog.delete'))
  291. <form action="{{ route('common-catalog.destroy', $item) }}" method="POST" class="mt-4"
  292. onsubmit="return confirm('Удалить позицию общего каталога?')">
  293. @csrf
  294. @method('DELETE')
  295. <button type="submit" class="btn btn-sm btn-danger">Удалить позицию</button>
  296. </form>
  297. @endif
  298. @endif
  299. </div>
  300. @endsection