Added basic searchdomain info view

This commit is contained in:
2025-12-14 18:37:46 +01:00
parent 34b7c3845d
commit b68925de42
4 changed files with 253 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Server.Helper; using Server.Helper;
using Shared.Models; using Shared.Models;
using Server.Exceptions; using Server.Exceptions;
using Server.Models;
namespace Server.Controllers; namespace Server.Controllers;
[ApiController] [ApiController]
@@ -11,15 +12,22 @@ namespace Server.Controllers;
public class HomeController : Controller public class HomeController : Controller
{ {
private readonly ILogger<EntityController> _logger; private readonly ILogger<EntityController> _logger;
private readonly SearchdomainManager _domainManager;
public HomeController(ILogger<EntityController> logger, IConfiguration config, SearchdomainManager domainManager, SearchdomainHelper searchdomainHelper, DatabaseHelper databaseHelper) public HomeController(ILogger<EntityController> logger, IConfiguration config, SearchdomainManager domainManager, SearchdomainHelper searchdomainHelper, DatabaseHelper databaseHelper)
{ {
_logger = logger; _logger = logger;
_domainManager = domainManager;
} }
[Authorize] [Authorize]
[HttpGet("/")] [HttpGet("/")]
public IActionResult Index() public IActionResult Index()
{ {
return View(); HomeIndexViewModel viewModel = new()
{
Searchdomains = _domainManager.ListSearchdomains()
};
return View(viewModel);
} }
} }

View File

@@ -0,0 +1,6 @@
namespace Server.Models;
public class HomeIndexViewModel
{
public required List<string> Searchdomains { get; set; }
}

View File

@@ -1,8 +1,217 @@
@{ @using Server.Models
@using System.Web
@model HomeIndexViewModel
@{
ViewData["Title"] = "Home Page"; ViewData["Title"] = "Home Page";
int i = 0;
Dictionary<int, string> domains = [];
Model.Searchdomains.ForEach(domain =>
{
domains[i++] = domain;
});
} }
<div class="text-center"> <div class="container-fluid mt-4">
<h1 class="display-4">Welcome</h1> <div class="row">
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<!-- Sidebar -->
<div class="col-md-4 sidebar">
<div class="card">
<div class="card-body p-2">
<ul class="list-group list-group-flush mb-2" style="max-height: 60vh; overflow-y: auto;">
@foreach (var domain in domains)
{
<li id="sidebar_domain_@(domain.Key)" class="domain-item list-group-item list-group-item-action text-nowrap" style="width: max-content" title="@domain.Value" onclick="selectDomain(@(domain.Key))">
@domain.Value
</li>
}
</ul>
<button class="btn btn-primary w-100">Add</button>
</div>
</div>
</div>
<!-- Main Content -->
<div class="col col-md-8">
<div class="card section-card">
<div class="card-body">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0 text-nowrap overflow-auto">Searchdomain1</h4>
<div class="col-md-3 text-end w-auto">
<button class="btn btn-warning btn-sm me-2">Rename</button>
<button class="btn btn-danger btn-sm">Delete</button>
</div>
</div>
<!-- Settings -->
<div class="row align-items-center mb-3">
<div class="col-md-6">
<label class="form-label">Settings</label>
<input
type="text"
class="form-control"
placeholder="JSON-string"
disabled
/>
</div>
<div class="col-md-2 mt-3 mt-md-0">
<button class="btn btn-warning w-100">Update</button>
</div>
</div>
<!-- Cache -->
<div class="d-flex align-items-center mb-4">
<div class="me-3">
<strong>Cache utilization:</strong> 2.47MiB
</div>
<button class="btn btn-primary btn-sm">Reset</button>
</div>
<!-- Recent Queries -->
<div class="card section-card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<strong>Recent queries</strong>
<input
type="text"
class="form-control form-control-sm w-25"
placeholder="filter"
/>
</div>
@* <div class="list-row">
<span>Some test query</span>
<button class="btn btn-primary btn-sm">Details</button>
</div>
<div class="list-row">
<span>Some other test query</span>
<button class="btn btn-primary btn-sm">Details</button>
</div> *@
</div>
</div>
<!-- Entities -->
<div class="card section-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<strong>Entities</strong>
<input
type="text"
class="form-control form-control-sm w-25"
placeholder="filter"
/>
</div>
<div class="spinner d-none"></div>
<table id="entitiesTable" class="table table-striped" style="max-height: 60vh; overflow-y: auto; display: block;">
<thead>
<tr>
<th class="visually-hidden">Name</th>
<th class="visually-hidden">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
@* <div class="list-row">
<span>Someentity</span>
<button class="btn btn-primary btn-sm">Details</button>
</div>
<div class="list-row">
<span>Some other test query</span>
<button class="btn btn-primary btn-sm">Details</button>
</div> *@
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<script>
var domains = JSON.parse('@Html.Raw(System.Text.Json.JsonSerializer.Serialize(domains))');
var entities = null;
function selectDomain(domainKey) {
// Deselect all domain items
document.querySelectorAll('.domain-item').forEach(item => {
item.classList.remove('active');
});
// Select the clicked domain item
var selectedItem = document.getElementById('sidebar_domain_' + domainKey);
selectedItem.classList.add('active');
// Update main content header
var domainName = domains[domainKey];
document.querySelector('.section-card h4').innerText = domainName;
// Request the entities from that searchdomain
let url = `/Entity/List?searchdomain=${encodeURIComponent(domainName)}&returnEmbeddings=false`;
let table = document.querySelector("#entitiesTable").parentElement;
clearEntitiesTable();
showEntitiesLoading(table);
fetch(url)
.then(response => response.json())
.then(data => {
entities = data.Results;
populateEntitiesTable();
hideEntitiesLoading(table);
})
.catch(error => {
console.error('Error fetching entities:', error);
flagSearchdomainAsErroneous(domainKey);
hideEntitiesLoading(table);
});
}
function clearEntitiesTable() {
var tableBody = document.querySelector('#entitiesTable tbody');
tableBody.innerHTML = '';
}
function populateEntitiesTable() {
if (!entities) return;
var tableBody = document.querySelector('#entitiesTable tbody');
clearEntitiesTable();
entities.forEach(entity => {
var row = document.createElement('tr');
var nameCell = document.createElement('td');
nameCell.textContent = entity.Name;
row.appendChild(nameCell);
var actionCell = document.createElement('td');
var detailsButton = document.createElement('button');
detailsButton.className = 'btn btn-primary btn-sm';
detailsButton.textContent = 'Details';
actionCell.appendChild(detailsButton);
row.appendChild(actionCell);
tableBody.appendChild(row);
});
}
function flagSearchdomainAsErroneous(domainKey) {
var domainItem = document.getElementById('sidebar_domain_' + domainKey);
domainItem.classList.add('list-group-item-danger');
}
function showEntitiesLoading(element = null) {
if (element == null) element = document;
element.querySelector('.spinner').classList.remove('d-none');
}
function hideEntitiesLoading(element = null) {
if (element == null) element = document;
element.querySelector('.spinner').classList.add('d-none');
}
</script>

View File

@@ -19,4 +19,29 @@ html {
body { body {
margin-bottom: 60px; margin-bottom: 60px;
}
/* Throbber */
.spinner-container {
display: flex;
align-items: center;
gap: 8px;
margin: 10px 0;
}
.spinner {
width: 20px;
height: 20px;
border: 3px solid #ccc;
border-top: 3px solid #0d6efd;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.d-none {
display: none;
} }