From dc9a60a7a22f15edde3c7dd2c8a2018c038e7d25 Mon Sep 17 00:00:00 2001 From: SysAdmin Date: Sun, 22 Feb 2026 23:55:08 +0000 Subject: [PATCH] feat(developers): simplify timezone dropdown and make email optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 100+ raw system timezones with curated list of 26 major zones with browser auto-detection via Intl API. Remove email requirement since applicants receive a @silverlabs.uk address — fallback to username@silverlabs.uk when no personal email is provided. Co-Authored-By: Claude Opus 4.6 --- BlazorApp/Components/Pages/Developers.razor | 57 +++++++++++++++++-- BlazorApp/Models/DeveloperApplication.cs | 3 +- .../Services/DeveloperApplicationService.cs | 25 +++++--- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/BlazorApp/Components/Pages/Developers.razor b/BlazorApp/Components/Pages/Developers.razor index a1c6df2..875e74d 100644 --- a/BlazorApp/Components/Pages/Developers.razor +++ b/BlazorApp/Components/Pages/Developers.razor @@ -2,6 +2,7 @@ @using SilverLabs.Website.Models @using SilverLabs.Website.Services @inject DeveloperApplicationService ApplicationService +@inject IJSRuntime JS @rendermode InteractiveServer Join the Team - SilverLabs @@ -85,8 +86,9 @@
- + + Leave blank to use your @@silverlabs.uk address
@@ -256,10 +258,35 @@ private readonly string[] _availablePlatforms = { "Windows", "macOS", "Linux", "Android", "iOS", "Other" }; - private static readonly List<(string Id, string Label)> _timezones = TimeZoneInfo.GetSystemTimeZones() - .OrderBy(tz => tz.BaseUtcOffset) - .Select(tz => (tz.Id, $"(UTC{(tz.BaseUtcOffset >= TimeSpan.Zero ? "+" : "")}{tz.BaseUtcOffset:hh\\:mm}) {tz.DisplayName}")) - .ToList(); + private static readonly List<(string Id, string Label)> _timezones = new() + { + ("Pacific/Midway", "(UTC-11:00) Midway Island"), + ("Pacific/Honolulu", "(UTC-10:00) Hawaii"), + ("America/Anchorage", "(UTC-09:00) Alaska"), + ("America/Los_Angeles", "(UTC-08:00) Pacific Time (US & Canada)"), + ("America/Denver", "(UTC-07:00) Mountain Time (US & Canada)"), + ("America/Chicago", "(UTC-06:00) Central Time (US & Canada)"), + ("America/New_York", "(UTC-05:00) Eastern Time (US & Canada)"), + ("America/Caracas", "(UTC-04:00) Venezuela"), + ("America/Halifax", "(UTC-04:00) Atlantic Time (Canada)"), + ("America/Sao_Paulo", "(UTC-03:00) Brazil"), + ("Atlantic/South_Georgia","(UTC-02:00) Mid-Atlantic"), + ("Atlantic/Azores", "(UTC-01:00) Azores"), + ("Europe/London", "(UTC+00:00) London, Dublin, Lisbon"), + ("Europe/Berlin", "(UTC+01:00) Berlin, Paris, Amsterdam"), + ("Europe/Bucharest", "(UTC+02:00) Bucharest, Helsinki, Athens"), + ("Europe/Moscow", "(UTC+03:00) Moscow, Istanbul"), + ("Asia/Dubai", "(UTC+04:00) Dubai, Baku"), + ("Asia/Karachi", "(UTC+05:00) Karachi, Tashkent"), + ("Asia/Kolkata", "(UTC+05:30) Mumbai, New Delhi"), + ("Asia/Dhaka", "(UTC+06:00) Dhaka, Almaty"), + ("Asia/Bangkok", "(UTC+07:00) Bangkok, Jakarta"), + ("Asia/Shanghai", "(UTC+08:00) Beijing, Singapore, Perth"), + ("Asia/Tokyo", "(UTC+09:00) Tokyo, Seoul"), + ("Australia/Sydney", "(UTC+10:00) Sydney, Melbourne"), + ("Pacific/Noumea", "(UTC+11:00) Solomon Islands"), + ("Pacific/Auckland", "(UTC+12:00) Auckland, Fiji"), + }; private static readonly System.Text.RegularExpressions.Regex UsernamePattern = new(@"^[a-zA-Z0-9_-]{3,30}$", System.Text.RegularExpressions.RegexOptions.Compiled); @@ -269,6 +296,26 @@ private bool IsSubmitDisabled => _submitting || _usernameCheckState == UsernameCheckState.Taken || _usernameCheckState == UsernameCheckState.Checking; + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender && string.IsNullOrEmpty(_application.Timezone)) + { + try + { + var detectedTz = await JS.InvokeAsync("eval", "Intl.DateTimeFormat().resolvedOptions().timeZone"); + if (!string.IsNullOrEmpty(detectedTz) && _timezones.Any(tz => tz.Id == detectedTz)) + { + _application.Timezone = detectedTz; + StateHasChanged(); + } + } + catch + { + // Browser may not support Intl API — ignore + } + } + } + private void SelectRole(ApplicationRole role) { _application.Role = role; diff --git a/BlazorApp/Models/DeveloperApplication.cs b/BlazorApp/Models/DeveloperApplication.cs index 3f51768..db5d99a 100644 --- a/BlazorApp/Models/DeveloperApplication.cs +++ b/BlazorApp/Models/DeveloperApplication.cs @@ -8,9 +8,8 @@ public class DeveloperApplication [StringLength(100, MinimumLength = 2)] public string FullName { get; set; } = string.Empty; - [Required(ErrorMessage = "Email is required")] [EmailAddress(ErrorMessage = "Invalid email address")] - public string Email { get; set; } = string.Empty; + public string? Email { get; set; } [Required(ErrorMessage = "Username is required")] [RegularExpression(@"^[a-zA-Z0-9_-]{3,30}$", ErrorMessage = "Username must be 3-30 characters, letters, numbers, hyphens and underscores only")] diff --git a/BlazorApp/Services/DeveloperApplicationService.cs b/BlazorApp/Services/DeveloperApplicationService.cs index 8455dae..298b646 100644 --- a/BlazorApp/Services/DeveloperApplicationService.cs +++ b/BlazorApp/Services/DeveloperApplicationService.cs @@ -48,11 +48,16 @@ public class DeveloperApplicationService { try { + // Use silverlabs.uk address when no personal email provided + var effectiveEmail = string.IsNullOrWhiteSpace(application.Email) + ? $"{application.DesiredUsername}@silverlabs.uk" + : application.Email.Trim(); + // 1. Register user on SilverDESK var registerPayload = new { username = application.DesiredUsername, - email = application.Email, + email = effectiveEmail, password = application.Password, fullName = application.FullName }; @@ -111,7 +116,7 @@ public class DeveloperApplicationService userId, ticketId, fullName = application.FullName, - email = application.Email, + email = effectiveEmail, desiredUsername = application.DesiredUsername, timezone = application.Timezone, appliedRole = application.Role.ToString(), @@ -126,29 +131,29 @@ public class DeveloperApplicationService if (appResponse.IsSuccessStatusCode) { - _logger.LogInformation("DeveloperApplication record created for {Email}", application.Email); + _logger.LogInformation("DeveloperApplication record created for {Email}", effectiveEmail); } else { var appError = await appResponse.Content.ReadAsStringAsync(); _logger.LogWarning("Failed to create DeveloperApplication record for {Email}: {StatusCode} - {Body}", - application.Email, appResponse.StatusCode, appError); + effectiveEmail, appResponse.StatusCode, appError); } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to create DeveloperApplication record for {Email} — user and ticket were created successfully", - application.Email); + effectiveEmail); } _logger.LogInformation("Developer application submitted for {Email} as {Role} — user registered and ticket created", - application.Email, application.Role); + effectiveEmail, application.Role); return (true, "Application submitted successfully! Your SilverDESK account has been created.", token); } catch (Exception ex) { - _logger.LogError(ex, "Error submitting developer application for {Email}", application.Email); + _logger.LogError(ex, "Error submitting developer application for {Username}", application.DesiredUsername); return (false, "Unable to connect to the application service. Please try again later.", null); } } @@ -177,12 +182,16 @@ public class DeveloperApplicationService private static string FormatTicketBody(DeveloperApplication app) { + var effectiveEmail = string.IsNullOrWhiteSpace(app.Email) + ? $"{app.DesiredUsername}@silverlabs.uk" + : app.Email.Trim(); + var sb = new StringBuilder(); sb.AppendLine("## Developer Program Application"); sb.AppendLine(); sb.AppendLine($"**Role:** {app.Role}"); sb.AppendLine($"**Full Name:** {app.FullName}"); - sb.AppendLine($"**Email:** {app.Email}"); + sb.AppendLine($"**Email:** {effectiveEmail}"); sb.AppendLine($"**Desired Username:** {app.DesiredUsername}"); sb.AppendLine($"**Timezone:** {app.Timezone}"); sb.AppendLine();