(ApiRoutes.Pipelines.GetExecutions(name, count), ct);
}
```
---
## Blazor Components
### PipelineViewer.razor Structure
```razor
@page "/admin/pipeline-viewer"
@attribute [Authorize]
@inject IPipelineApiClient PipelineApi
Pipeline Configuration Viewer
ETL Pipeline Configuration Viewer
@if (_config is not null)
{
}
```
### SqlQueryModal.razor
- Large modal (80% width)
- `` with monospace font
- Copy to clipboard button
- Basic SQL formatting (line breaks at clauses)
### PipelineScheduleSection.razor
- Props: ScheduleType (UpdateTypes), Config (PipelineScheduleDto), Query, OnViewQuery callback
- Shows enabled badge
- Settings table with Default/Override indicators
- Query preview with "View Full" button
---
## Implementation Notes
### Override Detection (in API)
```csharp
bool IsOverride(T? pipelineValue, T defaultValue) where T : struct =>
pipelineValue.HasValue && !pipelineValue.Value.Equals(defaultValue);
```
### Overdue Calculation (reuse existing)
```csharp
// Use DataUpdateRepository.IsOverdue() which includes 50% grace period
var isOverdue = DataUpdateRepository.IsOverdue(
lastSuccessfulRun, tableName, updateType, customIntervals);
```
### Connection Type Badge Colors
| Connection | Badge Style |
|------------|-------------|
| jde | `BadgeStyle.Info` (blue) |
| cms | `BadgeStyle.Success` (green) |
| giw | `BadgeStyle.Warning` (orange) |
### SQL Query Truncation
```csharp
static string Truncate(string? sql, int maxLength = 100) =>
sql is null ? "" :
sql.Length <= maxLength ? sql : sql[..maxLength] + "...";
```
---
## Security Considerations
1. **Authorization**: Page and API require `[Authorize]`
2. **SQL Visibility**: Full SQL/scripts only exposed if user has appropriate permissions (consider admin-only)
3. **No write operations**: This is read-only, no mutations exposed
---
## Dependencies
- Radzen.Blazor (existing)
- Existing `IEtlPipelineFactory` and `IDataUpdateRepository`
- Existing `pipelines.json` configuration
- Existing `UpdateTypes` enum
- Existing `ApiClientBase` pattern