< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_Shared__LoginPartial
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Shared/_LoginPartial.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 6
Coverable lines: 6
Total lines: 723
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ExecuteAsync()0%156120%
<ExecuteAsync()0%2040%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Shared/_LoginPartial.cshtml

#LineLine coverage
 1@using Microsoft.AspNetCore.Identity
 2@using OzonRepositories.Context.Identity
 3
 4@inject SignInManager<CustomIdentityUser> SignInManager
 5@inject UserManager<CustomIdentityUser> UserManager
 6
 7<style>
 8    .user-history-dropdown {
 9        position: relative;
 10        display: flex;
 11        min-width: 150px;
 12    }
 13
 14    .user-history-menu {
 15        display: none;
 16        position: absolute;
 17        right: 0;
 18        top: 100%;
 19        background-color: #fff;
 20        min-width: 350px;
 21        max-width: 450px;
 22        max-height: 500px;
 23        overflow-y: auto;
 24        box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
 25        border: 1px solid #dee2e6;
 26        border-radius: 0.25rem;
 27        z-index: 1050;
 28        margin-top: 5px;
 29        padding: 0.5rem 0;
 30    }
 31
 32    .user-history-menu.show {
 33        display: block;
 34    }
 35
 36    .user-history-header {
 37        padding: 0.75rem 1rem;
 38        border-bottom: 1px solid #dee2e6;
 39        background-color: #f8f9fa;
 40        font-weight: 600;
 41        font-size: 0.9rem;
 42        color: #495057;
 43    }
 44
 45    .user-history-item {
 46        display: block;
 47        padding: 0.75rem 1rem;
 48        color: #212529;
 49        text-decoration: none;
 50        border-bottom: 1px solid #f8f9fa;
 51        transition: background-color 0.15s ease-in-out;
 52    }
 53
 54    .user-history-item:hover {
 55        background-color: #f8f9fa;
 56        color: #0d6efd;
 57        text-decoration: none;
 58    }
 59
 60    .user-history-item:last-child {
 61        border-bottom: none;
 62    }
 63
 64    .user-history-title {
 65        font-weight: 500;
 66        font-size: 0.9rem;
 67        margin-bottom: 0.25rem;
 68        color: #212529;
 69    }
 70
 71    .user-history-path {
 72        font-size: 0.75rem;
 73        color: #6c757d;
 74        margin-bottom: 0.25rem;
 75    }
 76
 77    .user-history-time {
 78        font-size: 0.7rem;
 79        color: #adb5bd;
 80    }
 81
 82    .user-history-empty {
 83        padding: 1.5rem;
 84        text-align: center;
 85        color: #6c757d;
 86        font-size: 0.875rem;
 87    }
 88
 89    .user-history-footer {
 90        padding: 0.5rem 1rem;
 91        border-top: 1px solid #dee2e6;
 92        background-color: #f8f9fa;
 93        text-align: center;
 94    }
 95
 96    .user-history-footer a {
 97        font-size: 0.8rem;
 98        color: #0d6efd;
 99        text-decoration: none;
 100    }
 101
 102    .user-history-footer a:hover {
 103        text-decoration: underline;
 104    }
 105
 106    .user-history-divider {
 107        height: 1px;
 108        background-color: #dee2e6;
 109        margin: 0.5rem 0;
 110    }
 111
 112    .active-user-item {
 113        display: flex;
 114        flex-direction: column;
 115        padding: 0.5rem 1rem;
 116        border-bottom: 1px solid #f8f9fa;
 117    }
 118
 119    .active-user-item-header {
 120        display: flex;
 121        justify-content: space-between;
 122        align-items: center;
 123        width: 100%;
 124    }
 125
 126    .active-user-item:last-child {
 127        border-bottom: none;
 128    }
 129
 130    .active-user-item:hover {
 131        background-color: #f8f9fa;
 132    }
 133
 134    .active-user-name {
 135        display: flex;
 136        align-items: center;
 137        font-size: 0.875rem;
 138        color: #212529;
 139        flex: 1;
 140    }
 141
 142    .active-user-info {
 143        display: flex;
 144        align-items: center;
 145        gap: 0.5rem;
 146        min-width: 0;
 147    }
 148
 149    .active-user-status {
 150        margin-left: auto;
 151        flex-shrink: 0;
 152    }
 153
 154    .active-user-name i {
 155        margin-right: 0.5rem;
 156    }
 157
 158    .active-user-name i.fa-user-shield {
 159        color: #dc3545; /* Красный для админов */
 160    }
 161
 162    .active-user-name i.fa-user {
 163        color: #0d6efd; /* Синий для обычных пользователей */
 164    }
 165
 166    .active-user-status {
 167        font-size: 0.7rem;
 168    }
 169    /* Второй UL ограничиваем по ширине */
 170    .second-ul {
 171        flex: 0 0 auto; /* не растягивается */
 172        min-width: 100px; /* минимальная ширина */
 173        max-width: 300px; /* максимальная ширина */
 174    }
 175
 176    /* Если нужно, элементы второго UL тоже можно обрезать */
 177    .second-ul .nav-link {
 178        white-space: nowrap;
 179        overflow: hidden;
 180        text-overflow: ellipsis;
 181    }
 182</style>
 183
 184<ul class="navbar-nav second-ul">
 0185@if (SignInManager.IsSignedIn(User))
 186{
 187    <li class="nav-item">
 188        <a id="columnSettingsBtn" class="nav-link" href="#" onclick="showColumnSettingsModal(); return false;" title="На
 189            <i class="fas fa-columns"></i>
 190        </a>
 191    </li>
 192    <li class="nav-item user-history-dropdown">
 193        <a id="manage" class="nav-link text-dark" asp-area="Studio2" asp-controller="Profile" asp-action="Index"
 194           onmouseenter="showUserHistory()" onmouseleave="hideUserHistory()">
 195            @{
 0196                var isAdmin = User.IsInRole("Admin");
 0197                var iconClass = isAdmin ? "fas fa-user-shield" : "fas fa-user";
 0198                var iconColor = isAdmin ? "#dc3545" : "#0d6efd";
 199            }
 0200            <i class="@iconClass" style="color: @iconColor; margin-right: 0.5rem;"></i>@UserManager.GetUserName(User)
 201        </a>
 202        <div id="userHistoryMenu" class="user-history-menu" onmouseenter="showUserHistory()" onmouseleave="hideUserHisto
 203            <div class="user-history-header">
 204                <i class="fas fa-users me-2"></i>Активные пользователи
 205                <span id="activeUsersCountBadge" class="badge bg-primary ms-2" style="font-size: 0.75rem;">0</span>
 206            </div>
 207            <div id="activeUsersContent">
 208                <div class="user-history-empty">
 209                    <i class="fas fa-spinner fa-spin"></i> Загрузка...
 210                </div>
 211            </div>
 212            <div class="user-history-divider"></div>
 213            <div class="user-history-header">
 214                <i class="fas fa-history me-2"></i>История посещений
 215            </div>
 216            <div id="userHistoryContent">
 217                <div class="user-history-empty">
 218                    <i class="fas fa-spinner fa-spin"></i> Загрузка...
 219                </div>
 220            </div>
 221            <div class="user-history-footer">
 222                <a asp-area="Studio2" asp-controller="Profile" asp-action="Index">
 223                    <i class="fas fa-external-link-alt me-1"></i>Открыть полную историю
 224                </a>
 225            </div>
 226        </div>
 227    </li>
 228    <li class="nav-item">
 229        <a id="logout" asp-area="Identity" asp-page="/Account/Logout" class="nav-link btn btn-link text-dark border-0">В
 230    </li>
 231}
 232else
 233{
 234    @* <li class="nav-item">
 235        <a class="nav-link text-dark" id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
 236    </li> *@
 237    <li class="nav-item">
 238        <a class="nav-link text-dark" id="login" asp-area="Identity" asp-page="/Account/Login">Войти</a>
 239    </li>
 240}
 241</ul>
 242
 243<script>
 244    let userHistoryTimeout;
 245
 246    function showUserHistory() {
 247        clearTimeout(userHistoryTimeout);
 248        const menu = document.getElementById('userHistoryMenu');
 249        if (menu) {
 250            menu.classList.add('show');
 251            loadUserHistoryDropdown();
 252        }
 253    }
 254
 255    function hideUserHistory() {
 256        userHistoryTimeout = setTimeout(function() {
 257            const menu = document.getElementById('userHistoryMenu');
 258            if (menu) {
 259                menu.classList.remove('show');
 260            }
 261        }, 200);
 262    }
 263
 264    function loadUserHistoryDropdown() {
 265        try {
 266            const historyKey = 'userPageHistory';
 267            const historyData = localStorage.getItem(historyKey);
 268            const history = JSON.parse(historyData || '[]');
 269            const container = document.getElementById('userHistoryContent');
 270
 271            if (!container) return;
 272
 273            if (!history || history.length === 0) {
 274                container.innerHTML = '<div class="user-history-empty"><i class="fas fa-history fa-2x mb-2"></i><p>Истор
 275                return;
 276            }
 277
 278            // Показываем только последние 10 записей
 279            const recentHistory = history.slice(0, 10);
 280            let html = '';
 281
 282            recentHistory.forEach((item) => {
 283                const path = item.path || '';
 284                const title = item.title || path || 'Без названия';
 285                let displayTime = item.displayTime || item.timestamp;
 286
 287                // Форматируем время
 288                if (displayTime && displayTime.includes('T') && !displayTime.includes(',')) {
 289                    try {
 290                        const date = new Date(displayTime);
 291                        displayTime = date.toLocaleString('ru-RU', {
 292                            day: '2-digit',
 293                            month: '2-digit',
 294                            hour: '2-digit',
 295                            minute: '2-digit'
 296                        });
 297                    } catch (e) {
 298                        // Оставляем как есть
 299                    }
 300                }
 301
 302                // Определяем href
 303                let href = path;
 304                if (path) {
 305                    if (!path.startsWith('http') && !path.startsWith('/')) {
 306                        href = '/' + path;
 307                    }
 308                } else {
 309                    href = '#';
 310                }
 311
 312                const escapedHref = escapeHtml(href);
 313                const escapedTitle = escapeHtml(title);
 314                const escapedPath = escapeHtml(path || 'Не указан');
 315                const escapedTime = escapeHtml(displayTime);
 316
 317                html += `
 318                    <a href="${escapedHref}" class="user-history-item" ${href === '#' ? 'onclick="return false;"' : ''}>
 319                        <div class="user-history-title">${escapedTitle}</div>
 320                        <div class="user-history-path">${escapedPath}</div>
 321                        <div class="user-history-time"><i class="fas fa-clock me-1"></i>${escapedTime}</div>
 322                    </a>
 323                `;
 324            });
 325
 326            container.innerHTML = html;
 327        } catch (e) {
 328            console.error('Ошибка при загрузке истории:', e);
 329            const container = document.getElementById('userHistoryContent');
 330            if (container) {
 331                container.innerHTML = '<div class="user-history-empty">Ошибка загрузки истории</div>';
 332            }
 333        }
 334    }
 335
 336    function escapeHtml(text) {
 337        const div = document.createElement('div');
 338        div.textContent = text;
 339        return div.innerHTML;
 340    }
 341
 342    // Переменная для хранения SignalR соединения для активных пользователей
 343    let activeUsersConnectionDropdown = null;
 344
 345    // Инициализация SignalR соединения для активных пользователей в dropdown
 346    async function initializeActiveUsersDropdown() {
 347        if (!window.signalR) {
 348            console.error('SignalR library not loaded');
 349            const container = document.getElementById('activeUsersContent');
 350            if (container) {
 351                container.innerHTML = '<div class="user-history-empty">SignalR не загружен</div>';
 352            }
 353            return;
 354        }
 355
 356        try {
 357            // Используем общее соединение из ChatManager. Оно поднимается параллельно с этим
 358            // скриптом, поэтому дожидаемся его, а не открываем сразу второе соединение к тому
 359            // же хабу: лишние подключения множат обрывы и ошибки транспорта при деплое.
 360            const sharedConnection = window.Studio2SignalR
 361                ? await window.Studio2SignalR.waitForSharedConnection()
 362                : (window.ChatManager && window.ChatManager.connection
 363                    && window.ChatManager.connection.state === signalR.HubConnectionState.Connected
 364                    ? window.ChatManager.connection
 365                    : null);
 366
 367            if (sharedConnection) {
 368                activeUsersConnectionDropdown = sharedConnection;
 369
 370                // Добавляем обработчики событий для обновления dropdown
 371                // Можно добавлять несколько раз - SignalR поддерживает множественные обработчики
 372                activeUsersConnectionDropdown.on('UserConnected', (count, usersList) => {
 373                    updateActiveUsersDropdown(usersList || []);
 374                });
 375
 376                activeUsersConnectionDropdown.on('UserDisconnected', (count, usersList) => {
 377                    updateActiveUsersDropdown(usersList || []);
 378                });
 379            } else {
 380                // Создаем новое соединение только для dropdown
 381                activeUsersConnectionDropdown = new signalR.HubConnectionBuilder()
 382                    .withUrl('/notificationHub')
 383                    .configureLogging(window.Studio2SignalR ? window.Studio2SignalR.createLogger('online-users') : signa
 384                    .withAutomaticReconnect({
 385                        nextRetryDelayInMilliseconds: (retryContext) => {
 386                            if (retryContext.previousRetryCount < 3) {
 387                                return 1000;
 388                            } else if (retryContext.previousRetryCount < 6) {
 389                                return 5000;
 390                            } else {
 391                                return 10000;
 392                            }
 393                        }
 394                    })
 395                    .build();
 396
 397                // Обработчик подключения пользователя
 398                activeUsersConnectionDropdown.on('UserConnected', (count, usersList) => {
 399                    updateActiveUsersDropdown(usersList || []);
 400                });
 401
 402                // Обработчик отключения пользователя
 403                activeUsersConnectionDropdown.on('UserDisconnected', (count, usersList) => {
 404                    updateActiveUsersDropdown(usersList || []);
 405                });
 406
 407                // Запуск соединения
 408                await activeUsersConnectionDropdown.start();
 409                console.log('Active users dropdown connection started');
 410            }
 411
 412            // Получаем начальный список пользователей
 413            await refreshActiveUsersDropdown();
 414        } catch (err) {
 415            console.debug('Transient SignalR connection error for dropdown:', err?.message || err);
 416            const container = document.getElementById('activeUsersContent');
 417            if (container) {
 418                container.innerHTML = '<div class="user-history-empty">Связь обновляется...</div>';
 419            }
 420        }
 421    }
 422
 423    // Обновление списка активных пользователей в dropdown
 424    function updateActiveUsersDropdown(usersList) {
 425        const container = document.getElementById('activeUsersContent');
 426        const countBadge = document.getElementById('activeUsersCountBadge');
 427
 428        if (!container) return;
 429
 430        // Обрабатываем список пользователей (может быть массив строк или объектов)
 431        let processedUsers = [];
 432        if (usersList && usersList.length > 0) {
 433            if (typeof usersList[0] === 'string') {
 434                // Если это массив строк, преобразуем в объекты
 435                processedUsers = [...new Set(usersList)]
 436                    .filter(u => u && typeof u === 'string' && u.trim() !== '' && u !== 'Anonymous')
 437                    .map(u => ({ name: u.trim(), role: 'User' }));
 438            } else {
 439                // Если это массив объектов
 440                processedUsers = usersList
 441                    .filter(u => u && u.name && u.name !== 'Anonymous')
 442                    .map(u => ({
 443                        name: u.name.trim(),
 444                        role: u.role || 'User',
 445                        sessionDuration: u.sessionDuration || null,
 446                        currentPage: u.currentPage || null
 447                    }));
 448            }
 449        }
 450
 451        // Убираем дубликаты по имени и сортируем
 452        const uniqueUsers = processedUsers
 453            .filter((u, index, self) => index === self.findIndex(t => t.name === u.name))
 454            .sort((a, b) => a.name.localeCompare(b.name));
 455
 456        // Обновляем счетчик на основе уникальных пользователей
 457        if (countBadge) {
 458            countBadge.textContent = uniqueUsers.length;
 459        }
 460
 461        if (uniqueUsers.length === 0) {
 462            container.innerHTML = `
 463                <div class="user-history-empty">
 464                    <i class="fas fa-user-slash fa-2x mb-2"></i>
 465                    <p style="font-size: 0.75rem;">Нет активных пользователей</p>
 466                </div>
 467            `;
 468            return;
 469        }
 470
 471        // Проверяем, является ли текущий пользователь админом
 0472        const isCurrentUserAdmin = @(User.IsInRole("Admin") ? "true" : "false");
 473
 474        // Формируем HTML для списка пользователей
 475        let html = '';
 476        uniqueUsers.forEach((user, index) => {
 477            const userName = typeof user === 'object' ? user.name : user;
 478            const userRole = typeof user === 'object' ? user.role : 'User';
 479            const escapedUser = escapeHtml(userName);
 480            const userIndex = index;
 481
 482            // Определяем иконку по роли
 483            let iconClass = 'fas fa-user'; // По умолчанию для обычных пользователей
 484            let iconColor = '#0d6efd'; // Синий для обычных пользователей
 485            if (userRole === 'Admin') {
 486                iconClass = 'fas fa-user-shield';
 487                iconColor = '#dc3545'; // Красный для админов
 488            }
 489
 490            html += `
 491                <div class="active-user-item"
 492                     data-user-name="${escapedUser}"
 493                     data-user-index="${userIndex}"
 494                     onmouseenter="loadUserDetails('${escapedUser}', ${userIndex}, ${isCurrentUserAdmin})"
 495                     onmouseleave="clearUserDetails(${userIndex})">
 496                    <div class="active-user-item-header">
 497                        <div class="active-user-info">
 498                            <i class="${iconClass}" style="color: ${iconColor}; margin-right: 0.5rem;"></i>
 499                            <span style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;">${escapedUse
 500                        </div>
 501                        <span class="badge bg-success active-user-status" style="font-size: 0.65rem; margin-left: 0.5rem
 502                            <i class="fas fa-circle" style="font-size: 0.4rem;"></i> Онлайн
 503                        </span>
 504                    </div>
 505                    <div class="user-details" id="userDetails_${userIndex}" style="display: none; margin-top: 0.5rem; pa
 506                        <div style="font-size: 0.7rem; color: #6c757d;">
 507                            <i class="fas fa-spinner fa-spin me-1"></i>Загрузка...
 508                        </div>
 509                    </div>
 510                </div>
 511            `;
 512        });
 513
 514        container.innerHTML = html;
 515    }
 516
 517    // Функция для обновления списка активных пользователей в dropdown
 518    async function refreshActiveUsersDropdown() {
 519        // Используем существующее соединение из ChatManager, если оно есть
 520        let connection = null;
 521
 522        if (window.ChatManager && window.ChatManager.connection) {
 523            connection = window.ChatManager.connection;
 524        } else if (activeUsersConnectionDropdown) {
 525            connection = activeUsersConnectionDropdown;
 526        } else {
 527            // Пытаемся инициализировать соединение
 528            await initializeActiveUsersDropdown();
 529            if (window.ChatManager && window.ChatManager.connection) {
 530                connection = window.ChatManager.connection;
 531            } else if (activeUsersConnectionDropdown) {
 532                connection = activeUsersConnectionDropdown;
 533            }
 534        }
 535
 536        if (!connection) {
 537            const container = document.getElementById('activeUsersContent');
 538            if (container) {
 539                container.innerHTML = '<div class="user-history-empty">Инициализация подключения...</div>';
 540            }
 541            return;
 542        }
 543
 544        if (connection.state !== signalR.HubConnectionState.Connected) {
 545            const container = document.getElementById('activeUsersContent');
 546            if (container) {
 547                container.innerHTML = '<div class="user-history-empty">Ожидание подключения...</div>';
 548            }
 549            return;
 550        }
 551
 552        try {
 553            // Получаем список пользователей с ролями через метод Hub
 554            const usersList = await connection.invoke('GetConnectedUsersWithRoles');
 555            updateActiveUsersDropdown(Array.from(usersList));
 556        } catch (err) {
 557            console.error('Error refreshing active users dropdown:', err);
 558            const container = document.getElementById('activeUsersContent');
 559            if (container) {
 560                container.innerHTML = '<div class="user-history-empty">Ошибка загрузки</div>';
 561            }
 562        }
 563    }
 564
 565    // Обновляем функцию showUserHistory для загрузки активных пользователей
 566    const originalShowUserHistory = showUserHistory;
 567    showUserHistory = function() {
 568        clearTimeout(userHistoryTimeout);
 569        const menu = document.getElementById('userHistoryMenu');
 570        if (menu) {
 571            menu.classList.add('show');
 572            loadUserHistoryDropdown();
 573
 574            // Убеждаемся, что обработчики событий добавлены
 575            setupActiveUsersEventHandlers();
 576
 577            // Обновляем список активных пользователей
 578            refreshActiveUsersDropdown();
 579        }
 580    };
 581
 582    // Настройка обработчиков событий для активных пользователей
 583    function setupActiveUsersEventHandlers() {
 584        // Используем существующее соединение из ChatManager, если оно есть
 585        const connection = window.ChatManager && window.ChatManager.connection
 586            ? window.ChatManager.connection
 587            : activeUsersConnectionDropdown;
 588
 589        if (connection && connection.state === signalR.HubConnectionState.Connected) {
 590            // Добавляем обработчики событий (можно добавлять несколько раз)
 591            connection.on('UserConnected', (count, usersList) => {
 592                updateActiveUsersDropdown(usersList || []);
 593            });
 594
 595            connection.on('UserDisconnected', (count, usersList) => {
 596                updateActiveUsersDropdown(usersList || []);
 597            });
 598
 599            connection.on('UsersListUpdated', (usersList) => {
 600                updateActiveUsersDropdown(usersList || []);
 601            });
 602        }
 603    }
 604
 605    // Функция для загрузки детальной информации о пользователе при наведении
 606    async function loadUserDetails(userName, userIndex, isAdmin) {
 607        if (!isAdmin) return;
 608
 609        const detailsElement = document.getElementById(`userDetails_${userIndex}`);
 610        if (!detailsElement) return;
 611
 612        // Показываем элемент с индикатором загрузки
 613        detailsElement.style.display = 'block';
 614
 615        try {
 616            const connection = window.ChatManager && window.ChatManager.connection
 617                ? window.ChatManager.connection
 618                : activeUsersConnectionDropdown;
 619
 620            if (!connection || connection.state !== signalR.HubConnectionState.Connected) {
 621                detailsElement.innerHTML = '<div style="font-size: 0.7rem; color: #6c757d;">Соединение не установлено</d
 622                return;
 623            }
 624
 625            // Получаем список пользователей с ролями
 626            const usersList = await connection.invoke('GetConnectedUsersWithRoles');
 627            const user = Array.from(usersList).find(u => u.name === userName);
 628
 629            if (user && (user.sessionDuration || user.currentPage)) {
 630                let detailsHtml = '';
 631                if (user.sessionDuration) {
 632                    detailsHtml += `
 633                        <div style="font-size: 0.7rem; color: #6c757d; margin-bottom: 0.25rem;">
 634                            <i class="fas fa-clock me-1"></i>Сеанс: ${escapeHtml(user.sessionDuration)}
 635                        </div>
 636                    `;
 637                }
 638                if (user.currentPage) {
 639                    const pagePath = user.currentPage.startsWith('/') ? user.currentPage : '/' + user.currentPage;
 640                    const escapedPagePath = escapeHtml(pagePath);
 641                    const escapedPageDisplay = escapeHtml(user.currentPage);
 642                    detailsHtml += `
 643                        <div style="font-size: 0.7rem; color: #6c757d;">
 644                            <i class="fas fa-globe me-1"></i>
 645                            <a href="${escapedPagePath}" style="color: #0d6efd; text-decoration: none;"
 646                               onmouseover="this.style.textDecoration='underline'"
 647                               onmouseout="this.style.textDecoration='none'">
 648                                ${escapedPageDisplay}
 649                            </a>
 650                        </div>
 651                    `;
 652                }
 653                detailsElement.innerHTML = detailsHtml;
 654            } else {
 655                detailsElement.innerHTML = '<div style="font-size: 0.7rem; color: #6c757d;">Информация недоступна</div>'
 656            }
 657        } catch (err) {
 658            console.error('Error loading user details:', err);
 659            detailsElement.innerHTML = '<div style="font-size: 0.7rem; color: #dc3545;">Ошибка загрузки</div>';
 660        }
 661    }
 662
 663    // Функция для скрытия детальной информации
 664    function clearUserDetails(userIndex) {
 665        const detailsElement = document.getElementById(`userDetails_${userIndex}`);
 666        if (detailsElement) {
 667            detailsElement.style.display = 'none';
 668        }
 669    }
 670
 671    // Функция для отправки информации о текущей странице
 672    async function updateCurrentPage() {
 673        try {
 674            const connection = window.ChatManager && window.ChatManager.connection
 675                ? window.ChatManager.connection
 676                : activeUsersConnectionDropdown;
 677
 678            if (connection && connection.state === signalR.HubConnectionState.Connected) {
 679                const currentPath = window.location.pathname + window.location.search;
 680                await connection.invoke('UpdateCurrentPage', currentPath);
 681            }
 682        } catch (err) {
 683            console.error('Error updating current page:', err);
 684        }
 685    }
 686
 687    // Загружаем историю при загрузке страницы
 688    document.addEventListener('DOMContentLoaded', function() {
 689        // История будет загружена при первом наведении
 690        // Инициализируем подключение к SignalR для активных пользователей
 691        if (window.signalR) {
 692            initializeActiveUsersDropdown();
 693        } else {
 694            // Ждем загрузки SignalR
 695            window.addEventListener('load', function() {
 696                if (window.signalR) {
 697                    initializeActiveUsersDropdown();
 698                }
 699            });
 700        }
 701
 702        // Отправляем информацию о текущей странице при загрузке
 703        setTimeout(updateCurrentPage, 1000);
 704
 705        // Отправляем информацию о текущей странице при навигации
 706        let lastPath = window.location.pathname;
 707        setInterval(function() {
 708            const currentPath = window.location.pathname;
 709            if (currentPath !== lastPath) {
 710                lastPath = currentPath;
 711                updateCurrentPage();
 712            }
 713        }, 1000);
 714
 715        // Периодически проверяем соединение и добавляем обработчики, если нужно
 716        setInterval(function() {
 717            if (window.ChatManager && window.ChatManager.connection &&
 718                window.ChatManager.connection.state === signalR.HubConnectionState.Connected) {
 719                setupActiveUsersEventHandlers();
 720            }
 721        }, 2000); // Проверяем каждые 2 секунды
 722    });
 723</script>

Methods/Properties

ExecuteAsync()
<ExecuteAsync()