-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Create complete page and controller for Manager Chat page - Apply filter by status of dentist - Change UI of Chat detail page
- Loading branch information
Showing
6 changed files
with
483 additions
and
3 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
Dental_Clinic_System/Dental_Clinic_System/Areas/Manager/Controllers/ChatController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using Dental_Clinic_System.Models.Data; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace Dental_Clinic_System.Areas.Manager.Controllers | ||
{ | ||
[Area("Manager")] | ||
[Authorize(AuthenticationSchemes = "ManagerScheme", Roles = "Quản Lý")] | ||
public class ChatController : Controller | ||
{ | ||
|
||
private readonly DentalClinicDbContext _context; | ||
|
||
public ChatController(DentalClinicDbContext context) | ||
{ | ||
_context = context; | ||
} | ||
|
||
public async Task<IActionResult> Index(int patientID, string searchStatus) | ||
{ | ||
var clinicId = HttpContext.Session.GetInt32("clinicId"); | ||
if (clinicId == null) | ||
{ // Check if session has expired, log out | ||
return RedirectToAction("Logout", "ManagerAccount", new { area = "Manager" }); | ||
} | ||
|
||
var dentists = await _context.Dentists.Include(d => d.Account).Include(d => d.Clinic).Include(d => d.Degree).Where(d => d.ClinicID == clinicId).ToListAsync(); | ||
|
||
if (searchStatus != null) | ||
{ | ||
ViewBag.SearchStatus = searchStatus; | ||
dentists = dentists.Where(d => d.Account.AccountStatus == searchStatus).ToList(); | ||
} | ||
else | ||
{ | ||
ViewBag.SearchStatus = "Hoạt Động"; | ||
dentists = dentists.Where(d => d.Account.AccountStatus == "Hoạt Động").ToList(); | ||
} | ||
|
||
return View(dentists); | ||
} | ||
|
||
public async Task<IActionResult> ChatList(int dentistID) | ||
{ | ||
var chats = await _context.ChatHubMessages | ||
.Where(m => m.SenderId == dentistID || m.ReceiverId == dentistID) | ||
.Select(m => new | ||
{ | ||
Id = m.ID, | ||
Content = m.Content, | ||
Timestamp = m.Timestamp, | ||
SenderId = m.SenderId, | ||
ReceiverId = m.ReceiverId, | ||
SenderName = m.Sender.FirstName + " " + m.Sender.LastName , | ||
ReceiverName = m.Receiver.FirstName + " " + m.Receiver.LastName | ||
}) | ||
.GroupBy(m => m.SenderId == dentistID ? m.ReceiverId : m.SenderId) | ||
.Select(g => g.OrderByDescending(m => m.Timestamp).FirstOrDefault()) | ||
.ToListAsync(); | ||
|
||
var accounts = await _context.Accounts.ToDictionaryAsync(a => a.ID, a => a.Role); | ||
|
||
ViewBag.Accounts = accounts; | ||
ViewBag.ChatList = chats; | ||
ViewBag.DentistID = dentistID; | ||
return View(); | ||
} | ||
|
||
public async Task<IActionResult> ChatDentistDetail(int patientID, int dentistID) | ||
{ | ||
var messages = await _context.ChatHubMessages | ||
.Where(m => (m.SenderId == dentistID && m.ReceiverId == patientID) || (m.SenderId == patientID && m.ReceiverId == dentistID)) | ||
.OrderBy(m => m.Timestamp) | ||
.ToListAsync(); | ||
|
||
ViewBag.PatientID = patientID; | ||
ViewBag.Messages = messages; | ||
|
||
var patient = await _context.Accounts.FirstAsync(a => a.ID == patientID); | ||
|
||
var patientName = $"{patient.FirstName} {patient.LastName}"; | ||
|
||
var accounts = await _context.Accounts.ToDictionaryAsync(a => a.ID, a => a.Role); | ||
|
||
ViewBag.Accounts = accounts; | ||
ViewBag.PatientName = patientName ?? "Ẩn Danh"; | ||
ViewBag.DentistID = dentistID; | ||
|
||
return View("ChatDetail"); | ||
} | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
Dental_Clinic_System/Dental_Clinic_System/Areas/Manager/Views/Chat/ChatDetail.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
@{ | ||
ViewData["Title"] = "Danh sách chat"; | ||
Layout = "_LayoutManager"; | ||
var userID = ViewBag.DentistID; | ||
var patientID = ViewBag.PatientID; | ||
var messages = ViewBag.Messages; | ||
var patientName = ViewBag.PatientName; | ||
var accounts = ViewBag.Accounts as Dictionary<int, string>; // Assuming this is a dictionary with user ID as key and role as value | ||
} | ||
|
||
<head> | ||
<meta name="description" content="Đây là nơi nha sĩ dùng để trao đổi với bệnh nhân"> | ||
<link rel="stylesheet" href="~/assets/css/chat.css" /> | ||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | ||
</head> | ||
|
||
<body> | ||
<div class="msger__container" style="background: none"> | ||
<div class="msger" style="background: #fff; box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.3)"> | ||
<header class="msger-header" style="background: #579ffb"> | ||
<div class="msger-header-title" style="font-size: 2rem"> | ||
<i class="fa-solid fa-comments"></i> Chat with Patients | ||
</div> | ||
<div class="msger-header-options"> | ||
<div class="btn-wrapper" style="font-size: 2rem;"> | ||
<a href="~/Manager/Chat/ChatList?dentistID=@userID" class="back-btn" style="color: #fff; font-weight: bold"> | ||
Quay lại <i class="fa-solid fa-arrow-rotate-left"></i> | ||
</a> | ||
</div> | ||
</div> | ||
</header> | ||
|
||
<main class="msger-chat" id="messagesList" style="border-radius: 0 0 15px 15px; background: #fff"> | ||
@foreach (var message in messages) | ||
{ | ||
var isSenderDentist = accounts?.ContainsKey(message.SenderId) && accounts?[message.SenderId] == "Nha Sĩ"; | ||
var isReceiverDentist = accounts?.ContainsKey(message.ReceiverId) && accounts?[message.ReceiverId] == "Nha Sĩ"; | ||
|
||
@if (isSenderDentist) | ||
{ | ||
<div class="msg left-msg" style="display: block"> | ||
<div class="msg-bubble" style="background: none; padding: 0; max-width: 100%;"> | ||
<div class="msg-info" style="color: #000"> | ||
<div class="msg-info-name"><i class="fa-solid fa-user-doctor"></i> Nha sĩ —</div> | ||
<div class="msg-info-time">@message.Timestamp</div> | ||
</div> | ||
<div class="msg-text" style="color: #fff; background: #fa806a; padding: 5px 12px; display: inline-block; border-radius: 50px; max-width: 80%; | ||
word-wrap: break-word;"> | ||
@message.Content | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
else | ||
{ | ||
<div class="msg left-msg" style="display: block"> | ||
<div class="msg-bubble" style="background: none; padding: 0; max-width: 100%;"> | ||
<div class="msg-info" style="color: #000"> | ||
<div class="msg-info-name"><i class="fa-solid fa-circle-user"></i> @patientName —</div> | ||
<div class="msg-info-time">@message.Timestamp</div> | ||
</div> | ||
<div class="msg-text" style="color: #fff; background: #6e7df5; padding: 5px 12px; display: inline-block; border-radius: 50px; max-width: 80%; | ||
word-wrap: break-word;"> | ||
@message.Content | ||
</div> | ||
</div> | ||
</div> | ||
} | ||
} | ||
</main> | ||
</div> | ||
</div> | ||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.12/signalr.min.js"></script> | ||
<script> | ||
const userID = '@userID'; | ||
const patientName = '@patientName'; | ||
const connection = new signalR.HubConnectionBuilder() | ||
.withUrl("/chathub?userID=" + encodeURIComponent(userID)) | ||
.configureLogging(signalR.LogLevel.Information) | ||
.build(); | ||
connection.on("ReceiveMessage", (user, message) => { | ||
console.log(`Message received from ${user}: ${message}`); | ||
const msg = document.createElement("div"); | ||
const isUserDentist = user === userID; | ||
const msgClass = isUserDentist ? "right-msg" : "left-msg"; | ||
const userName = isUserDentist ? "Bạn" : patientName; | ||
const now = new Date(); | ||
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | ||
msg.classList.add("msg", msgClass); | ||
msg.innerHTML = ` | ||
<div class="msg-img" style="background-image: url('${isUserDentist ? 'https://firebasestorage.googleapis.com/v0/b/dental-care-3388d.appspot.com/o/Profile%2FPatient%2Fuser.png?alt=media&token=9010a4a6-0220-4d29-bb85-1fe425100744' : 'https://firebasestorage.googleapis.com/v0/b/dental-care-3388d.appspot.com/o/Profile%2FPatient%2Fuser.png?alt=media&token=9010a4a6-0220-4d29-bb85-1fe425100744'}')"></div> | ||
<div class="msg-bubble"> | ||
<div class="msg-info"> | ||
<div class="msg-info-name">${userName}</div> | ||
<div class="msg-info-time">${timeString}</div> | ||
</div> | ||
<div class="msg-text">${message}</div> | ||
</div> | ||
`; | ||
document.getElementById("messagesList").appendChild(msg); | ||
document.getElementById("messagesList").scrollTop = document.getElementById("messagesList").scrollHeight; // Ensure scrolls to bottom | ||
}); | ||
connection.start() | ||
.then(() => console.log("Connection established.")) | ||
.catch(err => console.error("Connection failed: ", err.toString())); | ||
function sendMessage() { | ||
const receiver = document.getElementById("receiverInput").value; | ||
const message = document.getElementById("messageInput").value; | ||
if (!message) return; | ||
console.log(`Sending message to ${receiver}: ${message}`); | ||
connection.invoke("SendMessageToUser", receiver, message) | ||
.then(() => { | ||
document.getElementById("messageInput").value = ''; | ||
console.log("Message sent successfully."); | ||
}) | ||
.catch(err => console.error("Send message failed: ", err.toString())); | ||
} | ||
function scrollToBottom() { | ||
const messagesList = document.getElementById("messagesList"); | ||
messagesList.scrollTop = messagesList.scrollHeight; | ||
} | ||
// Scroll to bottom when the page loads | ||
window.onload = scrollToBottom; | ||
</script> | ||
</body> | ||
|
108 changes: 108 additions & 0 deletions
108
Dental_Clinic_System/Dental_Clinic_System/Areas/Manager/Views/Chat/ChatList.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers | ||
@{ | ||
ViewData["Title"] = "Danh sách chat"; | ||
Layout = "_LayoutManager"; | ||
var chats = ViewBag.ChatList; | ||
var accounts = ViewBag.Accounts as Dictionary<int, string>; // Assuming this is a dictionary with user ID as key and role as value | ||
} | ||
|
||
<head> | ||
<meta name="description" content="Đây là nơi nha sĩ dùng để theo dõi tin nhắn đến từ phía của bệnh nhân"> | ||
<style> | ||
a { | ||
text-decoration: none !important; | ||
} | ||
sup { | ||
font-size: 18px; | ||
color: red; | ||
} | ||
.sidebar .nav-item a.Quan_ly_chat { | ||
color: #fff; | ||
background: #4880ff; | ||
} | ||
.chat-list { | ||
display: flex; | ||
flex-direction: column; | ||
width: 50%; | ||
margin: auto; | ||
border: 1px solid #1376f8; | ||
height: 80vh; | ||
overflow-y: auto; | ||
padding: 20px; | ||
border-radius: 15px; | ||
} | ||
.chat-item { | ||
padding: 10px 20px; | ||
border: 1px solid #1376f8; | ||
border-radius: 15px; | ||
cursor: pointer; | ||
margin-bottom: 10px; | ||
} | ||
.chat-item strong { | ||
font-size: 1.8rem; | ||
} | ||
.chat-item p { | ||
font-size: 1.6rem; | ||
color: #333; | ||
margin-bottom: 5px; | ||
} | ||
.chat-item:hover { | ||
background-color: #f1f1f1; | ||
border-radius: 15px; | ||
} | ||
.back-btn { | ||
color: #1376f8; | ||
font-size: 2rem; | ||
font-weight: 600; | ||
padding: 10px 20px; | ||
display: inline-flex; | ||
align-items: center; | ||
margin-bottom: 10px; | ||
gap: 5px; | ||
} | ||
.back-btn:hover { | ||
border-radius: 15px; | ||
background: #e8f1ff; | ||
} | ||
</style> | ||
</head> | ||
|
||
<body> | ||
<div> | ||
<div> | ||
<div style="text-align: right; width: 50%; margin: auto; margin-top: 10px;"> | ||
<a asp-controller="Chat" asp-action="Index" class="back-btn">Trở về danh sách nha sĩ <i class="fa-solid fa-rotate-left" aria-hidden="true"></i></a> | ||
</div> | ||
</div> | ||
<div class="chat-list" id="chatList"> | ||
@foreach (var chat in chats) | ||
{ | ||
int patientID = chat.SenderId; | ||
if (accounts?.ContainsKey(chat.SenderId) && accounts?[chat.SenderId] == "Nha Sĩ") | ||
{ | ||
patientID = chat.ReceiverId; | ||
} | ||
|
||
if (accounts.ContainsKey(patientID) && accounts[patientID] == "Bệnh Nhân") | ||
{ | ||
<a href="@Url.Action("ChatDentistDetail", "Chat", new { area = "Manager", patientID = patientID, dentistID = ViewBag.DentistID })"> | ||
<div class="chat-item"> | ||
<strong>@chat.ReceiverName</strong> | ||
<p>@chat.Content</p> | ||
<p>@chat.Timestamp</p> | ||
</div> | ||
</a> | ||
} | ||
} | ||
</div> | ||
</body> |
Oops, something went wrong.