Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ Web/Resgrid.WebCore/wwwroot/lib/*
/Web/Resgrid.Web/wwwroot/lib
.dual-graph/
.claude/settings.local.json
/Web/Resgrid.Web/wwwroot/js/ng/chunks
opencode.json
.dual-graph-pro/
.mcp.json
/.codex
/.claude
/Web/Resgrid.Web/wwwroot/js/ng
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.ar.resx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
<data name="VerificationSuccessful" xml:space="preserve"><value>نجح التحقق.</value></data>
<data name="VerificationFailed" xml:space="preserve"><value>فشل التحقق. يُرجى التحقق من الرمز والمحاولة مجدداً.</value></data>
<data name="VerificationRateLimited" xml:space="preserve"><value>محاولات تحقق كثيرة جداً. يُرجى المحاولة لاحقاً.</value></data>
<data name="VerificationContactNotConfigured" xml:space="preserve"><value>طريقة الاتصال المحددة غير مهيأة.</value></data>
<data name="VerificationInvalidContact" xml:space="preserve"><value>طريقة الاتصال المحددة غير صالحة لإرسال رمز التحقق.</value></data>
<data name="VerificationDeliveryFailed" xml:space="preserve"><value>تعذر إرسال رمز التحقق. يُرجى المحاولة مرة أخرى.</value></data>
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>متطلبات كلمة المرور</value></data>
<data name="PasswordRequirementsLength" xml:space="preserve"><value>على الأقل {0} أحرف</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>رقم واحد على الأقل</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Passwortanforderungen</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Password Requirements</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.es.resx
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Demasiados intentos de verificación. Por favor inténtelo más tarde.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>El método de contacto seleccionado no está configurado.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>El método de contacto seleccionado no es válido para recibir la verificación.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>No se pudo enviar el código de verificación. Por favor, inténtelo de nuevo.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Requisitos de contraseña</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.fr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Exigences du mot de passe</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.it.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Requisiti della password</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.pl.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Wymagania dotyczące hasła</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.sv.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Lösenordskrav</value></data>
Expand Down
9 changes: 9 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Home/EditProfile.uk.resx
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<data name="VerificationRateLimited" xml:space="preserve">
<value>Too many verification attempts. Please try again later.</value>
</data>
<data name="VerificationContactNotConfigured" xml:space="preserve">
<value>The selected contact method is not configured.</value>
</data>
<data name="VerificationInvalidContact" xml:space="preserve">
<value>The selected contact method is not valid for verification delivery.</value>
</data>
<data name="VerificationDeliveryFailed" xml:space="preserve">
<value>Unable to deliver the verification code. Please try again.</value>
</data>

<!-- Password requirements box -->
<data name="PasswordRequirementsHeader" xml:space="preserve"><value>Вимоги до пароля</value></data>
Expand Down
14 changes: 14 additions & 0 deletions Core/Resgrid.Model/ContactVerificationSendStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Resgrid.Model
{
/// <summary>
/// Describes the outcome of a request to send a contact verification code.
/// </summary>
public enum ContactVerificationSendStatus
{
Sent = 0,
ContactNotConfigured = 1,
InvalidContact = 2,
RateLimited = 3,
DeliveryFailed = 4
}
}
15 changes: 6 additions & 9 deletions Core/Resgrid.Model/Services/IContactVerificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,22 @@ public interface IContactVerificationService
{
/// <summary>
/// Generates a verification code and sends it to the user's email address.
/// Returns <c>false</c> if the user has no email, if rate limits are exceeded,
/// or if the send fails.
/// Returns a status describing whether the code was sent or why it could not be sent.
/// </summary>
Task<bool> SendEmailVerificationCodeAsync(string userId, int departmentId, CancellationToken cancellationToken = default);
Task<ContactVerificationSendStatus> SendEmailVerificationCodeAsync(string userId, int departmentId, CancellationToken cancellationToken = default);

/// <summary>
/// Generates a verification code and sends it via SMS to the user's mobile number.
/// Returns <c>false</c> if the user has no mobile number, if rate limits are exceeded,
/// or if the send fails.
/// Returns a status describing whether the code was sent or why it could not be sent.
/// </summary>
Task<bool> SendMobileVerificationCodeAsync(string userId, int departmentId, string departmentNumber, CancellationToken cancellationToken = default);
Task<ContactVerificationSendStatus> SendMobileVerificationCodeAsync(string userId, int departmentId, string departmentNumber, CancellationToken cancellationToken = default);

/// <summary>
/// Generates a verification code and delivers it via a Twilio voice call to the
/// user's home number, speaking the digits and repeating multiple times.
/// Returns <c>false</c> if the user has no home number, if rate limits are exceeded,
/// or if the call fails.
/// Returns a status describing whether the call was placed or why it could not be placed.
/// </summary>
Task<bool> SendHomeVerificationCodeAsync(string userId, int departmentId, string departmentNumber, CancellationToken cancellationToken = default);
Task<ContactVerificationSendStatus> SendHomeVerificationCodeAsync(string userId, int departmentId, string departmentNumber, CancellationToken cancellationToken = default);

/// <summary>
/// Validates the supplied <paramref name="code"/> against the stored code for the given
Expand Down
20 changes: 20 additions & 0 deletions Core/Resgrid.Model/UserProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,26 @@ public class UserProfile: IEntity
[ProtoMember(53)]
public bool EnableModernApplicationSounds { get; set; }

// ── Verification send-window tracking (separate from the daily confirm-attempt counters) ──

[ProtoMember(54)]
public int EmailVerificationSendCount { get; set; }

[ProtoMember(55)]
public int MobileVerificationSendCount { get; set; }

[ProtoMember(56)]
public int HomeVerificationSendCount { get; set; }

[ProtoMember(57)]
public DateTime? EmailVerificationSendWindowStart { get; set; }

[ProtoMember(58)]
public DateTime? MobileVerificationSendWindowStart { get; set; }

[ProtoMember(59)]
public DateTime? HomeVerificationSendWindowStart { get; set; }

[NotMapped]
[JsonIgnore]
public object IdValue
Expand Down
Loading
Loading