Updated the UI of the application

This commit is contained in:
2025-05-26 20:25:27 +03:00
parent 8ba9448beb
commit dc40f4ebe4
10 changed files with 220 additions and 71 deletions

View File

@@ -7,6 +7,7 @@
@using DiscordBotCore.PluginManagement.Models
@using WebUI.Models
@using WebUI.Services
@using WebUI.Components.Shared
@inject NavigationManager Navigation
@inject NotificationService NotificationService
@@ -17,58 +18,58 @@
@rendermode InteractiveServer
<h3>Upload Plugin</h3>
<CenteredCard Title="Upload plugin">
<EditForm Model="@pluginModel" OnValidSubmit="HandleValidSubmit" FormName="pluginForm">
<DataAnnotationsValidator />
<ValidationSummary />
<EditForm Model="@pluginModel" OnValidSubmit="HandleValidSubmit" FormName="pluginForm">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<ModernLabel Text="Plugin Name"></ModernLabel>
<RoundedTextBox @bind-Value="pluginModel.Name" Placeholder="Example Plugin"></RoundedTextBox>
</div>
<div class="form-group">
<label>Plugin Name:</label>
<InputText @bind-Value="pluginModel.Name" class="form-control" />
</div>
<div class="form-group">
<ModernLabel Text="Plugin version"></ModernLabel>
<RoundedTextBox @bind-Value="pluginModel.Version" Placeholder="1.0.0"></RoundedTextBox>
</div>
<div class="form-group">
<label>Plugin Version:</label>
<InputText @bind-Value="pluginModel.Version" class="form-control" />
@if (!IsValidVersion)
{
<div class="text-danger">Version must be in format X.X.X</div>
}
</div>
<div class="form-group">
<ModernCheckbox @bind-Checked="pluginModel.IsEnabled" Label="Enable Plugin"></ModernCheckbox>
</div>
<div class="form-group">
<InputCheckbox @bind-Value="pluginModel.IsEnabled" /> Enable Plugin
</div>
<div class="form-group">
<ModernFileUploader OnFileUploaded="HandleUploadedFile" AllowedFileTypes="@allowedFileTypes"/>
@if (_message is not null)
{
<p class="mt-2 text-green-600">@_message</p>
}
<div class="form-group">
<label>Select DLL File:</label>
<InputFile OnChange="HandleFileSelected" />
@if (selectedFileName != null)
{
<div>Selected: @selectedFileName</div>
}
</div>
</div>
<button class="btn btn-primary" type="submit">Upload</button>
</EditForm>
<button class="btn btn-primary" type="submit">Upload</button>
</EditForm>
</CenteredCard>
@code {
private PluginUploadModel pluginModel = new();
private IBrowserFile? selectedFile;
private string? selectedFileName;
private List<string> allowedFileTypes { get; set; } = new List<string> { ".dll"};
private string? _message;
private bool IsValidVersion => System.Text.RegularExpressions.Regex.IsMatch(pluginModel.Version ?? "", @"^\d+\.\d+\.\d+$");
private async Task HandleFileSelected(InputFileChangeEventArgs e)
private async Task HandleUploadedFile(IBrowserFile file)
{
selectedFile = e.File;
selectedFileName = selectedFile.Name;
var buffer = new byte[file.Size];
await file.OpenReadStream().ReadAsync(buffer);
_message = $"Uploaded file: {file.Name} ({file.Size} bytes)";
}
private async Task HandleValidSubmit()
{
if (!IsValidVersion || selectedFile is null || string.IsNullOrEmpty(selectedFileName))
if (!IsValidVersion || selectedFile is null || string.IsNullOrEmpty(selectedFile.Name))
{
NotificationService.Notify("Invalid field values. Please check the form.", NotificationType.Error);
return;
@@ -83,7 +84,7 @@
return;
}
string filePath = Path.Combine(pluginsFolder, selectedFileName);
string filePath = Path.Combine(pluginsFolder, selectedFile.Name);
await using var stream = selectedFile.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
await using var fileStream = File.Create(filePath);

View File

@@ -2,46 +2,41 @@
@using System.ComponentModel.DataAnnotations
@using DiscordBotCore.Configuration
@using DiscordBotCore.Logging
@using WebUI.Components.Shared
@inject NavigationManager Navigation
@rendermode InteractiveServer
@if (_settingsViewModel is not null)
{
<div class="container-fluid d-flex justify-content-center align-items-center" style="height: 95vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-4">
<h2 class="card-title text-center mb-4 fw-semibold">Bot Settings</h2>
<CenteredCard Title="Settings">
<EditForm Model="_settingsViewModel" OnValidSubmit="HandleSubmitTask">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" />
<EditForm Model="_settingsViewModel" OnValidSubmit="HandleSubmitTask">
<DataAnnotationsValidator />
<ValidationSummary class="text-danger" />
<div class="mb-3">
<label class="form-label" for="tokenInput">Token</label>
<InputText id="tokenInput" class="form-control" placeholder="Enter bot token"
@bind-Value="_settingsViewModel.Token" />
</div>
<div class="mb-3">
<label class="form-label" for="prefixInput">Prefix</label>
<InputText id="prefixInput" class="form-control" placeholder="!"
@bind-Value="_settingsViewModel.Prefix" />
</div>
<div class="mb-4">
<label class="form-label" for="serverIdsInput">Server IDs (comma-separated)</label>
<InputTextArea id="serverIdsInput" class="form-control" placeholder="12345, 67890" Rows="3"
@bind-Value="_settingsViewModel.ServerIds" />
</div>
<button type="submit" class="btn btn-primary w-100">
Save&nbsp;<i class="bi bi-check-lg"></i>
</button>
</EditForm>
<div class="mb-3">
<label class="form-label" for="tokenInput">Token</label>
<InputText id="tokenInput" class="form-control" placeholder="Enter bot token"
@bind-Value="_settingsViewModel.Token" />
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="prefixInput">Prefix</label>
<InputText id="prefixInput" class="form-control" placeholder="!"
@bind-Value="_settingsViewModel.Prefix" />
</div>
<div class="mb-4">
<label class="form-label" for="serverIdsInput">Server IDs (comma-separated)</label>
<InputTextArea id="serverIdsInput" class="form-control" placeholder="12345, 67890" Rows="3"
@bind-Value="_settingsViewModel.ServerIds" />
</div>
<button type="submit" class="btn btn-primary w-100">
Save&nbsp;<i class="bi bi-check-lg"></i>
</button>
</EditForm>
</CenteredCard>
}
@code {

View File

@@ -0,0 +1,14 @@
<div class="container-fluid d-flex justify-content-center align-items-center" style="height: 95vh;">
<div class="card shadow-lg border-0" style="max-width: 500px; width: 100%;">
<div class="card-body p-4">
<h2 class="card-title text-center mb-4 fw-semibold">@Title</h2>
@ChildContent
</div>
</div>
</div>
@code {
[Parameter] public string Title { get; set; } = string.Empty;
[Parameter] public RenderFragment ChildContent { get; set; } = default!;
}

View File

@@ -0,0 +1,28 @@
<div class="form-check d-flex align-items-center gap-2">
<input
class="form-check-input custom-checkbox"
type="checkbox"
id="@CheckboxId"
@bind="Checked" />
<label class="form-check-label text-secondary" for="@CheckboxId">
@Label
</label>
</div>
@code {
[Parameter] public string Label { get; set; } = string.Empty;
[Parameter] public bool Checked { get; set; }
[Parameter] public EventCallback<bool> CheckedChanged { get; set; }
private string CheckboxId { get; } = $"checkbox_{Guid.NewGuid().ToString("N")}";
private async Task OnChange(ChangeEventArgs e)
{
if (e.Value is bool value)
{
Checked = value;
await CheckedChanged.InvokeAsync(value);
}
}
}

View File

@@ -0,0 +1,78 @@
@inject IJSRuntime JS
<link rel="stylesheet" href="Components/Shared/ModernFileUploader/modernFileUploader.css" />
<div class="upload-box p-8 border-2 border-dashed rounded-xl text-center transition relative"
id="dropzone"
style="cursor: pointer;"
@onclick="TriggerFileInputClick">
@if (string.IsNullOrEmpty(SelectedFileName))
{
<p class="text-gray-600 mb-2">Click to upload a file</p>
}
@if (!string.IsNullOrEmpty(SelectedFileName))
{
<p class="text-green-600 text-sm">Selected: @SelectedFileName</p>
}
</div>
<InputFile id="hiddenFileInput" accepts=".pdf" OnChange="HandleFileChange" class="hidden" hidden="true" />
@if (!string.IsNullOrEmpty(UploadMessage))
{
<p class="mt-2 text-sm text-red-600">@UploadMessage</p>
}
<script src="Components/Shared/ModernFileUploader/modernFileUploader.js"></script>
@code {
private string? SelectedFileName;
private string? UploadMessage;
[Parameter] public List<string> AllowedFileTypes { get; set; } = new List<string>();
[Parameter] public long MaxFileSize { get; set; } = 10 * 1024 * 1024;
[Parameter] public EventCallback<IBrowserFile> OnFileUploaded { get; set; }
private DotNetObjectReference<ModernFileUploader>? dotNetRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetRef = DotNetObjectReference.Create(this);
}
}
private async Task TriggerFileInputClick()
{
await JS.InvokeVoidAsync("triggerInputClick", "hiddenFileInput");
}
private async Task HandleFileChange(InputFileChangeEventArgs e)
{
var file = e.File;
UploadMessage = null;
if (AllowedFileTypes.Count > 0 && !AllowedFileTypes.Any(type => file.Name.EndsWith(type, StringComparison.OrdinalIgnoreCase)))
{
UploadMessage = $"Invalid file type. Allowed types: {string.Join(", ", AllowedFileTypes)}.";
SelectedFileName = null;
return;
}
if (file.Size > MaxFileSize)
{
UploadMessage = $"File too large. Max allowed size is {MaxFileSize / (1024 * 1024)} MB.";
SelectedFileName = null;
return;
}
SelectedFileName = file.Name;
await OnFileUploaded.InvokeAsync(file);
}
public void Dispose()
{
dotNetRef?.Dispose();
}
}

View File

@@ -0,0 +1,7 @@
<label class="form-label fw-semibold text-secondary" style="font-size: 1rem;">
@Text
</label>
@code {
[Parameter] public string Text { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,24 @@
<div class="form-group">
<input
class="form-control rounded-pill shadow-sm px-4 py-2 border-0"
placeholder="@Placeholder"
@bind="Value"
@oninput="OnInput" />
</div>
@code {
[Parameter] public string Placeholder { get; set; } = string.Empty;
[Parameter] public string Value { get; set; } = string.Empty;
[Parameter] public EventCallback<string> ValueChanged { get; set; }
private async Task OnInput(ChangeEventArgs e)
{
if (e.Value is string newValue)
{
Value = newValue;
await ValueChanged.InvokeAsync(Value);
}
}
}

View File

@@ -15,8 +15,4 @@
<ProjectReference Include="..\DiscordBotCore\DiscordBotCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Components\Shared\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,3 @@
.upload-box.dragover {
border-color: #3b82f6;
}

View File

@@ -0,0 +1,3 @@
window.triggerInputClick = function (id) {
document.getElementById(id)?.click();
};