diff --git a/backend/src/Web/Features/Contents/Handlers/ChangeEmail.cs b/backend/src/Web/Features/Contents/Handlers/ChangeEmail.cs new file mode 100644 index 0000000..aabe6d1 --- /dev/null +++ b/backend/src/Web/Features/Contents/Handlers/ChangeEmail.cs @@ -0,0 +1,70 @@ +using FluentValidation; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Hutopy.Web.Features.Contents.Data; +using Hutopy.Web.Common.Security; + +namespace Hutopy.Web.Features.Contents.Handlers; + +[PublicAPI] +public record ChangeEmailRequest( + Guid CreatorId, + string? Email); + +[PublicAPI] +public sealed class ChangeEmailRequestValidator : Validator +{ + public ChangeEmailRequestValidator() + { + RuleFor(x => x.CreatorId) + .NotEmpty() + .WithMessage("Creator ID is required"); + + RuleFor(x => x.Email) + .Must(email => email == null || !string.IsNullOrWhiteSpace(email)) + .WithMessage("Email cannot be empty if provided"); + } +} + +[PublicAPI] +public class ChangeEmailHandler( + ContentDbContext context) + : Endpoint +{ + public override void Configure() + { + Post("/api/creators/{CreatorId}/email"); + Options(o => o.WithTags("Creators")); + } + + public override async Task HandleAsync( + ChangeEmailRequest request, + CancellationToken ct) + { + var creator = await context + .Creators + .Include(c => c.Presentation) + .SingleOrDefaultAsync( + c => c.Id == request.CreatorId, + cancellationToken: ct); + + if (creator is null) + { + await SendNotFoundAsync(ct); + return; + } + + // Check if the current user is the creator + if (creator.CreatedBy != User.GetUserId()) + { + await SendUnauthorizedAsync(ct); + return; + } + + creator.Presentation.Email = request.Email?.Trim(); + + await context.SaveChangesAsync(ct); + + await SendOkAsync(ct); + } +} \ No newline at end of file diff --git a/backend/src/Web/Features/Contents/Handlers/ChangePhoneNumber.cs b/backend/src/Web/Features/Contents/Handlers/ChangePhoneNumber.cs new file mode 100644 index 0000000..bd6fcb1 --- /dev/null +++ b/backend/src/Web/Features/Contents/Handlers/ChangePhoneNumber.cs @@ -0,0 +1,70 @@ +using FluentValidation; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Hutopy.Web.Features.Contents.Data; +using Hutopy.Web.Common.Security; + +namespace Hutopy.Web.Features.Contents.Handlers; + +[PublicAPI] +public record ChangePhoneNumberRequest( + Guid CreatorId, + string? PhoneNumber); + +[PublicAPI] +public sealed class ChangePhoneNumberRequestValidator : Validator +{ + public ChangePhoneNumberRequestValidator() + { + RuleFor(x => x.CreatorId) + .NotEmpty() + .WithMessage("Creator ID is required"); + + RuleFor(x => x.PhoneNumber) + .Must(phone => phone == null || !string.IsNullOrWhiteSpace(phone)) + .WithMessage("Phone number cannot be empty if provided"); + } +} + +[PublicAPI] +public class ChangePhoneNumberHandler( + ContentDbContext context) + : Endpoint +{ + public override void Configure() + { + Post("/api/creators/{CreatorId}/phone"); + Options(o => o.WithTags("Creators")); + } + + public override async Task HandleAsync( + ChangePhoneNumberRequest request, + CancellationToken ct) + { + var creator = await context + .Creators + .Include(c => c.Presentation) + .SingleOrDefaultAsync( + c => c.Id == request.CreatorId, + cancellationToken: ct); + + if (creator is null) + { + await SendNotFoundAsync(ct); + return; + } + + // Check if the current user is the creator + if (creator.CreatedBy != User.GetUserId()) + { + await SendUnauthorizedAsync(ct); + return; + } + + creator.Presentation.PhoneNumber = request.PhoneNumber?.Trim(); + + await context.SaveChangesAsync(ct); + + await SendOkAsync(ct); + } +} \ No newline at end of file diff --git a/backend/src/Web/Features/Contents/Handlers/ChangePresentationInfos.cs b/backend/src/Web/Features/Contents/Handlers/ChangePresentationInfos.cs index 366236f..739461f 100644 --- a/backend/src/Web/Features/Contents/Handlers/ChangePresentationInfos.cs +++ b/backend/src/Web/Features/Contents/Handlers/ChangePresentationInfos.cs @@ -7,9 +7,7 @@ namespace Hutopy.Web.Features.Contents.Handlers; public record ChangePresentationInfosRequest( Guid CreatorId, string Description, - string? VideoUrl, - string? PhoneNumber, - string? Email); + string? VideoUrl); [PublicAPI] public sealed class ChangePresentationInfosRequestValidator : Validator @@ -27,14 +25,6 @@ public sealed class ChangePresentationInfosRequestValidator : Validator x.VideoUrl) .Must(url => url == null || YouTubeUrlHelper.IsValidYouTubeUrlOrId(url)) .WithMessage("Invalid YouTube URL or video ID format"); - - RuleFor(x => x.PhoneNumber) - .Must(phone => phone == null || !string.IsNullOrWhiteSpace(phone)) - .WithMessage("Phone number cannot be empty if provided"); - - RuleFor(x => x.Email) - .Must(email => email == null || !string.IsNullOrWhiteSpace(email)) - .WithMessage("Email cannot be empty if provided"); } } @@ -71,8 +61,6 @@ public class ChangePresentationInfosHandler( creator.Presentation.VideoUrl = request.VideoUrl != null ? YouTubeUrlHelper.ExtractVideoId(request.VideoUrl.Trim()) : null; - creator.Presentation.PhoneNumber = request.PhoneNumber?.Trim(); - creator.Presentation.Email = request.Email?.Trim(); await context.SaveChangesAsync(ct); diff --git a/frontend/src/views/profile/creators/ChangeEmailDialog.vue b/frontend/src/views/profile/creators/ChangeEmailDialog.vue index 3be6545..72a74d7 100644 --- a/frontend/src/views/profile/creators/ChangeEmailDialog.vue +++ b/frontend/src/views/profile/creators/ChangeEmailDialog.vue @@ -50,13 +50,10 @@ async function saveEmail() { try { isLoading.value = true; - // Save presentation info + // Save email await client.post( - `/api/creators/${props.creator.id}/presentation-infos`, + `/api/creators/${props.creator.id}/email`, { - description: props.creator.presentation?.description || "", - videoUrl: props.creator.presentation?.videoUrl || "", - phoneNumber: props.creator.presentation?.phoneNumber || "", email: email.value || "" } ); diff --git a/frontend/src/views/profile/creators/ChangePhoneDialog.vue b/frontend/src/views/profile/creators/ChangePhoneDialog.vue index 39b20f4..d62195c 100644 --- a/frontend/src/views/profile/creators/ChangePhoneDialog.vue +++ b/frontend/src/views/profile/creators/ChangePhoneDialog.vue @@ -50,14 +50,11 @@ async function savePhoneNumber() { try { isLoading.value = true; - // Save presentation info + // Save phone number await client.post( - `/api/creators/${props.creator.id}/presentation-infos`, + `/api/creators/${props.creator.id}/phone`, { - description: props.creator.presentation?.description || "", - videoUrl: props.creator.presentation?.videoUrl || "", - phoneNumber: phoneNumber.value || "", - email: props.creator.presentation?.email || "" + phoneNumber: phoneNumber.value || "" } );