index.blade.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. @extends('layouts.app')
  2. @section('content')
  3. <div class="row mb-3">
  4. <div class="col-12">
  5. <h3>Экспорт и импорт данных за год</h3>
  6. </div>
  7. </div>
  8. <div class="row">
  9. <!-- Экспорт -->
  10. <div class="col-md-6 mb-4">
  11. <div class="card">
  12. <div class="card-header bg-primary text-white">
  13. <i class="bi bi-download me-2"></i>Экспорт данных
  14. </div>
  15. <div class="card-body">
  16. @if(count($years) > 0)
  17. <p class="text-muted">Экспортирует все данные за выбранный год в ZIP-архив, включая файлы (фото, документы, сертификаты, паспорта).</p>
  18. <div class="mb-3">
  19. <label for="exportYear" class="form-label">Год для экспорта</label>
  20. <select class="form-select" id="exportYear" name="year">
  21. <option value="">-- Выберите год --</option>
  22. @foreach($years as $year)
  23. <option value="{{ $year }}">{{ $year }}</option>
  24. @endforeach
  25. </select>
  26. </div>
  27. <button type="button" class="btn btn-outline-primary" id="btnGetExportStats" disabled>
  28. Показать статистику
  29. </button>
  30. <div id="exportStatsBlock" class="mt-3 is-hidden">
  31. <h6>Данные для экспорта за <span id="exportStatsYear"></span> год:</h6>
  32. <div class="table-responsive">
  33. <table class="table table-sm table-striped" id="exportStatsTable">
  34. <thead>
  35. <tr>
  36. <th>Сущность</th>
  37. <th class="text-end">Количество</th>
  38. </tr>
  39. </thead>
  40. <tbody></tbody>
  41. <tfoot>
  42. <tr class="table-dark">
  43. <th>Итого</th>
  44. <th class="text-end" id="exportStatsTotal"></th>
  45. </tr>
  46. </tfoot>
  47. </table>
  48. </div>
  49. <form action="{{ route('year-data.export') }}" method="post">
  50. @csrf
  51. <input type="hidden" name="year" id="exportYearInput">
  52. <button type="submit" class="btn btn-primary">
  53. <i class="bi bi-download me-2"></i>Начать экспорт
  54. </button>
  55. </form>
  56. </div>
  57. @else
  58. <div class="alert alert-info mb-0">
  59. Нет данных для экспорта
  60. </div>
  61. @endif
  62. </div>
  63. </div>
  64. </div>
  65. <!-- Импорт -->
  66. <div class="col-md-6 mb-4">
  67. <div class="card">
  68. <div class="card-header bg-success text-white">
  69. <i class="bi bi-upload me-2"></i>Импорт данных
  70. </div>
  71. <div class="card-body">
  72. <p class="text-muted">Импортирует данные из ранее созданного ZIP-архива экспорта.</p>
  73. <form action="{{ route('year-data.import') }}" method="post" enctype="multipart/form-data">
  74. @csrf
  75. <div class="mb-3">
  76. <label for="importYear" class="form-label">Год для импорта</label>
  77. <input type="number" class="form-control" id="importYear" name="year"
  78. min="2020" max="2100" value="{{ date('Y') }}" required>
  79. <div class="form-text">Год, в который будут импортированы данные</div>
  80. </div>
  81. <div class="mb-3">
  82. <label for="importFile" class="form-label">ZIP-архив с данными</label>
  83. <input type="file" class="form-control" id="importFile" name="import_file"
  84. accept=".zip" required>
  85. </div>
  86. <div class="mb-3 form-check">
  87. <input type="checkbox" class="form-check-input" id="clearExisting" name="clear_existing" value="1">
  88. <label class="form-check-label text-danger" for="clearExisting">
  89. <strong>Очистить существующие данные</strong> за этот год перед импортом
  90. </label>
  91. </div>
  92. <div class="alert alert-warning">
  93. <i class="bi bi-exclamation-triangle me-2"></i>
  94. <strong>Внимание!</strong> При включенной опции очистки все существующие данные за указанный год будут удалены перед импортом.
  95. </div>
  96. <button type="submit" class="btn btn-success" id="btnImport">
  97. <i class="bi bi-upload me-2"></i>Начать импорт
  98. </button>
  99. </form>
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. <!-- Недавние экспорты -->
  105. @if($exports->count() > 0)
  106. <div class="row">
  107. <div class="col-12">
  108. <div class="card">
  109. <div class="card-header">
  110. <i class="bi bi-clock-history me-2"></i>Недавние экспорты
  111. </div>
  112. <div class="card-body">
  113. <div class="table-responsive">
  114. <table class="table table-striped table-hover">
  115. <thead>
  116. <tr>
  117. <th>Файл</th>
  118. <th>Дата создания</th>
  119. <th>Действия</th>
  120. </tr>
  121. </thead>
  122. <tbody>
  123. @foreach($exports as $export)
  124. <tr>
  125. <td>{{ $export->original_name }}</td>
  126. <td>{{ $export->created_at->format('d.m.Y H:i') }}</td>
  127. <td>
  128. <a href="{{ $export->link }}" class="btn btn-sm btn-outline-primary" target="_blank">
  129. <i class="bi bi-download"></i> Скачать
  130. </a>
  131. </td>
  132. </tr>
  133. @endforeach
  134. </tbody>
  135. </table>
  136. </div>
  137. </div>
  138. </div>
  139. </div>
  140. </div>
  141. @endif
  142. @endsection
  143. @push('scripts')
  144. <script type="module">
  145. $(document).ready(function() {
  146. let selectedExportYear = null;
  147. $('#exportYear').on('change', function() {
  148. selectedExportYear = $(this).val();
  149. $('#btnGetExportStats').prop('disabled', !selectedExportYear);
  150. $('#exportStatsBlock').hide();
  151. });
  152. $('#btnGetExportStats').on('click', function() {
  153. if (!selectedExportYear) return;
  154. const btn = $(this);
  155. btn.prop('disabled', true).text('Загрузка...');
  156. $.ajax({
  157. url: '{{ route('year-data.stats') }}',
  158. method: 'GET',
  159. data: { year: selectedExportYear },
  160. success: function(response) {
  161. $('#exportStatsYear').text(response.year);
  162. $('#exportStatsTotal').text(response.total);
  163. $('#exportYearInput').val(response.year);
  164. const tbody = $('#exportStatsTable tbody');
  165. tbody.empty();
  166. for (const [entity, count] of Object.entries(response.stats)) {
  167. tbody.append(`<tr><td>${entity}</td><td class="text-end">${count}</td></tr>`);
  168. }
  169. $('#exportStatsBlock').show();
  170. },
  171. error: function(xhr) {
  172. customAlert('Ошибка получения статистики: ' + (xhr.responseJSON?.error || 'Неизвестная ошибка'));
  173. },
  174. complete: function() {
  175. btn.prop('disabled', false).text('Показать статистику');
  176. }
  177. });
  178. });
  179. // Валидация формы импорта
  180. $('form[action="{{ route('year-data.import') }}"]').on('submit', function(e) {
  181. const form = this;
  182. const file = $('#importFile').val();
  183. const clearExisting = $('#clearExisting').is(':checked');
  184. if (!file) {
  185. e.preventDefault();
  186. customAlert('Пожалуйста, выберите ZIP-архив для импорта');
  187. return false;
  188. }
  189. if (clearExisting) {
  190. e.preventDefault();
  191. customConfirm(
  192. 'Вы уверены, что хотите очистить существующие данные за этот год? Это действие необратимо!',
  193. function () {
  194. $('#btnImport').prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-2"></span>Загрузка...');
  195. form.submit();
  196. },
  197. 'Подтверждение удаления'
  198. );
  199. return false;
  200. }
  201. $('#btnImport').prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-2"></span>Загрузка...');
  202. });
  203. });
  204. </script>
  205. @endpush