@polymorphism-tech/morph-spec 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +314 -1673
- package/LICENSE +72 -72
- package/README.md +515 -516
- package/bin/detect-agents.js +225 -225
- package/bin/morph-spec.js +358 -173
- package/bin/render-template.js +302 -302
- package/bin/semantic-detect-agents.js +246 -246
- package/bin/task-manager.js +429 -0
- package/bin/validate-agents-skills.js +251 -251
- package/bin/validate-agents.js +69 -69
- package/bin/validate-phase.js +263 -263
- package/bin/validate.js +369 -0
- package/content/.azure/README.md +293 -293
- package/content/.azure/docs/azure-devops-setup.md +454 -454
- package/content/.azure/docs/branch-strategy.md +398 -398
- package/content/.azure/docs/local-development.md +515 -515
- package/content/.azure/pipelines/pipeline-variables.yml +34 -34
- package/content/.azure/pipelines/prod-pipeline.yml +319 -319
- package/content/.azure/pipelines/staging-pipeline.yml +234 -234
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -75
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -94
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -120
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -90
- package/content/.claude/commands/morph-apply.md +221 -158
- package/content/.claude/commands/morph-archive.md +79 -79
- package/content/.claude/commands/morph-infra.md +209 -209
- package/content/.claude/commands/morph-preflight.md +227 -0
- package/content/.claude/commands/morph-proposal.md +122 -101
- package/content/.claude/commands/morph-status.md +86 -86
- package/content/.claude/commands/morph-troubleshoot.md +122 -0
- package/content/.claude/settings.local.json +15 -15
- package/content/.claude/skills/checklists/code-review.md +226 -0
- package/content/.claude/skills/checklists/morph-checklist.md +117 -0
- package/content/.claude/skills/checklists/simulation-checklist.md +77 -0
- package/content/.claude/skills/infra/bicep-architect.md +126 -419
- package/content/.claude/skills/infra/container-specialist.md +131 -437
- package/content/.claude/skills/infra/devops-engineer.md +119 -405
- package/content/.claude/skills/integrations/asaas-financial.md +130 -333
- package/content/.claude/skills/integrations/azure-identity.md +142 -309
- package/content/.claude/skills/integrations/clerk-auth.md +108 -290
- package/content/.claude/skills/integrations/resend-email.md +119 -0
- package/content/.claude/skills/specialists/ai-system-architect.md +192 -604
- package/content/.claude/skills/specialists/azure-architect.md +142 -142
- package/content/.claude/skills/specialists/code-analyzer.md +235 -0
- package/content/.claude/skills/specialists/dotnet-senior.md +287 -0
- package/content/.claude/skills/specialists/ef-modeler.md +113 -200
- package/content/.claude/skills/specialists/hangfire-orchestrator.md +126 -245
- package/content/.claude/skills/specialists/ms-agent-expert.md +109 -263
- package/content/.claude/skills/specialists/po-pm-advisor.md +197 -197
- package/content/.claude/skills/specialists/standards-architect.md +156 -78
- package/content/.claude/skills/specialists/testing-specialist.md +126 -0
- package/content/.claude/skills/specialists/ui-ux-designer.md +191 -1060
- package/content/.claude/skills/stacks/dotnet-blazor.md +210 -588
- package/content/.claude/skills/stacks/dotnet-nextjs.md +154 -402
- package/content/.claude/skills/workflows/morph-replicate.md +213 -0
- package/content/.claude/{commands/morph-clarify.md → skills/workflows/phase-clarify.md} +5 -58
- package/content/.claude/{commands/morph-design.md → skills/workflows/phase-design.md} +16 -86
- package/content/.claude/{commands/morph-setup.md → skills/workflows/phase-setup.md} +9 -17
- package/content/.claude/skills/workflows/phase-tasks.md +164 -0
- package/content/.claude/{commands/morph-uiux.md → skills/workflows/phase-uiux.md} +15 -88
- package/content/.morph/.morphversion +5 -5
- package/content/.morph/archive/.gitkeep +25 -25
- package/content/.morph/config/agents.json +378 -242
- package/content/.morph/config/config.template.json +89 -108
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -392
- package/content/.morph/docs/workflows/design-impl.md +37 -0
- package/content/.morph/docs/workflows/fast-track.md +29 -0
- package/content/.morph/docs/workflows/full-morph.md +76 -0
- package/content/.morph/docs/workflows/standard.md +44 -0
- package/content/.morph/docs/workflows/ui-refresh.md +39 -0
- package/content/.morph/examples/api-nextjs/README.md +241 -241
- package/content/.morph/examples/api-nextjs/contracts.ts +307 -307
- package/content/.morph/examples/api-nextjs/spec.md +399 -399
- package/content/.morph/examples/api-nextjs/tasks.md +168 -168
- package/content/.morph/examples/micro-saas/README.md +125 -125
- package/content/.morph/examples/micro-saas/contracts.cs +358 -358
- package/content/.morph/examples/micro-saas/decisions.md +246 -246
- package/content/.morph/examples/micro-saas/spec.md +236 -236
- package/content/.morph/examples/micro-saas/tasks.md +150 -150
- package/content/.morph/examples/multi-agent/README.md +309 -309
- package/content/.morph/examples/multi-agent/contracts.cs +433 -433
- package/content/.morph/examples/multi-agent/spec.md +479 -479
- package/content/.morph/examples/multi-agent/tasks.md +185 -185
- package/content/.morph/examples/scheduled-reports/decisions.md +158 -0
- package/content/.morph/examples/scheduled-reports/proposal.md +95 -0
- package/content/.morph/examples/scheduled-reports/spec.md +267 -0
- package/content/.morph/examples/state-v3.json +188 -0
- package/content/.morph/features/.gitkeep +25 -25
- package/content/.morph/hooks/README.md +190 -239
- package/content/.morph/hooks/pre-commit-agents.sh +24 -24
- package/content/.morph/hooks/pre-commit-all.sh +48 -48
- package/content/.morph/hooks/pre-commit-specs.sh +49 -49
- package/content/.morph/hooks/pre-commit-tests.sh +60 -60
- package/content/.morph/project.md +160 -160
- package/content/.morph/schemas/agent.schema.json +296 -296
- package/content/.morph/schemas/tasks.schema.json +220 -0
- package/content/.morph/specs/.gitkeep +20 -20
- package/content/.morph/standards/agent-framework-blazor-ui.md +359 -0
- package/content/.morph/standards/agent-framework-production.md +410 -0
- package/content/.morph/standards/agent-framework-setup.md +413 -453
- package/content/.morph/standards/agent-framework-workflows.md +349 -0
- package/content/.morph/standards/architecture.md +325 -325
- package/content/.morph/standards/azure.md +605 -379
- package/content/.morph/standards/coding.md +377 -377
- package/content/.morph/standards/dotnet10-migration.md +520 -494
- package/content/.morph/standards/fluent-ui-setup.md +590 -590
- package/content/.morph/standards/migration-guide.md +514 -514
- package/content/.morph/standards/passkeys-auth.md +423 -423
- package/content/.morph/standards/vector-search-rag.md +536 -536
- package/content/.morph/state.json +17 -17
- package/content/.morph/templates/FluentDesignTheme.cs +149 -149
- package/content/.morph/templates/MudTheme.cs +281 -281
- package/content/.morph/templates/agent.cs +163 -172
- package/content/.morph/templates/clarify-questions.md +159 -0
- package/content/.morph/templates/component.razor +239 -239
- package/content/.morph/templates/contracts/Commands.cs +74 -0
- package/content/.morph/templates/contracts/Entities.cs +25 -0
- package/content/.morph/templates/contracts/Queries.cs +74 -0
- package/content/.morph/templates/contracts/README.md +74 -0
- package/content/.morph/templates/contracts.cs +217 -217
- package/content/.morph/templates/decisions.md +123 -106
- package/content/.morph/templates/design-system.css +226 -226
- package/content/.morph/templates/infra/.dockerignore.example +89 -89
- package/content/.morph/templates/infra/Dockerfile.example +82 -82
- package/content/.morph/templates/infra/README.md +286 -286
- package/content/.morph/templates/infra/app-insights.bicep +63 -63
- package/content/.morph/templates/infra/app-service.bicep +164 -164
- package/content/.morph/templates/infra/container-app-env.bicep +49 -49
- package/content/.morph/templates/infra/container-app.bicep +156 -156
- package/content/.morph/templates/infra/deploy-checklist.md +426 -0
- package/content/.morph/templates/infra/deploy.ps1 +229 -229
- package/content/.morph/templates/infra/deploy.sh +208 -208
- package/content/.morph/templates/infra/key-vault.bicep +91 -91
- package/content/.morph/templates/infra/main.bicep +189 -189
- package/content/.morph/templates/infra/parameters.dev.json +29 -29
- package/content/.morph/templates/infra/parameters.prod.json +29 -29
- package/content/.morph/templates/infra/parameters.staging.json +29 -29
- package/content/.morph/templates/infra/sql-database.bicep +103 -103
- package/content/.morph/templates/infra/storage.bicep +106 -106
- package/content/.morph/templates/integrations/asaas-client.cs +387 -387
- package/content/.morph/templates/integrations/asaas-webhook.cs +351 -351
- package/content/.morph/templates/integrations/azure-identity-config.cs +288 -288
- package/content/.morph/templates/integrations/clerk-config.cs +258 -258
- package/content/.morph/templates/job.cs +171 -171
- package/content/.morph/templates/migration.cs +83 -83
- package/content/.morph/templates/proposal.md +141 -155
- package/content/.morph/templates/recap.md +94 -105
- package/content/.morph/templates/repository.cs +141 -141
- package/content/.morph/templates/saas/subscription.cs +347 -347
- package/content/.morph/templates/saas/tenant.cs +338 -338
- package/content/.morph/templates/service.cs +139 -139
- package/content/.morph/templates/simulation.md +353 -0
- package/content/.morph/templates/spec.md +149 -148
- package/content/.morph/templates/sprint-status.yaml +68 -68
- package/content/.morph/templates/state.template.json +222 -222
- package/content/.morph/templates/story.md +143 -143
- package/content/.morph/templates/tasks.md +257 -235
- package/content/.morph/templates/test.cs +239 -239
- package/content/.morph/templates/ui-components.md +362 -276
- package/content/.morph/templates/ui-design-system.md +286 -286
- package/content/.morph/templates/ui-flows.md +336 -336
- package/content/.morph/templates/ui-mockups.md +133 -133
- package/content/.morph/test-infra/example.bicep +59 -59
- package/content/CLAUDE.md +150 -442
- package/content/README.md +79 -79
- package/detectors/config-detector.js +223 -223
- package/detectors/conversation-analyzer.js +163 -163
- package/detectors/index.js +84 -84
- package/detectors/standards-generator.js +275 -275
- package/detectors/structure-detector.js +245 -250
- package/docs/README.md +144 -149
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
- package/docs/api/scripts/collapse.js +38 -38
- package/docs/api/scripts/commonNav.js +28 -28
- package/docs/api/scripts/linenumber.js +25 -25
- package/docs/api/scripts/nav.js +12 -12
- package/docs/api/scripts/polyfill.js +3 -3
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/docs/api/scripts/prettify/lang-css.js +2 -2
- package/docs/api/scripts/prettify/prettify.js +28 -28
- package/docs/api/scripts/search.js +98 -98
- package/docs/api/styles/jsdoc.css +776 -776
- package/docs/api/styles/prettify.css +80 -80
- package/docs/examples.md +328 -328
- package/docs/getting-started.md +301 -302
- package/docs/installation.md +361 -361
- package/docs/templates.md +418 -418
- package/docs/validation-checklist.md +265 -266
- package/package.json +80 -80
- package/scripts/postinstall.js +132 -132
- package/src/commands/advance-phase.js +183 -0
- package/src/commands/analyze-blazor-concurrency.js +193 -0
- package/src/commands/create-story.js +351 -351
- package/src/commands/detect-agents.js +139 -0
- package/src/commands/detect.js +104 -104
- package/src/commands/doctor.js +356 -280
- package/src/commands/generate.js +149 -149
- package/src/commands/init.js +258 -245
- package/src/commands/lint-fluent.js +352 -0
- package/src/commands/rollback-phase.js +185 -0
- package/src/commands/session-summary.js +291 -0
- package/src/commands/shard-spec.js +224 -224
- package/src/commands/sprint-status.js +250 -250
- package/src/commands/state.js +333 -333
- package/src/commands/sync.js +167 -167
- package/src/commands/task.js +78 -0
- package/src/commands/troubleshoot.js +222 -0
- package/src/commands/update.js +192 -159
- package/src/commands/validate-blazor-state.js +210 -0
- package/src/commands/validate-blazor.js +156 -0
- package/src/commands/validate-css.js +84 -0
- package/src/commands/validate-phase.js +221 -0
- package/src/lib/blazor-concurrency-analyzer.js +288 -0
- package/src/lib/blazor-state-validator.js +291 -0
- package/src/lib/blazor-validator.js +374 -0
- package/src/lib/complexity-analyzer.js +441 -292
- package/src/lib/continuous-validator.js +421 -0
- package/src/lib/css-validator.js +352 -0
- package/src/lib/decision-constraint-loader.js +109 -0
- package/src/lib/design-system-generator.js +298 -298
- package/src/lib/learning-system.js +520 -0
- package/src/lib/mockup-generator.js +366 -0
- package/src/lib/recap-generator.js +205 -0
- package/src/lib/state-manager.js +397 -340
- package/src/lib/troubleshoot-grep.js +194 -0
- package/src/lib/troubleshoot-index.js +144 -0
- package/src/lib/ui-detector.js +350 -0
- package/src/lib/validation-runner.js +231 -0
- package/src/lib/validators/architecture-validator.js +387 -0
- package/src/lib/validators/contract-compliance-validator.js +273 -0
- package/src/lib/validators/package-validator.js +360 -0
- package/src/lib/validators/ui-contrast-validator.js +422 -0
- package/src/utils/file-copier.js +179 -139
- package/src/utils/logger.js +32 -32
- package/src/utils/version-checker.js +175 -175
- package/content/.claude/commands/morph-costs.md +0 -206
- package/content/.claude/commands/morph-tasks.md +0 -319
- package/content/.claude/skills/specialists/cost-guardian.md +0 -110
- package/content/.claude/skills/stacks/shopify.md +0 -445
- package/content/.morph/config/azure-pricing.json +0 -70
- package/content/.morph/config/azure-pricing.schema.json +0 -50
- package/content/.morph/hooks/pre-commit-costs.sh +0 -91
- package/docs/api/cost-calculator.js.html +0 -513
- package/docs/api/design-system-generator.js.html +0 -382
- package/docs/api/global.html +0 -5263
- package/docs/api/index.html +0 -96
- package/docs/api/state-manager.js.html +0 -423
- package/src/commands/cost.js +0 -181
- package/src/commands/update-pricing.js +0 -206
- package/src/lib/cost-calculator.js +0 -429
|
@@ -1,239 +1,239 @@
|
|
|
1
|
-
@* ============================================================
|
|
2
|
-
BLAZOR COMPONENT TEMPLATE
|
|
3
|
-
Generated by MORPH Framework
|
|
4
|
-
============================================================ *@
|
|
5
|
-
|
|
6
|
-
@page "/{feature}/{feature}s"
|
|
7
|
-
@attribute [Authorize(Policy = "CanView{Feature}")]
|
|
8
|
-
@inject I{Feature}Service {Feature}Service
|
|
9
|
-
@inject ILogger<{Feature}List> Logger
|
|
10
|
-
@inject NavigationManager Navigation
|
|
11
|
-
|
|
12
|
-
<PageTitle>{Feature}s</PageTitle>
|
|
13
|
-
|
|
14
|
-
<div class="container-fluid">
|
|
15
|
-
<div class="row mb-4">
|
|
16
|
-
<div class="col">
|
|
17
|
-
<h1>{Feature}s</h1>
|
|
18
|
-
</div>
|
|
19
|
-
<div class="col-auto">
|
|
20
|
-
<AuthorizeView Policy="CanManage{Feature}">
|
|
21
|
-
<button class="btn btn-primary" @onclick="ShowCreateModal">
|
|
22
|
-
<i class="bi bi-plus-lg"></i> New {Feature}
|
|
23
|
-
</button>
|
|
24
|
-
</AuthorizeView>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
|
|
28
|
-
@if (_isLoading)
|
|
29
|
-
{
|
|
30
|
-
<div class="d-flex justify-content-center py-5">
|
|
31
|
-
<div class="spinner-border text-primary" role="status">
|
|
32
|
-
<span class="visually-hidden">Loading...</span>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
}
|
|
36
|
-
else if (_error is not null)
|
|
37
|
-
{
|
|
38
|
-
<div class="alert alert-danger" role="alert">
|
|
39
|
-
<i class="bi bi-exclamation-triangle"></i>
|
|
40
|
-
@_error
|
|
41
|
-
<button class="btn btn-link" @onclick="LoadDataAsync">Retry</button>
|
|
42
|
-
</div>
|
|
43
|
-
}
|
|
44
|
-
else if (_items is null || !_items.Any())
|
|
45
|
-
{
|
|
46
|
-
<div class="text-center py-5">
|
|
47
|
-
<i class="bi bi-inbox display-1 text-muted"></i>
|
|
48
|
-
<p class="lead mt-3">No {feature}s found</p>
|
|
49
|
-
<AuthorizeView Policy="CanManage{Feature}">
|
|
50
|
-
<button class="btn btn-primary" @onclick="ShowCreateModal">
|
|
51
|
-
Create your first {feature}
|
|
52
|
-
</button>
|
|
53
|
-
</AuthorizeView>
|
|
54
|
-
</div>
|
|
55
|
-
}
|
|
56
|
-
else
|
|
57
|
-
{
|
|
58
|
-
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
|
59
|
-
@foreach (var item in _items)
|
|
60
|
-
{
|
|
61
|
-
<div class="col">
|
|
62
|
-
<div class="card h-100">
|
|
63
|
-
<div class="card-body">
|
|
64
|
-
<h5 class="card-title">@item.Name</h5>
|
|
65
|
-
<p class="card-text">
|
|
66
|
-
<span class="badge @GetStatusBadgeClass(item.Status)">
|
|
67
|
-
@item.Status
|
|
68
|
-
</span>
|
|
69
|
-
</p>
|
|
70
|
-
</div>
|
|
71
|
-
<div class="card-footer bg-transparent">
|
|
72
|
-
<small class="text-muted">
|
|
73
|
-
Created @item.CreatedAt.ToString("d")
|
|
74
|
-
</small>
|
|
75
|
-
<AuthorizeView Policy="CanManage{Feature}">
|
|
76
|
-
<div class="float-end">
|
|
77
|
-
<button class="btn btn-sm btn-outline-primary"
|
|
78
|
-
@onclick="() => ShowEditModal(item)">
|
|
79
|
-
Edit
|
|
80
|
-
</button>
|
|
81
|
-
</div>
|
|
82
|
-
</AuthorizeView>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
}
|
|
87
|
-
</div>
|
|
88
|
-
}
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
@* Modal for Create/Edit *@
|
|
92
|
-
@if (_showModal)
|
|
93
|
-
{
|
|
94
|
-
<div class="modal fade show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5)">
|
|
95
|
-
<div class="modal-dialog">
|
|
96
|
-
<div class="modal-content">
|
|
97
|
-
<div class="modal-header">
|
|
98
|
-
<h5 class="modal-title">
|
|
99
|
-
@(_editingItem is null ? "New {Feature}" : "Edit {Feature}")
|
|
100
|
-
</h5>
|
|
101
|
-
<button type="button" class="btn-close" @onclick="CloseModal"></button>
|
|
102
|
-
</div>
|
|
103
|
-
<EditForm Model="_formModel" OnValidSubmit="HandleSubmitAsync">
|
|
104
|
-
<DataAnnotationsValidator />
|
|
105
|
-
<div class="modal-body">
|
|
106
|
-
<div class="mb-3">
|
|
107
|
-
<label class="form-label">Name</label>
|
|
108
|
-
<InputText class="form-control" @bind-Value="_formModel.Name" />
|
|
109
|
-
<ValidationMessage For="() => _formModel.Name" />
|
|
110
|
-
</div>
|
|
111
|
-
@* Add more fields as needed *@
|
|
112
|
-
</div>
|
|
113
|
-
<div class="modal-footer">
|
|
114
|
-
<button type="button" class="btn btn-secondary" @onclick="CloseModal">
|
|
115
|
-
Cancel
|
|
116
|
-
</button>
|
|
117
|
-
<button type="submit" class="btn btn-primary" disabled="@_isSaving">
|
|
118
|
-
@if (_isSaving)
|
|
119
|
-
{
|
|
120
|
-
<span class="spinner-border spinner-border-sm"></span>
|
|
121
|
-
}
|
|
122
|
-
Save
|
|
123
|
-
</button>
|
|
124
|
-
</div>
|
|
125
|
-
</EditForm>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
@code {
|
|
132
|
-
private List<{Feature}Dto>? _items;
|
|
133
|
-
private bool _isLoading = true;
|
|
134
|
-
private bool _showModal;
|
|
135
|
-
private bool _isSaving;
|
|
136
|
-
private string? _error;
|
|
137
|
-
private {Feature}Dto? _editingItem;
|
|
138
|
-
private {Feature}FormModel _formModel = new();
|
|
139
|
-
|
|
140
|
-
protected override async Task OnInitializedAsync()
|
|
141
|
-
{
|
|
142
|
-
await LoadDataAsync();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private async Task LoadDataAsync()
|
|
146
|
-
{
|
|
147
|
-
_isLoading = true;
|
|
148
|
-
_error = null;
|
|
149
|
-
|
|
150
|
-
try
|
|
151
|
-
{
|
|
152
|
-
_items = await {Feature}Service.GetAllAsync();
|
|
153
|
-
}
|
|
154
|
-
catch (Exception ex)
|
|
155
|
-
{
|
|
156
|
-
Logger.LogError(ex, "Failed to load {feature}s");
|
|
157
|
-
_error = "Failed to load data. Please try again.";
|
|
158
|
-
}
|
|
159
|
-
finally
|
|
160
|
-
{
|
|
161
|
-
_isLoading = false;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
private void ShowCreateModal()
|
|
166
|
-
{
|
|
167
|
-
_editingItem = null;
|
|
168
|
-
_formModel = new {Feature}FormModel();
|
|
169
|
-
_showModal = true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private void ShowEditModal({Feature}Dto item)
|
|
173
|
-
{
|
|
174
|
-
_editingItem = item;
|
|
175
|
-
_formModel = new {Feature}FormModel
|
|
176
|
-
{
|
|
177
|
-
Name = item.Name
|
|
178
|
-
// Map other fields
|
|
179
|
-
};
|
|
180
|
-
_showModal = true;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private void CloseModal()
|
|
184
|
-
{
|
|
185
|
-
_showModal = false;
|
|
186
|
-
_formModel = new();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private async Task HandleSubmitAsync()
|
|
190
|
-
{
|
|
191
|
-
_isSaving = true;
|
|
192
|
-
|
|
193
|
-
try
|
|
194
|
-
{
|
|
195
|
-
if (_editingItem is null)
|
|
196
|
-
{
|
|
197
|
-
// Create
|
|
198
|
-
var request = new Create{Feature}Request(_formModel.Name);
|
|
199
|
-
await {Feature}Service.CreateAsync(request);
|
|
200
|
-
}
|
|
201
|
-
else
|
|
202
|
-
{
|
|
203
|
-
// Update
|
|
204
|
-
var request = new Update{Feature}Request(_formModel.Name);
|
|
205
|
-
await {Feature}Service.UpdateAsync(_editingItem.Id, request);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
CloseModal();
|
|
209
|
-
await LoadDataAsync();
|
|
210
|
-
}
|
|
211
|
-
catch (Exception ex)
|
|
212
|
-
{
|
|
213
|
-
Logger.LogError(ex, "Failed to save {feature}");
|
|
214
|
-
// Show error to user
|
|
215
|
-
}
|
|
216
|
-
finally
|
|
217
|
-
{
|
|
218
|
-
_isSaving = false;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private static string GetStatusBadgeClass({Feature}Status status) => status switch
|
|
223
|
-
{
|
|
224
|
-
{Feature}Status.Active => "bg-success",
|
|
225
|
-
{Feature}Status.Pending => "bg-warning text-dark",
|
|
226
|
-
{Feature}Status.Completed => "bg-info",
|
|
227
|
-
{Feature}Status.Failed => "bg-danger",
|
|
228
|
-
_ => "bg-secondary"
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
private class {Feature}FormModel
|
|
232
|
-
{
|
|
233
|
-
[Required]
|
|
234
|
-
[StringLength(200, MinimumLength = 3)]
|
|
235
|
-
public string Name { get; set; } = string.Empty;
|
|
236
|
-
|
|
237
|
-
// Add more fields as needed
|
|
238
|
-
}
|
|
239
|
-
}
|
|
1
|
+
@* ============================================================
|
|
2
|
+
BLAZOR COMPONENT TEMPLATE
|
|
3
|
+
Generated by MORPH Framework
|
|
4
|
+
============================================================ *@
|
|
5
|
+
|
|
6
|
+
@page "/{feature}/{feature}s"
|
|
7
|
+
@attribute [Authorize(Policy = "CanView{Feature}")]
|
|
8
|
+
@inject I{Feature}Service {Feature}Service
|
|
9
|
+
@inject ILogger<{Feature}List> Logger
|
|
10
|
+
@inject NavigationManager Navigation
|
|
11
|
+
|
|
12
|
+
<PageTitle>{Feature}s</PageTitle>
|
|
13
|
+
|
|
14
|
+
<div class="container-fluid">
|
|
15
|
+
<div class="row mb-4">
|
|
16
|
+
<div class="col">
|
|
17
|
+
<h1>{Feature}s</h1>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="col-auto">
|
|
20
|
+
<AuthorizeView Policy="CanManage{Feature}">
|
|
21
|
+
<button class="btn btn-primary" @onclick="ShowCreateModal">
|
|
22
|
+
<i class="bi bi-plus-lg"></i> New {Feature}
|
|
23
|
+
</button>
|
|
24
|
+
</AuthorizeView>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
@if (_isLoading)
|
|
29
|
+
{
|
|
30
|
+
<div class="d-flex justify-content-center py-5">
|
|
31
|
+
<div class="spinner-border text-primary" role="status">
|
|
32
|
+
<span class="visually-hidden">Loading...</span>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
}
|
|
36
|
+
else if (_error is not null)
|
|
37
|
+
{
|
|
38
|
+
<div class="alert alert-danger" role="alert">
|
|
39
|
+
<i class="bi bi-exclamation-triangle"></i>
|
|
40
|
+
@_error
|
|
41
|
+
<button class="btn btn-link" @onclick="LoadDataAsync">Retry</button>
|
|
42
|
+
</div>
|
|
43
|
+
}
|
|
44
|
+
else if (_items is null || !_items.Any())
|
|
45
|
+
{
|
|
46
|
+
<div class="text-center py-5">
|
|
47
|
+
<i class="bi bi-inbox display-1 text-muted"></i>
|
|
48
|
+
<p class="lead mt-3">No {feature}s found</p>
|
|
49
|
+
<AuthorizeView Policy="CanManage{Feature}">
|
|
50
|
+
<button class="btn btn-primary" @onclick="ShowCreateModal">
|
|
51
|
+
Create your first {feature}
|
|
52
|
+
</button>
|
|
53
|
+
</AuthorizeView>
|
|
54
|
+
</div>
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
{
|
|
58
|
+
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
|
59
|
+
@foreach (var item in _items)
|
|
60
|
+
{
|
|
61
|
+
<div class="col">
|
|
62
|
+
<div class="card h-100">
|
|
63
|
+
<div class="card-body">
|
|
64
|
+
<h5 class="card-title">@item.Name</h5>
|
|
65
|
+
<p class="card-text">
|
|
66
|
+
<span class="badge @GetStatusBadgeClass(item.Status)">
|
|
67
|
+
@item.Status
|
|
68
|
+
</span>
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="card-footer bg-transparent">
|
|
72
|
+
<small class="text-muted">
|
|
73
|
+
Created @item.CreatedAt.ToString("d")
|
|
74
|
+
</small>
|
|
75
|
+
<AuthorizeView Policy="CanManage{Feature}">
|
|
76
|
+
<div class="float-end">
|
|
77
|
+
<button class="btn btn-sm btn-outline-primary"
|
|
78
|
+
@onclick="() => ShowEditModal(item)">
|
|
79
|
+
Edit
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</AuthorizeView>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
}
|
|
87
|
+
</div>
|
|
88
|
+
}
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
@* Modal for Create/Edit *@
|
|
92
|
+
@if (_showModal)
|
|
93
|
+
{
|
|
94
|
+
<div class="modal fade show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5)">
|
|
95
|
+
<div class="modal-dialog">
|
|
96
|
+
<div class="modal-content">
|
|
97
|
+
<div class="modal-header">
|
|
98
|
+
<h5 class="modal-title">
|
|
99
|
+
@(_editingItem is null ? "New {Feature}" : "Edit {Feature}")
|
|
100
|
+
</h5>
|
|
101
|
+
<button type="button" class="btn-close" @onclick="CloseModal"></button>
|
|
102
|
+
</div>
|
|
103
|
+
<EditForm Model="_formModel" OnValidSubmit="HandleSubmitAsync">
|
|
104
|
+
<DataAnnotationsValidator />
|
|
105
|
+
<div class="modal-body">
|
|
106
|
+
<div class="mb-3">
|
|
107
|
+
<label class="form-label">Name</label>
|
|
108
|
+
<InputText class="form-control" @bind-Value="_formModel.Name" />
|
|
109
|
+
<ValidationMessage For="() => _formModel.Name" />
|
|
110
|
+
</div>
|
|
111
|
+
@* Add more fields as needed *@
|
|
112
|
+
</div>
|
|
113
|
+
<div class="modal-footer">
|
|
114
|
+
<button type="button" class="btn btn-secondary" @onclick="CloseModal">
|
|
115
|
+
Cancel
|
|
116
|
+
</button>
|
|
117
|
+
<button type="submit" class="btn btn-primary" disabled="@_isSaving">
|
|
118
|
+
@if (_isSaving)
|
|
119
|
+
{
|
|
120
|
+
<span class="spinner-border spinner-border-sm"></span>
|
|
121
|
+
}
|
|
122
|
+
Save
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</EditForm>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@code {
|
|
132
|
+
private List<{Feature}Dto>? _items;
|
|
133
|
+
private bool _isLoading = true;
|
|
134
|
+
private bool _showModal;
|
|
135
|
+
private bool _isSaving;
|
|
136
|
+
private string? _error;
|
|
137
|
+
private {Feature}Dto? _editingItem;
|
|
138
|
+
private {Feature}FormModel _formModel = new();
|
|
139
|
+
|
|
140
|
+
protected override async Task OnInitializedAsync()
|
|
141
|
+
{
|
|
142
|
+
await LoadDataAsync();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async Task LoadDataAsync()
|
|
146
|
+
{
|
|
147
|
+
_isLoading = true;
|
|
148
|
+
_error = null;
|
|
149
|
+
|
|
150
|
+
try
|
|
151
|
+
{
|
|
152
|
+
_items = await {Feature}Service.GetAllAsync();
|
|
153
|
+
}
|
|
154
|
+
catch (Exception ex)
|
|
155
|
+
{
|
|
156
|
+
Logger.LogError(ex, "Failed to load {feature}s");
|
|
157
|
+
_error = "Failed to load data. Please try again.";
|
|
158
|
+
}
|
|
159
|
+
finally
|
|
160
|
+
{
|
|
161
|
+
_isLoading = false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private void ShowCreateModal()
|
|
166
|
+
{
|
|
167
|
+
_editingItem = null;
|
|
168
|
+
_formModel = new {Feature}FormModel();
|
|
169
|
+
_showModal = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private void ShowEditModal({Feature}Dto item)
|
|
173
|
+
{
|
|
174
|
+
_editingItem = item;
|
|
175
|
+
_formModel = new {Feature}FormModel
|
|
176
|
+
{
|
|
177
|
+
Name = item.Name
|
|
178
|
+
// Map other fields
|
|
179
|
+
};
|
|
180
|
+
_showModal = true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private void CloseModal()
|
|
184
|
+
{
|
|
185
|
+
_showModal = false;
|
|
186
|
+
_formModel = new();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private async Task HandleSubmitAsync()
|
|
190
|
+
{
|
|
191
|
+
_isSaving = true;
|
|
192
|
+
|
|
193
|
+
try
|
|
194
|
+
{
|
|
195
|
+
if (_editingItem is null)
|
|
196
|
+
{
|
|
197
|
+
// Create
|
|
198
|
+
var request = new Create{Feature}Request(_formModel.Name);
|
|
199
|
+
await {Feature}Service.CreateAsync(request);
|
|
200
|
+
}
|
|
201
|
+
else
|
|
202
|
+
{
|
|
203
|
+
// Update
|
|
204
|
+
var request = new Update{Feature}Request(_formModel.Name);
|
|
205
|
+
await {Feature}Service.UpdateAsync(_editingItem.Id, request);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
CloseModal();
|
|
209
|
+
await LoadDataAsync();
|
|
210
|
+
}
|
|
211
|
+
catch (Exception ex)
|
|
212
|
+
{
|
|
213
|
+
Logger.LogError(ex, "Failed to save {feature}");
|
|
214
|
+
// Show error to user
|
|
215
|
+
}
|
|
216
|
+
finally
|
|
217
|
+
{
|
|
218
|
+
_isSaving = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private static string GetStatusBadgeClass({Feature}Status status) => status switch
|
|
223
|
+
{
|
|
224
|
+
{Feature}Status.Active => "bg-success",
|
|
225
|
+
{Feature}Status.Pending => "bg-warning text-dark",
|
|
226
|
+
{Feature}Status.Completed => "bg-info",
|
|
227
|
+
{Feature}Status.Failed => "bg-danger",
|
|
228
|
+
_ => "bg-secondary"
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
private class {Feature}FormModel
|
|
232
|
+
{
|
|
233
|
+
[Required]
|
|
234
|
+
[StringLength(200, MinimumLength = 3)]
|
|
235
|
+
public string Name { get; set; } = string.Empty;
|
|
236
|
+
|
|
237
|
+
// Add more fields as needed
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// {{FEATURE_NAME_TITLE}} - Commands (CQRS)
|
|
3
|
+
// Generated by MORPH-SPEC Framework
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
using MediatR;
|
|
7
|
+
|
|
8
|
+
namespace {{NAMESPACE}}.Application.Features.{{FEATURE_NAME_PASCAL}}.Commands;
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Create Command
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// Command to create a new {{FEATURE_NAME_TITLE}}
|
|
16
|
+
/// </summary>
|
|
17
|
+
public record Create{{FEATURE_NAME_PASCAL}}Command : IRequest<Create{{FEATURE_NAME_PASCAL}}Result>
|
|
18
|
+
{
|
|
19
|
+
// TODO: Add required properties
|
|
20
|
+
// public required string Name { get; init; }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public record Create{{FEATURE_NAME_PASCAL}}Result
|
|
24
|
+
{
|
|
25
|
+
public bool IsSuccess { get; init; }
|
|
26
|
+
public int? Id { get; init; }
|
|
27
|
+
public string? Error { get; init; }
|
|
28
|
+
|
|
29
|
+
public static Create{{FEATURE_NAME_PASCAL}}Result Success(int id) => new() { IsSuccess = true, Id = id };
|
|
30
|
+
public static Create{{FEATURE_NAME_PASCAL}}Result Failure(string error) => new() { IsSuccess = false, Error = error };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Update Command
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
/// <summary>
|
|
38
|
+
/// Command to update an existing {{FEATURE_NAME_TITLE}}
|
|
39
|
+
/// </summary>
|
|
40
|
+
public record Update{{FEATURE_NAME_PASCAL}}Command : IRequest<Update{{FEATURE_NAME_PASCAL}}Result>
|
|
41
|
+
{
|
|
42
|
+
public required int Id { get; init; }
|
|
43
|
+
// TODO: Add updatable properties
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public record Update{{FEATURE_NAME_PASCAL}}Result
|
|
47
|
+
{
|
|
48
|
+
public bool IsSuccess { get; init; }
|
|
49
|
+
public string? Error { get; init; }
|
|
50
|
+
|
|
51
|
+
public static Update{{FEATURE_NAME_PASCAL}}Result Success() => new() { IsSuccess = true };
|
|
52
|
+
public static Update{{FEATURE_NAME_PASCAL}}Result Failure(string error) => new() { IsSuccess = false, Error = error };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Delete Command
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/// <summary>
|
|
60
|
+
/// Command to delete a {{FEATURE_NAME_TITLE}}
|
|
61
|
+
/// </summary>
|
|
62
|
+
public record Delete{{FEATURE_NAME_PASCAL}}Command : IRequest<Delete{{FEATURE_NAME_PASCAL}}Result>
|
|
63
|
+
{
|
|
64
|
+
public required int Id { get; init; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public record Delete{{FEATURE_NAME_PASCAL}}Result
|
|
68
|
+
{
|
|
69
|
+
public bool IsSuccess { get; init; }
|
|
70
|
+
public string? Error { get; init; }
|
|
71
|
+
|
|
72
|
+
public static Delete{{FEATURE_NAME_PASCAL}}Result Success() => new() { IsSuccess = true };
|
|
73
|
+
public static Delete{{FEATURE_NAME_PASCAL}}Result Failure(string error) => new() { IsSuccess = false, Error = error };
|
|
74
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// {{FEATURE_NAME_TITLE}} - Entity Interfaces
|
|
3
|
+
// Generated by MORPH-SPEC Framework
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
namespace {{NAMESPACE}}.Domain.Entities;
|
|
7
|
+
|
|
8
|
+
/// <summary>
|
|
9
|
+
/// Base interface for {{FEATURE_NAME_TITLE}} entity
|
|
10
|
+
/// </summary>
|
|
11
|
+
public interface I{{FEATURE_NAME_PASCAL}}Entity
|
|
12
|
+
{
|
|
13
|
+
int Id { get; }
|
|
14
|
+
DateTime CreatedAt { get; }
|
|
15
|
+
DateTime? UpdatedAt { get; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// TODO: Add specific entity interfaces below
|
|
19
|
+
// Example:
|
|
20
|
+
// public interface IScheduledReport : I{{FEATURE_NAME_PASCAL}}Entity
|
|
21
|
+
// {
|
|
22
|
+
// string Name { get; }
|
|
23
|
+
// ReportType Type { get; }
|
|
24
|
+
// string CronExpression { get; }
|
|
25
|
+
// }
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// {{FEATURE_NAME_TITLE}} - Queries (CQRS)
|
|
3
|
+
// Generated by MORPH-SPEC Framework
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
using MediatR;
|
|
7
|
+
|
|
8
|
+
namespace {{NAMESPACE}}.Application.Features.{{FEATURE_NAME_PASCAL}}.Queries;
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Get By Id Query
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// Query to get a {{FEATURE_NAME_TITLE}} by ID
|
|
16
|
+
/// </summary>
|
|
17
|
+
public record Get{{FEATURE_NAME_PASCAL}}ByIdQuery : IRequest<{{FEATURE_NAME_PASCAL}}Dto?>
|
|
18
|
+
{
|
|
19
|
+
public required int Id { get; init; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Get All Query (with pagination)
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
/// <summary>
|
|
27
|
+
/// Query to get all {{FEATURE_NAME_TITLE}} with pagination
|
|
28
|
+
/// </summary>
|
|
29
|
+
public record Get{{FEATURE_NAME_PASCAL}}ListQuery : IRequest<{{FEATURE_NAME_PASCAL}}ListResult>
|
|
30
|
+
{
|
|
31
|
+
public int Page { get; init; } = 1;
|
|
32
|
+
public int PageSize { get; init; } = 10;
|
|
33
|
+
public string? SearchTerm { get; init; }
|
|
34
|
+
public string? SortBy { get; init; }
|
|
35
|
+
public bool SortDescending { get; init; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public record {{FEATURE_NAME_PASCAL}}ListResult
|
|
39
|
+
{
|
|
40
|
+
public required IReadOnlyList<{{FEATURE_NAME_PASCAL}}Dto> Items { get; init; }
|
|
41
|
+
public int TotalCount { get; init; }
|
|
42
|
+
public int Page { get; init; }
|
|
43
|
+
public int PageSize { get; init; }
|
|
44
|
+
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
|
|
45
|
+
public bool HasNextPage => Page < TotalPages;
|
|
46
|
+
public bool HasPreviousPage => Page > 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// DTOs
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/// <summary>
|
|
54
|
+
/// DTO for {{FEATURE_NAME_TITLE}}
|
|
55
|
+
/// </summary>
|
|
56
|
+
public record {{FEATURE_NAME_PASCAL}}Dto
|
|
57
|
+
{
|
|
58
|
+
public int Id { get; init; }
|
|
59
|
+
public DateTime CreatedAt { get; init; }
|
|
60
|
+
public DateTime? UpdatedAt { get; init; }
|
|
61
|
+
|
|
62
|
+
// TODO: Add mapped properties from entity
|
|
63
|
+
// public string Name { get; init; } = string.Empty;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// <summary>
|
|
67
|
+
/// Summary DTO for lists (less data)
|
|
68
|
+
/// </summary>
|
|
69
|
+
public record {{FEATURE_NAME_PASCAL}}SummaryDto
|
|
70
|
+
{
|
|
71
|
+
public int Id { get; init; }
|
|
72
|
+
// TODO: Add only essential properties for list display
|
|
73
|
+
// public string Name { get; init; } = string.Empty;
|
|
74
|
+
}
|