From dc26a3d2e9fb03e32f1a1fe5a170f438dded52dd Mon Sep 17 00:00:00 2001 From: Marco Minerva Date: Tue, 28 Jul 2026 14:57:57 +0200 Subject: [PATCH] Improve UI/UX for chat, documents, and home pages - Chat page: Add empty state, clarify tooltips/placeholders, and enhance error/copy messages. - Documents page: Add chunking/embedding info, clarify file support, show document count badge, and display empty state. Refactor table/button markup and improve upload toasts. - Home page: Redesign with hero section, icons, workflow cards, and styled features list. Clarify README link. - Add Home.razor.css for new visual styles. --- .../Components/Pages/Ask.razor | 27 ++-- .../Components/Pages/Documents.razor | 106 +++++++------- .../Components/Pages/Home.razor | 131 ++++++++++++++---- .../Components/Pages/Home.razor.css | 14 ++ 4 files changed, 195 insertions(+), 83 deletions(-) create mode 100644 SqlDatabaseVectorSearch/Components/Pages/Home.razor.css diff --git a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor index d5cad35..5005400 100644 --- a/SqlDatabaseVectorSearch/Components/Pages/Ask.razor +++ b/SqlDatabaseVectorSearch/Components/Pages/Ask.razor @@ -9,6 +9,15 @@
+ @if (messages.Count == 0) + { +
+ +

Chat with your documents

+

Ask a question about the documents you have uploaded. Press the up arrow key to recall your previous question.

+
+ } + @foreach (var message in messages) { if (message.Role == "user") @@ -98,18 +107,20 @@ @@ -129,7 +140,7 @@ private bool isAsking = false; private bool showCopyConfirmation = false; - private string toolTipText = "Copy to Clipboard"; + private string toolTipText = "Copy to clipboard"; protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -206,7 +217,7 @@ } catch (Exception ex) { - assistantMessage.Text = $"There was an error while processing the question: {ex.Message}"; + assistantMessage.Text = $"There was an error while processing your question: {ex.Message}"; assistantMessage.Status = MessageStatus.Completed; } finally @@ -237,7 +248,7 @@ await Task.Delay(3000); // Shows the checkmark for 3 seconds - toolTipText = "Copy to Clipboard"; + toolTipText = "Copy to clipboard"; showCopyConfirmation = false; await InvokeAsync(StateHasChanged); } diff --git a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor index 8498767..9750b68 100644 --- a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor +++ b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor @@ -8,10 +8,11 @@ Documents -

+

- Upload new document + Upload a new document

+

The document is split into chunks, and an embedding is generated and stored for each chunk.

@@ -20,7 +21,7 @@
- + @@ -58,53 +59,64 @@ else

Available documents + @documents.Count

-
- - - - - - - - - - - - - @foreach (var document in documents) - { - - - - - - - + @if (documents.Count == 0) + { +
+ +

No documents have been indexed yet. Upload one to get started.

+
+ } + else + { +
+
IDNameContent typeChunksCreated
-
- -
-
@document.Id@document.Name - - @document.ContentType - - @document.ChunkCount@document.LocalCreationDateString
+ + + + + + + + - } - -
IDNameContent typeChunksCreated
-
-
-
-
-
- + + + @foreach (var document in documents) + { + + +
+ +
+ + @document.Id + @document.Name + + + @document.ContentType + + + @document.ChunkCount + @document.LocalCreationDateString + + } + + +
+
+
+
+
+ +
-
+ } } @code { @@ -179,7 +191,7 @@ else var documentId = string.IsNullOrWhiteSpace(Model.DocumentId) ? null : (Guid?)Guid.Parse(Model.DocumentId); await vectorSearchService.ImportAsync(EmbeddingRequest.Create(stream, fileName, documentId)); - ToastService.Notify(await CreateToastMessageAsync(ToastType.Success, "Upload document", $"The document {fileName} has been successfully uploaded and indexed.")); + ToastService.Notify(await CreateToastMessageAsync(ToastType.Success, "Upload document", $"The document '{fileName}' has been successfully uploaded and indexed.")); Model = new UploadDocument(); await JSRuntime.InvokeVoidAsync("resetFileInput", "fileInput"); @@ -188,7 +200,7 @@ else } catch (Exception ex) { - ToastService.Notify(await CreateToastMessageAsync(ToastType.Danger, "Upload error", $"There was an error while uploading the document {fileName}: {ex.Message}")); + ToastService.Notify(await CreateToastMessageAsync(ToastType.Danger, "Upload error", $"There was an error while uploading the document '{fileName}': {ex.Message}")); } finally { diff --git a/SqlDatabaseVectorSearch/Components/Pages/Home.razor b/SqlDatabaseVectorSearch/Components/Pages/Home.razor index 6e2d27e..fa9adc4 100644 --- a/SqlDatabaseVectorSearch/Components/Pages/Home.razor +++ b/SqlDatabaseVectorSearch/Components/Pages/Home.razor @@ -3,37 +3,112 @@ SQL Database Vector Search -

SQL Database Vector Search

+
+

+ + SQL Database Vector Search +

+

+ A Blazor Web App and Minimal API for Retrieval Augmented Generation (RAG) and vector search using the native + VECTOR type in + Azure SQL Database, with + Azure OpenAI and + Microsoft Agent Framework. +

+ +
-

- A Blazor Web App and Minimal API for Retrieval Augmented Generation (RAG) and vector search using the native VECTOR type in Azure SQL Database with Azure OpenAI and Microsoft Agent Framework. -

+

+ + How it works +

-

- This application allows you to: -

    -
  • Load documents (PDF, DOCX, TXT, MD)
  • -
  • Generate embeddings and save them as vectors in Azure SQL Database
  • -
  • Perform semantic search and RAG using Azure OpenAI agents
  • -
  • Interact via a Blazor Web App or programmatically via Minimal API
  • -
- Document import, question reformulation, and RAG are orchestrated with Microsoft Agent Framework. -

+
+
+
+
+ +

1. Import

+

+ Upload a PDF, DOCX, TXT or MD file. Its content is split into chunks, and each chunk is turned into + an embedding by an Agent Framework workflow. +

+
+
+
+
+
+
+ +

2. Store

+

+ Embeddings are persisted in Azure SQL Database using the native VECTOR type, so no + external vector store is required. +

+
+
+
+
+
+
+ +

3. Ask

+

+ Your question is reformulated with the conversation context, matched against the stored vectors, and + answered with citations. +

+
+
+
+
-

Supported Features

-
    -
  • Microsoft Agent Framework orchestration: Import documents through an embedding workflow and answer questions with dedicated reformulation and RAG agents.
  • -
  • Conversation history with question reformulation: Rewrite follow-up questions with the current conversation context before vector search.
  • -
  • SQL vector-search context: Retrieve relevant chunks from Azure SQL Database through native VECTOR search.
  • -
  • Information about token usage: Access token usage for reformulation and final answer generation.
  • -
  • Response streaming: Receive answer tokens in real time in the chat page and streaming API.
  • -
  • Markdown source citations: Get citations directly in the answer text with source name, page number when available, and a short supporting excerpt.
  • +

    + + Supported features +

    + +
      +
    • + + Microsoft Agent Framework orchestration: documents are imported through an embedding workflow, + and questions are answered by dedicated reformulation and RAG agents. +
    • +
    • + + Conversation history with question reformulation: follow-up questions are rewritten with the + current conversation context before the vector search is performed. +
    • +
    • + + SQL vector search: the most relevant chunks are retrieved from Azure SQL Database through + native VECTOR cosine-distance search. +
    • +
    • + + Token usage details: input, output and total tokens are reported for both question + reformulation and answer generation. +
    • +
    • + + Response streaming: answers are streamed token by token in the chat page and through the + Server-Sent Events API. +
    • +
    • + + Markdown source citations: citations are embedded in the answer with the source name, the page + number when available, and a short supporting excerpt. +
    -

    - Try uploading a document or ask a question to get started! -

    - -

    - For API usage and more details, see the README. +

    + + For API usage and more details, see the + README.

    diff --git a/SqlDatabaseVectorSearch/Components/Pages/Home.razor.css b/SqlDatabaseVectorSearch/Components/Pages/Home.razor.css new file mode 100644 index 0000000..fd29ea3 --- /dev/null +++ b/SqlDatabaseVectorSearch/Components/Pages/Home.razor.css @@ -0,0 +1,14 @@ +.hero { + background: linear-gradient(135deg, #eef4ff 0%, #f8f9fa 100%); + border: 1px solid #e3e8f0; +} + +.inline-logo { + height: 1.5em; + vertical-align: middle; +} + +.card:hover { + transform: translateY(-2px); + transition: transform 0.15s ease-in-out; +}