| name | migrate-page |
| description | Migrate a page from legacy MVC to Angular SPA + API. Orchestrates grid, map, modal, and workflow sub-skills as needed. |
| allowed-tools | ["Read","Glob","Grep","Edit","Write","Bash(dotnet build:*)","Bash(npm run gen-model:*)","Bash(dotnet test:*)","Bash(npm test:*)","Skill(migrate-grid)","Skill(migrate-map)","Skill(crud-modal)","Skill(migrate-workflow)"] |
| argument-hint | <EntityName> |
Migrate Page Skill
When the user invokes /migrate-page <EntityName>:
1. Analyze Legacy Code
First, thoroughly examine the existing MVC implementation:
- Read the legacy controller:
Neptune.WebMvc/Controllers/{Entity}Controller.cs
- Read all views in:
Neptune.WebMvc/Views/{Entity}/
- Check for partials in:
Neptune.WebMvc/Views/Shared/ that may be used
- Look for JavaScript in:
Neptune.WebMvc/Scripts/ related to the entity
Identify and document:
- All CRUD operations (Index, Detail, New, Edit, Delete)
- Special/custom endpoints
- Bootstrap patterns used (panels, tables, forms, modals)
- Client-side validation or JavaScript behavior
- Related entities and their relationships
- Authorization/permission requirements
Identify Components Requiring Specialized Skills
Based on your analysis, note which of these components the page contains:
| Component Found | Specialized Skill | When to Use |
|---|
| Data tables/grids | /migrate-grid | Index pages, detail pages with related entity lists |
| Maps with boundaries/locations | /migrate-map | Pages showing spatial data |
| Create/Edit forms | /crud-modal | Pages with New/Edit functionality |
| Multi-step wizards | /migrate-workflow | Pages with sequential data entry steps |
Document which skills you'll need for this migration.
2. Plan API Layer
Before writing code, plan the artifacts needed:
DTOs to create:
{Entity}GridDto — Fields needed for list/index view
{Entity}Dto — Fields for detail view including related data
{Entity}UpsertDto — Fields for create/edit forms
{Entity}SimpleDto — If used in dropdowns elsewhere
Identify:
- Which related entities need to be Included for extension methods
- Query optimization opportunities (avoid N+1)
- Validation rules to implement
3. Create API Files
Create the following files in order:
3.1 DTOs
Location: Neptune.Models/DataTransferObjects/
{Entity}GridDto.cs
{Entity}Dto.cs
{Entity}UpsertDto.cs
3.2 Extension Methods
Extension methods are generated by scaffold in Neptune.EFModels/Entities/Generated/ExtensionMethods/{Entity}ExtensionMethods.cs. If you need custom extension methods beyond what scaffold generates, add them in Neptune.EFModels/Entities/ as a partial class or separate file:
AsDto() — Full detail mapping
AsGridDto() — Grid row mapping
AsSimpleDto() — Simple/dropdown mapping
3.3 Static Helpers
Location: Neptune.EFModels/Entities/{PluralEntity}.cs
ListAsDtoAsync() or ListAsGridDto()
GetByIDAsDto()
GetByID()
GetByIDWithChangeTracking()
CreateNew()
Update()
DeleteAsync()
3.4 Controller
Location: Neptune.API/Controllers/{Entity}Controller.cs
- Extend
SitkaController<{Entity}Controller>(dbContext, logger, neptuneConfiguration) with primary constructor (3 params)
- Implement endpoints matching the DTOs
3.5 Authorization
Apply authorization attributes to controller endpoints based on legacy feature attributes.
Step 1: Identify legacy features
Search for Feature] in the legacy controller:
grep -E "Feature\]" Neptune.WebMvc/Controllers/{Entity}Controller.cs
Step 2: Map to Neptune authorization attributes
| Legacy Feature Pattern | New Attribute | Roles |
|---|
| Public/anonymous endpoint | [AllowAnonymous] | No auth required |
| Admin-only operations | [AdminFeature] | Admin, SitkaAdmin |
| SitkaAdmin-only operations | [SitkaAdminFeature] | SitkaAdmin |
| Jurisdiction edit operations | [JurisdictionEditFeature] | Admin, SitkaAdmin, JurisdictionManager, JurisdictionEditor (+ jurisdiction match) |
| Jurisdiction manage operations | [JurisdictionManageFeature] | Admin, SitkaAdmin, JurisdictionManager (+ jurisdiction match) |
| Any authenticated user | [UserViewFeature] | All logged-in users |
| Unassigned users | [LoggedInUnclassifiedFeature] | Unassigned users |
Step 3: Apply attributes
- Add
using Neptune.API.Services.Authorization; to the controller
- Add
using Microsoft.AspNetCore.Authorization; if using [AllowAnonymous]
- Apply appropriate attribute to each endpoint:
- GET endpoints (public data): Usually
[AllowAnonymous] or [UserViewFeature]
- POST/PUT/DELETE: Apply the feature attribute that matches the legacy feature
Example:
[HttpGet]
[UserViewFeature]
public ActionResult<List<EntityGridDto>> List() { ... }
[HttpPost]
[AdminFeature]
public ActionResult<EntityDto> Create([FromBody] EntityUpsertDto dto) { ... }
[HttpPut("{entityID}")]
[JurisdictionEditFeature]
public ActionResult<EntityDto> Update([FromRoute] int entityID, [FromBody] EntityUpsertDto dto) { ... }
4. Generate TypeScript
After API code is complete:
# Build the API to generate swagger.json
dotnet build Neptune.API/Neptune.API.csproj -c Debug
# Generate TypeScript models
cd Neptune.Web
npm run gen-model
Verify the generated files in Neptune.Web/src/app/shared/generated/.
5. Create Angular Components
5.1 Component Structure
Location: Neptune.Web/src/app/pages/{entity}/
Create the folder structure based on identified components:
{entity}-list/ — If page has a main grid (use /migrate-grid)
{entity}-detail/ — If page has a detail view
{entity}-modal/ — If page needs create/edit forms (use /crud-modal)
All components must be standalone with explicit imports. Use @Input() with BehaviorSubject pattern for route params.
5.2 Grids
If the page contains data grids, follow the /migrate-grid skill for:
- Column definition patterns
- DTO design for grid rows
- Grid component usage (
neptune-grid)
5.3 Maps
If the page contains maps, follow the /migrate-map skill for:
- GeoJSON endpoint patterns
- Map component integration (
neptune-map)
- Layer component usage
5.4 CRUD Modals
If the page needs create/edit/delete functionality, follow the /crud-modal skill for:
- Modal component structure
- Form creation with generated helpers
- Validation patterns
- Delete confirmation patterns
5.5 Route Configuration
Add route to Neptune.Web/src/app/app.routes.ts:
{
path: 'entities',
children: [
{
path: '',
loadComponent: () => import('./pages/entity/entity-list/entity-list.component').then(m => m.EntityListComponent)
},
{
path: ':entityID',
loadComponent: () => import('./pages/entity/entity-detail/entity-detail.component').then(m => m.EntityDetailComponent)
}
]
}
5.6 Route Guards
Apply guards as needed:
| Guard | Purpose |
|---|
UnsavedChangesGuard | Prevents navigation away from unsaved form changes |
Auth checks are typically done within components using AuthenticationService.
6. Write Tests
6.1 API Tests (MSTest)
Location: Neptune.Tests/
- Test all controller actions
- Use in-memory database
- Test success and error cases
- Remember 3-param constructor:
new EntityController(dbContext, logger, Options.Create(new NeptuneConfiguration()))
6.2 Angular Tests (Jasmine)
Location: Same folder as component with .spec.ts extension
- Test component creation
- Test data loading
- Test user interactions
- Mock services with
jasmine.createSpyObj
7. Remove/Deprecate Legacy Code
After migration is complete and tested:
- Add
[Obsolete("Migrated to Angular SPA")] attribute to legacy controller
- Or remove legacy controller/views entirely if safe
- Update any internal links to point to new Angular routes
8. Run Tests and Verify
# Run API tests
dotnet test Neptune.Tests/Neptune.Tests.csproj
# Run Angular tests
cd Neptune.Web
npm test
# Manual verification
npm start
# Navigate to new pages and verify functionality
Checklist Before Completion
Core Migration
Authorization
If Page Has Grids (per /migrate-grid)
If Page Has Maps (per /migrate-map)
If Page Has CRUD (per /crud-modal)
Final