|
@@ -3,17 +3,33 @@
|
|
|
$messages = $messages ?? collect();
|
|
$messages = $messages ?? collect();
|
|
|
$users = $users ?? collect();
|
|
$users = $users ?? collect();
|
|
|
$responsibleUserIds = array_map('intval', $responsibleUserIds ?? []);
|
|
$responsibleUserIds = array_map('intval', $responsibleUserIds ?? []);
|
|
|
|
|
+ $managerUserId = isset($managerUserId) ? (int) $managerUserId : null;
|
|
|
|
|
+ $brigadierUserId = isset($brigadierUserId) ? (int) $brigadierUserId : null;
|
|
|
|
|
+ $currentUserId = (int) auth()->id();
|
|
|
$contextKey = $contextKey ?? 'chat';
|
|
$contextKey = $contextKey ?? 'chat';
|
|
|
$title = $title ?? 'Чат';
|
|
$title = $title ?? 'Чат';
|
|
|
$submitLabel = $submitLabel ?? 'Отправить';
|
|
$submitLabel = $submitLabel ?? 'Отправить';
|
|
|
$canSendNotifications = hasRole('admin,manager');
|
|
$canSendNotifications = hasRole('admin,manager');
|
|
|
$notificationValue = old('notification_type', \App\Models\ChatMessage::NOTIFICATION_NONE);
|
|
$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', []))
|
|
$selectedTargetUserIds = collect(old('target_user_ids', []))
|
|
|
->map(static fn ($id) => (int) $id)
|
|
->map(static fn ($id) => (int) $id)
|
|
|
->filter()
|
|
->filter()
|
|
|
->unique()
|
|
->unique()
|
|
|
->values()
|
|
->values()
|
|
|
->all();
|
|
->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
|
|
@endphp
|
|
|
|
|
|
|
|
<div class="chat-block mt-3" data-chat-block data-context-key="{{ $contextKey }}">
|
|
<div class="chat-block mt-3" data-chat-block data-context-key="{{ $contextKey }}">
|
|
@@ -30,10 +46,8 @@
|
|
|
<strong>{{ $message->user?->name ?? 'Пользователь' }}</strong>
|
|
<strong>{{ $message->user?->name ?? 'Пользователь' }}</strong>
|
|
|
@if($message->notification_type === \App\Models\ChatMessage::NOTIFICATION_USER && $message->targetUser)
|
|
@if($message->notification_type === \App\Models\ChatMessage::NOTIFICATION_USER && $message->targetUser)
|
|
|
<span class="text-muted">для {{ $message->targetUser->name }}</span>
|
|
<span class="text-muted">для {{ $message->targetUser->name }}</span>
|
|
|
- @elseif($message->notification_type === \App\Models\ChatMessage::NOTIFICATION_RESPONSIBLES)
|
|
|
|
|
- <span class="badge text-bg-light border">Уведомления: админы/менеджер/бригадир</span>
|
|
|
|
|
- @elseif($message->notification_type === \App\Models\ChatMessage::NOTIFICATION_ALL)
|
|
|
|
|
- <span class="badge text-bg-light border">Уведомления: выбранные получатели</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
|
|
@endif
|
|
|
</div>
|
|
</div>
|
|
|
<small class="text-muted">{{ $message->created_at?->format('d.m.Y H:i') }}</small>
|
|
<small class="text-muted">{{ $message->created_at?->format('d.m.Y H:i') }}</small>
|
|
@@ -73,73 +87,66 @@
|
|
|
<form action="{{ $action }}" method="post" enctype="multipart/form-data" class="mt-3" data-chat-form>
|
|
<form action="{{ $action }}" method="post" enctype="multipart/form-data" class="mt-3" data-chat-form>
|
|
|
@csrf
|
|
@csrf
|
|
|
|
|
|
|
|
- <div class="row g-2 align-items-start">
|
|
|
|
|
- <div class="col-12 col-lg-5">
|
|
|
|
|
- <label class="form-label" for="chat-message-{{ $contextKey }}">Сообщение</label>
|
|
|
|
|
|
|
+ {{-- Уведомления: свитч + саммари --}}
|
|
|
|
|
+ @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
|
|
<textarea
|
|
|
class="form-control"
|
|
class="form-control"
|
|
|
id="chat-message-{{ $contextKey }}"
|
|
id="chat-message-{{ $contextKey }}"
|
|
|
name="message"
|
|
name="message"
|
|
|
- rows="3"
|
|
|
|
|
|
|
+ rows="2"
|
|
|
placeholder="Введите сообщение"
|
|
placeholder="Введите сообщение"
|
|
|
>{{ old('message') }}</textarea>
|
|
>{{ old('message') }}</textarea>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="col-12 col-md-4 col-lg-3">
|
|
|
|
|
- <label class="form-label" for="chat-notification-{{ $contextKey }}">Уведомление</label>
|
|
|
|
|
- <select
|
|
|
|
|
- class="form-select chat-notification-type"
|
|
|
|
|
- id="chat-notification-{{ $contextKey }}"
|
|
|
|
|
- name="notification_type"
|
|
|
|
|
- data-chat-context-key="{{ $contextKey }}"
|
|
|
|
|
- @disabled(!$canSendNotifications)
|
|
|
|
|
- >
|
|
|
|
|
- <option value="{{ \App\Models\ChatMessage::NOTIFICATION_NONE }}" @selected($notificationValue === \App\Models\ChatMessage::NOTIFICATION_NONE)>Нет</option>
|
|
|
|
|
- @if($canSendNotifications)
|
|
|
|
|
- <option value="{{ \App\Models\ChatMessage::NOTIFICATION_RESPONSIBLES }}" @selected($notificationValue === \App\Models\ChatMessage::NOTIFICATION_RESPONSIBLES)>Админы, менеджер, бригадир</option>
|
|
|
|
|
- <option value="{{ \App\Models\ChatMessage::NOTIFICATION_ALL }}" @selected($notificationValue === \App\Models\ChatMessage::NOTIFICATION_ALL)>Все</option>
|
|
|
|
|
- @endif
|
|
|
|
|
- </select>
|
|
|
|
|
- @if(!$canSendNotifications)
|
|
|
|
|
- <input type="hidden" name="notification_type" value="{{ \App\Models\ChatMessage::NOTIFICATION_NONE }}">
|
|
|
|
|
- @endif
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <input class="d-none" id="chat-attachments-{{ $contextKey }}" type="file" name="attachments[]" multiple data-chat-file-input>
|
|
|
|
|
|
|
|
- <div class="col-12 col-md-4 col-lg-2">
|
|
|
|
|
- <label class="form-label" for="chat-attachments-{{ $contextKey }}">Файлы</label>
|
|
|
|
|
- <input class="form-control" id="chat-attachments-{{ $contextKey }}" type="file" name="attachments[]" multiple>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <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>
|
|
|
|
|
|
|
|
- <div class="col-12">
|
|
|
|
|
- <div class="row g-2 align-items-center {{ $canSendNotifications && $notificationValue !== \App\Models\ChatMessage::NOTIFICATION_NONE ? '' : 'd-none' }}"
|
|
|
|
|
- data-chat-recipient-picker-wrap>
|
|
|
|
|
- <div class="col-12 col-md-auto">
|
|
|
|
|
- <button
|
|
|
|
|
- type="button"
|
|
|
|
|
- class="btn btn-outline-secondary btn-sm"
|
|
|
|
|
- data-chat-open-recipient-modal
|
|
|
|
|
- data-bs-toggle="modal"
|
|
|
|
|
- data-bs-target="#chatRecipientsModal-{{ $contextKey }}"
|
|
|
|
|
- >
|
|
|
|
|
- Выбрать получателей
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="col-12 col-md">
|
|
|
|
|
- <div class="small text-muted chat-recipient-summary" data-chat-recipient-summary>
|
|
|
|
|
- Получатели не выбраны
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div data-chat-hidden-targets>
|
|
|
|
|
- @foreach($selectedTargetUserIds as $selectedTargetUserId)
|
|
|
|
|
- <input type="hidden" name="target_user_ids[]" value="{{ $selectedTargetUserId }}">
|
|
|
|
|
- @endforeach
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <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 class="col-12 text-end">
|
|
|
|
|
- <button class="btn btn-primary btn-sm" type="submit">{{ $submitLabel }}</button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div data-chat-hidden-targets>
|
|
|
|
|
+ @foreach($selectedTargetUserIds as $selectedTargetUserId)
|
|
|
|
|
+ <input type="hidden" name="target_user_ids[]" value="{{ $selectedTargetUserId }}">
|
|
|
|
|
+ @endforeach
|
|
|
</div>
|
|
</div>
|
|
|
</form>
|
|
</form>
|
|
|
|
|
|
|
@@ -152,29 +159,61 @@
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="modal-body" data-chat-recipient-modal-body>
|
|
<div class="modal-body" data-chat-recipient-modal-body>
|
|
|
- <div class="d-flex justify-content-between align-items-center gap-2 mb-2 flex-wrap">
|
|
|
|
|
- <div class="small text-muted" data-chat-recipient-hint></div>
|
|
|
|
|
|
|
+ {{-- Свитч: показать всех пользователей --}}
|
|
|
|
|
+ <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">
|
|
<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>
|
|
|
|
|
|
|
+ <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>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="chat-recipient-list">
|
|
<div class="chat-recipient-list">
|
|
|
- @foreach($users as $userId => $userName)
|
|
|
|
|
|
|
+ @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"
|
|
<label class="form-check mb-2 chat-recipient-item"
|
|
|
data-chat-recipient-item
|
|
data-chat-recipient-item
|
|
|
data-user-id="{{ $userId }}"
|
|
data-user-id="{{ $userId }}"
|
|
|
- data-user-name="{{ $userName }}"
|
|
|
|
|
- data-chat-responsible="{{ in_array((int) $userId, $responsibleUserIds, true) ? '1' : '0' }}">
|
|
|
|
|
|
|
+ data-user-name="{{ $displayLabel }}"
|
|
|
|
|
+ data-chat-responsible="{{ in_array((int) $userId, $responsibleUserIds, true) ? '1' : '0' }}"
|
|
|
|
|
+ data-chat-self="{{ $isSelf ? '1' : '0' }}">
|
|
|
<input
|
|
<input
|
|
|
class="form-check-input"
|
|
class="form-check-input"
|
|
|
type="checkbox"
|
|
type="checkbox"
|
|
|
value="{{ $userId }}"
|
|
value="{{ $userId }}"
|
|
|
data-chat-recipient-checkbox
|
|
data-chat-recipient-checkbox
|
|
|
- @checked(in_array((int) $userId, $selectedTargetUserIds, true))
|
|
|
|
|
|
|
+ @disabled($isSelf)
|
|
|
|
|
+ @checked(!$isSelf && in_array((int) $userId, $selectedTargetUserIds, true))
|
|
|
>
|
|
>
|
|
|
- <span class="form-check-label">{{ $userName }}</span>
|
|
|
|
|
|
|
+ <span class="form-check-label {{ $isSelf ? 'text-muted' : '' }}">{{ $displayLabel }}@if($isSelf) (вы)@endif</span>
|
|
|
</label>
|
|
</label>
|
|
|
@endforeach
|
|
@endforeach
|
|
|
</div>
|
|
</div>
|
|
@@ -196,17 +235,32 @@
|
|
|
const messages = block.querySelector('[data-chat-messages]');
|
|
const messages = block.querySelector('[data-chat-messages]');
|
|
|
const scrollButton = block.querySelector('[data-chat-scroll-bottom]');
|
|
const scrollButton = block.querySelector('[data-chat-scroll-bottom]');
|
|
|
const form = block.querySelector('[data-chat-form]');
|
|
const form = block.querySelector('[data-chat-form]');
|
|
|
- const notificationType = block.querySelector('.chat-notification-type');
|
|
|
|
|
- const recipientPickerWrap = block.querySelector('[data-chat-recipient-picker-wrap]');
|
|
|
|
|
- const hiddenTargets = block.querySelector('[data-chat-hidden-targets]');
|
|
|
|
|
|
|
+ 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 summary = block.querySelector('[data-chat-recipient-summary]');
|
|
|
|
|
+ const hiddenTargets = block.querySelector('[data-chat-hidden-targets]');
|
|
|
const modal = block.querySelector('.modal');
|
|
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) => {
|
|
const scrollToBottom = (force = false) => {
|
|
|
- if (!messages) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (!messages) return;
|
|
|
const isNearBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < 48;
|
|
const isNearBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < 48;
|
|
|
if (force || isNearBottom) {
|
|
if (force || isNearBottom) {
|
|
|
messages.scrollTop = messages.scrollHeight;
|
|
messages.scrollTop = messages.scrollHeight;
|
|
@@ -214,14 +268,12 @@
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const syncScrollButton = () => {
|
|
const syncScrollButton = () => {
|
|
|
- if (!messages || !scrollButton) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (!messages || !scrollButton) return;
|
|
|
const shouldShow = messages.scrollHeight - messages.scrollTop - messages.clientHeight > 80;
|
|
const shouldShow = messages.scrollHeight - messages.scrollTop - messages.clientHeight > 80;
|
|
|
scrollButton.classList.toggle('d-none', !shouldShow);
|
|
scrollButton.classList.toggle('d-none', !shouldShow);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // --- Получатели ---
|
|
|
const getSelectedIds = () => Array.from(hiddenTargets.querySelectorAll('input[name="target_user_ids[]"]'))
|
|
const getSelectedIds = () => Array.from(hiddenTargets.querySelectorAll('input[name="target_user_ids[]"]'))
|
|
|
.map((input) => Number(input.value))
|
|
.map((input) => Number(input.value))
|
|
|
.filter((value) => value > 0);
|
|
.filter((value) => value > 0);
|
|
@@ -241,11 +293,9 @@
|
|
|
.filter((item) => !item.hidden);
|
|
.filter((item) => !item.hidden);
|
|
|
|
|
|
|
|
const syncRecipientSummary = () => {
|
|
const syncRecipientSummary = () => {
|
|
|
- if (!summary || !notificationType) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!summary) return;
|
|
|
|
|
|
|
|
- if (notificationType.value === 'none') {
|
|
|
|
|
|
|
+ if (!notifyToggle || !notifyToggle.checked) {
|
|
|
summary.textContent = 'Уведомления выключены';
|
|
summary.textContent = 'Уведомления выключены';
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -261,31 +311,38 @@
|
|
|
.filter(Boolean)
|
|
.filter(Boolean)
|
|
|
.map((item) => item.dataset.userName);
|
|
.map((item) => item.dataset.userName);
|
|
|
|
|
|
|
|
- summary.textContent = 'Выбрано: ' + names.join(', ');
|
|
|
|
|
|
|
+ summary.textContent = 'Получатели: ' + names.join(', ');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const applyRecipientFilter = (preserveSelection = true) => {
|
|
|
|
|
- if (!notificationType || !modal) {
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ 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 isResponsibles = notificationType.value === 'responsibles';
|
|
|
|
|
- const isAll = notificationType.value === 'all';
|
|
|
|
|
|
|
+ const applyRecipientFilter = (preserveSelection = true) => {
|
|
|
|
|
+ if (!modal) return;
|
|
|
|
|
+
|
|
|
|
|
+ const isAll = showAllToggle && showAllToggle.checked;
|
|
|
const recipientItems = Array.from(block.querySelectorAll('[data-chat-recipient-item]'));
|
|
const recipientItems = Array.from(block.querySelectorAll('[data-chat-recipient-item]'));
|
|
|
const selectedIds = new Set(getSelectedIds());
|
|
const selectedIds = new Set(getSelectedIds());
|
|
|
- const visibleIds = [];
|
|
|
|
|
|
|
|
|
|
recipientItems.forEach((item) => {
|
|
recipientItems.forEach((item) => {
|
|
|
|
|
+ const isSelf = item.dataset.chatSelf === '1';
|
|
|
const isResponsible = item.dataset.chatResponsible === '1';
|
|
const isResponsible = item.dataset.chatResponsible === '1';
|
|
|
- const visible = isAll || (isResponsibles && isResponsible);
|
|
|
|
|
|
|
+ const visible = isAll || isResponsible;
|
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
|
|
|
|
|
item.hidden = !visible;
|
|
item.hidden = !visible;
|
|
|
- checkbox.disabled = !visible;
|
|
|
|
|
|
|
+ checkbox.disabled = !visible || isSelf;
|
|
|
|
|
+ checkbox.checked = checkbox.checked && !isSelf;
|
|
|
|
|
|
|
|
- if (visible) {
|
|
|
|
|
- visibleIds.push(Number(item.dataset.userId));
|
|
|
|
|
- } else {
|
|
|
|
|
|
|
+ if (!visible || isSelf) {
|
|
|
checkbox.checked = false;
|
|
checkbox.checked = false;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -308,7 +365,7 @@
|
|
|
return !checkbox.disabled && checkbox.checked;
|
|
return !checkbox.disabled && checkbox.checked;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- if (!hasVisibleSelected && visibleIds.length) {
|
|
|
|
|
|
|
+ if (!hasVisibleSelected) {
|
|
|
recipientItems.forEach((item) => {
|
|
recipientItems.forEach((item) => {
|
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
if (!checkbox.disabled) {
|
|
if (!checkbox.disabled) {
|
|
@@ -317,18 +374,12 @@
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- const hint = block.querySelector('[data-chat-recipient-hint]');
|
|
|
|
|
- if (hint) {
|
|
|
|
|
- hint.textContent = isResponsibles
|
|
|
|
|
- ? 'Доступны только админы, менеджер и бригадир текущей сущности.'
|
|
|
|
|
- : 'Доступны все пользователи.';
|
|
|
|
|
- }
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const commitRecipientSelection = () => {
|
|
const commitRecipientSelection = () => {
|
|
|
- if (!notificationType || notificationType.value === 'none') {
|
|
|
|
|
|
|
+ if (!notifyToggle || !notifyToggle.checked) {
|
|
|
setSelectedIds([]);
|
|
setSelectedIds([]);
|
|
|
|
|
+ syncNotificationType();
|
|
|
syncRecipientSummary();
|
|
syncRecipientSummary();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -340,18 +391,14 @@
|
|
|
.filter((value) => value > 0);
|
|
.filter((value) => value > 0);
|
|
|
|
|
|
|
|
setSelectedIds(ids);
|
|
setSelectedIds(ids);
|
|
|
|
|
+ syncNotificationType();
|
|
|
syncRecipientSummary();
|
|
syncRecipientSummary();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // --- Инициализация скролла ---
|
|
|
if (messages) {
|
|
if (messages) {
|
|
|
- requestAnimationFrame(() => {
|
|
|
|
|
- scrollToBottom(true);
|
|
|
|
|
- syncScrollButton();
|
|
|
|
|
- });
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- scrollToBottom(true);
|
|
|
|
|
- syncScrollButton();
|
|
|
|
|
- }, 150);
|
|
|
|
|
|
|
+ requestAnimationFrame(() => { scrollToBottom(true); syncScrollButton(); });
|
|
|
|
|
+ setTimeout(() => { scrollToBottom(true); syncScrollButton(); }, 150);
|
|
|
messages.addEventListener('scroll', syncScrollButton);
|
|
messages.addEventListener('scroll', syncScrollButton);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -359,20 +406,23 @@
|
|
|
scrollButton.addEventListener('click', () => scrollToBottom(true));
|
|
scrollButton.addEventListener('click', () => scrollToBottom(true));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (notificationType) {
|
|
|
|
|
- notificationType.addEventListener('change', (event) => {
|
|
|
|
|
- const enabled = event.target.value !== 'none';
|
|
|
|
|
|
|
+ // --- Свитч уведомлений ---
|
|
|
|
|
+ if (notifyToggle) {
|
|
|
|
|
+ notifyToggle.addEventListener('change', () => {
|
|
|
|
|
+ const enabled = notifyToggle.checked;
|
|
|
|
|
|
|
|
- if (recipientPickerWrap) {
|
|
|
|
|
- recipientPickerWrap.classList.toggle('d-none', !enabled);
|
|
|
|
|
|
|
+ if (summaryWrap) {
|
|
|
|
|
+ summaryWrap.classList.toggle('d-none', !enabled);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (!enabled) {
|
|
if (!enabled) {
|
|
|
setSelectedIds([]);
|
|
setSelectedIds([]);
|
|
|
|
|
+ syncNotificationType();
|
|
|
syncRecipientSummary();
|
|
syncRecipientSummary();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Включили — сразу открываем модалку с ответственными
|
|
|
applyRecipientFilter(false);
|
|
applyRecipientFilter(false);
|
|
|
commitRecipientSelection();
|
|
commitRecipientSelection();
|
|
|
|
|
|
|
@@ -382,45 +432,55 @@
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // --- Свитч "Показать всех" в модалке ---
|
|
|
|
|
+ if (showAllToggle) {
|
|
|
|
|
+ showAllToggle.addEventListener('change', () => {
|
|
|
|
|
+ applyRecipientFilter(false);
|
|
|
|
|
+ syncNotificationType();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // --- Открытие модалки вручную ---
|
|
|
block.querySelector('[data-chat-open-recipient-modal]')?.addEventListener('click', () => {
|
|
block.querySelector('[data-chat-open-recipient-modal]')?.addEventListener('click', () => {
|
|
|
applyRecipientFilter(true);
|
|
applyRecipientFilter(true);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // --- Выбрать/снять всех ---
|
|
|
block.querySelector('[data-chat-check-visible]')?.addEventListener('click', () => {
|
|
block.querySelector('[data-chat-check-visible]')?.addEventListener('click', () => {
|
|
|
visibleRecipientItems().forEach((item) => {
|
|
visibleRecipientItems().forEach((item) => {
|
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
- if (checkbox) {
|
|
|
|
|
- checkbox.checked = true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (checkbox) checkbox.checked = true;
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
block.querySelector('[data-chat-uncheck-visible]')?.addEventListener('click', () => {
|
|
block.querySelector('[data-chat-uncheck-visible]')?.addEventListener('click', () => {
|
|
|
visibleRecipientItems().forEach((item) => {
|
|
visibleRecipientItems().forEach((item) => {
|
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
const checkbox = item.querySelector('[data-chat-recipient-checkbox]');
|
|
|
- if (checkbox) {
|
|
|
|
|
- checkbox.checked = false;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (checkbox) checkbox.checked = false;
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // --- Применить / закрыть модалку ---
|
|
|
block.querySelector('[data-chat-apply-recipients]')?.addEventListener('click', commitRecipientSelection);
|
|
block.querySelector('[data-chat-apply-recipients]')?.addEventListener('click', commitRecipientSelection);
|
|
|
modal?.addEventListener('hidden.bs.modal', () => {
|
|
modal?.addEventListener('hidden.bs.modal', () => {
|
|
|
- if (notificationType && notificationType.value !== 'none') {
|
|
|
|
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
commitRecipientSelection();
|
|
commitRecipientSelection();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // --- Сабмит формы ---
|
|
|
form?.addEventListener('submit', () => {
|
|
form?.addEventListener('submit', () => {
|
|
|
- if (notificationType && notificationType.value !== 'none') {
|
|
|
|
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
commitRecipientSelection();
|
|
commitRecipientSelection();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // --- Начальное состояние ---
|
|
|
applyRecipientFilter(true);
|
|
applyRecipientFilter(true);
|
|
|
- if (notificationType && notificationType.value !== 'none') {
|
|
|
|
|
|
|
+ if (notifyToggle && notifyToggle.checked) {
|
|
|
commitRecipientSelection();
|
|
commitRecipientSelection();
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ syncNotificationType();
|
|
|
syncRecipientSummary();
|
|
syncRecipientSummary();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|