From 3a8426a0e6d1e6b569539916c37ad7cad81ced3b Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sun, 1 Sep 2024 11:58:16 +0200 Subject: [PATCH 1/6] feat(Post): Add Customer Support Ticket ID property on Post entity This commit adds a new property called "CustomerSupportTicketId" to the Post model. This property is used to store the Customer Support ticket ID associated with a post. It can take a positive integer value up to 9 digits or be set as null if no ticket is associated. The purpose of this property is to allow WG staff to track and manage player reports. Implements database requirements for #165. --- WowsKarma.Api/Data/Models/Post.cs | 12 + .../20240901093435_AddCSTicketId.Designer.cs | 602 ++++++++++++++++++ .../20240901093435_AddCSTicketId.cs | 28 + .../ApiDb/ApiDbContextModelSnapshot.cs | 9 +- 4 files changed, 648 insertions(+), 3 deletions(-) create mode 100644 WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.Designer.cs create mode 100644 WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.cs diff --git a/WowsKarma.Api/Data/Models/Post.cs b/WowsKarma.Api/Data/Models/Post.cs index 1e80c98..f258b79 100644 --- a/WowsKarma.Api/Data/Models/Post.cs +++ b/WowsKarma.Api/Data/Models/Post.cs @@ -92,6 +92,18 @@ public sealed record Post : ITimestamped, IDisposable /// public bool ModLocked { get; set; } + /// + /// The Customer Support ticket ID associated with this post. + /// + /// + /// This is used by WG staff to track and manage player reports. + /// + /// + /// Set as if no ticket is associated. + /// When present, takes form as a positive integer, up to 9 digits. + /// + [Range(1, 999_999_999)] + public int? CustomerSupportTicketId { get; set; } /// public void Dispose() diff --git a/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.Designer.cs b/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.Designer.cs new file mode 100644 index 0000000..597dfce --- /dev/null +++ b/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.Designer.cs @@ -0,0 +1,602 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nodsoft.Wargaming.Api.Common.Data.Responses.Wows; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using WowsKarma.Api.Data; +using WowsKarma.Api.Data.Models.Replays; +using WowsKarma.Common.Models; + +#nullable disable + +namespace WowsKarma.Api.Migrations +{ + [DbContext(typeof(ApiDbContext))] + [Migration("20240901093435_AddCSTicketId")] + partial class AddCSTicketId + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "clan_role", new[] { "unknown", "commander", "executive_officer", "recruitment_officer", "commissioned_officer", "officer", "private" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "mod_action_type", new[] { "deletion", "update" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "notification_type", new[] { "unknown", "other", "post_added", "post_edited", "post_deleted", "post_mod_edited", "post_mod_deleted", "platform_ban" }); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Clan", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsDisbanded") + .HasColumnType("boolean"); + + b.Property("LeagueColor") + .HasColumnType("bigint"); + + b.Property("MembersUpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tag") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Clans"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.ClanMember", b => + { + b.Property("PlayerId") + .HasColumnType("bigint"); + + b.Property("ClanId") + .HasColumnType("bigint"); + + b.Property("JoinedAt") + .HasColumnType("date"); + + b.Property("LeftAt") + .HasColumnType("date"); + + b.Property("Role") + .HasColumnType("clan_role"); + + b.HasKey("PlayerId"); + + b.HasIndex("ClanId"); + + b.ToTable("ClanMembers"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.NotificationBase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("AcknowledgedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmittedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("notification_type"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.ToTable("Notifications"); + + b.HasDiscriminator("Type").IsComplete(false).HasValue(NotificationType.Unknown); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.PlatformBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BannedUntil") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("ModId") + .HasColumnType("bigint"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.Property("Reverted") + .HasColumnType("boolean"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ModId"); + + b.HasIndex("UserId"); + + b.ToTable("PlatformBans"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Player", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CourtesyRating") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("GameKarma") + .HasColumnType("integer"); + + b.Property("LastBattleTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OptOutChanged") + .HasColumnType("timestamp with time zone"); + + b.Property("OptedOut") + .HasColumnType("boolean"); + + b.Property("PerformanceRating") + .HasColumnType("integer"); + + b.Property("PostsBanned") + .HasColumnType("boolean"); + + b.Property("SiteKarma") + .HasColumnType("integer"); + + b.Property("TeamplayRating") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.Property("WgAccountCreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("WgHidden") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("Players"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuthorId") + .HasColumnType("bigint"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("NOW()"); + + b.Property("CustomerSupportTicketId") + .HasColumnType("integer"); + + b.Property("Flairs") + .HasColumnType("integer"); + + b.Property("ModLocked") + .HasColumnType("boolean"); + + b.Property("NegativeKarmaAble") + .HasColumnType("boolean"); + + b.Property("PlayerId") + .HasColumnType("bigint"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("ReplayId") + .HasColumnType("uuid"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.HasIndex("PlayerId"); + + b.HasIndex("ReplayId") + .IsUnique(); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.PostModAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActionType") + .HasColumnType("mod_action_type"); + + b.Property("ModId") + .HasColumnType("bigint"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ModId"); + + b.HasIndex("PostId"); + + b.ToTable("PostModActions"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Replays.Replay", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ArenaInfo") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("BlobName") + .IsRequired() + .HasColumnType("text"); + + b.Property>("ChatMessages") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MinimapRendered") + .HasColumnType("boolean"); + + b.Property>("Players") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("PostId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Replays"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PlatformBanNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("BanId") + .HasColumnType("uuid"); + + b.HasIndex("BanId"); + + b.HasDiscriminator().HasValue(NotificationType.PlatformBan); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostAddedNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("PostId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid"); + + b.HasIndex("PostId"); + + b.HasDiscriminator().HasValue(NotificationType.PostAdded); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostDeletedNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("PostId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid"); + + b.HasIndex("PostId"); + + b.HasDiscriminator().HasValue(NotificationType.PostDeleted); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostEditedNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("PostId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid"); + + b.HasIndex("PostId"); + + b.HasDiscriminator().HasValue(NotificationType.PostEdited); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostModDeletedNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("ModActionId") + .HasColumnType("uuid"); + + b.HasIndex("ModActionId"); + + b.HasDiscriminator().HasValue(NotificationType.PostModDeleted); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostModEditedNotification", b => + { + b.HasBaseType("WowsKarma.Api.Data.Models.Notifications.NotificationBase"); + + b.Property("ModActionId") + .HasColumnType("uuid"); + + b.HasIndex("ModActionId"); + + b.ToTable("Notifications", t => + { + t.Property("ModActionId") + .HasColumnName("PostModEditedNotification_ModActionId"); + }); + + b.HasDiscriminator().HasValue(NotificationType.PostModEdited); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.ClanMember", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Clan", "Clan") + .WithMany("Members") + .HasForeignKey("ClanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WowsKarma.Api.Data.Models.Player", "Player") + .WithOne("ClanMember") + .HasForeignKey("WowsKarma.Api.Data.Models.ClanMember", "PlayerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Clan"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.NotificationBase", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Player", "Account") + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.PlatformBan", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Player", "Mod") + .WithMany() + .HasForeignKey("ModId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WowsKarma.Api.Data.Models.Player", "User") + .WithMany("PlatformBans") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Mod"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Post", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Player", "Author") + .WithMany("PostsSent") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("WowsKarma.Api.Data.Models.Player", "Player") + .WithMany("PostsReceived") + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("WowsKarma.Api.Data.Models.Replays.Replay", "Replay") + .WithOne("Post") + .HasForeignKey("WowsKarma.Api.Data.Models.Post", "ReplayId"); + + b.Navigation("Author"); + + b.Navigation("Player"); + + b.Navigation("Replay"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.PostModAction", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Player", "Mod") + .WithMany() + .HasForeignKey("ModId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WowsKarma.Api.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Mod"); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PlatformBanNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.PlatformBan", "Ban") + .WithMany() + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostAddedNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostDeletedNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostEditedNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.Post", "Post") + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostModDeletedNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.PostModAction", "ModAction") + .WithMany() + .HasForeignKey("ModActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ModAction"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Notifications.PostModEditedNotification", b => + { + b.HasOne("WowsKarma.Api.Data.Models.PostModAction", "ModAction") + .WithMany() + .HasForeignKey("ModActionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ModAction"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Clan", b => + { + b.Navigation("Members"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Player", b => + { + b.Navigation("ClanMember"); + + b.Navigation("PlatformBans"); + + b.Navigation("PostsReceived"); + + b.Navigation("PostsSent"); + }); + + modelBuilder.Entity("WowsKarma.Api.Data.Models.Replays.Replay", b => + { + b.Navigation("Post") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.cs b/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.cs new file mode 100644 index 0000000..1fe46b1 --- /dev/null +++ b/WowsKarma.Api/Migrations/20240901093435_AddCSTicketId.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WowsKarma.Api.Migrations +{ + /// + public partial class AddCSTicketId : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CustomerSupportTicketId", + table: "Posts", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CustomerSupportTicketId", + table: "Posts"); + } + } +} diff --git a/WowsKarma.Api/Migrations/ApiDb/ApiDbContextModelSnapshot.cs b/WowsKarma.Api/Migrations/ApiDb/ApiDbContextModelSnapshot.cs index 87968ea..cc557df 100644 --- a/WowsKarma.Api/Migrations/ApiDb/ApiDbContextModelSnapshot.cs +++ b/WowsKarma.Api/Migrations/ApiDb/ApiDbContextModelSnapshot.cs @@ -1,11 +1,11 @@ // using System; using System.Collections.Generic; +using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Nodsoft.Wargaming.Api.Common.Data.Responses.Wows; -using Nodsoft.WowsReplaysUnpack.Core.Models; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using WowsKarma.Api.Data; using WowsKarma.Api.Data.Models.Replays; @@ -22,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.1") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "clan_role", new[] { "unknown", "commander", "executive_officer", "recruitment_officer", "commissioned_officer", "officer", "private" }); @@ -234,6 +234,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasDefaultValueSql("NOW()"); + b.Property("CustomerSupportTicketId") + .HasColumnType("integer"); + b.Property("Flairs") .HasColumnType("integer"); @@ -305,7 +308,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("ArenaInfo") + b.Property("ArenaInfo") .IsRequired() .HasColumnType("jsonb"); From 495032cc7d0fa6d8d7390bb9c513f971a054bb76 Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sun, 1 Sep 2024 12:03:49 +0200 Subject: [PATCH 2/6] feat(dto): Add SupportTicketStatus field to PlayerPostDTO + Mappings This commit adds a new field, SupportTicketStatus, to the PlayerPostDTO class in the WowsKarma.Common project. The SupportTicketStatus field represents the status of the Customer Support ticket associated with the post when applicable. Additionally, mappings are configured in the Conversions utility class to map the CustomerSupportTicketId property of Post to TicketId property of SupportTicketStatus in PlayerPostDTO. This allows for seamless conversion between Post and PlayerPostDTO objects while preserving the relevant information about Customer Support tickets. --- WowsKarma.Api/Utilities/Conversions.cs | 16 +++++++++--- WowsKarma.Common/Models/DTOs/PlayerPostDTO.cs | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/WowsKarma.Api/Utilities/Conversions.cs b/WowsKarma.Api/Utilities/Conversions.cs index edf1928..c69d72d 100644 --- a/WowsKarma.Api/Utilities/Conversions.cs +++ b/WowsKarma.Api/Utilities/Conversions.cs @@ -15,12 +15,17 @@ public static class Conversions public static void ConfigureMapping() { TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo(); - + TypeAdapterConfig .NewConfig() .IgnoreNullValues(true) .Ignore(dest => dest.Author) - .Ignore(dest => dest.Player); + .Ignore(dest => dest.Player) + .Map( + dest => dest.CustomerSupportTicketId, + src => src.SupportTicketStatus.TicketId, + srcCond => srcCond.SupportTicketStatus.TicketId != null + ); TypeAdapterConfig .NewConfig() @@ -34,7 +39,12 @@ public static void ConfigureMapping() : ReplayState.Processing ) .Map(dest => dest.Author.Clan, src => src.Author.ClanMember.Clan) - .Map(dest => dest.Player.Clan, src => src.Player.ClanMember.Clan); + .Map(dest => dest.Player.Clan, src => src.Player.ClanMember.Clan) + .Map(dest => dest.SupportTicketStatus, src => new PlayerPostDTO.CustomerSupportStatus + { + HasTicket = src.CustomerSupportTicketId != null, + TicketId = src.CustomerSupportTicketId + }); TypeAdapterConfig .NewConfig() diff --git a/WowsKarma.Common/Models/DTOs/PlayerPostDTO.cs b/WowsKarma.Common/Models/DTOs/PlayerPostDTO.cs index 87693cd..489f5c8 100644 --- a/WowsKarma.Common/Models/DTOs/PlayerPostDTO.cs +++ b/WowsKarma.Common/Models/DTOs/PlayerPostDTO.cs @@ -26,4 +26,29 @@ public record PlayerPostDTO // Computed by DB Engine (hopefully) public DateTimeOffset? CreatedAt { get; init; } public DateTimeOffset? UpdatedAt { get; init; } + + /// + /// The status of the Customer Support ticket associated with the post when applicable. + /// + public CustomerSupportStatus SupportTicketStatus { get; init; } + + /// + /// Defines the status of the Customer Support ticket associated with the post when applicable. + /// + public struct CustomerSupportStatus + { + /// + /// Whether the post has an associated Customer Support ticket. + /// + public bool HasTicket { get; init; } + + /// + /// The Customer Support ticket ID associated with this post. + /// + /// + /// This is only available when is , + /// and only visible to the post's author, platform staff, and Wargaming staff. + /// + public int? TicketId { get; init; } + } } From 2d228ab663bb916de5f6c44431a28d2de9f27081 Mon Sep 17 00:00:00 2001 From: Sakura Akeno Isayeki Date: Sun, 1 Sep 2024 15:10:30 +0200 Subject: [PATCH 3/6] feat(post): Add support for CS ticket ID field in post editor - Added a new property `supportTicketStatus` to the `PlayerPostEditorDto` interface and implemented it in the `PostEditorComponent` - Created a new component `CsTicketIdHelpComponent` to display information about Customer Support tickets - Added a new HTML template file for the `CsTicketIdHelpComponent` - Updated the HTML template of the `PostEditorComponent` to include a CS ticket ID field when certain conditions are met - Updated the CSS styles of the `SeedTokenChangeComponent` (removed) - Updated the TypeScript code of the `SeedTokenChangeComponent` (removed) - Updated the HTML template of the `PostComponent` to display CS ticket related icons and tooltips Implements frontend requirements for #165. --- .../services/api/models/player-post-dto.ts | 4 + .../cs-ticket-id-help.component.html | 63 +++++++++ .../cs-ticket-id-help.component.ts | 56 ++++++++ .../post-editor/post-editor.component.html | 39 ++++-- .../post-editor/post-editor.component.ts | 16 ++- .../seed-token-change.component.scss | 0 .../seed-token-change.component.ts | 1 - .../src/app/shared/post/post.component.html | 128 +++++++++--------- 8 files changed, 234 insertions(+), 73 deletions(-) create mode 100644 wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.html create mode 100644 wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.ts delete mode 100644 wowskarma.app/src/app/shared/modals/seed-token-change/seed-token-change.component.scss diff --git a/wowskarma.app/src/app/services/api/models/player-post-dto.ts b/wowskarma.app/src/app/services/api/models/player-post-dto.ts index f623c8e..10615fc 100644 --- a/wowskarma.app/src/app/services/api/models/player-post-dto.ts +++ b/wowskarma.app/src/app/services/api/models/player-post-dto.ts @@ -20,6 +20,10 @@ export interface PlayerPostDto { replayState?: ReplayState title?: null | string; updatedAt?: null | string; + supportTicketStatus?: { + hasTicket: boolean; + ticketId: number | null; + } } enum ReplayState { diff --git a/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.html b/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.html new file mode 100644 index 0000000..5264d4e --- /dev/null +++ b/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.html @@ -0,0 +1,63 @@ + + + diff --git a/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.ts b/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.ts new file mode 100644 index 0000000..79cb23e --- /dev/null +++ b/wowskarma.app/src/app/shared/modals/cs-ticket-id-help/cs-ticket-id-help.component.ts @@ -0,0 +1,56 @@ +import { ChangeDetectionStrategy, Component, computed, inject, Input } from '@angular/core'; +import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap"; +import { AuthService } from "../../../services/api/services/auth.service"; +import { AppConfigService } from "../../../services/app-config.service"; + +@Component({ + selector: 'app-cs-ticket-id', + standalone: true, + imports: [], + templateUrl: './cs-ticket-id-help.component.html', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class CsTicketIdHelpComponent { + @Input() modal!: NgbModalRef; + + appConfig = inject(AppConfigService); + + region = computed(() => this.appConfig.currentRegion); + + csLinks = computed<[string, string]>(() => { + /* + * In order: + * - Gameplay / Collusions + * - Chat Issues + * + * See: https://github.com/SakuraIsayeki/WOWS-Karma/issues/165 + */ + + if (this.region() === 'EU') { + return [ + 'https://eu.wargaming.net/support/en/products/wows/help/29948/29949/29955/29957/', + 'https://eu.wargaming.net/support/en/products/wows/help/29948/29949/29951/29952/' + ]; + } else if (this.region() === 'NA') { + return [ + 'https://na.wargaming.net/support/en/products/wows/help/31336/31337/31338/31339/', + 'https://na.wargaming.net/support/en/products/wows/help/31336/31337/31345/' + ]; + } else if (this.region() === 'SEA') { + return [ + "https://asia.wargaming.net/support/en/products/wows/help/28687/28688/28689/", + "https://asia.wargaming.net/support/en/products/wows/help/28687/28688/28694/" + ]; + } + + return ['', '']; + }) + + constructor() { + } + + static OpenModal(modalService: NgbModal) { + const modalRef = modalService.open(CsTicketIdHelpComponent, { size: "lg" }); + modalRef.componentInstance.modal = modalRef; + } +} diff --git a/wowskarma.app/src/app/shared/modals/post-editor/post-editor.component.html b/wowskarma.app/src/app/shared/modals/post-editor/post-editor.component.html index dc658a7..54bcc24 100644 --- a/wowskarma.app/src/app/shared/modals/post-editor/post-editor.component.html +++ b/wowskarma.app/src/app/shared/modals/post-editor/post-editor.component.html @@ -69,20 +69,41 @@
{{group.label}}
-
-

Replay File

+
+
+

Replay File

- + -
- +
+ +
+ + + @if (flairGroups.length > 1 && (flairGroups[1].control.value === false || flairGroups[2].control.value === false)) { +
+
+ + + (optional) + + + + +
+ + +
+ }
- -