1500 lines
50 KiB
Plaintext
1500 lines
50 KiB
Plaintext
@page "/Chat"
|
|
@page "/"
|
|
@using Common.Dtos.Conversation
|
|
@using Common.Dtos.Group
|
|
@using Common.Enums
|
|
@using HushianWebApp.Components
|
|
@using HushianWebApp.Service
|
|
@using HushianWebApp.Services
|
|
@using Microsoft.AspNetCore.SignalR.Client
|
|
@inject ChatService chatService
|
|
@inject GroupService groupService
|
|
@inject UserService userService
|
|
@inject IJSRuntime JS
|
|
@inject ToastService toastService
|
|
@inject ILocalStorageService localStorageService;
|
|
@inject HttpClient _Http;
|
|
@inject NavigationManager nav
|
|
<ConfirmDialog @ref="dialog" />
|
|
<Modal @ref="modal" IsVerticallyCentered="true" IsScrollable="true" />
|
|
<PageTitle>گفتمان</PageTitle>
|
|
<div class="container-fluid">
|
|
<div class="row" style="height:85vh">
|
|
<!-- Sidebar (A) -->
|
|
<div class="col-md-3 sidebar-container" id="A">
|
|
<!-- A1: Header -->
|
|
<div class="sidebar-header" id="A1">
|
|
<div class="header-content">
|
|
<Icon Name="IconName.ChatDots" Class="header-icon" />
|
|
<span class="header-text">گفتگو های اخیر</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- A2: Buttons -->
|
|
<div class="sidebar-tabs" id="A2">
|
|
<!-- Inbox1 -->
|
|
<Button Outline="@isSelectedInbox1" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(1);}" Size=ButtonSize.Small Color="ButtonColor.Warning"
|
|
class=@($"tab-button inbox1-button {(isSelectedInbox1 ? "active-tab inbox1-active" : "")}")>
|
|
<Icon Name="IconName.Inbox" Class="tab-icon" />
|
|
<span class="tab-text">پیام های آمده</span>
|
|
<Badge Color="BadgeColor.Warning" Class="tab-badge">@Inbox1Items.Count()</Badge>
|
|
</Button>
|
|
|
|
<!-- Inbox2 -->
|
|
<Button Outline="@isSelectedInbox2" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(2);}" Size=ButtonSize.Small Color="ButtonColor.Primary"
|
|
class=@($"tab-button inbox2-button {(isSelectedInbox2 ? "active-tab inbox2-active" : "")}")>
|
|
<Icon Name="IconName.Send" Class="tab-icon" />
|
|
<span class="tab-text">پیام های من</span>
|
|
<Badge Color="BadgeColor.Warning" Class="tab-badge">@Inbox2Items.Count()</Badge>
|
|
</Button>
|
|
|
|
<!-- Inbox3 -->
|
|
<Button Outline="@isSelectedInbox3" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(3);}" Size=ButtonSize.Small Color="ButtonColor.Danger"
|
|
class=@($"tab-button inbox3-button {(isSelectedInbox3 ? "active-tab inbox3-active" : "")}")>
|
|
<Icon Name="IconName.Archive" Class="tab-icon" />
|
|
<span class="tab-text">پیام های بسته</span>
|
|
</Button>
|
|
</div>
|
|
|
|
<!-- A3: Chat list -->
|
|
<div class="sidebar-chat-list" id="A3">
|
|
<Spinner Class="me-3" Type="SpinnerType.Dots" Color="SpinnerColor.Primary" Visible="@chatloading" Size="SpinnerSize.Small" />
|
|
@if (isSelectedInbox1)
|
|
{
|
|
@foreach (var item in Inbox1Items)
|
|
{
|
|
<div class="chat-list-item" @onclick="async()=>await onClickSelectedChat(1,item)">
|
|
|
|
<div class="item-content">
|
|
<div class="item-header">
|
|
<strong class="item-name">@item.UserFullName </strong>
|
|
@if (!string.IsNullOrEmpty(item.GroupName))
|
|
{
|
|
<div class="mb-3">
|
|
<Badge Color="BadgeColor.Info" VisuallyHiddenText="Visually hidden text for Info">@item.GroupName</Badge>
|
|
</div>
|
|
}
|
|
|
|
<div class="item-time">
|
|
<small class="time-text">@item.LastMsgdate</small>
|
|
<small class="time-text">@item.LastMsgtime</small>
|
|
|
|
</div>
|
|
</div>
|
|
<div class="item-message">@item.LastText</div>
|
|
</div>
|
|
<div class="item-badge">
|
|
<Badge Color="BadgeColor.Danger" Class="unread-badge">@item.Responses.Count()</Badge>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@if (isSelectedInbox2)
|
|
{
|
|
@foreach (var item in Inbox2Items)
|
|
{
|
|
<div class="chat-list-item" @onclick="async()=>await onClickSelectedChat(2,item)">
|
|
|
|
<div class="item-content">
|
|
<div class="item-header">
|
|
<strong class="item-name">@item.UserFullName</strong>
|
|
@if (!string.IsNullOrEmpty(item.GroupName))
|
|
{
|
|
<div class="mb-3">
|
|
<Badge Color="BadgeColor.Info" VisuallyHiddenText="Visually hidden text for Info">@item.GroupName</Badge>
|
|
</div>
|
|
}
|
|
<div class="item-time">
|
|
<small class="time-text">@item.LastMsgdate</small>
|
|
<small class="time-text">@item.LastMsgtime</small>
|
|
</div>
|
|
</div>
|
|
<div class="item-message">@item.LastText</div>
|
|
</div>
|
|
@if (item.Responses.Count(c => !c.IsRead && c.Type == ConversationType.UE) > 0)
|
|
{
|
|
<div class="item-badge">
|
|
<Badge Color="BadgeColor.Danger" Class="unread-badge">@item.Responses.Count(c => !c.IsRead && c.Type == ConversationType.UE)</Badge>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
}
|
|
|
|
@if (isSelectedInbox3)
|
|
{
|
|
@foreach (var item in Inbox3Items)
|
|
{
|
|
<div class="chat-list-item" @onclick="async()=>await onClickSelectedChat(3,item)">
|
|
|
|
<div class="item-content">
|
|
<div class="item-header">
|
|
<strong class="item-name">@item.UserFullName</strong>
|
|
@if (!string.IsNullOrEmpty(item.GroupName))
|
|
{
|
|
<div class="mb-3">
|
|
<Badge Color="BadgeColor.Info" VisuallyHiddenText="Visually hidden text for Info">@item.GroupName</Badge>
|
|
</div>
|
|
}
|
|
<div class="item-time">
|
|
<small class="time-text">@item.LastMsgdate</small>
|
|
<small class="time-text">@item.LastMsgtime</small>
|
|
</div>
|
|
</div>
|
|
<div class="item-message">@item.LastText</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Main Chat Section (B) -->
|
|
<div class="col-md-9 d-flex flex-column" id="B">
|
|
<div class="input-group">
|
|
@if (ChatCurrent != null)
|
|
{
|
|
<p type="text" class="form-control fw-bold text-primary" style="border:none;align-self: center;" aria-describedby="basic-addon1">@SelectedChatUserName</p>
|
|
<div class="d-flex gap-2 ms-auto">
|
|
@if (ChatCurrent.status == Common.Enums.ConversationStatus.InProgress)
|
|
{
|
|
<Button Color="ButtonColor.Danger" Size=ButtonSize.ExtraSmall Outline="true" Class="finish-conversation-btn"
|
|
@onclick="CloseChat">
|
|
<Icon Name="IconName.Escape" /> اتمام گفتگو
|
|
</Button>
|
|
|
|
<Button Color="ButtonColor.Secondary" Size=ButtonSize.ExtraSmall Outline="true" Class="toexper-btn" @onclick="onclickAttachedto">
|
|
<Icon Name="IconName.EnvelopeArrowUp" /> پیوست به...
|
|
</Button>
|
|
}
|
|
else if (ChatCurrent.status == Common.Enums.ConversationStatus.Finished
|
|
&& (CurrentUser.Role == "Company" || ChatCurrent.ExperID == CurrentUser.ExperID))
|
|
{
|
|
<Button Color="ButtonColor.Success" Size=ButtonSize.ExtraSmall Outline="true" Class="open-conversation-btn"
|
|
@onclick="OpenChat">
|
|
|
|
<Icon Name="IconName.Escape" /> باز کردن گفتگو
|
|
</Button>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
else if (isSelectedInbox1)
|
|
{
|
|
<div class="warning-note">
|
|
<div class="warning-icon">
|
|
<Icon Name="IconName.ExclamationTriangle" Color="IconColor.Warning" Size="IconSize.x3" />
|
|
</div>
|
|
<div class="warning-content">
|
|
<h6 class="warning-title">نکته مهم</h6>
|
|
<p class="warning-text">از انتخاب گفتگو مطمئن شوید، بعد از انتخاب شما مسئول بررسی میباشد</p>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
<!-- B1: Chat area -->
|
|
<div class="flex-fill chat-area-container" id="B1">
|
|
@if (ChatCurrent != null && ChatCurrent.Responses != null)
|
|
{
|
|
<div class="chat-container p-3">
|
|
@{
|
|
bool target = false;
|
|
}
|
|
@foreach (var msg in ChatCurrent?.Responses)
|
|
{
|
|
@if (!target && ((!msg.IsRead && msg.Type == Common.Enums.ConversationType.UE) || ChatCurrent.Responses.Last() == msg))
|
|
{
|
|
target = true;
|
|
<div id="target" class="chat-separator">
|
|
@if (!msg.IsRead && msg.Type == Common.Enums.ConversationType.UE)
|
|
{
|
|
<div class="separator-line new-message">
|
|
<span class="separator-text">پیام جدید</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<div class="d-flex mb-2 @(msg.Type==Common.Enums.ConversationType.UE ? "justify-content-end" : "justify-content-start")">
|
|
|
|
<div class="chat-bubble @(msg.Type==Common.Enums.ConversationType.UE ? "chat-mine": "chat-other")" data-id="@msg.ID">
|
|
@if (msg.FileContent != null && msg.FileContent.Length > 0 && !string.IsNullOrWhiteSpace(msg.FileType) && msg.FileType.StartsWith("image/"))
|
|
{
|
|
<a href="@GetImageDataUrl(msg.FileType, msg.FileContent)" download="@GetDownloadFileName(msg.FileName, msg.FileType)" title="دانلود تصویر" style="display:inline-block">
|
|
<img src="@GetImageDataUrl(msg.FileType, msg.FileContent)" alt="image" style="max-width: 220px; border-radius: 8px; display: block; cursor: pointer;" />
|
|
</a>
|
|
@if (!string.IsNullOrWhiteSpace(msg.text))
|
|
{
|
|
<div style="margin-top:6px">@msg.text</div>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
@msg.text
|
|
}
|
|
</div>
|
|
@if (msg.Type != Common.Enums.ConversationType.UE)
|
|
{
|
|
if (msg.IsRead)
|
|
{
|
|
<Icon Style="align-self: self-end;" Name="IconName.CheckAll" Size="IconSize.x5" Color="IconColor.Success" />
|
|
}
|
|
else
|
|
{
|
|
<Icon Style="align-self: self-end;" Name="IconName.CheckLg" Size="IconSize.x5" />
|
|
}
|
|
}
|
|
</div>
|
|
}
|
|
@{
|
|
target = false;
|
|
}
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="d-flex justify-content-center align-items-center flex-column" style="height: 80%;">
|
|
|
|
<Spinner Type="SpinnerType.Dots" Color="SpinnerColor.Primary" Visible="@chatloading" />
|
|
<p style="margin-top: 15px; font-size: 1.5rem; color: #0d6efd; font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.2);">
|
|
هوشیان
|
|
</p>
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
@if (ChatCurrent != null && ChatCurrent.status != Common.Enums.ConversationStatus.Finished && ChatCurrent.Responses != null)
|
|
{
|
|
<!-- B2: Message input -->
|
|
<div class="message-input-container" id="B2">
|
|
<div class="input-wrapper">
|
|
<input type="text" @bind-value="MsgInput" class="message-input" placeholder="پیام خود را بنویسید..." />
|
|
<Button Color="ButtonColor.Primary" Size=ButtonSize.Small @onclick="OnClickSendMsg" Class="send-btn">
|
|
<Icon Name="IconName.Send" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
@code {
|
|
Common.Dtos.CurrentUserInfo CurrentUser { get; set; }
|
|
List<Read_GroupDto> _Group = new List<Read_GroupDto>();
|
|
//-------------------------------------
|
|
bool isSelectedInbox1 = false;
|
|
List<ChatItemDto> Inbox1Items { get; set; } = new();
|
|
bool isSelectedInbox2 = true;
|
|
List<ChatItemDto> Inbox2Items { get; set; } = new();
|
|
bool isSelectedInbox3 = false;
|
|
List<ChatItemDto> Inbox3Items { get; set; } = new();
|
|
/////////////
|
|
ChatItemDto? ChatCurrent { get; set; } = null;
|
|
string MsgInput { get; set; }
|
|
bool chatloading = false;
|
|
string SelectedChatUserName = "مهدی ربیع نژاد";
|
|
private bool _shouldObserveVisibility = false;
|
|
private ConfirmDialog dialog = default!;
|
|
private Modal modal = default!;
|
|
private HubConnection? hubConnection;
|
|
}
|
|
@functions {
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
CurrentUser = await userService.GetCurrentUserInfo();
|
|
_Group = await groupService.GetGroups();
|
|
Inbox1Items = await chatService.ChatAwaitingOurResponse();
|
|
Inbox2Items = await chatService.MyChatsIsInProgress();
|
|
Inbox3Items = await chatService.MyChatsIsFinished();
|
|
|
|
//-------------hub
|
|
var token = await localStorageService.GetItem<string>("C/key");
|
|
string AddressHub = _Http.BaseAddress.AbsoluteUri.Replace("api/", "");
|
|
|
|
hubConnection = new HubConnectionBuilder()
|
|
.WithUrl($"{_Http.BaseAddress.AbsoluteUri.Replace("api/", "")}chatNotificationHub", options =>
|
|
{
|
|
options.AccessTokenProvider = () => Task.FromResult(token);
|
|
})
|
|
.WithAutomaticReconnect()
|
|
.Build();
|
|
|
|
hubConnection.On<ChatItemResponseDto>("ReceiveNewChatItemFromUser", async (chatitem) =>
|
|
{
|
|
if (ChatCurrent!=null && ChatCurrent.ID == chatitem.ChatItemID)
|
|
{
|
|
ChatCurrent.Responses.Add(chatitem);
|
|
StateHasChanged();
|
|
await MarkAsRead(chatitem.ID);
|
|
// Scroll to target if exists, otherwise scroll to bottom
|
|
await JS.InvokeVoidAsync("scrollToTargetOrBottom");
|
|
}
|
|
else if (Inbox2Items.Any(a => a.ID == chatitem.ChatItemID))
|
|
{
|
|
Inbox2Items = await chatService.MyChatsIsInProgress();
|
|
StateHasChanged();
|
|
}
|
|
});
|
|
|
|
hubConnection.On<int>("CheckMarkAsRead", async (chatresponseid) =>
|
|
{
|
|
if (ChatCurrent.Responses.Any(a => a.ID == chatresponseid && !a.IsRead && (a.Type == Common.Enums.ConversationType.EU || a.Type == Common.Enums.ConversationType.CU)))
|
|
{ ChatCurrent.Responses.First(a => a.ID == chatresponseid).IsRead = true; StateHasChanged(); }
|
|
});
|
|
|
|
//NewChat
|
|
hubConnection.On<int>("NewChat", async (companyid) =>
|
|
{
|
|
if (CurrentUser.CompanyID==companyid)
|
|
{ Inbox1Items = await chatService.ChatAwaitingOurResponse(); StateHasChanged(); }
|
|
});
|
|
await hubConnection.StartAsync();
|
|
//---------end hub
|
|
|
|
await base.OnInitializedAsync();
|
|
}
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
if (_shouldObserveVisibility)
|
|
{
|
|
_shouldObserveVisibility = false;
|
|
await JS.InvokeVoidAsync("observeVisibility", DotNetObjectReference.Create(this));
|
|
await JS.InvokeVoidAsync("scrollToTarget");
|
|
}
|
|
}
|
|
|
|
async Task OnclickInbox(int ID)
|
|
{
|
|
switch (ID)
|
|
{
|
|
case 1:
|
|
isSelectedInbox1 = true;
|
|
isSelectedInbox2 = false;
|
|
isSelectedInbox3 = false;
|
|
Inbox1Items = await chatService.ChatAwaitingOurResponse();
|
|
break;
|
|
|
|
case 2:
|
|
isSelectedInbox2 = true;
|
|
isSelectedInbox1 = false;
|
|
isSelectedInbox3 = false;
|
|
Inbox2Items = await chatService.MyChatsIsInProgress();
|
|
break;
|
|
|
|
case 3:
|
|
isSelectedInbox3 = true;
|
|
isSelectedInbox2 = false;
|
|
isSelectedInbox1 = false;
|
|
Inbox3Items = await chatService.MyChatsIsFinished();
|
|
break;
|
|
}
|
|
ChatCurrent = null;
|
|
StateHasChanged();
|
|
}
|
|
async Task OnClickSendMsg()
|
|
{
|
|
if (!string.IsNullOrEmpty(MsgInput) && ChatCurrent != null)
|
|
{
|
|
Common.Enums.ConversationType type = CurrentUser.Role == "Company" ? Common.Enums.ConversationType.CU : Common.Enums.ConversationType.EU;
|
|
var geter= await chatService.ADDChatResponse(ChatCurrent.ID, MsgInput, type);
|
|
if(geter!=null)
|
|
{ChatCurrent?.Responses.Add(geter);
|
|
ChatCurrent.LastText = MsgInput;
|
|
await Task.Yield();
|
|
await JS.InvokeVoidAsync("scrollToBottom", "B1");
|
|
MsgInput = string.Empty;}
|
|
}
|
|
}
|
|
async Task onClickSelectedChat(int InboxID, ChatItemDto chatItem)
|
|
{
|
|
|
|
chatloading = true;
|
|
SelectedChatUserName = "در حال گفتگو با " + chatItem.UserFullName;
|
|
ChatCurrent = chatItem;
|
|
|
|
_shouldObserveVisibility = true; // فعال کن تا در OnAfterRenderAsync صدا زده بشه
|
|
|
|
StateHasChanged(); // مجبور کن Blazor رندر کنه
|
|
chatloading = false;
|
|
|
|
}
|
|
[JSInvokable]
|
|
public async Task MarkAsRead(int id)
|
|
{
|
|
var allowcjange = nav.Uri.Split('/');
|
|
if (allowcjange.Length == 4 && (allowcjange[3].ToLower() == "chat" || string.IsNullOrEmpty(allowcjange[3]) ))
|
|
{
|
|
var msg = ChatCurrent.Responses.FirstOrDefault(m => m.ID == id);
|
|
if (msg != null && !msg.IsRead && msg.Type == Common.Enums.ConversationType.UE)
|
|
{
|
|
msg.IsRead = true;
|
|
await chatService.MarkAsReadChatItemAsync(id);
|
|
}
|
|
// StateHasChanged();
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
}
|
|
async Task onclickAttachedto()
|
|
{
|
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
|
parameters.Add("chatID", ChatCurrent.ID);
|
|
parameters.Add("OnMultipleOfThree", EventCallback.Factory.Create(this, CallBackAttachedto));
|
|
|
|
await modal.ShowAsync<AttachedtoComponent>("پیوست کارشناس", parameters: parameters);
|
|
|
|
}
|
|
async Task CallBackAttachedto()
|
|
{
|
|
await modal.HideAsync();
|
|
toastService.Notify(new ToastMessage(ToastType.Success, "کارشناس جدید به این گفتگو پیوست"));
|
|
}
|
|
async Task OpenChat()
|
|
{
|
|
if (CurrentUser.Role == "Company" || CurrentUser.Role == "Exper" && ChatCurrent.ExperID == CurrentUser.ExperID)
|
|
{
|
|
if (ChatCurrent.status != Common.Enums.ConversationStatus.Finished) return;
|
|
if (await chatService.OpenChat(ChatCurrent.ID))
|
|
{
|
|
ChatCurrent.status = Common.Enums.ConversationStatus.InProgress;
|
|
StateHasChanged();
|
|
}
|
|
else toastService.Notify(new ToastMessage(ToastType.Danger, "تغییر وضعیت گفتگو موفق نبود"));
|
|
}
|
|
else toastService.Notify(new ToastMessage(ToastType.Danger, "دسترسی به این گفتگو ندارید"));
|
|
|
|
}
|
|
async Task CloseChat()
|
|
{
|
|
if (ChatCurrent.status == Common.Enums.ConversationStatus.Finished) return;
|
|
var options = new ConfirmDialogOptions
|
|
{
|
|
YesButtonText = "بله",
|
|
YesButtonColor = ButtonColor.Success,
|
|
NoButtonText = "انصراف",
|
|
NoButtonColor = ButtonColor.Danger
|
|
};
|
|
|
|
var confirmation = await dialog.ShowAsync(
|
|
title: "پایان دادن به گفتگو",
|
|
message1: "اطمینان دارید ؟",
|
|
confirmDialogOptions: options);
|
|
|
|
if (confirmation)
|
|
{
|
|
if (await chatService.ChatIsFinish(ChatCurrent.ID))
|
|
{
|
|
ChatCurrent.status = Common.Enums.ConversationStatus.Finished;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
private static string GetImageDataUrl(string? fileType, byte[]? content)
|
|
=> (string.IsNullOrWhiteSpace(fileType) || content == null || content.Length == 0)
|
|
? string.Empty
|
|
: $"data:{fileType};base64,{Convert.ToBase64String(content)}";
|
|
|
|
private static string GetDownloadFileName(string? fileName, string? fileType)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(fileName)) return fileName;
|
|
var ext = "";
|
|
if (!string.IsNullOrWhiteSpace(fileType) && fileType.StartsWith("image/"))
|
|
{
|
|
ext = "." + fileType.Split('/').Last();
|
|
}
|
|
return $"image_{DateTimeOffset.Now.ToUnixTimeSeconds()}{ext}";
|
|
}
|
|
}
|
|
|
|
<style>
|
|
/* Enhanced Sidebar Styling */
|
|
.sidebar-container {
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 20px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
backdrop-filter: blur(10px);
|
|
padding: 0.5rem;
|
|
height: 85vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar-header {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
border-radius: 15px;
|
|
margin-bottom: 0.5rem;
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%;
|
|
left: -50%;
|
|
width: 200%;
|
|
height: 200%;
|
|
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.1) 50%, transparent 70%);
|
|
animation: shimmer 3s infinite;
|
|
}
|
|
|
|
@@keyframes shimmer {
|
|
0% {
|
|
transform: translateX(-100%) translateY(-100%) rotate(45deg);
|
|
}
|
|
|
|
100% {
|
|
transform: translateX(100%) translateY(100%) rotate(45deg);
|
|
}
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
.header-icon {
|
|
font-size: 1.5rem;
|
|
color: white;
|
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
|
}
|
|
|
|
.header-text {
|
|
color: white;
|
|
font-size: 1.1rem;
|
|
font-weight: 700;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.sidebar-tabs {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.tab-button {
|
|
border-radius: 12px;
|
|
padding: 0.75rem 1rem;
|
|
transition: all 0.3s ease;
|
|
border: 2px solid transparent;
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tab-button::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
|
transition: left 0.5s ease;
|
|
}
|
|
|
|
.tab-button:hover::before {
|
|
left: 100%;
|
|
}
|
|
|
|
.tab-button:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 16px rgba(13, 110, 253, 0.2);
|
|
border-color: #0d6efd;
|
|
}
|
|
|
|
.tab-button.active-tab {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
color: white;
|
|
border-color: #0d6efd;
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
|
|
}
|
|
|
|
/* Smaller tab buttons */
|
|
.tab-button {
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: 0.8rem;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.tab-text {
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.tab-icon {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* Inbox1 - Yellow theme */
|
|
.inbox1-button {
|
|
border-color: #ffc107;
|
|
color: #856404;
|
|
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
|
|
}
|
|
|
|
.inbox1-button:hover {
|
|
border-color: #e0a800;
|
|
background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
|
|
color: #856404;
|
|
}
|
|
|
|
.inbox1-active {
|
|
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%) !important;
|
|
color: #212529 !important;
|
|
border-color: #ffc107 !important;
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3) !important;
|
|
}
|
|
|
|
/* Inbox2 - Blue theme */
|
|
.inbox2-button {
|
|
border-color: #0d6efd;
|
|
color: #0d6efd;
|
|
background: linear-gradient(135deg, #e7f1ff 0%, #cce7ff 100%);
|
|
}
|
|
|
|
.inbox2-button:hover {
|
|
border-color: #0b5ed7;
|
|
background: linear-gradient(135deg, #cce7ff 0%, #b3d9ff 100%);
|
|
color: #0b5ed7;
|
|
}
|
|
|
|
.inbox2-active {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%) !important;
|
|
color: white !important;
|
|
border-color: #0d6efd !important;
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3) !important;
|
|
}
|
|
|
|
/* Inbox3 - Red theme */
|
|
.inbox3-button {
|
|
border-color: #dc3545;
|
|
color: #721c24;
|
|
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
|
|
}
|
|
|
|
.inbox3-button:hover {
|
|
border-color: #c82333;
|
|
background: linear-gradient(135deg, #f5c6cb 0%, #f1b0b7 100%);
|
|
color: #721c24;
|
|
}
|
|
|
|
.inbox3-active {
|
|
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important;
|
|
color: white !important;
|
|
border-color: #dc3545 !important;
|
|
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3) !important;
|
|
}
|
|
|
|
.tab-icon {
|
|
margin-right: 0.5rem;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.tab-text {
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.tab-badge {
|
|
margin-left: auto;
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.sidebar-chat-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
border-radius: 15px;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
backdrop-filter: blur(10px);
|
|
padding: 0.5rem;
|
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.sidebar-chat-list::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.sidebar-chat-list::-webkit-scrollbar-track {
|
|
background: rgba(241, 241, 241, 0.5);
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.sidebar-chat-list::-webkit-scrollbar-thumb {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
border-radius: 10px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.sidebar-chat-list::-webkit-scrollbar-thumb:hover {
|
|
background: linear-gradient(135deg, #0b5ed7 0%, #0a4b9e 100%);
|
|
}
|
|
|
|
.chat-list-item {
|
|
height: 75px;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 1rem;
|
|
margin-bottom: 0.75rem;
|
|
border-radius: 12px;
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
border: 1px solid #e9ecef;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.chat-list-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(13, 110, 253, 0.1), transparent);
|
|
transition: left 0.5s ease;
|
|
}
|
|
|
|
.chat-list-item:hover::before {
|
|
left: 100%;
|
|
}
|
|
|
|
.chat-list-item:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 16px rgba(13, 110, 253, 0.15);
|
|
border-color: #0d6efd;
|
|
}
|
|
|
|
.item-avatar {
|
|
width: 45px;
|
|
height: 45px;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 1rem;
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.avatar-icon {
|
|
color: white;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.item-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.item-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.item-name {
|
|
color: #333;
|
|
font-size: 0.95rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.item-time {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
gap: 0.25rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.time-text {
|
|
color: #6c757d;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.item-message {
|
|
color: #6c757d;
|
|
font-size: 0.85rem;
|
|
line-height: 1.4;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
margin: 0;
|
|
}
|
|
|
|
.item-badge {
|
|
margin-left: 0.75rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.unread-badge {
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 10px;
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@@keyframes pulse {
|
|
0%, 100% {
|
|
transform: scale(1);
|
|
box-shadow: 0 2px 4px rgba(220, 53, 69, 0.3);
|
|
}
|
|
|
|
50% {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.4);
|
|
}
|
|
}
|
|
|
|
/* Improved input group styling */
|
|
.input-group {
|
|
margin-bottom: 1rem;
|
|
border-radius: 0.5rem;
|
|
overflow: hidden;
|
|
}
|
|
/* Improved header layout */
|
|
.input-group {
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 15px;
|
|
padding: 0.5rem;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.input-group p {
|
|
margin: 0;
|
|
font-size: 1.1rem;
|
|
font-weight: 700;
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.input-group-text-chat {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: .375rem .75rem;
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
color: var(--bs-body-color);
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
border-radius: var(--bs-border-radius);
|
|
}
|
|
/* Enhanced message input styling */
|
|
.message-input-container {
|
|
margin: 0.5rem 0;
|
|
padding: 0.5rem;
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 25px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.message-input-container:hover {
|
|
border-color: #0d6efd;
|
|
box-shadow: 0 6px 12px rgba(13, 110, 253, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.message-input-container:focus-within {
|
|
border-color: #0d6efd;
|
|
box-shadow: 0 8px 16px rgba(13, 110, 253, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.input-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.message-input {
|
|
flex: 1;
|
|
border: none;
|
|
background: transparent;
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: 0.95rem;
|
|
color: #495057;
|
|
outline: none;
|
|
border-radius: 20px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.message-input::placeholder {
|
|
color: #adb5bd;
|
|
font-weight: 400;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.message-input:focus::placeholder {
|
|
color: #6c757d;
|
|
}
|
|
|
|
.message-input:focus {
|
|
background: rgba(13, 110, 253, 0.05);
|
|
}
|
|
|
|
.send-btn {
|
|
border-radius: 50%;
|
|
width: 38px;
|
|
height: 38px;
|
|
padding: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
border: none;
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
|
|
transition: all 0.3s ease;
|
|
color: white;
|
|
}
|
|
|
|
.send-btn:hover {
|
|
transform: translateY(-2px) scale(1.05);
|
|
box-shadow: 0 6px 16px rgba(13, 110, 253, 0.4);
|
|
background: linear-gradient(135deg, #0b5ed7 0%, #0a4b9e 100%);
|
|
}
|
|
|
|
.send-btn:active {
|
|
transform: translateY(0) scale(0.95);
|
|
box-shadow: 0 2px 8px rgba(13, 110, 253, 0.3);
|
|
}
|
|
|
|
.send-btn i {
|
|
font-size: 1.1rem;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.send-btn:hover i {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
/* Enhanced chat area styling */
|
|
.chat-area-container {
|
|
height: 400px;
|
|
overflow-y: auto;
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 20px;
|
|
padding: 1.5rem;
|
|
margin: 1rem 0;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
position: relative;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.chat-area-container::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.chat-area-container::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.chat-area-container::-webkit-scrollbar-thumb {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
border-radius: 10px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.chat-area-container::-webkit-scrollbar-thumb:hover {
|
|
background: linear-gradient(135deg, #0b5ed7 0%, #0a4b9e 100%);
|
|
}
|
|
|
|
.chat-container {
|
|
background: transparent;
|
|
border-radius: 15px;
|
|
padding: 0;
|
|
}
|
|
|
|
.chat-container p {
|
|
color: #6c757d;
|
|
font-size: 0.875rem;
|
|
margin: 0.5rem 0;
|
|
text-align: center;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Enhanced chat bubble styling */
|
|
.chat-bubble {
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 18px;
|
|
max-width: 75%;
|
|
word-wrap: break-word;
|
|
white-space: pre-wrap;
|
|
font-size: 0.95rem;
|
|
line-height: 1.4;
|
|
position: relative;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.chat-mine {
|
|
background: linear-gradient(135deg, #005eff 0%, #267fff 100%);
|
|
color: white;
|
|
border-top-left-radius: 4px;
|
|
box-shadow: 0 4px 12px rgba(0, 94, 255, 0.3);
|
|
}
|
|
|
|
.chat-mine:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 6px 16px rgba(0, 94, 255, 0.4);
|
|
}
|
|
|
|
.chat-other {
|
|
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
|
color: #333;
|
|
border-top-right-radius: 4px;
|
|
border: 1px solid #e9ecef;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.chat-other:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
.chat-ai {
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
color: #495057;
|
|
border-top-right-radius: 4px;
|
|
border: 1px solid #dee2e6;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
/* Message status indicators */
|
|
.chat-bubble + i {
|
|
margin-left: 0.5rem;
|
|
opacity: 0.7;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.chat-bubble + i:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Empty state styling */
|
|
.chat-area-container .d-flex.justify-content-center {
|
|
border-radius: 15px;
|
|
padding: 2rem;
|
|
margin: 1rem;
|
|
}
|
|
|
|
.chat-area-container .d-flex.justify-content-center p {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
font-size: 1.75rem;
|
|
font-weight: 800;
|
|
text-shadow: none;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.input-group-text-chat {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: .375rem .75rem;
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
color: var(--bs-body-color);
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
border-radius: var(--bs-border-radius);
|
|
}
|
|
|
|
/* Beautiful chat separator styling */
|
|
.chat-separator {
|
|
text-align: center;
|
|
margin: 1.5rem 0;
|
|
position: relative;
|
|
}
|
|
|
|
.separator-line {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
margin: 0 1rem;
|
|
}
|
|
|
|
.separator-line::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: linear-gradient(90deg, transparent 0%, rgba(13, 110, 253, 0.3) 20%, rgba(13, 110, 253, 0.6) 50%, rgba(13, 110, 253, 0.3) 80%, transparent 100%);
|
|
transform: translateY(-50%);
|
|
z-index: 1;
|
|
}
|
|
|
|
.separator-text {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
color: white;
|
|
padding: 0.25rem 1rem;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
box-shadow: 0 2px 8px rgba(13, 110, 253, 0.3);
|
|
position: relative;
|
|
z-index: 2;
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
animation: separatorPulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
@@keyframes separatorPulse {
|
|
0%, 100% {
|
|
transform: scale(1);
|
|
box-shadow: 0 2px 8px rgba(13, 110, 253, 0.3);
|
|
}
|
|
|
|
50% {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.4);
|
|
}
|
|
}
|
|
|
|
.separator-text::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -2px;
|
|
left: -2px;
|
|
right: -2px;
|
|
bottom: -2px;
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0b5ed7 100%);
|
|
border-radius: 20px;
|
|
z-index: -1;
|
|
opacity: 0.3;
|
|
filter: blur(4px);
|
|
}
|
|
|
|
/* Hover effect for separator */
|
|
.separator-line:hover .separator-text {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 6px 16px rgba(13, 110, 253, 0.5);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
/* New message separator with different color scheme */
|
|
.separator-line.new-message::before {
|
|
background: linear-gradient(90deg, transparent 0%, rgba(255, 193, 7, 0.3) 20%, rgba(255, 193, 7, 0.6) 50%, rgba(255, 193, 7, 0.3) 80%, transparent 100%);
|
|
}
|
|
|
|
.separator-line.new-message .separator-text {
|
|
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
|
|
color: #212529;
|
|
box-shadow: 0 2px 8px rgba(255, 193, 7, 0.3);
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.separator-line.new-message .separator-text::before {
|
|
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
|
|
}
|
|
|
|
@@keyframes newMessagePulse {
|
|
0%, 100% {
|
|
transform: scale(1);
|
|
box-shadow: 0 2px 8px rgba(255, 193, 7, 0.3);
|
|
}
|
|
|
|
50% {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4);
|
|
}
|
|
}
|
|
|
|
.separator-line.new-message .separator-text {
|
|
animation: newMessagePulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
.separator-line.new-message:hover .separator-text {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 6px 16px rgba(255, 193, 7, 0.5);
|
|
background: linear-gradient(135deg, #e0a800 0%, #d39e00 100%);
|
|
}
|
|
|
|
/* Beautiful Warning Note Styling */
|
|
.warning-note {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
|
|
border: 2px solid #ffc107;
|
|
border-radius: 20px;
|
|
padding: 0.625rem;
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
animation: warningGlow 3s ease-in-out infinite;
|
|
}
|
|
|
|
.warning-note::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 193, 7, 0.1), transparent);
|
|
animation: warningShimmer 4s infinite;
|
|
}
|
|
|
|
@@keyframes warningGlow {
|
|
0%, 100% {
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
50% {
|
|
box-shadow: 0 6px 16px rgba(255, 193, 7, 0.3), 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
}
|
|
}
|
|
|
|
@@keyframes warningShimmer {
|
|
0% {
|
|
left: -100%;
|
|
}
|
|
|
|
100% {
|
|
left: 100%;
|
|
}
|
|
}
|
|
|
|
.warning-icon {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 25px;
|
|
height: 25px;
|
|
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
|
|
border-radius: 50%;
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4);
|
|
animation: warningPulse 2s ease-in-out infinite;
|
|
}
|
|
|
|
@@keyframes warningPulse {
|
|
0%, 100% {
|
|
transform: scale(1);
|
|
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4);
|
|
}
|
|
|
|
50% {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 6px 16px rgba(255, 193, 7, 0.6);
|
|
}
|
|
}
|
|
|
|
.warning-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.warning-title {
|
|
color: #856404;
|
|
font-weight: 700;
|
|
font-size: 0.9rem;
|
|
margin: 0 0 0.25rem 0;
|
|
text-shadow: 0 1px 2px rgba(133, 100, 4, 0.1);
|
|
}
|
|
|
|
.warning-text {
|
|
color: #856404;
|
|
font-size: 0.8rem;
|
|
line-height: 1.3;
|
|
margin: 0;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.warning-note:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 20px rgba(255, 193, 7, 0.3), 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.warning-note:hover .warning-icon {
|
|
animation: warningPulse 1s ease-in-out infinite;
|
|
}
|
|
|
|
/* Enhanced button styling */
|
|
.finish-conversation-btn {
|
|
border-radius: 20px;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
padding: 0.375rem 0.75rem;
|
|
transition: all 0.3s ease;
|
|
border-width: 2px;
|
|
box-shadow: 0 2px 4px rgba(220, 53, 69, 0.2);
|
|
}
|
|
|
|
.finish-conversation-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
|
|
border-color: #dc3545;
|
|
background-color: #dc3545;
|
|
color: white;
|
|
}
|
|
|
|
.open-conversation-btn {
|
|
border-radius: 20px;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
padding: 0.375rem 0.75rem;
|
|
transition: all 0.3s ease;
|
|
border-width: 2px;
|
|
box-shadow: 0 2px 4px rgba(220, 53, 69, 0.2);
|
|
}
|
|
|
|
.open-conversation-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
|
|
border-color: #23caba;
|
|
background-color: #23caba;
|
|
color: white;
|
|
}
|
|
|
|
.toexper-btn {
|
|
border-radius: 20px;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
padding: 0.375rem 0.75rem;
|
|
transition: all 0.3s ease;
|
|
border-width: 2px;
|
|
box-shadow: 0 2px 4px rgba(108, 117, 125, 0.2);
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
}
|
|
|
|
.toexper-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(108, 117, 125, 0.3);
|
|
border-color: #6c757d;
|
|
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
|
|
color: white;
|
|
}
|
|
|
|
</style>
|
|
<script>
|
|
window.getWindowSize = () => {
|
|
return {
|
|
width: window.innerWidth,
|
|
height: window.innerHeight
|
|
};
|
|
};
|
|
|
|
window.registerResizeCallback = (dotNetHelper) => {
|
|
window.onresize = () => {
|
|
dotNetHelper.invokeMethodAsync("OnResize");
|
|
};
|
|
};
|
|
|
|
window.scrollToBottom = (elementId) => {
|
|
const el = document.getElementById(elementId);
|
|
if (el) {
|
|
el.scrollTo({
|
|
top: el.scrollHeight,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
};
|
|
|
|
// Scroll to target element (new message separator)
|
|
window.scrollToTarget = () => {
|
|
const targetElement = document.getElementById('target');
|
|
if (targetElement) {
|
|
const chatContainer = document.getElementById('B1');
|
|
if (chatContainer) {
|
|
// Calculate the position of target element relative to chat container
|
|
const targetRect = targetElement.getBoundingClientRect();
|
|
const containerRect = chatContainer.getBoundingClientRect();
|
|
const relativeTop = targetRect.top - containerRect.top;
|
|
|
|
// Scroll to show the target element with some padding
|
|
const scrollPosition = chatContainer.scrollTop + relativeTop - 100; // 100px padding
|
|
chatContainer.scrollTo({
|
|
top: scrollPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
} else {
|
|
// If no target element exists, scroll to bottom smoothly
|
|
window.scrollToBottom('B1');
|
|
}
|
|
};
|
|
|
|
// Observe visibility of chat bubbles using existing scroll-visibility.js approach
|
|
window.observeVisibility = (dotNetHelper) => {
|
|
const elements = document.querySelectorAll(".chat-bubble[data-id]");
|
|
const observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.getAttribute("data-id");
|
|
dotNetHelper.invokeMethodAsync("MarkAsRead", parseInt(id));
|
|
observer.unobserve(entry.target); // Don't observe again
|
|
}
|
|
});
|
|
}, {
|
|
threshold: 0.6 // 60% of the message must be visible
|
|
});
|
|
|
|
elements.forEach(el => observer.observe(el));
|
|
};
|
|
|
|
// Auto scroll to target when new messages arrive
|
|
window.autoScrollToNewMessage = () => {
|
|
setTimeout(() => {
|
|
window.scrollToTarget();
|
|
}, 100); // Small delay to ensure DOM is updated
|
|
};
|
|
|
|
// Check if target exists and scroll accordingly
|
|
window.scrollToTargetOrBottom = () => {
|
|
const targetElement = document.getElementById('target');
|
|
if (targetElement && targetElement.style.display !== 'none') {
|
|
window.scrollToTarget();
|
|
} else {
|
|
window.scrollToBottom('B1');
|
|
}
|
|
};
|
|
</script>
|