feat(developers): overhaul signup to auto-register SilverDESK accounts
All checks were successful
Build and Deploy / deploy (push) Successful in 41s

Users now pick a password and get a SilverDESK account immediately on
submit. The form includes debounced username availability checking,
password fields with validation, and a post-submit link to SilverDESK.
The approval flow no longer creates a SilverDESK user (already exists)
and only provisions Mattermost + Mailcow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 11:03:16 +00:00
parent a4d2e571d5
commit d0785e04e1
5 changed files with 181 additions and 24 deletions

View File

@@ -28,7 +28,11 @@
</div>
<h2>Application Submitted</h2>
<p>@_resultMessage</p>
<a href="/" class="dev-btn dev-btn-secondary">Back to Home</a>
<p class="dev-account-note">Your SilverDESK account has been created. You can log in with the credentials you just chose.</p>
<div class="dev-success-actions">
<a href="https://silverdesk.silverlabs.uk" target="_blank" class="dev-btn dev-btn-primary">Track Your Application on SilverDESK</a>
<a href="/" class="dev-btn dev-btn-secondary">Back to Home</a>
</div>
</div>
}
else
@@ -88,8 +92,21 @@
<div class="form-group">
<label for="username">Desired Username</label>
<InputText id="username" @bind-Value="_application.DesiredUsername" class="form-input" placeholder="janedoe" />
<InputText id="username" @bind-Value="_application.DesiredUsername" class="form-input" placeholder="janedoe"
@oninput="OnUsernameInput" />
<span class="form-hint">This will be your handle across SilverLabs services</span>
@if (_usernameCheckState == UsernameCheckState.Checking)
{
<span class="username-status username-checking">Checking availability...</span>
}
else if (_usernameCheckState == UsernameCheckState.Available)
{
<span class="username-status username-available">&#10003; Username is available</span>
}
else if (_usernameCheckState == UsernameCheckState.Taken)
{
<span class="username-status username-taken">&#10007; Username is already taken</span>
}
<ValidationMessage For="() => _application.DesiredUsername" />
</div>
@@ -101,6 +118,26 @@
</div>
</div>
<!-- Password -->
<div class="dev-section">
<h2 class="dev-section-title">Create Your Password</h2>
<p class="dev-section-desc">This will be your password for SilverDESK and associated services.</p>
<div class="form-grid">
<div class="form-group">
<label for="password">Password</label>
<InputText id="password" type="password" @bind-Value="_application.Password" class="form-input" placeholder="Min. 8 characters" />
<span class="form-hint">Must include uppercase, lowercase, and a number</span>
<ValidationMessage For="() => _application.Password" />
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<InputText id="confirmPassword" type="password" @bind-Value="_application.ConfirmPassword" class="form-input" placeholder="Re-enter your password" />
<ValidationMessage For="() => _application.ConfirmPassword" />
</div>
</div>
</div>
<!-- Platforms -->
<div class="dev-section">
<h2 class="dev-section-title">Devices & Platforms</h2>
@@ -198,8 +235,13 @@
private string? _resultMessage;
private string? _errorMessage;
private UsernameCheckState _usernameCheckState = UsernameCheckState.None;
private CancellationTokenSource? _usernameCheckCts;
private readonly string[] _availablePlatforms = { "Windows", "macOS", "Linux", "Android", "iOS", "Other" };
private enum UsernameCheckState { None, Checking, Available, Taken }
private void SelectRole(ApplicationRole role)
{
_application.Role = role;
@@ -213,6 +255,40 @@
_application.Platforms.Add(platform);
}
private async Task OnUsernameInput(ChangeEventArgs e)
{
var username = e.Value?.ToString() ?? "";
_application.DesiredUsername = username;
_usernameCheckCts?.Cancel();
if (username.Length < 3)
{
_usernameCheckState = UsernameCheckState.None;
return;
}
_usernameCheckState = UsernameCheckState.Checking;
_usernameCheckCts = new CancellationTokenSource();
var token = _usernameCheckCts.Token;
try
{
await Task.Delay(500, token);
var available = await ApplicationService.CheckUsernameAsync(username);
if (!token.IsCancellationRequested)
{
_usernameCheckState = available ? UsernameCheckState.Available : UsernameCheckState.Taken;
StateHasChanged();
}
}
catch (TaskCanceledException)
{
// Debounce cancelled — newer keystroke took over
}
}
private async Task HandleSubmit()
{
_errorMessage = null;
@@ -220,7 +296,7 @@
try
{
var (success, message) = await ApplicationService.SubmitApplicationAsync(_application);
var (success, message, token) = await ApplicationService.SubmitApplicationAsync(_application);
if (success)
{