Update document management UI and functionality

- Changed the "Documents" icon in `MainLayout.razor`.
- Added a `ConfirmDialog` component in `Documents.razor` for user confirmations.
- Adjusted the capitalization of the creation date column header.
- Restructured the delete button layout for better responsiveness and changed its icon.
- Updated lifecycle method from `OnInitializedAsync` to `OnAfterRenderAsync` for improved document loading.
- Introduced a new asynchronous method for obtaining local date strings.
- Updated toast messages to use the new date formatting method.
- Added a new method for converting UTC dates to local time.
- Introduced a `SelectableDocument` record class to enhance document management.
This commit is contained in:
Marco Minerva
2025-02-14 17:38:03 +01:00
parent 09f15a9cb7
commit 83e8f8ff23
2 changed files with 42 additions and 11 deletions
@@ -42,7 +42,7 @@
new() { Id = "1", Href = "/", IconName = IconName.HouseDoorFill, Text = "Home", Match = NavLinkMatch.All}, new() { Id = "1", Href = "/", IconName = IconName.HouseDoorFill, Text = "Home", Match = NavLinkMatch.All},
new() { Id = "2", Href = "/counter", IconName = IconName.PlusSquareFill, Text = "Counter"}, new() { Id = "2", Href = "/counter", IconName = IconName.PlusSquareFill, Text = "Counter"},
new() { Id = "3", Href = "/weather", IconName = IconName.Table, Text = "Weather"}, new() { Id = "3", Href = "/weather", IconName = IconName.Table, Text = "Weather"},
new() { Id = "4", Href= "/documents", IconName = IconName.FileTypeTxt, Text = "Documents" } new() { Id = "4", Href= "/documents", IconName = IconName.FileText, Text = "Documents" }
]; ];
return navItems; return navItems;
@@ -6,6 +6,8 @@
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
<Toasts class="p-3" Messages="messages" Placement="ToastsPlacement.TopRight" /> <Toasts class="p-3" Messages="messages" Placement="ToastsPlacement.TopRight" />
<ConfirmDialog @ref="dialog" />
<PageTitle>Documents</PageTitle> <PageTitle>Documents</PageTitle>
<h2 class="mb-4">Upload new document</h2> <h2 class="mb-4">Upload new document</h2>
@@ -40,7 +42,7 @@
<th></th> <th></th>
<th>Name</th> <th>Name</th>
<th>Number of chunks</th> <th>Number of chunks</th>
<th>Creation Date</th> <th>Creation date</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -58,12 +60,19 @@
</tbody> </tbody>
</table> </table>
<div class="row">
<div class="col-md-2 col-sm-3 col-2">
<div class="d-grid gap-2">
<Button @ref="deleteButton" Color="ButtonColor.Danger" Disabled="@(!documents.Any(d => d.IsSelected))" @onclick="DeleteSelectedDocuments"> <Button @ref="deleteButton" Color="ButtonColor.Danger" Disabled="@(!documents.Any(d => d.IsSelected))" @onclick="DeleteSelectedDocuments">
<Icon Name="IconName.X" /><span class="d-none d-lg-inline ps-3">Delete</span> <Icon Name="IconName.Trash" /><span class="d-none d-lg-inline ps-3">Delete</span>
</Button> </Button>
</div>
</div>
</div>
} }
@code { @code {
private ConfirmDialog dialog = default!;
private Button uploadButton = default!; private Button uploadButton = default!;
private Button deleteButton = default!; private Button deleteButton = default!;
@@ -73,8 +82,13 @@
[SupplyParameterFromForm] [SupplyParameterFromForm]
public IBrowserFile? File { get; set; } public IBrowserFile? File { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (!firstRender)
{
return;
}
await LoadDocumentsAsync(); await LoadDocumentsAsync();
} }
@@ -87,9 +101,11 @@
{ {
documents.Add(new SelectableDocument(dbDocument.Id, dbDocument.Name, dbDocument.CreationDate, dbDocument.ChunkCount) documents.Add(new SelectableDocument(dbDocument.Id, dbDocument.Name, dbDocument.CreationDate, dbDocument.ChunkCount)
{ {
LocalCreationDateString = await JSRuntime.InvokeAsync<string>("getLocalTime", dbDocument.CreationDate) LocalCreationDateString = await GetLocalDateTimeStringAsync(dbDocument.CreationDate)
}); });
} }
StateHasChanged();
} }
private void HandleFileSelected(InputFileChangeEventArgs e) private void HandleFileSelected(InputFileChangeEventArgs e)
@@ -114,7 +130,7 @@
await vectorSearchService.ImportAsync(stream, File.Name, MimeUtility.GetMimeMapping(File.Name), null); await vectorSearchService.ImportAsync(stream, File.Name, MimeUtility.GetMimeMapping(File.Name), null);
CreateToastMessage(ToastType.Success, "Upload document", $"The document {File.Name} has been successfully uploaded and indexed.", DateTime.Now.ToString("g")); CreateToastMessage(ToastType.Success, "Upload document", $"The document {File.Name} has been successfully uploaded and indexed.", await GetLocalDateTimeStringAsync(DateTimeOffset.UtcNow));
await LoadDocumentsAsync(); await LoadDocumentsAsync();
} }
@@ -128,9 +144,19 @@
{ {
var selectedDocuments = documents?.Where(d => d.IsSelected) ?? []; var selectedDocuments = documents?.Where(d => d.IsSelected) ?? [];
var confirmation = await dialog.ShowAsync(
title: "Delete the selected document?",
message1: "This will delete the documents and all the corresponding embeddings. The operation cannot be undone.",
message2: "Do you want to proceed?");
if (!confirmation)
{
return;
}
try try
{ {
uploadButton.ShowLoading(); deleteButton.ShowLoading();
foreach (var document in selectedDocuments) foreach (var document in selectedDocuments)
{ {
@@ -138,11 +164,11 @@
} }
await LoadDocumentsAsync(); await LoadDocumentsAsync();
CreateToastMessage(ToastType.Info, "Delete documents", "The selected documents have been successfully deleted.", DateTime.Now.ToString("g")); CreateToastMessage(ToastType.Info, "Delete documents", "The selected documents have been successfully deleted.", await GetLocalDateTimeStringAsync(DateTimeOffset.UtcNow));
} }
finally finally
{ {
uploadButton.HideLoading(); deleteButton.HideLoading();
} }
} }
@@ -160,6 +186,11 @@
messages.Add(toastMessage); messages.Add(toastMessage);
} }
private async Task<string> GetLocalDateTimeStringAsync(DateTimeOffset dateTime)
{
return await JSRuntime.InvokeAsync<string>("getLocalTime", dateTime);
}
private record class SelectableDocument(Guid Id, string Name, DateTimeOffset CreationDate, int ChunkCount) : Document(Id, Name, CreationDate, ChunkCount) private record class SelectableDocument(Guid Id, string Name, DateTimeOffset CreationDate, int ChunkCount) : Document(Id, Name, CreationDate, ChunkCount)
{ {
public bool IsSelected { get; set; } public bool IsSelected { get; set; }