Skip to content

Commit ed2d491

Browse files
Tom BrewerTom Brewer
authored andcommitted
feat: ability to update solution types
1 parent 5df5a16 commit ed2d491

File tree

3 files changed

+177
-3
lines changed

3 files changed

+177
-3
lines changed

Apollo.Components/Solutions/SolutionExplorerTab.razor

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,29 @@
99
@implements IDisposable
1010
<div class="pa-4 mud-height-full">
1111
<MudStack>
12-
<MudStack Row="true">
12+
<MudStack Row="true" AlignItems="AlignItems.Center">
1313
<MudSelect T="SolutionModel?" Label="Solution" AnchorOrigin="Origin.BottomCenter" Value="@State?.Project" ValueChanged="HandleProjectChange" Class="flex-grow-1">
1414
@foreach (var solution in State?.Solutions)
1515
{
1616
bool selected = State.HasActiveSolution && solution.Name.Equals(State.Project.Name, StringComparison.OrdinalIgnoreCase);
1717
<MudSelectItem Class="@(selected ? "mud-secondary-text" : "")" Value="@solution">@solution.Name</MudSelectItem>
1818
}
1919
</MudSelect>
20-
<MudMenu ActivationEvent="@MouseEvent.RightClick" Dense="true" Class="d-flex" ListClass="mw-160">
20+
<ApolloIconButton Size="Size.Small" Tooltip="Edit Solution Properties" Icon="@ApolloIcons.Edit" OnClick="@PromptSolutionProperties" Disabled="@(!(State?.HasActiveSolution ?? false))" />
21+
<MudMenu Dense="true" ListClass="mw-160">
2122
<ActivatorContent>
22-
<MudIconButton Icon="@ApolloIcons.Add" OnClick="@AddFile">Add</MudIconButton>
23+
<ApolloIconButton Size="Size.Small" Icon="@Icons.Material.TwoTone.MoreVert" />
2324
</ActivatorContent>
2425
<ChildContent>
2526
<FileMenuItem Name="Add Solution"
2627
Icon="@ApolloIcons.Solution"
2728
OnClick="@PromptAddSolution" />
29+
30+
<FileMenuItem Name="Edit Properties"
31+
Icon="@ApolloIcons.Edit"
32+
OnClick="@PromptSolutionProperties"
33+
Disabled="@(!(State?.HasActiveSolution ?? false))"/>
34+
2835
<MudDivider />
2936
<FileMenuItem Name="Add File" Icon="@ApolloIcons.FileAdd" OnClick="@PromptAddFile"/>
3037
<FileMenuItem Name="Add Folder" Icon="@ApolloIcons.CreateFolder" OnClick="@AddFolder"/>
@@ -199,4 +206,24 @@
199206
var dialog = await DialogService.ShowAsync<NewSolutionDialog>("Create New Solution", options);
200207
var result = await dialog.Result;
201208
}
209+
210+
private async Task PromptSolutionProperties()
211+
{
212+
if (!State.HasActiveSolution)
213+
return;
214+
215+
var parameters = new DialogParameters
216+
{
217+
{"Solution", State.Project}
218+
};
219+
220+
var options = new DialogOptions
221+
{
222+
MaxWidth = MaxWidth.Medium,
223+
CloseOnEscapeKey = true
224+
};
225+
226+
var dialog = await DialogService.ShowAsync<SolutionPropertiesDialog>("Solution Properties", parameters, options);
227+
var result = await dialog.Result;
228+
}
202229
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
@using Apollo.Contracts.Solutions
2+
@using Apollo.Components.Shared
3+
4+
<MudDialog Class="pa-4">
5+
<TitleContent>
6+
Solution Properties
7+
</TitleContent>
8+
<DialogContent>
9+
<MudForm Class="pa-4" Style="min-width: 500px" @ref="_form">
10+
<MudTextField @ref="_nameField"
11+
@bind-Value="_name"
12+
Label="Solution Name"
13+
Error="@_nameError"
14+
ErrorText="@_nameErrorMessage"
15+
OnlyValidateIfDirty="true"
16+
Validation="@(new Func<string, bool>(ValidateName))"/>
17+
18+
<MudTextField @bind-Value="_description"
19+
Label="Description"
20+
Lines="3"
21+
Counter="200"
22+
MaxLength="200"/>
23+
24+
<MudSelect T="ProjectType"
25+
@bind-Value="_projectType"
26+
Label="Project Type">
27+
<MudSelectItem Value="@ProjectType.Console">Console Application</MudSelectItem>
28+
<MudSelectItem Value="@ProjectType.WebApi">Web API</MudSelectItem>
29+
<MudSelectItem Value="@ProjectType.ClassLibrary">Class Library</MudSelectItem>
30+
<MudSelectItem Value="@ProjectType.RazorClassLibrary">Razor Class Library</MudSelectItem>
31+
</MudSelect>
32+
</MudForm>
33+
</DialogContent>
34+
<DialogActions>
35+
<MudButton OnClick="Cancel">Cancel</MudButton>
36+
<PrimaryActionButton OnClick="Submit" Text="Save" />
37+
</DialogActions>
38+
</MudDialog>
39+
40+
@code {
41+
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; }
42+
[Inject] private SolutionsState State { get; set; }
43+
44+
[Parameter] public SolutionModel Solution { get; set; } = default!;
45+
46+
private MudTextField<string> _nameField = default!;
47+
private MudForm _form = default!;
48+
private string _name = "";
49+
private string _description = "";
50+
private ProjectType _projectType = ProjectType.Console;
51+
private bool _nameError;
52+
private string _nameErrorMessage = "";
53+
54+
protected override void OnInitialized()
55+
{
56+
if (Solution != null)
57+
{
58+
_name = Solution.Name;
59+
_description = Solution.Description ?? "";
60+
_projectType = Solution.ProjectType;
61+
}
62+
}
63+
64+
protected override async Task OnAfterRenderAsync(bool firstRender)
65+
{
66+
if (firstRender)
67+
{
68+
await Task.Delay(50);
69+
await _nameField.FocusAsync();
70+
}
71+
}
72+
73+
private async Task Submit()
74+
{
75+
await _form.Validate();
76+
77+
if (!_form.IsValid)
78+
{
79+
StateHasChanged();
80+
return;
81+
}
82+
83+
await State.UpdateSolutionPropertiesAsync(Solution, _name, _description, _projectType);
84+
MudDialog.Close(DialogResult.Ok(true));
85+
}
86+
87+
private bool ValidateName(string name)
88+
{
89+
var isDuplicate = State.Solutions.Any(s =>
90+
s.Name.Equals(name, StringComparison.OrdinalIgnoreCase) &&
91+
!s.Name.Equals(Solution.Name, StringComparison.OrdinalIgnoreCase));
92+
93+
_nameError = string.IsNullOrWhiteSpace(name) || isDuplicate;
94+
95+
_nameErrorMessage = _nameError ?
96+
(string.IsNullOrWhiteSpace(name) ? "Name is required" : "Solution name already exists")
97+
: "";
98+
99+
return !_nameError;
100+
}
101+
102+
private void Cancel() => MudDialog.Cancel();
103+
}
104+

Apollo.Components/Solutions/SolutionsState.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,49 @@ public void MoveFile(SolutionFile file, Folder destinationFolder)
418418
SolutionFilesChanged?.Invoke();
419419
}
420420

421+
public async Task UpdateSolutionPropertiesAsync(SolutionModel solution, string name, string? description, ProjectType projectType)
422+
{
423+
var oldName = solution.Name;
424+
var isCurrentProject = solution.Name.Equals(Project.Name, StringComparison.OrdinalIgnoreCase);
425+
426+
solution.Name = name;
427+
solution.Description = description;
428+
solution.ProjectType = projectType;
429+
430+
if (!oldName.Equals(name, StringComparison.OrdinalIgnoreCase))
431+
{
432+
var rootFolder = solution.GetRootFolder();
433+
rootFolder.Name = name;
434+
rootFolder.Uri = $"virtual/{name}";
435+
rootFolder.ModifiedAt = DateTimeOffset.Now;
436+
437+
foreach (var item in solution.Items.Where(i => i != rootFolder))
438+
{
439+
item.Uri = item.Uri.Replace($"virtual/{oldName}/", $"virtual/{name}/", StringComparison.OrdinalIgnoreCase);
440+
}
441+
442+
var solutionIndex = Solutions.FindIndex(s => s.Name.Equals(oldName, StringComparison.OrdinalIgnoreCase));
443+
if (solutionIndex >= 0)
444+
{
445+
Solutions[solutionIndex] = solution;
446+
}
447+
448+
if (isCurrentProject)
449+
{
450+
Project = solution;
451+
ProjectChanged?.Invoke();
452+
}
453+
}
454+
else if (isCurrentProject)
455+
{
456+
Project = solution;
457+
ProjectChanged?.Invoke();
458+
}
459+
460+
SolutionFilesChanged?.Invoke();
461+
await _solutionStorageService.SaveSolutionAsync(solution);
462+
}
463+
421464
public void CloseActiveSolution()
422465
{
423466
Project = default!;

0 commit comments

Comments
 (0)