Added support chat sync. Added admin support chat pages
This commit is contained in:
parent
0b6882d3f8
commit
8f3f9fa1fb
11 changed files with 508 additions and 93 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -443,3 +443,4 @@ Moonlight/obj/project.assets.json
|
||||||
Moonlight/obj/project.nuget.cache
|
Moonlight/obj/project.nuget.cache
|
||||||
Moonlight/obj/project.packagespec.json
|
Moonlight/obj/project.packagespec.json
|
||||||
Moonlight/obj/Debug/net6.0/Moonlight.GeneratedMSBuildEditorConfig.editorconfig
|
Moonlight/obj/Debug/net6.0/Moonlight.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
*.editorconfig
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
using System.Security.Cryptography;
|
using Logging.Net;
|
||||||
using System.Text;
|
|
||||||
using JWT.Algorithms;
|
|
||||||
using JWT.Builder;
|
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
|
|
|
@ -3,7 +3,7 @@ using Moonlight.App.Services.Sessions;
|
||||||
|
|
||||||
namespace Moonlight.App.Services.Support;
|
namespace Moonlight.App.Services.Support;
|
||||||
|
|
||||||
public class SupportAdminServer
|
public class SupportAdminService
|
||||||
{
|
{
|
||||||
private readonly SupportServerService SupportServerService;
|
private readonly SupportServerService SupportServerService;
|
||||||
private readonly IdentityService IdentityService;
|
private readonly IdentityService IdentityService;
|
||||||
|
@ -14,7 +14,7 @@ public class SupportAdminServer
|
||||||
private User Self;
|
private User Self;
|
||||||
private User Recipient;
|
private User Recipient;
|
||||||
|
|
||||||
public SupportAdminServer(
|
public SupportAdminService(
|
||||||
SupportServerService supportServerService,
|
SupportServerService supportServerService,
|
||||||
IdentityService identityService,
|
IdentityService identityService,
|
||||||
MessageService messageService)
|
MessageService messageService)
|
||||||
|
@ -60,6 +60,11 @@ public class SupportAdminServer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Close()
|
||||||
|
{
|
||||||
|
await SupportServerService.Close(Recipient);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
MessageService.Unsubscribe($"support.{Recipient.Id}.message", this);
|
MessageService.Unsubscribe($"support.{Recipient.Id}.message", this);
|
|
@ -27,38 +27,72 @@ public class SupportServerService : IDisposable
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
message.CreatedAt = DateTime.UtcNow;
|
try
|
||||||
message.Sender = sender;
|
|
||||||
message.Recipient = recipient;
|
|
||||||
message.IsSupport = isSupport;
|
|
||||||
|
|
||||||
SupportMessageRepository.Add(message);
|
|
||||||
|
|
||||||
await MessageService.Emit($"support.{recipient.Id}.message", message);
|
|
||||||
|
|
||||||
if (!recipient.SupportPending)
|
|
||||||
{
|
{
|
||||||
recipient.SupportPending = true;
|
message.CreatedAt = DateTime.UtcNow;
|
||||||
UserRepository.Update(recipient);
|
message.Sender = sender;
|
||||||
|
message.Recipient = recipient;
|
||||||
|
message.IsSupport = isSupport;
|
||||||
|
|
||||||
var systemMessage = new SupportMessage()
|
SupportMessageRepository.Add(message);
|
||||||
|
|
||||||
|
await MessageService.Emit($"support.{recipient.Id}.message", message);
|
||||||
|
|
||||||
|
if (!recipient.SupportPending)
|
||||||
{
|
{
|
||||||
Recipient = recipient,
|
recipient.SupportPending = true;
|
||||||
Sender = null,
|
UserRepository.Update(recipient);
|
||||||
IsSystem = true,
|
|
||||||
Message = "The support team has been notified. Please be patient"
|
|
||||||
};
|
|
||||||
|
|
||||||
SupportMessageRepository.Add(systemMessage);
|
if (!message.IsSupport)
|
||||||
|
{
|
||||||
|
var systemMessage = new SupportMessage()
|
||||||
|
{
|
||||||
|
Recipient = recipient,
|
||||||
|
Sender = null,
|
||||||
|
IsSystem = true,
|
||||||
|
Message = "The support team has been notified. Please be patient"
|
||||||
|
};
|
||||||
|
|
||||||
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
|
SupportMessageRepository.Add(systemMessage);
|
||||||
|
|
||||||
Logger.Info("Support ticket created: " + recipient.Id);
|
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
|
||||||
//TODO: Ping or so
|
}
|
||||||
|
|
||||||
|
await MessageService.Emit($"support.new", recipient);
|
||||||
|
|
||||||
|
Logger.Info("Support ticket created: " + recipient.Id);
|
||||||
|
//TODO: Ping or so
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error("Error sending message");
|
||||||
|
Logger.Error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Close(User user)
|
||||||
|
{
|
||||||
|
var recipient = UserRepository.Get().First(x => x.Id == user.Id);
|
||||||
|
|
||||||
|
recipient.SupportPending = false;
|
||||||
|
UserRepository.Update(recipient);
|
||||||
|
|
||||||
|
var systemMessage = new SupportMessage()
|
||||||
|
{
|
||||||
|
Recipient = recipient,
|
||||||
|
Sender = null,
|
||||||
|
IsSystem = true,
|
||||||
|
Message = "The ticket is now closed. Type a message to open it again"
|
||||||
|
};
|
||||||
|
|
||||||
|
SupportMessageRepository.Add(systemMessage);
|
||||||
|
|
||||||
|
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
|
||||||
|
await MessageService.Emit($"support.close", recipient);
|
||||||
|
}
|
||||||
|
|
||||||
public Task<SupportMessage[]> GetMessages(User r)
|
public Task<SupportMessage[]> GetMessages(User r)
|
||||||
{
|
{
|
||||||
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
|
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
<Folder Include="App\Http\Middleware" />
|
<Folder Include="App\Http\Middleware" />
|
||||||
<Folder Include="App\Models\AuditLogData" />
|
<Folder Include="App\Models\AuditLogData" />
|
||||||
<Folder Include="resources\lang" />
|
<Folder Include="resources\lang" />
|
||||||
<Folder Include="Shared\Views\Admin\Support" />
|
|
||||||
<Folder Include="wwwroot\assets\media" />
|
<Folder Include="wwwroot\assets\media" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace Moonlight
|
||||||
|
|
||||||
// Support
|
// Support
|
||||||
builder.Services.AddSingleton<SupportServerService>();
|
builder.Services.AddSingleton<SupportServerService>();
|
||||||
builder.Services.AddScoped<SupportAdminServer>();
|
builder.Services.AddScoped<SupportAdminService>();
|
||||||
builder.Services.AddScoped<SupportClientService>();
|
builder.Services.AddScoped<SupportClientService>();
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
157
Moonlight/Shared/Views/Admin/Support/Index.razor
Normal file
157
Moonlight/Shared/Views/Admin/Support/Index.razor
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
@page "/admin/support"
|
||||||
|
@using Moonlight.App.Repositories
|
||||||
|
@using Moonlight.App.Database.Entities
|
||||||
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
@using Moonlight.App.Database
|
||||||
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
|
@inject SupportMessageRepository SupportMessageRepository
|
||||||
|
@inject ConfigService ConfigService
|
||||||
|
@inject MessageService MessageService
|
||||||
|
@implements IDisposable
|
||||||
|
|
||||||
|
<OnlyAdmin>
|
||||||
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex flex-column flex-xl-row p-7">
|
||||||
|
<div class="flex-lg-row-fluid me-xl-15 mb-20 mb-xl-0">
|
||||||
|
<div class="mb-0">
|
||||||
|
<h1 class="text-dark mb-10">
|
||||||
|
<TL>Open tickets</TL>
|
||||||
|
</h1>
|
||||||
|
<div class="mb-10">
|
||||||
|
@if (Users.Any())
|
||||||
|
{
|
||||||
|
foreach (var user in Users)
|
||||||
|
{
|
||||||
|
<div class="d-flex mb-10">
|
||||||
|
<span class="svg-icon svg-icon-2x me-5 ms-n1 mt-2 svg-icon-success">
|
||||||
|
<i class="text-primary bx bx-md bx-message-dots"></i>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<a href="/admin/support/view/@(user.Id)" class="text-dark text-hover-primary fs-4 me-3 fw-semibold">
|
||||||
|
@(user.FirstName) @(user.LastName)
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="text-muted fw-semibold fs-6">
|
||||||
|
@{
|
||||||
|
var lastMessage = MessageCache.ContainsKey(user) ? MessageCache[user] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (lastMessage == null)
|
||||||
|
{
|
||||||
|
<TL>No message sent yet</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@(lastMessage.Message)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<TL>No support ticket is currently open</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-column flex-lg-row-auto w-100 mw-lg-300px mw-xxl-350px">
|
||||||
|
|
||||||
|
<div class="card-rounded bg-primary bg-opacity-5 p-10 mb-15">
|
||||||
|
<h2 class="text-dark fw-bold mb-11">
|
||||||
|
<TL>Actions</TL>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center mb-10">
|
||||||
|
<!--begin::Icon-->
|
||||||
|
<i class="bi bi-file-earmark-text text-primary fs-1 me-5"></i>
|
||||||
|
<!--end::SymIconbol-->
|
||||||
|
|
||||||
|
<!--begin::Info-->
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<h5 class="text-gray-800 fw-bold">Project Briefing</h5>
|
||||||
|
|
||||||
|
<!--begin::Section-->
|
||||||
|
<div class="fw-semibold">
|
||||||
|
<!--begin::Desc-->
|
||||||
|
<span class="text-muted">Check out our</span>
|
||||||
|
<!--end::Desc-->
|
||||||
|
|
||||||
|
<!--begin::Link-->
|
||||||
|
<a href="#" class="link-primary">Support Policy</a>
|
||||||
|
<!--end::Link-->
|
||||||
|
</div>
|
||||||
|
<!--end::Section-->
|
||||||
|
</div>
|
||||||
|
<!--end::Info-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
</OnlyAdmin>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private User[] Users;
|
||||||
|
private Dictionary<User, SupportMessage?> MessageCache;
|
||||||
|
|
||||||
|
private LazyLoader? LazyLoader;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
MessageCache = new();
|
||||||
|
|
||||||
|
MessageService.Subscribe<Index, User>("support.new", this, async user =>
|
||||||
|
{
|
||||||
|
if (LazyLoader != null)
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageService.Subscribe<Index, User>("support.close", this, async user =>
|
||||||
|
{
|
||||||
|
if (LazyLoader != null)
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task Load(LazyLoader arg)
|
||||||
|
{
|
||||||
|
// We dont want cache here
|
||||||
|
Users = (new UserRepository(new DataContext(ConfigService)))
|
||||||
|
.Get()
|
||||||
|
.Where(x => x.SupportPending)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var user in Users)
|
||||||
|
{
|
||||||
|
var lastMessage = SupportMessageRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Recipient)
|
||||||
|
.OrderByDescending(x => x.Id)
|
||||||
|
.FirstOrDefault(x => x.Recipient!.Id == user.Id);
|
||||||
|
|
||||||
|
MessageCache.Add(user, lastMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
MessageService.Unsubscribe("support.new", this);
|
||||||
|
MessageService.Unsubscribe("support.close", this);
|
||||||
|
}
|
||||||
|
}
|
194
Moonlight/Shared/Views/Admin/Support/View.razor
Normal file
194
Moonlight/Shared/Views/Admin/Support/View.razor
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
@page "/admin/support/view/{Id:int}"
|
||||||
|
@using Moonlight.App.Services.Support
|
||||||
|
@using Moonlight.App.Database.Entities
|
||||||
|
@using Moonlight.App.Helpers
|
||||||
|
@using Moonlight.App.Repositories
|
||||||
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
|
@inject SupportAdminService SupportAdminService
|
||||||
|
@inject UserRepository UserRepository
|
||||||
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
@inject ResourceService ResourceService
|
||||||
|
|
||||||
|
<OnlyAdmin>
|
||||||
|
<LazyLoader Load="Load">
|
||||||
|
@if (User == null)
|
||||||
|
{
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<TL>User not found</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="d-flex flex-column flex-xl-row p-7">
|
||||||
|
<div class="flex-lg-row-fluid me-xl-15 mb-20 mb-xl-0">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<LazyLoader Load="LoadMessages">
|
||||||
|
<div class="scroll-y me-n5 pe-5" style="max-height: 65vh; display: flex; flex-direction: column-reverse;">
|
||||||
|
@foreach (var message in Messages)
|
||||||
|
{
|
||||||
|
if (message.IsSystem || message.IsSupport)
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-end mb-10 ">
|
||||||
|
<div class="d-flex flex-column align-items-end">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<div class="me-3">
|
||||||
|
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||||
|
<a class="fs-5 fw-bold text-gray-900 text-hover-primary ms-1">
|
||||||
|
@if (message.IsSupport && !message.IsSystem)
|
||||||
|
{
|
||||||
|
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>System</span>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="symbol symbol-35px symbol-circle ">
|
||||||
|
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-5 rounded bg-light-primary text-dark fw-semibold mw-lg-400px text-end">
|
||||||
|
@if (message.IsSystem)
|
||||||
|
{
|
||||||
|
<TL>@(message.Message)</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@(message.Message)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-start mb-10 ">
|
||||||
|
<div class="d-flex flex-column align-items-start">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<div class="symbol symbol-35px symbol-circle ">
|
||||||
|
<img alt="Avatar" src="@(ResourceService.Avatar(message.Sender))">
|
||||||
|
</div>
|
||||||
|
<div class="ms-3">
|
||||||
|
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
||||||
|
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||||
|
</a>
|
||||||
|
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
||||||
|
@(message.Message)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<textarea @bind="Content" class="form-control form-control-flush mb-3" rows="1" placeholder="Type a message">
|
||||||
|
</textarea>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="d-flex align-items-center me-2">
|
||||||
|
<button class="btn btn-sm btn-icon btn-active-light-primary me-1" type="button">
|
||||||
|
<i class="bx bx-upload fs-3"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Send"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Sending"))"
|
||||||
|
CssClasses="btn-primary"
|
||||||
|
OnClick="Send">
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-column flex-lg-row-auto w-100 mw-lg-300px mw-xxl-350px">
|
||||||
|
<div class="card p-10 mb-15">
|
||||||
|
<h2 class="text-dark fw-bold mb-11">
|
||||||
|
<TL>User information</TL>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center mb-6">
|
||||||
|
<spna class="fw-semibold text-gray-800 fs-5 m-0">
|
||||||
|
<TL>Firstname</TL>: @(User.FirstName)
|
||||||
|
</spna>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-6">
|
||||||
|
<spna class="fw-semibold text-gray-800 fs-5 m-0">
|
||||||
|
<TL>Lastname</TL>: @(User.LastName)
|
||||||
|
</spna>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-6">
|
||||||
|
<spna class="fw-semibold text-gray-800 fs-5 m-0">
|
||||||
|
<TL>Email</TL>: @(User.Email)
|
||||||
|
</spna>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-6">
|
||||||
|
<spna class="fw-semibold text-gray-800 fs-5 m-0">
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Close ticket"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Closing"))"
|
||||||
|
CssClasses="btn-danger"
|
||||||
|
OnClick="CloseTicket">
|
||||||
|
</WButton>
|
||||||
|
</spna>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</LazyLoader>
|
||||||
|
</OnlyAdmin>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
private User? User;
|
||||||
|
private SupportMessage[] Messages;
|
||||||
|
private string Content = "";
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader arg)
|
||||||
|
{
|
||||||
|
User = UserRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Id == Id);
|
||||||
|
|
||||||
|
if (User != null)
|
||||||
|
{
|
||||||
|
SupportAdminService.OnNewMessage += OnNewMessage;
|
||||||
|
|
||||||
|
await SupportAdminService.Start(User);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnNewMessage(object? sender, SupportMessage e)
|
||||||
|
{
|
||||||
|
Messages = (await SupportAdminService.GetMessages()).Reverse().ToArray();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadMessages(LazyLoader arg)
|
||||||
|
{
|
||||||
|
Messages = (await SupportAdminService.GetMessages()).Reverse().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Send()
|
||||||
|
{
|
||||||
|
await SupportAdminService.SendMessage(Content);
|
||||||
|
Content = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CloseTicket()
|
||||||
|
{
|
||||||
|
await SupportAdminService.Close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,80 +14,89 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="scroll-y me-n5 pe-5" style="max-height: 65vh; display: flex; flex-direction: column-reverse;">
|
<LazyLoader Load="LoadMessages">
|
||||||
@foreach (var message in Messages)
|
<div class="scroll-y me-n5 pe-5" style="max-height: 65vh; display: flex; flex-direction: column-reverse;">
|
||||||
{
|
@foreach (var message in Messages)
|
||||||
if (message.IsSystem || message.IsSupport)
|
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-content-start mb-10 ">
|
if (message.IsSystem || message.IsSupport)
|
||||||
<div class="d-flex flex-column align-items-start">
|
{
|
||||||
<div class="d-flex align-items-center mb-2">
|
<div class="d-flex justify-content-start mb-10 ">
|
||||||
<div class="symbol symbol-35px symbol-circle ">
|
<div class="d-flex flex-column align-items-start">
|
||||||
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<div class="symbol symbol-35px symbol-circle ">
|
||||||
|
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
||||||
|
</div>
|
||||||
|
<div class="ms-3">
|
||||||
|
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
||||||
|
@if (message.IsSupport && !message.IsSystem)
|
||||||
|
{
|
||||||
|
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>System</span>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-3">
|
|
||||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
||||||
@if (message.IsSupport && !message.IsSystem)
|
@if (message.IsSystem)
|
||||||
{
|
{
|
||||||
|
<TL>@(message.Message)</TL>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@(message.Message)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-end mb-10 ">
|
||||||
|
<div class="d-flex flex-column align-items-end">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<div class="me-3">
|
||||||
|
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||||
|
<a class="fs-5 fw-bold text-gray-900 text-hover-primary ms-1">
|
||||||
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||||
}
|
</a>
|
||||||
else
|
</div>
|
||||||
{
|
<div class="symbol symbol-35px symbol-circle ">
|
||||||
<span>System</span>
|
<img alt="Avatar" src="@(ResourceService.Avatar(message.Sender))">
|
||||||
}
|
</div>
|
||||||
</a>
|
</div>
|
||||||
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
|
||||||
|
<div class="p-5 rounded bg-light-primary text-dark fw-semibold mw-lg-400px text-end">
|
||||||
|
@(message.Message)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
|
||||||
@(message.Message)
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
else
|
<div class="d-flex justify-content-start mb-10 ">
|
||||||
{
|
<div class="d-flex flex-column align-items-start">
|
||||||
<div class="d-flex justify-content-end mb-10 ">
|
<div class="d-flex align-items-center mb-2">
|
||||||
<div class="d-flex flex-column align-items-end">
|
<div class="symbol symbol-35px symbol-circle ">
|
||||||
<div class="d-flex align-items-center mb-2">
|
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
||||||
<div class="me-3">
|
|
||||||
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
|
||||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary ms-1">
|
|
||||||
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="symbol symbol-35px symbol-circle ">
|
|
||||||
<img alt="Avatar" src="@(ResourceService.Avatar(message.Sender))">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ms-3">
|
||||||
<div class="p-5 rounded bg-light-primary text-dark fw-semibold mw-lg-400px text-end">
|
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
||||||
@(message.Message)
|
<span>System</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<div class="d-flex justify-content-start mb-10 ">
|
|
||||||
<div class="d-flex flex-column align-items-start">
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<div class="symbol symbol-35px symbol-circle ">
|
|
||||||
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
|
||||||
</div>
|
|
||||||
<div class="ms-3">
|
|
||||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
|
||||||
<span>System</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
||||||
<TL>Welcome to the support chat. Ask your question here and we will help you</TL>
|
<TL>Welcome to the support chat. Ask your question here and we will help you</TL>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</LazyLoader>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<textarea @bind="Content" class="form-control form-control-flush mb-3" rows="1" placeholder="Type a message">
|
<textarea @bind="Content" class="form-control form-control-flush mb-3" rows="1" placeholder="Type a message">
|
||||||
|
@ -125,8 +134,6 @@
|
||||||
SupportClientService.OnNewMessage += OnNewMessage;
|
SupportClientService.OnNewMessage += OnNewMessage;
|
||||||
|
|
||||||
await SupportClientService.Start();
|
await SupportClientService.Start();
|
||||||
|
|
||||||
Messages = (await SupportClientService.GetMessages()).Reverse().ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnNewMessage(object? sender, SupportMessage e)
|
private async void OnNewMessage(object? sender, SupportMessage e)
|
||||||
|
@ -141,4 +148,9 @@
|
||||||
Content = "";
|
Content = "";
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadMessages(LazyLoader arg)
|
||||||
|
{
|
||||||
|
Messages = (await SupportClientService.GetMessages()).Reverse().ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -199,6 +199,14 @@ build_metadata.AdditionalFiles.CssScope =
|
||||||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXFNlcnZlcnNcTmV3LnJhem9y
|
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXFNlcnZlcnNcTmV3LnJhem9y
|
||||||
build_metadata.AdditionalFiles.CssScope =
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
||||||
|
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Admin/Support/Index.razor]
|
||||||
|
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXFN1cHBvcnRcSW5kZXgucmF6b3I=
|
||||||
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
||||||
|
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Admin/Support/View.razor]
|
||||||
|
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXFN1cHBvcnRcVmlldy5yYXpvcg==
|
||||||
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
||||||
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Index.razor]
|
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Index.razor]
|
||||||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEluZGV4LnJhem9y
|
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEluZGV4LnJhem9y
|
||||||
build_metadata.AdditionalFiles.CssScope =
|
build_metadata.AdditionalFiles.CssScope =
|
||||||
|
|
|
@ -215,3 +215,12 @@ less than a minute ago;less than a minute ago
|
||||||
1 hour ago;1 hour ago
|
1 hour ago;1 hour ago
|
||||||
1 minute ago;1 minute ago
|
1 minute ago;1 minute ago
|
||||||
Failed;Failed
|
Failed;Failed
|
||||||
|
hours ago; hours ago
|
||||||
|
Open tickets;Open tickets
|
||||||
|
Actions;Actions
|
||||||
|
No support ticket is currently open;No support ticket is currently open
|
||||||
|
User information;User information
|
||||||
|
Close ticket;Close ticket
|
||||||
|
Closing;Closing
|
||||||
|
The support team has been notified. Please be patient;The support team has been notified. Please be patient
|
||||||
|
The ticket is now closed. Type a message to open it again;The ticket is now closed. Type a message to open it again
|
||||||
|
|
Loading…
Reference in a new issue