feat(client): add PipelineApiClient and admin components

- Add IPipelineApiClient interface in Core ApiContracts
- Add PipelineApiClient implementation extending ApiClientBase
- Create Components/Admin directory for admin UI components
- Add SqlQueryModal component for displaying SQL queries with copy-to-clipboard
- Add PipelineScheduleSection component for pipeline schedule display
- Register IPipelineApiClient in Program.cs DI container
- Add Admin components namespace to _Imports.razor
This commit is contained in:
Joseph Doherty
2026-01-07 08:14:37 -05:00
parent 676f090fc8
commit 2a15028e00
6 changed files with 264 additions and 0 deletions
@@ -0,0 +1,123 @@
@namespace JdeScoping.Client.Components.Admin
@inject IJSRuntime JS
@if (Visible)
{
<div class="sql-modal-overlay" @onclick="Close">
<div class="sql-modal-content" @onclick:stopPropagation="true">
<div class="sql-modal-header">
<RadzenText TextStyle="TextStyle.H5" class="rz-m-0">@Title</RadzenText>
<RadzenButton Icon="close" ButtonStyle="ButtonStyle.Light" Size="ButtonSize.Small" Click="Close" />
</div>
<div class="sql-modal-body">
<pre>@FormattedSql</pre>
</div>
<div class="sql-modal-footer">
<RadzenButton Text="Copy to Clipboard" Icon="content_copy" ButtonStyle="ButtonStyle.Secondary" Click="CopyToClipboard" class="rz-mr-2" />
<RadzenButton Text="Close" Click="Close" />
</div>
</div>
</div>
}
<style>
.sql-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.sql-modal-content {
background: white;
border-radius: 8px;
width: 80vw;
max-width: 1200px;
max-height: 80vh;
display: flex;
flex-direction: column;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.sql-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
border-bottom: 1px solid #dee2e6;
}
.sql-modal-body {
flex: 1;
overflow: auto;
padding: 1.5rem;
background: #f8f9fa;
}
.sql-modal-body pre {
margin: 0;
white-space: pre-wrap;
word-break: break-word;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.875rem;
line-height: 1.5;
}
.sql-modal-footer {
display: flex;
justify-content: flex-end;
padding: 1rem 1.5rem;
border-top: 1px solid #dee2e6;
}
</style>
@code {
[Parameter] public bool Visible { get; set; }
[Parameter] public EventCallback<bool> VisibleChanged { get; set; }
[Parameter] public string? Title { get; set; }
[Parameter] public string? Sql { get; set; }
private string FormattedSql => FormatSql(Sql);
private static string FormatSql(string? sql)
{
if (string.IsNullOrWhiteSpace(sql))
return "";
// Basic SQL formatting - add line breaks before major clauses
return sql
.Replace(" SELECT ", "\nSELECT ")
.Replace(" FROM ", "\nFROM ")
.Replace(" WHERE ", "\nWHERE ")
.Replace(" AND ", "\n AND ")
.Replace(" OR ", "\n OR ")
.Replace(" LEFT ", "\nLEFT ")
.Replace(" RIGHT ", "\nRIGHT ")
.Replace(" INNER ", "\nINNER ")
.Replace(" OUTER ", "\nOUTER ")
.Replace(" JOIN ", " JOIN\n ")
.Replace(" ORDER BY ", "\nORDER BY ")
.Replace(" GROUP BY ", "\nGROUP BY ")
.Replace(" HAVING ", "\nHAVING ")
.Trim();
}
private async Task CopyToClipboard()
{
if (!string.IsNullOrWhiteSpace(Sql))
{
await JS.InvokeVoidAsync("navigator.clipboard.writeText", Sql);
}
}
private async Task Close()
{
await VisibleChanged.InvokeAsync(false);
}
}