|
|
@@ -0,0 +1,491 @@
|
|
|
+@php
|
|
|
+ /** @var \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection $messages */
|
|
|
+ $messages = $messages ?? collect();
|
|
|
+ $users = $users ?? collect();
|
|
|
+ $responsibleUserIds = array_map('intval', $responsibleUserIds ?? []);
|
|
|
+ $managerUserId = isset($managerUserId) ? (int) $managerUserId : null;
|
|
|
+ $brigadierUserId = isset($brigadierUserId) ? (int) $brigadierUserId : null;
|
|
|
+ $currentUserId = (int) auth()->id();
|
|
|
+ $contextKey = $contextKey ?? 'chat';
|
|
|
+ $title = $title ?? 'Чат';
|
|
|
+ $submitLabel = $submitLabel ?? 'Отправить';
|
|
|
+ $canSendNotifications = hasRole('admin,manager');
|
|
|
+ $notificationValue = old('notification_type', \App\Models\ChatMessage::NOTIFICATION_NONE);
|
|
|
+ $notificationEnabled = $canSendNotifications && $notificationValue !== \App\Models\ChatMessage::NOTIFICATION_NONE;
|
|
|
+ $showAllUsers = $notificationValue === \App\Models\ChatMessage::NOTIFICATION_ALL;
|
|
|
+ $selectedTargetUserIds = collect(old('target_user_ids', []))
|
|
|
+ ->map(static fn ($id) => (int) $id)
|
|
|
+ ->filter()
|
|
|
+ ->unique()
|
|
|
+ ->values()
|
|
|
+ ->all();
|
|
|
+
|
|
|
+ // Роли в порядке сортировки
|
|
|
+ $roleOrder = [\App\Models\Role::ADMIN, \App\Models\Role::MANAGER, \App\Models\Role::BRIGADIER];
|
|
|
+ $roleNames = \App\Models\Role::NAMES;
|
|
|
+
|
|
|
+ // Сортировка пользователей: админы -> менеджеры -> бригадиры -> остальные
|
|
|
+ $sortedUsers = $users->sortBy(function ($user) use ($roleOrder) {
|
|
|
+ $role = $user['role'] ?? '';
|
|
|
+ $index = array_search($role, $roleOrder, true);
|
|
|
+ return $index === false ? 999 : $index;
|
|
|
+ });
|
|
|
+@endphp
|
|
|
+
|
|
|
+<div class="chat-block mt-3" data-chat-block data-context-key="{{ $contextKey }}">
|
|
|
+ <hr>
|
|
|
+ <h5>{{ $title }}</h5>
|
|
|
+
|
|
|
+ <div class="chat-card">
|
|
|
+ <div class="chat-messages-wrap">
|
|
|
+ <div class="chat-messages" data-chat-messages>
|
|
|
+ @forelse($messages as $message)
|
|
|
+ <div class="chat-message">
|
|
|
+ <div class="chat-message-header">
|
|
|
+ <div>
|
|
|
+ <strong>{{ $message->user?->name ?? 'Пользователь' }}</strong>
|
|
|
+ @if($message->notification_type === \App\Models\ChatMessage::NOTIFICATION_USER && $message->targetUser)
|
|
|
+ <span class="text-muted">для {{ $message->targetUser->name }}</span>
|
|
|
+ @elseif(in_array($message->notification_type, [\App\Models\ChatMessage::NOTIFICATION_RESPONSIBLES, \App\Models\ChatMessage::NOTIFICATION_ALL], true))
|
|
|
+ <span class="badge text-bg-light border">Уведомления: {{ $message->notifiedUsers->pluck('name')->join(', ') ?: 'получатели' }}</span>
|
|
|
+ @endif
|
|
|
+ </div>
|
|
|
+ <small class="text-muted">{{ $message->created_at?->format('d.m.Y H:i') }}</small>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ @if(!empty($message->message))
|
|
|
+ <div class="chat-message-text">{{ $message->message }}</div>
|
|
|
+ @endif
|
|
|
+
|
|
|
+ @if($message->files->isNotEmpty())
|
|
|
+ <div class="chat-message-files">
|
|
|
+ @foreach($message->files as $file)
|
|
|
+ @if(\Illuminate\Support\Str::startsWith((string) $file->mime_type, 'image/'))
|
|
|
+ <a href="{{ $file->link }}" target="_blank" data-toggle="lightbox" data-gallery="chat-{{ $contextKey }}" data-size="fullscreen">
|
|
|
+ <img src="{{ $file->link }}" alt="{{ $file->original_name }}" class="img-thumbnail">
|
|
|
+ </a>
|
|
|
+ @else
|
|
|
+ <a href="{{ $file->link }}" target="_blank" class="btn btn-sm btn-outline-secondary">
|
|
|
+ <i class="bi bi-paperclip"></i> {{ $file->original_name }}
|
|
|
+ </a>
|
|
|
+ @endif
|
|
|
+ @endforeach
|
|
|
+ </div>
|
|
|
+ @endif
|
|
|
+ </div>
|
|
|
+ @empty
|
|
|
+ <div class="text-muted px-1">Сообщений пока нет.</div>
|
|
|
+ @endforelse
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <button type="button" class="btn btn-primary btn-sm chat-scroll-bottom d-none" data-chat-scroll-bottom>
|
|
|
+ <i class="bi bi-arrow-down"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <form action="{{ $action }}" method="post" enctype="multipart/form-data" class="mt-3" data-chat-form>
|
|
|
+ @csrf
|
|
|
+
|
|
|
+ {{-- Уведомления: свитч + саммари --}}
|
|
|
+ @if($canSendNotifications)
|
|
|
+ <div class="d-flex align-items-center gap-3 mb-2">
|
|
|
+
|
|
|
+ <div class="form-check form-switch mb-0">
|
|
|
+ <input
|
|
|
+ class="form-check-input"
|
|
|
+ type="checkbox"
|
|
|
+ role="switch"
|
|
|
+ id="chat-notify-toggle-{{ $contextKey }}"
|
|
|
+ data-chat-notify-toggle
|
|
|
+ @checked($notificationEnabled)
|
|
|
+ >
|
|
|
+ <label class="form-check-label small" for="chat-notify-toggle-{{ $contextKey }}">
|
|
|
+ <i class="bi bi-bell"></i> Уведомить
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="small text-muted {{ $notificationEnabled ? '' : 'd-none' }}" data-chat-recipient-summary-wrap>
|
|
|
+ <a href="#" class="text-decoration-none" data-chat-open-recipient-modal data-bs-toggle="modal" data-bs-target="#chatRecipientsModal-{{ $contextKey }}">
|
|
|
+ <span data-chat-recipient-summary>Получатели не выбраны</span>
|
|
|
+ <i class="bi bi-pencil-square ms-1"></i>
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {{-- Скрытый select для notification_type (значение управляется JS) --}}
|
|
|
+ <input type="hidden" name="notification_type" value="{{ $notificationValue }}" data-chat-notification-type>
|
|
|
+ @else
|
|
|
+ <input type="hidden" name="notification_type" value="{{ \App\Models\ChatMessage::NOTIFICATION_NONE }}">
|
|
|
+ @endif
|
|
|
+
|
|
|
+ {{-- Строка ввода: textarea + иконка файла + кнопка отправить --}}
|
|
|
+ <div class="d-flex align-items-center gap-2">
|
|
|
+ <div class="flex-grow-1">
|
|
|
+ <textarea
|
|
|
+ class="form-control"
|
|
|
+ id="chat-message-{{ $contextKey }}"
|
|
|
+ name="message"
|
|
|
+ rows="2"
|
|
|
+ placeholder="Введите сообщение"
|
|
|
+ >{{ old('message') }}</textarea>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <input class="d-none" id="chat-attachments-{{ $contextKey }}" type="file" name="attachments[]" multiple data-chat-file-input>
|
|
|
+
|
|
|
+ <button type="button" class="btn btn-outline-secondary position-relative d-flex align-items-center justify-content-center" style="width: 38px; height: 38px; padding: 0;" title="Прикрепить файл" data-chat-attach-btn>
|
|
|
+ <i class="bi bi-paperclip"></i>
|
|
|
+ <span class="d-none position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary" style="font-size: .65em;" data-chat-file-count></span>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="btn btn-primary d-flex align-items-center justify-content-center" style="width: 38px; height: 38px; padding: 0;" type="submit" title="{{ $submitLabel }}">
|
|
|
+ <i class="bi bi-send"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div data-chat-hidden-targets>
|
|
|
+ @foreach($selectedTargetUserIds as $selectedTargetUserId)
|
|
|
+ <input type="hidden" name="target_user_ids[]" value="{{ $selectedTargetUserId }}">
|
|
|
+ @endforeach
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+
|
|
|
+ @if($canSendNotifications)
|
|
|
+ <div class="modal fade" id="chatRecipientsModal-{{ $contextKey }}" tabindex="-1" aria-labelledby="chatRecipientsModalLabel-{{ $contextKey }}" aria-hidden="true">
|
|
|
+ <div class="modal-dialog modal-dialog-scrollable">
|
|
|
+ <div class="modal-content">
|
|
|
+ <div class="modal-header">
|
|
|
+ <h1 class="modal-title fs-5" id="chatRecipientsModalLabel-{{ $contextKey }}">Получатели уведомления</h1>
|
|
|
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
|
|
+ </div>
|
|
|
+ <div class="modal-body" data-chat-recipient-modal-body>
|
|
|
+ {{-- Свитч: показать всех пользователей --}}
|
|
|
+ <div class="d-flex justify-content-between align-items-center mb-3">
|
|
|
+ <div class="form-check form-switch mb-0">
|
|
|
+ <input
|
|
|
+ class="form-check-input"
|
|
|
+ type="checkbox"
|
|
|
+ role="switch"
|
|
|
+ id="chat-show-all-toggle-{{ $contextKey }}"
|
|
|
+ data-chat-show-all-toggle
|
|
|
+ @checked($showAllUsers)
|
|
|
+ >
|
|
|
+ <label class="form-check-label" for="chat-show-all-toggle-{{ $contextKey }}">
|
|
|
+ Показать всех
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ <div class="d-flex gap-2">
|
|
|
+ <button type="button" class="btn btn-sm btn-outline-primary" data-chat-check-visible>Выбрать всех</button>
|
|
|
+ <button type="button" class="btn btn-sm btn-outline-secondary" data-chat-uncheck-visible>Снять всех</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="chat-recipient-list">
|
|
|
+ @foreach($sortedUsers as $userId => $userData)
|
|
|
+ @php
|
|
|
+ $userRole = $userData['role'] ?? '';
|
|
|
+ $isManagerOfEntity = $managerUserId && (int) $userId === $managerUserId;
|
|
|
+ $isBrigadierOfEntity = $brigadierUserId && (int) $userId === $brigadierUserId;
|
|
|
+ $roleLabel = $roleNames[$userRole] ?? '';
|
|
|
+ $displayLabel = $userData['name'];
|
|
|
+ if ($roleLabel) {
|
|
|
+ $displayLabel .= ' (' . $roleLabel . ')';
|
|
|
+ }
|
|
|
+ if ($isManagerOfEntity) {
|
|
|
+ $displayLabel .= ' — менеджер площадки';
|
|
|
+ }
|
|
|
+ if ($isBrigadierOfEntity) {
|
|
|
+ $displayLabel .= ' — бригадир площадки';
|
|
|
+ }
|
|
|
+ $isSelf = (int) $userId === $currentUserId;
|
|
|
+ @endphp
|
|
|
+ <label class="form-check mb-2 chat-recipient-item"
|
|
|
+ data-chat-recipient-item
|
|
|
+ data-user-id="{{ $userId }}"
|
|
|
+ data-user-name="{{ $displayLabel }}"
|
|
|
+ data-chat-responsible="{{ in_array((int) $userId, $responsibleUserIds, true) ? '1' : '0' }}"
|
|
|
+ data-chat-self="{{ $isSelf ? '1' : '0' }}">
|
|
|
+ <input
|
|
|
+ class="form-check-input"
|
|
|
+ type="checkbox"
|
|
|
+ value="{{ $userId }}"
|
|
|
+ data-chat-recipient-checkbox
|
|
|
+ @disabled($isSelf)
|
|
|
+ @checked(!$isSelf && in_array((int) $userId, $selectedTargetUserIds, true))
|
|
|
+ >
|
|
|
+ <span class="form-check-label {{ $isSelf ? 'text-muted' : '' }}">{{ $displayLabel }}@if($isSelf) (вы)@endif</span>
|
|
|
+ </label>
|
|
|
+ @endforeach
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="modal-footer">
|
|
|
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
|
|
|
+ <button type="button" class="btn btn-primary btn-sm" data-chat-apply-recipients data-bs-dismiss="modal">Применить</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ @endif
|
|
|
+</div>
|
|
|
+
|
|
|
+@once
|
|
|
+ @push('scripts')
|
|
|
+ <script type="module">
|
|
|
+ function initChatBlock(block) {
|
|
|
+ const messages = block.querySelector('[data-chat-messages]');
|
|
|
+ const scrollButton = block.querySelector('[data-chat-scroll-bottom]');
|
|
|
+ const form = block.querySelector('[data-chat-form]');
|
|
|
+ const notifyToggle = block.querySelector('[data-chat-notify-toggle]');
|
|
|
+ const notificationType = block.querySelector('[data-chat-notification-type]');
|
|
|
+ const showAllToggle = block.querySelector('[data-chat-show-all-toggle]');
|
|
|
+ const summaryWrap = block.querySelector('[data-chat-recipient-summary-wrap]');
|
|
|
+ const summary = block.querySelector('[data-chat-recipient-summary]');
|
|
|
+ const hiddenTargets = block.querySelector('[data-chat-hidden-targets]');
|
|
|
+ const modal = block.querySelector('.modal');
|
|
|
+ const fileInput = block.querySelector('[data-chat-file-input]');
|
|
|
+ const attachBtn = block.querySelector('[data-chat-attach-btn]');
|
|
|
+ const fileCount = block.querySelector('[data-chat-file-count]');
|
|
|
+
|
|
|
+ // --- Файлы ---
|
|
|
+ if (attachBtn && fileInput) {
|
|
|
+ attachBtn.addEventListener('click', () => fileInput.click());
|
|
|
+ fileInput.addEventListener('change', () => {
|
|
|
+ const count = fileInput.files?.length || 0;
|
|
|
+ if (fileCount) {
|
|
|
+ fileCount.textContent = String(count);
|
|
|
+ fileCount.classList.toggle('d-none', count === 0);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Скролл ---
|
|
|
+ const scrollToBottom = (force = false) => {
|
|
|
+ if (!messages) return;
|
|
|
+ const isNearBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < 48;
|
|
|
+ if (force || isNearBottom) {
|
|
|
+ messages.scrollTop = messages.scrollHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const syncScrollButton = () => {
|
|
|
+ if (!messages || !scrollButton) return;
|
|
|
+ const shouldShow = messages.scrollHeight - messages.scrollTop - messages.clientHeight > 80;
|
|
|
+ scrollButton.classList.toggle('d-none', !shouldShow);
|
|
|
+ };
|
|
|
+
|
|
|
+ // --- Получатели ---
|
|
|
+ const getSelectedIds = () => Array.from(hiddenTargets.querySelectorAll('input[name="target_user_ids[]"]'))
|
|
|
+ .map((input) => Number(input.value))
|
|
|
+ .filter((value) => value > 0);
|
|
|
+
|
|
|
+ const setSelectedIds = (ids) => {
|
|
|
+ hiddenTargets.innerHTML = '';
|
|
|
+ ids.forEach((id) => {
|
|
|
+ const input = document.createElement('input');
|
|
|
+ input.type = 'hidden';
|
|
|
+ input.name = 'target_user_ids[]';
|
|
|
+ input.value = String(id);
|
|
|
+ hiddenTargets.appendChild(input);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const visibleRecipientItems = () => Array.from(block.querySelectorAll('[data-chat-recipient-item]'))
|
|
|
+ .filter((item) => !item.hidden);
|
|
|
+
|
|
|
+ const syncRecipientSummary = () => {
|
|
|
+ if (!summary) return;
|
|
|
+
|
|
|
+ if (!notifyToggle || !notifyToggle.checked) {
|
|
|
+ summary.textContent = 'Уведомления выключены';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const selectedIds = getSelectedIds();
|
|
|
+ if (!selectedIds.length) {
|
|
|
+ summary.textContent = 'Получатели не выбраны';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const names = selectedIds
|
|
|
+ .map((id) => block.querySelector('[data-chat-recipient-item][data-user-id="' + id + '"]'))
|
|
|
+ .filter(Boolean)
|
|
|
+ .map((item) => item.dataset.userName);
|
|
|
+
|
|
|
+ summary.textContent = 'Получатели: ' + names.join(', ');
|
|
|
+ };
|
|
|
+
|
|
|
+ const syncNotificationType = () => {
|
|
|
+ if (!notificationType) return;
|
|
|
+ if (!notifyToggle || !notifyToggle.checked) {
|
|
|
+ notificationType.value = 'none';
|
|
|
+ } else if (showAllToggle && showAllToggle.checked) {
|
|
|
+ notificationType.value = 'all';
|
|
|
+ } else {
|
|
|
+ notificationType.value = 'responsibles';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const applyRecipientFilter = (preserveSelection = true) => {
|
|
|
+ if (!modal) return;
|
|
|
+
|
|
|
+ const isAll = showAllToggle && showAllToggle.checked;
|
|
|
+ const recipientItems = Array.from(block.querySelectorAll('[data-chat-recipient-item]'));
|
|
|
+ const selectedIds = new Set(getSelectedIds());
|
|
|
+
|
|
|
+ recipientItems.forEach((item) => {
|
|
|
+ const isSelf = item.dataset.chatSelf === '1';
|
|
|
+ const isResponsible = item.dataset.chatResponsible === '1';
|
|
|
+ const visible = isAll || isResponsible;
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+
|
|
|
+ item.hidden = !visible;
|
|
|
+ checkbox.disabled = !visible || isSelf;
|
|
|
+ checkbox.checked = checkbox.checked && !isSelf;
|
|
|
+
|
|
|
+ if (!visible || isSelf) {
|
|
|
+ checkbox.checked = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!preserveSelection) {
|
|
|
+ recipientItems.forEach((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ if (!checkbox.disabled) {
|
|
|
+ checkbox.checked = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ recipientItems.forEach((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ checkbox.checked = !checkbox.disabled && selectedIds.has(Number(item.dataset.userId));
|
|
|
+ });
|
|
|
+
|
|
|
+ const hasVisibleSelected = recipientItems.some((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ return !checkbox.disabled && checkbox.checked;
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!hasVisibleSelected) {
|
|
|
+ recipientItems.forEach((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ if (!checkbox.disabled) {
|
|
|
+ checkbox.checked = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const commitRecipientSelection = () => {
|
|
|
+ if (!notifyToggle || !notifyToggle.checked) {
|
|
|
+ setSelectedIds([]);
|
|
|
+ syncNotificationType();
|
|
|
+ syncRecipientSummary();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const ids = visibleRecipientItems()
|
|
|
+ .map((item) => item.querySelector('[data-chat-recipient-checkbox]'))
|
|
|
+ .filter((checkbox) => checkbox && checkbox.checked)
|
|
|
+ .map((checkbox) => Number(checkbox.value))
|
|
|
+ .filter((value) => value > 0);
|
|
|
+
|
|
|
+ setSelectedIds(ids);
|
|
|
+ syncNotificationType();
|
|
|
+ syncRecipientSummary();
|
|
|
+ };
|
|
|
+
|
|
|
+ // --- Инициализация скролла ---
|
|
|
+ if (messages) {
|
|
|
+ requestAnimationFrame(() => { scrollToBottom(true); syncScrollButton(); });
|
|
|
+ setTimeout(() => { scrollToBottom(true); syncScrollButton(); }, 150);
|
|
|
+ messages.addEventListener('scroll', syncScrollButton);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scrollButton) {
|
|
|
+ scrollButton.addEventListener('click', () => scrollToBottom(true));
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Свитч уведомлений ---
|
|
|
+ if (notifyToggle) {
|
|
|
+ notifyToggle.addEventListener('change', () => {
|
|
|
+ const enabled = notifyToggle.checked;
|
|
|
+
|
|
|
+ if (summaryWrap) {
|
|
|
+ summaryWrap.classList.toggle('d-none', !enabled);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!enabled) {
|
|
|
+ setSelectedIds([]);
|
|
|
+ syncNotificationType();
|
|
|
+ syncRecipientSummary();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Включили — сразу открываем модалку с ответственными
|
|
|
+ applyRecipientFilter(false);
|
|
|
+ commitRecipientSelection();
|
|
|
+
|
|
|
+ if (modal) {
|
|
|
+ bootstrap.Modal.getOrCreateInstance(modal).show();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Свитч "Показать всех" в модалке ---
|
|
|
+ if (showAllToggle) {
|
|
|
+ showAllToggle.addEventListener('change', () => {
|
|
|
+ applyRecipientFilter(false);
|
|
|
+ syncNotificationType();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- Открытие модалки вручную ---
|
|
|
+ block.querySelector('[data-chat-open-recipient-modal]')?.addEventListener('click', () => {
|
|
|
+ applyRecipientFilter(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ // --- Выбрать/снять всех ---
|
|
|
+ block.querySelector('[data-chat-check-visible]')?.addEventListener('click', () => {
|
|
|
+ visibleRecipientItems().forEach((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ if (checkbox) checkbox.checked = true;
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ block.querySelector('[data-chat-uncheck-visible]')?.addEventListener('click', () => {
|
|
|
+ visibleRecipientItems().forEach((item) => {
|
|
|
+ const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
+ if (checkbox) checkbox.checked = false;
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // --- Применить / закрыть модалку ---
|
|
|
+ block.querySelector('[data-chat-apply-recipients]')?.addEventListener('click', commitRecipientSelection);
|
|
|
+ modal?.addEventListener('hidden.bs.modal', () => {
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
+ commitRecipientSelection();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // --- Сабмит формы ---
|
|
|
+ form?.addEventListener('submit', () => {
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
+ commitRecipientSelection();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // --- Начальное состояние ---
|
|
|
+ applyRecipientFilter(true);
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
+ commitRecipientSelection();
|
|
|
+ } else {
|
|
|
+ syncNotificationType();
|
|
|
+ syncRecipientSummary();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ document.querySelectorAll('[data-chat-block]').forEach(initChatBlock);
|
|
|
+ </script>
|
|
|
+ @endpush
|
|
|
+@endonce
|