Skip to content

Commit 79fe178

Browse files
Merge pull request #446 from TransactionProcessing/copilot/add-contract-screens-features
Add contract management screens with product and transaction fee support
2 parents 3978bf5 + 2639100 commit 79fe178

6 files changed

Lines changed: 1084 additions & 14 deletions

File tree

EstateManagementUI.BlazorServer/Components/Pages/Contracts/Edit.razor

Lines changed: 637 additions & 0 deletions
Large diffs are not rendered by default.

EstateManagementUI.BlazorServer/Components/Pages/Contracts/Index.razor

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@page "/contracts"
2+
@rendermode InteractiveServer
23
@inject IMediator Mediator
34
@inject NavigationManager NavigationManager
45

@@ -28,7 +29,7 @@
2829
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
2930
@foreach (var contract in contracts)
3031
{
31-
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow cursor-pointer" @onclick='() => NavigationManager.NavigateTo($"/contracts/{contract.ContractId}")'>
32+
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
3233
<div class="flex items-start justify-between mb-4">
3334
<div class="w-12 h-12 bg-purple-600 text-white rounded-lg flex items-center justify-center">
3435
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -41,13 +42,25 @@
4142
<p class="text-sm text-gray-600 mb-4">
4243
<span class="font-medium">Operator:</span> @contract.OperatorName
4344
</p>
44-
<div class="flex items-center justify-between">
45+
<div class="flex items-center justify-between mb-4">
4546
<span class="text-sm text-gray-600">
4647
@(contract.Products?.Count ?? 0) Product(s)
4748
</span>
48-
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
49-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
50-
</svg>
49+
</div>
50+
<div class="flex items-center justify-end space-x-2 pt-4 border-t">
51+
<button class="btn btn-secondary btn-sm" @onclick='() => NavigationManager.NavigateTo($"/contracts/{contract.ContractId}")' title="View">
52+
<svg class="w-4 h-4 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
53+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
54+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
55+
</svg>
56+
View
57+
</button>
58+
<button class="btn btn-primary btn-sm" @onclick='() => NavigationManager.NavigateTo($"/contracts/{contract.ContractId}/edit")' title="Edit">
59+
<svg class="w-4 h-4 inline-block mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
60+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
61+
</svg>
62+
Edit
63+
</button>
5164
</div>
5265
</div>
5366
}
@@ -73,11 +86,7 @@
7386
{
7487
try
7588
{
76-
var correlationId = new CorrelationId(Guid.NewGuid());
77-
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
78-
var accessToken = "stubbed-token";
79-
80-
var result = await Mediator.Send(new Queries.GetContractsQuery(correlationId, accessToken, estateId));
89+
var result = await Mediator.Send(new Queries.GetContractsQuery(CorrelationIdHelper.New(), "stubbed-token", Guid.Parse("11111111-1111-1111-1111-111111111111")));
8190
if (result.IsSuccess)
8291
{
8392
contracts = result.Data;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
@page "/contracts/new"
2+
@rendermode InteractiveServer
3+
@using System.ComponentModel.DataAnnotations
4+
@inject IMediator Mediator
5+
@inject NavigationManager NavigationManager
6+
7+
<PageTitle>Create New Contract</PageTitle>
8+
9+
<div class="space-y-6">
10+
<!-- Page Header -->
11+
<div class="flex items-center justify-between">
12+
<div>
13+
<h1 class="text-2xl font-bold text-gray-900">Create New Contract</h1>
14+
<p class="text-gray-600 mt-1">Add a new contract to the estate</p>
15+
</div>
16+
<button class="btn btn-secondary" @onclick="Cancel">
17+
Cancel
18+
</button>
19+
</div>
20+
21+
@if (!string.IsNullOrWhiteSpace(errorMessage))
22+
{
23+
<div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
24+
<p class="font-medium">Error</p>
25+
<p class="text-sm">@errorMessage</p>
26+
</div>
27+
}
28+
29+
<!-- Form -->
30+
<div class="bg-white rounded-lg shadow-md p-6">
31+
<EditForm Model="@model" OnValidSubmit="@HandleSubmit">
32+
<DataAnnotationsValidator />
33+
34+
<!-- Contract Details Section -->
35+
<div class="mb-6">
36+
<h3 class="text-lg font-semibold text-gray-900 mb-4">Contract Details</h3>
37+
<div class="space-y-4">
38+
<div>
39+
<label class="block text-sm font-medium text-gray-700 mb-1">
40+
Description <span class="text-red-500">*</span>
41+
</label>
42+
<InputText @bind-Value="model.Description" class="input w-full" placeholder="Enter contract description" />
43+
<ValidationMessage For="@(() => model.Description)" class="text-red-600 text-sm mt-1" />
44+
</div>
45+
46+
<div>
47+
<label class="block text-sm font-medium text-gray-700 mb-1">
48+
Operator <span class="text-red-500">*</span>
49+
</label>
50+
@if (isLoadingOperators)
51+
{
52+
<div class="text-sm text-gray-500">Loading operators...</div>
53+
}
54+
else if (operators != null && operators.Any())
55+
{
56+
<InputSelect @bind-Value="model.OperatorId" class="input w-full">
57+
<option value="">-- Select Operator --</option>
58+
@foreach (var op in operators)
59+
{
60+
<option value="@op.OperatorId">@op.Name</option>
61+
}
62+
</InputSelect>
63+
<ValidationMessage For="@(() => model.OperatorId)" class="text-red-600 text-sm mt-1" />
64+
}
65+
else
66+
{
67+
<p class="text-sm text-red-600">No operators available. Please create an operator first.</p>
68+
}
69+
</div>
70+
</div>
71+
</div>
72+
73+
<!-- Submit Button -->
74+
<div class="flex justify-end space-x-4 pt-4 border-t">
75+
<button type="button" class="btn btn-secondary" @onclick="Cancel" disabled="@isSaving">
76+
Cancel
77+
</button>
78+
<button type="submit" class="btn btn-primary" disabled="@isSaving">
79+
@if (isSaving)
80+
{
81+
<span class="inline-block animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></span>
82+
<span>Saving...</span>
83+
}
84+
else
85+
{
86+
<span>Create Contract</span>
87+
}
88+
</button>
89+
</div>
90+
</EditForm>
91+
</div>
92+
</div>
93+
94+
@code {
95+
private CreateContractFormModel model = new();
96+
private bool isSaving = false;
97+
private bool isLoadingOperators = true;
98+
private string? errorMessage;
99+
private List<OperatorModel>? operators;
100+
101+
protected override async Task OnInitializedAsync()
102+
{
103+
await LoadOperators();
104+
}
105+
106+
private async Task LoadOperators()
107+
{
108+
try
109+
{
110+
isLoadingOperators = true;
111+
var result = await Mediator.Send(new Queries.GetOperatorsQuery(CorrelationIdHelper.New(), "stubbed-token", Guid.Parse("11111111-1111-1111-1111-111111111111")));
112+
if (result.IsSuccess)
113+
{
114+
operators = result.Data;
115+
}
116+
}
117+
finally
118+
{
119+
isLoadingOperators = false;
120+
}
121+
}
122+
123+
private async Task HandleSubmit()
124+
{
125+
isSaving = true;
126+
errorMessage = null;
127+
128+
try
129+
{
130+
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
131+
var accessToken = "stubbed-token";
132+
133+
// Create contract
134+
var createCommand = new Commands.CreateContractCommand(
135+
CorrelationIdHelper.New(),
136+
accessToken,
137+
estateId,
138+
model.Description!,
139+
Guid.Parse(model.OperatorId!)
140+
);
141+
142+
var createResult = await Mediator.Send(createCommand);
143+
144+
if (!createResult.IsSuccess)
145+
{
146+
errorMessage = createResult.Message ?? "Failed to create contract";
147+
return;
148+
}
149+
150+
// Navigate to contracts list with success
151+
NavigationManager.NavigateTo("/contracts");
152+
}
153+
catch (Exception ex)
154+
{
155+
errorMessage = $"An error occurred: {ex.Message}";
156+
}
157+
finally
158+
{
159+
isSaving = false;
160+
}
161+
}
162+
163+
private void Cancel()
164+
{
165+
NavigationManager.NavigateTo("/contracts");
166+
}
167+
168+
public class CreateContractFormModel
169+
{
170+
[Required(ErrorMessage = "Description is required")]
171+
public string? Description { get; set; }
172+
173+
[Required(ErrorMessage = "Operator is required")]
174+
public string? OperatorId { get; set; }
175+
}
176+
}

0 commit comments

Comments
 (0)