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
23 changes: 23 additions & 0 deletions src/Orbit.Api/Controllers/ProfileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace Orbit.Api.Controllers;
public partial class ProfileController(IMediator mediator, ILogger<ProfileController> logger) : ControllerBase
{
public record SetTimezoneRequest(string TimeZone);
public record SetNameRequest(string Name);
public record SetAiMemoryRequest([property: JsonRequired] bool Enabled);
public record SetAiSummaryRequest([property: JsonRequired] bool Enabled);
public record SetLanguageRequest(string Language);
Expand Down Expand Up @@ -59,6 +60,25 @@ public async Task<IActionResult> SetTimezone(
: BadRequest(new { error = result.Error });
}

[HttpPut("name")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> SetName(
[FromBody] SetNameRequest request,
CancellationToken cancellationToken)
{
var command = new SetNameCommand(HttpContext.GetUserId(), request.Name);
var result = await mediator.Send(command, cancellationToken);

if (result.IsSuccess)
LogNameChanged(logger, HttpContext.GetUserId());

return result.IsSuccess
? NoContent()
: BadRequest(new { error = result.Error });
}

[HttpPut("ai-memory")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
Expand Down Expand Up @@ -258,4 +278,7 @@ public async Task<IActionResult> ExportUserData(CancellationToken cancellationTo
[LoggerMessage(EventId = 8, Level = LogLevel.Information, Message = "Account reset for user {UserId}")]
private static partial void LogAccountReset(ILogger logger, Guid userId);

[LoggerMessage(EventId = 9, Level = LogLevel.Information, Message = "Display name changed for user {UserId}")]
private static partial void LogNameChanged(ILogger logger, Guid userId);

}
1 change: 1 addition & 0 deletions src/Orbit.Application/Common/AppConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static class AppConstants
public const int MaxNotificationsReturned = 50;
public const int MaxChecklistItems = 50;
public const int MaxTagNameLength = 50;
public const int MaxUserNameLength = DomainConstants.MaxUserNameLength;
public const int MaxLanguageLength = 10;
public const int MaxVerificationAttempts = 3;
public const int MaxChatMessageLength = 4000;
Expand Down
33 changes: 33 additions & 0 deletions src/Orbit.Application/Profile/Commands/SetNameCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using MediatR;
using Orbit.Application.Common;
using Orbit.Domain.Common;
using Orbit.Domain.Entities;
using Orbit.Domain.Interfaces;

namespace Orbit.Application.Profile.Commands;

public record SetNameCommand(Guid UserId, string Name) : IRequest<Result>;

public class SetNameCommandHandler(
IGenericRepository<User> userRepository,
IUnitOfWork unitOfWork) : IRequestHandler<SetNameCommand, Result>
{
public async Task<Result> Handle(SetNameCommand request, CancellationToken cancellationToken)
{
var user = await userRepository.FindOneTrackedAsync(
u => u.Id == request.UserId,
cancellationToken: cancellationToken);

if (user is null)
return Result.Failure(ErrorMessages.UserNotFound, ErrorCodes.UserNotFound);

var result = user.SetName(request.Name);

if (result.IsFailure)
return result;

await unitOfWork.SaveChangesAsync(cancellationToken);

return Result.Success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using FluentValidation;
using Orbit.Application.Common;
using Orbit.Application.Profile.Commands;

namespace Orbit.Application.Profile.Validators;

public class SetNameCommandValidator : AbstractValidator<SetNameCommand>
{
public SetNameCommandValidator()
{
RuleFor(x => x.UserId)
.NotEmpty();

RuleFor(x => x.Name)
.NotEmpty()
.Must(name => name is null || name.Trim().Length <= AppConstants.MaxUserNameLength)
.WithMessage($"Name must be at most {AppConstants.MaxUserNameLength} characters");
}
}
1 change: 1 addition & 0 deletions src/Orbit.Domain/Common/DomainConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public static class DomainConstants
{
public const int MaxScheduledReminders = 5;
public const int MaxHabitEmojiLength = 32;
public const int MaxUserNameLength = 50;
}
13 changes: 13 additions & 0 deletions src/Orbit.Domain/Entities/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ public void UpdateProfile(string name, string email)
Email = email.Trim().ToLowerInvariant();
}

public Result SetName(string name)
{
if (string.IsNullOrWhiteSpace(name))
return Result.Failure("Name is required");

var trimmedName = name.Trim();
if (trimmedName.Length > DomainConstants.MaxUserNameLength)
return Result.Failure($"Name must be at most {DomainConstants.MaxUserNameLength} characters");

Name = trimmedName;
return Result.Success();
}

public Result SetTimeZone(string ianaTimeZoneId)
{
try
Expand Down
76 changes: 76 additions & 0 deletions src/Orbit.Infrastructure/Email/EmailCopy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace Orbit.Infrastructure.Email;

/// <summary>
/// Localized strings (en / pt-BR) for every transactional email, including
/// subjects. Preheaders reuse each email's intro line. Markup lives in the
/// embedded templates; only copy lives here.
/// </summary>
public static class EmailCopy
{
public sealed record VerificationCodeCopy(
string Subject, string Heading, string Intro, string Cta, string Warning, string Footer, string Preheader);

public sealed record WelcomeCopy(
string Subject, string Heading, string Intro, string FeaturesTitle,
string Feature1, string Feature2, string Feature3, string Cta, string Footer, string Preheader);

public sealed record AccountDeletionCopy(
string Subject, string Heading, string Intro, string CodeLabel, string Warning, string Footer, string Preheader);

public static VerificationCodeCopy VerificationCode(bool isPtBr)
{
var intro = isPtBr
? "Use o código abaixo para entrar no Orbit. Ele expira em 5 minutos."
: "Use the code below to sign in to Orbit. It expires in 5 minutes.";

return new VerificationCodeCopy(
Subject: isPtBr ? "Seu código de verificação do Orbit" : "Your Orbit verification code",
Heading: isPtBr ? "Seu código de verificação" : "Your verification code",
Intro: intro,
Cta: isPtBr ? "Entrar no Orbit" : "Sign in to Orbit",
Warning: isPtBr
? "Se você não solicitou este código, pode ignorar este e-mail."
: "If you didn't request this code, you can safely ignore this email.",
Footer: TeamFooter(isPtBr),
Preheader: intro);
}

public static WelcomeCopy Welcome(bool isPtBr, string userName)
{
var intro = isPtBr
? "Estamos animados em ter você no Orbit. Agora você pode construir hábitos melhores, acompanhar seu progresso e manter suas metas em dia."
: "We're excited to have you on Orbit. You're now ready to build better habits, track your progress, and stay on top of your goals.";

return new WelcomeCopy(
Subject: isPtBr ? "Boas-vindas ao Orbit!" : "Welcome to Orbit!",
Heading: isPtBr ? $"Boas-vindas, {userName}!" : $"Welcome aboard, {userName}!",
Intro: intro,
FeaturesTitle: isPtBr ? "O que você pode fazer:" : "Here's what you can do:",
Feature1: isPtBr ? "Crie hábitos diários, semanais ou personalizados" : "Create daily, weekly, or custom habits",
Feature2: isPtBr ? "Acompanhe sequências e veja seu progresso" : "Track streaks and view your progress",
Feature3: isPtBr ? "Receba insights de IA sobre suas rotinas" : "Get AI-powered insights on your routines",
Cta: isPtBr ? "Começar" : "Get Started",
Footer: TeamFooter(isPtBr),
Preheader: intro);
}

public static AccountDeletionCopy AccountDeletion(bool isPtBr)
{
var intro = isPtBr
? "Você solicitou a exclusão da sua conta Orbit. Essa ação é irreversível. Todos os seus dados serão permanentemente excluídos, incluindo hábitos, histórico, conversas e configurações."
: "You requested to delete your Orbit account. This action is irreversible. All your data will be permanently deleted, including habits, history, conversations, and settings.";

return new AccountDeletionCopy(
Subject: isPtBr ? "Confirme a exclusão da sua conta Orbit" : "Confirm your Orbit account deletion",
Heading: isPtBr ? "Exclusão de conta" : "Account deletion",
Intro: intro,
CodeLabel: isPtBr ? "Use o código abaixo para confirmar:" : "Use the code below to confirm:",
Warning: isPtBr
? "Se você não solicitou isso, ignore este e-mail. Sua conta permanecerá segura."
: "If you didn't request this, ignore this email. Your account will remain safe.",
Footer: TeamFooter(isPtBr),
Preheader: intro);
}

private static string TeamFooter(bool isPtBr) => isPtBr ? "Equipe Orbit" : "The Orbit Team";
}
13 changes: 13 additions & 0 deletions src/Orbit.Infrastructure/Email/EmailLayout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Orbit.Infrastructure.Email;

/// <summary>
/// Layout-level values for the shared email chrome: document language, hidden
/// preview line, footer line, hosted logo URL, and whether the header band
/// renders the violet gradient (welcome email) or the plain canvas.
/// </summary>
public sealed record EmailLayout(
string Lang,
string Preheader,
string Footer,
string LogoUrl,
bool GradientHeader);
62 changes: 62 additions & 0 deletions src/Orbit.Infrastructure/Email/EmailTemplateRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Collections.Concurrent;
using System.Text.RegularExpressions;

namespace Orbit.Infrastructure.Email;

/// <summary>
/// Renders transactional emails from embedded-resource templates by replacing
/// <c>{{token}}</c> placeholders in a single pass, so substituted values are
/// never re-scanned for tokens. An unknown token in a template throws.
/// </summary>
public static partial class EmailTemplateRenderer
{
private const string CanvasColor = "#020618";
private const string GradientTopColor = "#22094F";

private static readonly ConcurrentDictionary<string, string> TemplateCache = new();

[GeneratedRegex(@"\{\{(\w+)\}\}")]
private static partial Regex TokenPattern();

public static string RenderHtml(string emailName, EmailLayout layout, IReadOnlyDictionary<string, string> tokens)
{
var content = ReplaceTokens(LoadTemplate($"{emailName}.html"), tokens);

var layoutTokens = new Dictionary<string, string>
{
["lang"] = layout.Lang,
["preheader"] = layout.Preheader,
["footer"] = layout.Footer,
["logoUrl"] = layout.LogoUrl,
["headerBgColor"] = layout.GradientHeader ? GradientTopColor : CanvasColor,
["headerBackground"] = layout.GradientHeader
? $"background: linear-gradient(180deg, {GradientTopColor} 0%, {CanvasColor} 100%);"
: $"background-color: {CanvasColor};",
["content"] = content,
};

return ReplaceTokens(LoadTemplate("Layout.html"), layoutTokens);
}

public static string RenderText(string emailName, IReadOnlyDictionary<string, string> tokens)
=> ReplaceTokens(LoadTemplate($"{emailName}.txt"), tokens);

private static string ReplaceTokens(string template, IReadOnlyDictionary<string, string> tokens)
=> TokenPattern().Replace(template, match =>
{
var tokenName = match.Groups[1].Value;
return tokens.TryGetValue(tokenName, out var value)
? value
: throw new InvalidOperationException($"Email template token has no value: {tokenName}");
});

private static string LoadTemplate(string fileName)
=> TemplateCache.GetOrAdd(fileName, static name =>
{
var resourceName = $"Orbit.Infrastructure.Email.Templates.{name}";
using var stream = typeof(EmailTemplateRenderer).Assembly.GetManifestResourceStream(resourceName)
?? throw new InvalidOperationException($"Embedded email template not found: {resourceName}");
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
});
}
11 changes: 11 additions & 0 deletions src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<h1 style="margin: 0 0 8px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 24px; font-weight: 600; line-height: 1.3; color: #FB2C36; text-align: center;">{{heading}}</h1>
<p style="margin: 0 0 16px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.55; color: #CAD5E2; text-align: center;">{{intro}}</p>
<p style="margin: 0 0 16px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; font-weight: 600; color: #F8FAFC; text-align: center;">{{codeLabel}}</p>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">

Check warning on line 4 in src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "cellpadding" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCufoDBGmgFv6p5I&open=AZ69aCufoDBGmgFv6p5I&pullRequest=204

Check warning on line 4 in src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <img> instead of the presentation role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCufoDBGmgFv6p5L&open=AZ69aCufoDBGmgFv6p5L&pullRequest=204

Check warning on line 4 in src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this layout table with a CSS layout.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCufoDBGmgFv6p5K&open=AZ69aCufoDBGmgFv6p5K&pullRequest=204
<tr>
<td align="center" bgcolor="#241727" style="background-color: #241727; border-radius: 14px; padding: 22px 16px;">

Check warning on line 6 in src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "align" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCufoDBGmgFv6p5M&open=AZ69aCufoDBGmgFv6p5M&pullRequest=204

Check warning on line 6 in src/Orbit.Infrastructure/Email/Templates/AccountDeletion.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "bgcolor" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCufoDBGmgFv6p5N&open=AZ69aCufoDBGmgFv6p5N&pullRequest=204
<span style="font-family: Roboto, 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 32px; font-weight: 500; letter-spacing: 8px; color: #FB2C36; white-space: nowrap;">{{code}}</span>
</td>
</tr>
</table>
<p style="margin: 24px 0 0; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 1.55; color: #90A1B9; text-align: center;">{{warning}}</p>
13 changes: 13 additions & 0 deletions src/Orbit.Infrastructure/Email/Templates/AccountDeletion.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Orbit

{{heading}}

{{intro}}

{{codeLabel}}

{{code}}

{{warning}}

{{footer}}
45 changes: 45 additions & 0 deletions src/Orbit.Infrastructure/Email/Templates/Layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="{{lang}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="dark">
<meta name="supported-color-schemes" content="dark">
<title>Orbit</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&family=Inter:wght@600;700&display=swap');
</style>
</head>
<body style="margin: 0; padding: 0; background-color: #020618;">
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{preheader}}&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background-color: #020618;">

Check warning on line 15 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this layout table with a CSS layout.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5R&open=AZ69aCuooDBGmgFv6p5R&pullRequest=204

Check warning on line 15 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "cellspacing" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5O&open=AZ69aCuooDBGmgFv6p5O&pullRequest=204

Check warning on line 15 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <img> instead of the presentation role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5S&open=AZ69aCuooDBGmgFv6p5S&pullRequest=204

Check warning on line 15 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "width" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5Q&open=AZ69aCuooDBGmgFv6p5Q&pullRequest=204
<tr>
<td align="center" bgcolor="{{headerBgColor}}" style="{{headerBackground}}">

Check warning on line 17 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "bgcolor" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5U&open=AZ69aCuooDBGmgFv6p5U&pullRequest=204
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="max-width: 600px;">

Check warning on line 18 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "cellpadding" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5W&open=AZ69aCuooDBGmgFv6p5W&pullRequest=204

Check warning on line 18 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <img> instead of the presentation role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5Z&open=AZ69aCuooDBGmgFv6p5Z&pullRequest=204

Check warning on line 18 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "width" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5X&open=AZ69aCuooDBGmgFv6p5X&pullRequest=204
<tr>
<td align="center" style="padding: 44px 24px 28px;">

Check warning on line 20 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "align" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5a&open=AZ69aCuooDBGmgFv6p5a&pullRequest=204
<img src="{{logoUrl}}" width="56" height="56" alt="Orbit" style="display: block; border: 0; font-family: Inter, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 24px; font-weight: 700; color: #F8FAFC; letter-spacing: -0.5px;">
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" style="padding: 0 20px;">

Check warning on line 28 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "align" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5b&open=AZ69aCuooDBGmgFv6p5b&pullRequest=204
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="max-width: 600px;">

Check warning on line 29 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this layout table with a CSS layout.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5f&open=AZ69aCuooDBGmgFv6p5f&pullRequest=204

Check warning on line 29 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "cellpadding" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5d&open=AZ69aCuooDBGmgFv6p5d&pullRequest=204

Check warning on line 29 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "width" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5e&open=AZ69aCuooDBGmgFv6p5e&pullRequest=204

Check warning on line 29 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <img> instead of the presentation role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5g&open=AZ69aCuooDBGmgFv6p5g&pullRequest=204
<tr>
<td bgcolor="#111526" style="background-color: #111526; border: 1px solid #282C3B; border-radius: 20px; padding: 40px 32px;">

Check warning on line 31 in src/Orbit.Infrastructure/Email/Templates/Layout.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "bgcolor" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuooDBGmgFv6p5h&open=AZ69aCuooDBGmgFv6p5h&pullRequest=204
{{content}}
</td>
</tr>
<tr>
<td align="center" style="padding: 32px 24px 48px;">
<p style="margin: 0; font-family: Roboto, 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 12px; letter-spacing: 0.2px; color: #90A1B9;">{{footer}}</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
21 changes: 21 additions & 0 deletions src/Orbit.Infrastructure/Email/Templates/Support.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<h1 style="margin: 0 0 20px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 20px; font-weight: 600; line-height: 1.3; color: #7F46F7;">Support Request</h1>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">

Check warning on line 2 in src/Orbit.Infrastructure/Email/Templates/Support.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "width" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuwoDBGmgFv6p5l&open=AZ69aCuwoDBGmgFv6p5l&pullRequest=204

Check warning on line 2 in src/Orbit.Infrastructure/Email/Templates/Support.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "cellspacing" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuwoDBGmgFv6p5j&open=AZ69aCuwoDBGmgFv6p5j&pullRequest=204

Check warning on line 2 in src/Orbit.Infrastructure/Email/Templates/Support.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this layout table with a CSS layout.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuwoDBGmgFv6p5m&open=AZ69aCuwoDBGmgFv6p5m&pullRequest=204

Check warning on line 2 in src/Orbit.Infrastructure/Email/Templates/Support.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <img> instead of the presentation role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuwoDBGmgFv6p5n&open=AZ69aCuwoDBGmgFv6p5n&pullRequest=204
<tr>
<td style="padding: 0 0 2px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 12px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: #90A1B9;">From</td>
</tr>
<tr>
<td style="padding: 0 0 14px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; color: #F8FAFC;">{{fromName}} &lt;{{fromEmail}}&gt;</td>
</tr>
<tr>
<td style="padding: 0 0 2px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 12px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: #90A1B9;">Subject</td>
</tr>
<tr>
<td style="padding: 0 0 18px; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; color: #F8FAFC;">{{subject}}</td>
</tr>
<tr>
<td height="1" bgcolor="#282C3B" style="height: 1px; line-height: 1px; font-size: 1px;">&nbsp;</td>

Check warning on line 16 in src/Orbit.Infrastructure/Email/Templates/Support.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this deprecated "bgcolor" attribute.

See more on https://sonarcloud.io/project/issues?id=thomasluizon_orbit-api&issues=AZ69aCuwoDBGmgFv6p5p&open=AZ69aCuwoDBGmgFv6p5p&pullRequest=204
</tr>
<tr>
<td style="padding: 18px 0 0; font-family: Rubik, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.55; color: #CAD5E2;">{{message}}</td>
</tr>
</table>
8 changes: 8 additions & 0 deletions src/Orbit.Infrastructure/Email/Templates/Support.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Orbit Support Request

From: {{fromName}} <{{fromEmail}}>
Subject: {{subject}}

{{message}}

Reply directly to respond to the user.
Loading
Loading