refactor(webui): remove pipeline viewer feature

Remove the read-only pipeline viewer from the web UI:
- Delete PipelineViewer.razor page and supporting components
- Delete PipelineController and PipelineMapper from API
- Delete Pipeline DTOs from Core
- Delete PipelineApiClient from Client
- Remove navigation link and DI registrations
- Delete obsolete plan documents

The ConfigManager utility retains pipeline editing capabilities.
This commit is contained in:
Joseph Doherty
2026-01-21 10:14:43 -05:00
parent 94d5a864e0
commit ceb63bfefb
20 changed files with 329 additions and 2776 deletions
@@ -1,488 +0,0 @@
# ETL Pipeline Viewer Component Design
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Build a Blazor component that visualizes ETL pipeline configuration and execution status.
**Architecture:** Server-side API loads pipeline config and exposes DTOs; Blazor client consumes via typed HttpClient following existing ApiClientBase pattern.
**Tech Stack:** Blazor WebAssembly, Radzen components, existing DataSync config types, existing DataUpdateRepository
---
## Codex Review Feedback (Addressed)
| Issue | Resolution |
|-------|------------|
| Client loading pipelines.json directly breaks CLEAN | API loads config, returns DTO to client |
| Duplicate config records diverge from DataSync | Reuse existing DataSync Configuration types |
| GetRecentUpdatesAsync missing UpdateType filter | Add UpdateType parameter to signature |
| Status/overdue logic diverges from DataSync | Reuse existing `DataUpdateRepository.IsOverdue()` |
| Only LastSuccessfulRun, hiding failures | Add LastRun + LastRunWasSuccessful |
| API patterns don't match ApiRoutes + ApiClientBase | Add `ApiRoutes.Pipelines`, implement `PipelineApiClient` |
| Duration non-nullable for in-progress | Make Duration nullable |
| ScheduleType as string | Use `UpdateTypes` enum |
| Missing authorization | Add `[Authorize]` to page and controller |
---
## Component Structure
### Page Layout
```
┌─────────────────────────────────────────────────────────────┐
│ ETL Pipeline Configuration Viewer [Authorize] │
├─────────────────────────────────────────────────────────────┤
│ Pipeline: [Dropdown - Alphabetical list] │
├─────────────────────────────────────────────────────────────┤
│ SCHEDULE STATUS SUMMARY │
│ ┌─────────┬────────────┬────────────┬─────────────┬───────┐ │
│ │ Type │ Last Run │ Success? │ Next Req. │Status │ │
│ ├─────────┼────────────┼────────────┼─────────────┼───────┤ │
│ │ Mass │ 01/05 02:00│ ✓ │ 01/12 02:00 │ OK │ │
│ │ Daily │ 01/07 01:00│ ✓ │ 01/08 01:00 │ OK │ │
│ │ Hourly │ 01/07 10:00│ ✗ │ 01/07 11:00 │ Over │ │
│ └─────────┴────────────┴────────────┴─────────────┴───────┘ │
├─────────────────────────────────────────────────────────────┤
│ RECENT EXECUTION HISTORY (Last 10 per type) │
│ ┌─────────┬────────────┬──────────┬─────────┬─────┬───────┐ │
│ │ Type │ Start │ End │ Duration│ Rows│ Result│ │
│ └─────────┴────────────┴──────────┴─────────┴─────┴───────┘ │
├─────────────────────────────────────────────────────────────┤
│ COMMON PIPELINE INFO │
│ [Source Card] [Destination Card] [Scripts Card] │
├─────────────────────────────────────────────────────────────┤
│ MASS REFRESH [Enabled] │
│ [Schedule Settings] [Query Preview + Modal] │
├─────────────────────────────────────────────────────────────┤
│ DAILY REFRESH [Enabled] │
├─────────────────────────────────────────────────────────────┤
│ HOURLY REFRESH [Enabled] │
└─────────────────────────────────────────────────────────────┘
```
---
## Architecture
### CLEAN Architecture Layers
```
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION (JdeScoping.Client) │
│ - PipelineViewer.razor (page) │
│ - PipelineScheduleSection.razor (component) │
│ - SqlQueryModal.razor (component) │
│ - PipelineApiClient : ApiClientBase │
└─────────────────────────────────────────────────────────────┘
│ HTTP via ApiRoutes.Pipelines
┌─────────────────────────────────────────────────────────────┐
│ APPLICATION (JdeScoping.Api) │
│ - PipelineController │
│ - Returns DTOs from Core.ApiContracts │
└─────────────────────────────────────────────────────────────┘
│ Depends on
┌─────────────────────────────────────────────────────────────┐
│ INFRASTRUCTURE (JdeScoping.DataSync) │
│ - IEtlPipelineFactory (existing - loads pipelines.json) │
│ - IDataUpdateRepository (add GetRecentUpdatesAsync) │
│ - Reuse existing Configuration/* types │
└─────────────────────────────────────────────────────────────┘
│ Depends on
┌─────────────────────────────────────────────────────────────┐
│ DOMAIN (JdeScoping.Core) │
│ - ApiRoutes.Pipelines (route constants) │
│ - ApiContracts.Pipelines/* (DTOs) │
│ - Models.Enums.UpdateTypes (existing) │
└─────────────────────────────────────────────────────────────┘
```
---
## Files to Create/Modify
### Core Project (`src/JdeScoping.Core/`)
| File | Action | Description |
|------|--------|-------------|
| `ApiContracts/ApiRoutes.cs` | Modify | Add `Pipelines` routes |
| `ApiContracts/Pipelines/PipelineListResponse.cs` | Create | List of pipeline names |
| `ApiContracts/Pipelines/PipelineConfigDto.cs` | Create | Pipeline config DTO (sanitized) |
| `ApiContracts/Pipelines/PipelineStatusDto.cs` | Create | Schedule status DTO |
| `ApiContracts/Pipelines/PipelineExecutionDto.cs` | Create | Execution history DTO |
### DataSync Project (`src/JdeScoping.DataSync/`)
| File | Action | Description |
|------|--------|-------------|
| `Contracts/IDataUpdateRepository.cs` | Modify | Add `GetRecentUpdatesAsync` |
| `Services/DataUpdateRepository.cs` | Modify | Implement new method |
### API Project (`src/JdeScoping.Api/`)
| File | Action | Description |
|------|--------|-------------|
| `Controllers/PipelineController.cs` | Create | API endpoints |
### Client Project (`src/JdeScoping.Client/`)
| File | Action | Description |
|------|--------|-------------|
| `Services/IPipelineApiClient.cs` | Create | Client interface |
| `Services/PipelineApiClient.cs` | Create | Client implementation |
| `Pages/Admin/PipelineViewer.razor` | Create | Main page |
| `Components/Admin/PipelineScheduleSection.razor` | Create | Schedule section |
| `Components/Admin/SqlQueryModal.razor` | Create | SQL modal |
---
## API Routes (add to `ApiRoutes.cs`)
```csharp
/// <summary>
/// Routes for pipeline configuration API endpoints.
/// </summary>
public static class Pipelines
{
/// <summary>Base route for pipeline endpoints.</summary>
public const string Base = "api/pipelines";
/// <summary>Route template for getting a pipeline by name.</summary>
public const string ByName = "{name}";
/// <summary>Route template for getting pipeline status.</summary>
public const string Status = "{name}/status";
/// <summary>Route template for getting pipeline executions.</summary>
public const string Executions = "{name}/executions";
/// <summary>Builds the route to get a specific pipeline config.</summary>
public static string GetByName(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}";
/// <summary>Builds the route to get pipeline status.</summary>
public static string GetStatus(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}/status";
/// <summary>Builds the route to get pipeline executions.</summary>
public static string GetExecutions(string name, int count = 10) =>
$"api/pipelines/{Uri.EscapeDataString(name)}/executions?count={count}";
}
```
---
## DTOs (in `Core/ApiContracts/Pipelines/`)
### PipelineListResponse.cs
```csharp
namespace JdeScoping.Core.ApiContracts.Pipelines;
/// <summary>
/// Response containing list of available pipeline names.
/// </summary>
public record PipelineListResponse(List<string> PipelineNames);
```
### PipelineConfigDto.cs
```csharp
namespace JdeScoping.Core.ApiContracts.Pipelines;
/// <summary>
/// Pipeline configuration DTO for display (sanitized - no raw SQL exposed by default).
/// </summary>
public record PipelineConfigDto(
string Name,
PipelineSourceDto Source,
PipelineDestinationDto Destination,
PipelineSchedulesDto Schedules,
int PreScriptCount,
int PostScriptCount,
List<string>? PreScripts, // Only populated if user has admin role
List<string>? PostScripts); // Only populated if user has admin role
public record PipelineSourceDto(
string Connection,
string? QueryPreview, // First 100 chars
string? MassQueryPreview, // First 100 chars
string? Query, // Full query - only if admin
string? MassQuery, // Full query - only if admin
List<PipelineParameterDto> Parameters);
public record PipelineParameterDto(
string Name,
string? Format,
string Source);
public record PipelineDestinationDto(
string Table,
string OperationType, // "BulkMerge" or "BulkImport"
List<string>? MatchColumns,
List<string>? ExcludeFromUpdate);
public record PipelineSchedulesDto(
PipelineScheduleDto Mass,
PipelineScheduleDto Daily,
PipelineScheduleDto Hourly);
public record PipelineScheduleDto(
bool Enabled,
int IntervalMinutes,
bool PrePurge,
bool ReIndex,
bool IntervalIsOverride,
bool PrePurgeIsOverride,
bool ReIndexIsOverride);
```
### PipelineStatusDto.cs
```csharp
namespace JdeScoping.Core.ApiContracts.Pipelines;
using JdeScoping.Core.Models.Enums;
/// <summary>
/// Pipeline schedule status for each update type.
/// </summary>
public record PipelineStatusResponse(List<PipelineScheduleStatusDto> Statuses);
public record PipelineScheduleStatusDto(
UpdateTypes ScheduleType,
DateTime? LastRun,
bool LastRunWasSuccessful,
DateTime? LastSuccessfulRun,
DateTime? NextRequiredRun,
bool IsOverdue,
int IntervalMinutes);
```
### PipelineExecutionDto.cs
```csharp
namespace JdeScoping.Core.ApiContracts.Pipelines;
using JdeScoping.Core.Models.Enums;
/// <summary>
/// Pipeline execution history.
/// </summary>
public record PipelineExecutionsResponse(List<PipelineExecutionDto> Executions);
public record PipelineExecutionDto(
UpdateTypes ScheduleType,
DateTime StartTime,
DateTime? EndTime,
TimeSpan? Duration, // Nullable for in-progress runs
long RecordCount,
bool WasSuccessful);
```
---
## Repository Update (IDataUpdateRepository)
```csharp
/// <summary>
/// Gets the last N execution records for a specific table and optional update type.
/// </summary>
/// <param name="tableName">The table name to filter by.</param>
/// <param name="updateType">Optional update type filter. If null, returns all types.</param>
/// <param name="count">Maximum records to return per update type.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>List of DataUpdate records ordered by StartDt descending.</returns>
Task<List<DataUpdate>> GetRecentUpdatesAsync(
string tableName,
UpdateTypes? updateType = null,
int count = 10,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets the last run (successful or not) for each update type for a table.
/// </summary>
/// <param name="tableName">The table name.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Dictionary keyed by UpdateType.</returns>
Task<Dictionary<UpdateTypes, DataUpdate>> GetLastRunsAsync(
string tableName,
CancellationToken cancellationToken = default);
```
---
## API Controller
```csharp
[ApiController]
[Route(ApiRoutes.Pipelines.Base)]
[Authorize]
public class PipelineController : ControllerBase
{
private readonly IEtlPipelineFactory _pipelineFactory;
private readonly IDataUpdateRepository _dataUpdateRepository;
[HttpGet]
public ActionResult<PipelineListResponse> GetPipelineNames()
{
var names = _pipelineFactory.GetAvailableTables()
.OrderBy(n => n)
.ToList();
return Ok(new PipelineListResponse(names));
}
[HttpGet(ApiRoutes.Pipelines.ByName)]
public ActionResult<PipelineConfigDto> GetPipeline(string name)
{
// Map from internal config to DTO
// Truncate queries for preview, include full if admin
}
[HttpGet(ApiRoutes.Pipelines.Status)]
public async Task<ActionResult<PipelineStatusResponse>> GetStatus(string name)
{
// Get last runs, calculate IsOverdue using DataUpdateRepository.IsOverdue()
}
[HttpGet(ApiRoutes.Pipelines.Executions)]
public async Task<ActionResult<PipelineExecutionsResponse>> GetExecutions(
string name, [FromQuery] int count = 10)
{
// Get recent updates from repository
}
}
```
---
## Client Implementation
### PipelineApiClient.cs
```csharp
public interface IPipelineApiClient
{
Task<ApiResult<PipelineListResponse>> GetPipelineNamesAsync(CancellationToken ct = default);
Task<ApiResult<PipelineConfigDto>> GetPipelineAsync(string name, CancellationToken ct = default);
Task<ApiResult<PipelineStatusResponse>> GetStatusAsync(string name, CancellationToken ct = default);
Task<ApiResult<PipelineExecutionsResponse>> GetExecutionsAsync(string name, int count = 10, CancellationToken ct = default);
}
public class PipelineApiClient : ApiClientBase, IPipelineApiClient
{
public PipelineApiClient(HttpClient httpClient) : base(httpClient) { }
public Task<ApiResult<PipelineListResponse>> GetPipelineNamesAsync(CancellationToken ct = default)
=> GetAsync<PipelineListResponse>(ApiRoutes.Pipelines.Base, ct);
public Task<ApiResult<PipelineConfigDto>> GetPipelineAsync(string name, CancellationToken ct = default)
=> GetAsync<PipelineConfigDto>(ApiRoutes.Pipelines.GetByName(name), ct);
public Task<ApiResult<PipelineStatusResponse>> GetStatusAsync(string name, CancellationToken ct = default)
=> GetAsync<PipelineStatusResponse>(ApiRoutes.Pipelines.GetStatus(name), ct);
public Task<ApiResult<PipelineExecutionsResponse>> GetExecutionsAsync(string name, int count = 10, CancellationToken ct = default)
=> GetAsync<PipelineExecutionsResponse>(ApiRoutes.Pipelines.GetExecutions(name, count), ct);
}
```
---
## Blazor Components
### PipelineViewer.razor Structure
```razor
@page "/admin/pipeline-viewer"
@attribute [Authorize]
@inject IPipelineApiClient PipelineApi
<PageTitle>Pipeline Configuration Viewer</PageTitle>
<RadzenText TextStyle="TextStyle.H4">ETL Pipeline Configuration Viewer</RadzenText>
<!-- Pipeline Selector -->
<RadzenDropDown @bind-Value="_selectedPipeline"
Data="_pipelineNames"
Change="OnPipelineChanged" />
@if (_config is not null)
{
<!-- Status Summary Table -->
<!-- Execution History Table -->
<!-- Common Info Cards -->
<!-- Schedule Sections (Mass, Daily, Hourly) -->
}
<SqlQueryModal @bind-Visible="_showSqlModal" Title="@_sqlModalTitle" Sql="@_sqlModalContent" />
```
### SqlQueryModal.razor
- Large modal (80% width)
- `<pre><code>` 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>(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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,329 @@
# Remove Pipeline Viewer Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Remove the pipeline viewer feature from the web UI and clean up all supporting code (controllers, DTOs, client services, routes).
**Architecture:** Delete files in dependency order (DTOs/interfaces first would break compilation), so we delete leaf components first (views, client), then API layer, then shared contracts/DTOs. Modify DI registrations and navigation.
**Tech Stack:** Blazor WebAssembly, ASP.NET Core API, C#
---
## Task 1: Remove Client Components (Blazor Pages)
**Files:**
- Delete: `src/JdeScoping.Client/Pages/Admin/PipelineViewer.razor`
- Delete: `src/JdeScoping.Client/Components/Admin/PipelineScheduleSection.razor`
- Delete: `src/JdeScoping.Client/Components/Admin/SqlQueryModal.razor`
**Step 1: Delete PipelineViewer.razor**
```bash
rm src/JdeScoping.Client/Pages/Admin/PipelineViewer.razor
```
**Step 2: Delete PipelineScheduleSection.razor**
```bash
rm src/JdeScoping.Client/Components/Admin/PipelineScheduleSection.razor
```
**Step 3: Delete SqlQueryModal.razor (if exists)**
```bash
rm -f src/JdeScoping.Client/Components/Admin/SqlQueryModal.razor
```
**Step 4: Verify files deleted**
```bash
ls src/JdeScoping.Client/Pages/Admin/ | grep -i pipeline || echo "No pipeline pages remain"
ls src/JdeScoping.Client/Components/Admin/ | grep -i pipeline || echo "No pipeline components remain"
```
Expected: No pipeline-related files in Admin folders.
---
## Task 2: Remove Navigation Link
**Files:**
- Modify: `src/JdeScoping.Client/Layout/MainLayout.razor:16`
**Step 1: Remove the pipeline viewer nav link**
In `MainLayout.razor`, delete this line (around line 16):
```razor
<NavLink class="nav-link" href="/admin/pipeline-viewer">Pipeline Viewer</NavLink>
```
**Step 2: Verify the change**
```bash
grep -n "pipeline-viewer" src/JdeScoping.Client/Layout/MainLayout.razor || echo "Nav link removed"
```
Expected: "Nav link removed"
---
## Task 3: Remove Client Service
**Files:**
- Delete: `src/JdeScoping.Client/Services/PipelineApiClient.cs`
- Modify: `src/JdeScoping.Client/Program.cs:58`
**Step 1: Delete PipelineApiClient.cs**
```bash
rm src/JdeScoping.Client/Services/PipelineApiClient.cs
```
**Step 2: Remove DI registration from Program.cs**
In `src/JdeScoping.Client/Program.cs`, delete this line (around line 58):
```csharp
builder.Services.AddScoped<IPipelineApiClient, PipelineApiClient>();
```
**Step 3: Verify the changes**
```bash
grep -n "PipelineApiClient" src/JdeScoping.Client/Program.cs || echo "DI registration removed"
ls src/JdeScoping.Client/Services/PipelineApiClient.cs 2>/dev/null || echo "File deleted"
```
Expected: Both checks show removal confirmed.
---
## Task 4: Remove API Controller and Mapper
**Files:**
- Delete: `src/JdeScoping.Api/Controllers/PipelineController.cs`
- Delete: `src/JdeScoping.Api/Mapping/PipelineMapper.cs`
- Delete: `src/JdeScoping.Api/Mapping/IPipelineMapper.cs`
- Modify: `src/JdeScoping.Api/DependencyInjection.cs:43`
**Step 1: Delete PipelineController.cs**
```bash
rm src/JdeScoping.Api/Controllers/PipelineController.cs
```
**Step 2: Delete PipelineMapper.cs**
```bash
rm src/JdeScoping.Api/Mapping/PipelineMapper.cs
```
**Step 3: Delete IPipelineMapper.cs**
```bash
rm src/JdeScoping.Api/Mapping/IPipelineMapper.cs
```
**Step 4: Remove DI registration from DependencyInjection.cs**
In `src/JdeScoping.Api/DependencyInjection.cs`, delete this line (around line 43):
```csharp
services.AddSingleton<IPipelineMapper, PipelineMapper>();
```
**Step 5: Verify the changes**
```bash
ls src/JdeScoping.Api/Controllers/PipelineController.cs 2>/dev/null || echo "Controller deleted"
ls src/JdeScoping.Api/Mapping/*Pipeline* 2>/dev/null || echo "Mapper files deleted"
grep -n "PipelineMapper" src/JdeScoping.Api/DependencyInjection.cs || echo "DI registration removed"
```
Expected: All three checks confirm removal.
---
## Task 5: Remove Core DTOs and Interface
**Files:**
- Delete: `src/JdeScoping.Core/Models/Pipelines/` (entire folder)
- Delete: `src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs`
**Step 1: Delete the Pipelines DTO folder**
```bash
rm -rf src/JdeScoping.Core/Models/Pipelines/
```
**Step 2: Delete IPipelineApiClient.cs**
```bash
rm src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs
```
**Step 3: Verify deletions**
```bash
ls src/JdeScoping.Core/Models/Pipelines/ 2>/dev/null || echo "Pipelines folder deleted"
ls src/JdeScoping.Core/ApiContracts/IPipelineApiClient.cs 2>/dev/null || echo "Interface deleted"
```
Expected: Both checks confirm deletion.
---
## Task 6: Remove API Routes
**Files:**
- Modify: `src/JdeScoping.Core/ApiContracts/ApiRoutes.cs:155-188`
**Step 1: Remove the Pipelines class from ApiRoutes.cs**
Delete the entire `Pipelines` static class (lines 155-188):
```csharp
/// <summary>
/// Routes for pipeline configuration API endpoints.
/// </summary>
public static class Pipelines
{
/// <summary>Base route for pipeline endpoints.</summary>
public const string Base = "api/pipelines";
/// <summary>Route template for getting a pipeline by name.</summary>
public const string ByName = "{name}";
/// <summary>Route template for getting pipeline status.</summary>
public const string Status = "{name}/status";
/// <summary>Route template for getting pipeline executions.</summary>
public const string Executions = "{name}/executions";
/// <summary>Builds the route to get a specific pipeline config.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <returns>The formatted route.</returns>
public static string GetByName(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}";
/// <summary>Builds the route to get pipeline status.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <returns>The formatted route.</returns>
public static string GetStatus(string name) => $"api/pipelines/{Uri.EscapeDataString(name)}/status";
/// <summary>Builds the route to get pipeline executions.</summary>
/// <param name="name">The pipeline name to URL-encode.</param>
/// <param name="count">The number of recent executions to retrieve.</param>
/// <returns>The formatted route.</returns>
public static string GetExecutions(string name, int count = 10) =>
$"api/pipelines/{Uri.EscapeDataString(name)}/executions?count={count}";
}
```
**Step 2: Verify the change**
```bash
grep -n "Pipelines" src/JdeScoping.Core/ApiContracts/ApiRoutes.cs || echo "Pipelines routes removed"
```
Expected: "Pipelines routes removed"
---
## Task 7: Delete Plan Documents
**Files:**
- Delete: `docs/plans/2026-01-07-pipeline-viewer-design.md`
- Delete: `docs/plans/2026-01-07-pipeline-viewer-implementation.md`
**Step 1: Delete design document**
```bash
rm docs/plans/2026-01-07-pipeline-viewer-design.md
```
**Step 2: Delete implementation plan**
```bash
rm docs/plans/2026-01-07-pipeline-viewer-implementation.md
```
**Step 3: Verify deletions**
```bash
ls docs/plans/*pipeline-viewer* 2>/dev/null || echo "Plan documents deleted"
```
Expected: "Plan documents deleted"
---
## Task 8: Build and Verify
**Step 1: Build the solution**
```bash
cd /Users/dohertj2/Desktop/JdeScopingTool/NEW && dotnet build
```
Expected: Build succeeds with no errors.
**Step 2: Search for any remaining pipeline viewer references**
```bash
grep -r "PipelineViewer\|PipelineApiClient\|IPipelineApiClient\|PipelineController\|IPipelineMapper\|PipelineMapper\|pipeline-viewer" src/ --include="*.cs" --include="*.razor" || echo "No references remain"
```
Expected: "No references remain"
**Step 3: Run tests**
```bash
dotnet test
```
Expected: All tests pass.
---
## Task 9: Commit
**Step 1: Stage all changes**
```bash
git add -A
```
**Step 2: Commit**
```bash
git commit -m "$(cat <<'EOF'
refactor(webui): remove pipeline viewer feature
Remove the read-only pipeline viewer from the web UI:
- Delete PipelineViewer.razor page and supporting components
- Delete PipelineController and PipelineMapper from API
- Delete Pipeline DTOs from Core
- Delete PipelineApiClient from Client
- Remove navigation link and DI registrations
- Delete obsolete plan documents
The ConfigManager utility retains pipeline editing capabilities.
EOF
)"
```
---
## Summary
| Task | Action | Files Affected |
|------|--------|----------------|
| 1 | Delete Blazor components | 3 files deleted |
| 2 | Remove nav link | 1 file modified |
| 3 | Remove client service | 1 deleted, 1 modified |
| 4 | Remove API layer | 3 deleted, 1 modified |
| 5 | Remove Core DTOs | 5 files deleted |
| 6 | Remove API routes | 1 file modified |
| 7 | Delete plan docs | 2 files deleted |
| 8 | Build and verify | - |
| 9 | Commit | - |
**Total:** 14 files deleted, 4 files modified