diff --git a/src/Orbit.Api/Controllers/ProfileController.cs b/src/Orbit.Api/Controllers/ProfileController.cs index dd3b439c..be0cdc29 100644 --- a/src/Orbit.Api/Controllers/ProfileController.cs +++ b/src/Orbit.Api/Controllers/ProfileController.cs @@ -148,23 +148,4 @@ public async Task CompleteOnboarding(CancellationToken cancellati return result.IsSuccess ? Ok() : BadRequest(new { error = result.Error }); } - [HttpPut("missions/dismiss")] - public async Task DismissMissions(CancellationToken cancellationToken) - { - var command = new DismissMissionsCommand(HttpContext.GetUserId()); - var result = await mediator.Send(command, cancellationToken); - return result.IsSuccess ? Ok() : BadRequest(new { error = result.Error }); - } - - public record MarkTourRequest(string PageName); - - [HttpPut("tour")] - public async Task MarkTourCompleted( - [FromBody] MarkTourRequest request, - CancellationToken cancellationToken) - { - var command = new MarkTourCompletedCommand(HttpContext.GetUserId(), request.PageName); - var result = await mediator.Send(command, cancellationToken); - return result.IsSuccess ? Ok() : BadRequest(new { error = result.Error }); - } } diff --git a/src/Orbit.Application/Profile/Commands/DismissMissionsCommand.cs b/src/Orbit.Application/Profile/Commands/DismissMissionsCommand.cs deleted file mode 100644 index 3426ec6f..00000000 --- a/src/Orbit.Application/Profile/Commands/DismissMissionsCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -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 DismissMissionsCommand(Guid UserId) : IRequest; - -public class DismissMissionsCommandHandler( - IGenericRepository userRepository, - IUnitOfWork unitOfWork) : IRequestHandler -{ - public async Task Handle(DismissMissionsCommand request, CancellationToken cancellationToken) - { - var user = await userRepository.FindOneTrackedAsync( - u => u.Id == request.UserId, - cancellationToken: cancellationToken); - - if (user is null) - return Result.Failure(ErrorMessages.UserNotFound); - - user.DismissMissions(); - - await unitOfWork.SaveChangesAsync(cancellationToken); - - return Result.Success(); - } -} diff --git a/src/Orbit.Application/Profile/Commands/MarkTourCompletedCommand.cs b/src/Orbit.Application/Profile/Commands/MarkTourCompletedCommand.cs deleted file mode 100644 index 22e6387d..00000000 --- a/src/Orbit.Application/Profile/Commands/MarkTourCompletedCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -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 MarkTourCompletedCommand(Guid UserId, string PageName) : IRequest; - -public class MarkTourCompletedCommandHandler( - IGenericRepository userRepository, - IUnitOfWork unitOfWork) : IRequestHandler -{ - public async Task Handle(MarkTourCompletedCommand request, CancellationToken cancellationToken) - { - var user = await userRepository.FindOneTrackedAsync( - u => u.Id == request.UserId, - cancellationToken: cancellationToken); - - if (user is null) - return Result.Failure(ErrorMessages.UserNotFound); - - user.MarkTourCompleted(request.PageName); - - await unitOfWork.SaveChangesAsync(cancellationToken); - - return Result.Success(); - } -} diff --git a/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs b/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs index 67c6e45a..aedaea75 100644 --- a/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs +++ b/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs @@ -14,8 +14,6 @@ public record ProfileResponse( bool AiMemoryEnabled, bool AiSummaryEnabled, bool HasCompletedOnboarding, - bool HasDismissedMissions, - string? CompletedTours, string? Language, string Plan, bool HasProAccess, @@ -74,8 +72,6 @@ public async Task> Handle(GetProfileQuery request, Cance user.AiMemoryEnabled, user.AiSummaryEnabled, user.HasCompletedOnboarding, - user.HasDismissedMissions, - user.CompletedTours, user.Language, user.HasProAccess ? "pro" : "free", user.HasProAccess, diff --git a/src/Orbit.Domain/Entities/User.cs b/src/Orbit.Domain/Entities/User.cs index 6306d13a..578f1c34 100644 --- a/src/Orbit.Domain/Entities/User.cs +++ b/src/Orbit.Domain/Entities/User.cs @@ -17,8 +17,6 @@ public class User : Entity public bool AiMemoryEnabled { get; private set; } = true; public bool AiSummaryEnabled { get; private set; } = true; public bool HasCompletedOnboarding { get; private set; } = false; - public bool HasDismissedMissions { get; private set; } = false; - public string? CompletedTours { get; private set; } public string? Language { get; private set; } public UserPlan Plan { get; private set; } = UserPlan.Free; public string? StripeCustomerId { get; private set; } @@ -133,17 +131,6 @@ public Result SetColorScheme(string? colorScheme) public void CompleteOnboarding() => HasCompletedOnboarding = true; - public void DismissMissions() => HasDismissedMissions = true; - - public void MarkTourCompleted(string pageName) - { - var tours = string.IsNullOrEmpty(CompletedTours) - ? new HashSet() - : new HashSet(CompletedTours.Split(',')); - tours.Add(pageName); - CompletedTours = string.Join(",", tours); - } - public void SetStripeCustomerId(string customerId) => StripeCustomerId = customerId; public void SetStripeSubscription(string subscriptionId, DateTime expiresAt, SubscriptionInterval? interval = null) diff --git a/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.Designer.cs b/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.Designer.cs new file mode 100644 index 00000000..2cfe2e04 --- /dev/null +++ b/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.Designer.cs @@ -0,0 +1,844 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Orbit.Infrastructure.Persistence; + +#nullable disable + +namespace Orbit.Infrastructure.Migrations +{ + [DbContext(typeof(OrbitDbContext))] + [Migration("20260401005230_RemoveMissionsAndTours")] + partial class RemoveMissionsAndTours + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("HabitGoals", b => + { + b.Property("GoalId") + .HasColumnType("uuid"); + + b.Property("HabitId") + .HasColumnType("uuid"); + + b.HasKey("GoalId", "HabitId"); + + b.HasIndex("HabitId"); + + b.ToTable("HabitGoals"); + }); + + modelBuilder.Entity("HabitTags", b => + { + b.Property("HabitId") + .HasColumnType("uuid"); + + b.Property("TagId") + .HasColumnType("uuid"); + + b.HasKey("HabitId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("HabitTags"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.ApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("IsRevoked") + .HasColumnType("boolean"); + + b.Property("KeyHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("KeyPrefix") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("character varying(12)"); + + b.Property("LastUsedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("KeyPrefix"); + + b.HasIndex("UserId"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.AppConfig", b => + { + b.Property("Key") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Key"); + + b.ToTable("AppConfigs"); + + b.HasData( + new + { + Key = "MaxUserFacts", + Description = "Maximum number of facts the AI can remember per user", + Value = "50" + }, + new + { + Key = "MaxHabitDepth", + Description = "Maximum nesting depth for sub-habits", + Value = "5" + }, + new + { + Key = "MaxTagsPerHabit", + Description = "Maximum number of tags per habit", + Value = "5" + }, + new + { + Key = "ReferralRewardDays", + Description = "Days of Pro added per successful referral", + Value = "10" + }, + new + { + Key = "MaxReferrals", + Description = "Maximum successful referrals per user", + Value = "10" + }); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Goal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CompletedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CurrentValue") + .HasColumnType("numeric"); + + b.Property("Deadline") + .HasColumnType("date"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Position") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TargetValue") + .HasColumnType("numeric"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Unit") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Goals"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.GoalProgressLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("GoalId") + .HasColumnType("uuid"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("PreviousValue") + .HasColumnType("numeric"); + + b.Property("Value") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("GoalId"); + + b.ToTable("GoalProgressLogs"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Habit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChecklistItems") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Days") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DueDate") + .HasColumnType("date"); + + b.Property("DueEndTime") + .HasColumnType("time without time zone"); + + b.Property("DueTime") + .HasColumnType("time without time zone"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("FrequencyQuantity") + .HasColumnType("integer"); + + b.Property("FrequencyUnit") + .HasColumnType("integer"); + + b.Property("IsBadHabit") + .HasColumnType("boolean"); + + b.Property("IsCompleted") + .HasColumnType("boolean"); + + b.Property("IsFlexible") + .HasColumnType("boolean"); + + b.Property("IsGeneral") + .HasColumnType("boolean"); + + b.Property("ParentHabitId") + .HasColumnType("uuid"); + + b.Property("Position") + .HasColumnType("integer"); + + b.Property("ReminderEnabled") + .HasColumnType("boolean"); + + b.Property("ReminderTimes") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'[15]'::jsonb"); + + b.Property("ScheduledReminders") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'[]'::jsonb"); + + b.Property("SlipAlertEnabled") + .HasColumnType("boolean"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ParentHabitId"); + + b.HasIndex("UserId"); + + b.ToTable("Habits"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.HabitLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("HabitId") + .HasColumnType("uuid"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("HabitId", "Date"); + + b.ToTable("HabitLogs"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("HabitId") + .HasColumnType("uuid"); + + b.Property("IsRead") + .HasColumnType("boolean"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "IsRead"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.PushSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Auth") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Endpoint") + .IsRequired() + .HasColumnType("text"); + + b.Property("P256dh") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Endpoint") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscriptions"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Referral", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CompletedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferredUserId") + .HasColumnType("uuid"); + + b.Property("ReferrerId") + .HasColumnType("uuid"); + + b.Property("RewardGrantedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ReferredUserId") + .IsUnique(); + + b.HasIndex("ReferrerId"); + + b.ToTable("Referrals"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.SentReminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("HabitId") + .HasColumnType("uuid"); + + b.Property("MinutesBefore") + .HasColumnType("integer"); + + b.Property("ReminderTimeUtc") + .HasColumnType("time without time zone"); + + b.Property("SentAtUtc") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("HabitId", "Date", "MinutesBefore") + .IsUnique(); + + b.ToTable("SentReminders"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.SentSlipAlert", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("HabitId") + .HasColumnType("uuid"); + + b.Property("SentAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("WeekStart") + .HasColumnType("date"); + + b.HasKey("Id"); + + b.HasIndex("HabitId", "WeekStart") + .IsUnique(); + + b.ToTable("SentSlipAlerts"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.StreakFreeze", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UsedOnDate") + .HasColumnType("date"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "UsedOnDate") + .IsUnique(); + + b.ToTable("StreakFreezes"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Color") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "Name") + .IsUnique(); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdRewardBonusMessages") + .HasColumnType("integer"); + + b.Property("AdRewardsClaimedToday") + .HasColumnType("integer"); + + b.Property("AiMemoryEnabled") + .HasColumnType("boolean"); + + b.Property("AiMessagesResetAt") + .HasColumnType("timestamp with time zone"); + + b.Property("AiMessagesUsedThisMonth") + .HasColumnType("integer"); + + b.Property("AiSummaryEnabled") + .HasColumnType("boolean"); + + b.Property("ColorScheme") + .HasColumnType("text"); + + b.Property("CreatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("CurrentStreak") + .HasColumnType("integer"); + + b.Property("DeactivatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("GoogleAccessToken") + .HasColumnType("text"); + + b.Property("GoogleRefreshToken") + .HasColumnType("text"); + + b.Property("HasCompletedOnboarding") + .HasColumnType("boolean"); + + b.Property("HasImportedCalendar") + .HasColumnType("boolean"); + + b.Property("IsDeactivated") + .HasColumnType("boolean"); + + b.Property("IsLifetimePro") + .HasColumnType("boolean"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("LastActiveDate") + .HasColumnType("date"); + + b.Property("LastAdRewardAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("LongestStreak") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Plan") + .HasColumnType("integer"); + + b.Property("PlanExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferralCode") + .HasColumnType("text"); + + b.Property("ReferralCouponId") + .HasColumnType("text"); + + b.Property("ReferredByUserId") + .HasColumnType("uuid"); + + b.Property("ScheduledDeletionAt") + .HasColumnType("timestamp with time zone"); + + b.Property("StripeCustomerId") + .HasColumnType("text"); + + b.Property("StripeSubscriptionId") + .HasColumnType("text"); + + b.Property("SubscriptionInterval") + .HasColumnType("integer"); + + b.Property("ThemePreference") + .HasColumnType("text"); + + b.Property("TimeZone") + .HasColumnType("text"); + + b.Property("TotalXp") + .HasColumnType("integer"); + + b.Property("TrialEndsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("WeekStartDay") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("ReferralCode") + .IsUnique() + .HasFilter("\"ReferralCode\" IS NOT NULL"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.UserAchievement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AchievementId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("EarnedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "AchievementId") + .IsUnique(); + + b.ToTable("UserAchievements"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.UserFact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("DeletedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtractedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("FactText") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("UpdatedAtUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "IsDeleted"); + + b.ToTable("UserFacts"); + }); + + modelBuilder.Entity("HabitGoals", b => + { + b.HasOne("Orbit.Domain.Entities.Goal", null) + .WithMany() + .HasForeignKey("GoalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Orbit.Domain.Entities.Habit", null) + .WithMany() + .HasForeignKey("HabitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("HabitTags", b => + { + b.HasOne("Orbit.Domain.Entities.Habit", null) + .WithMany() + .HasForeignKey("HabitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Orbit.Domain.Entities.Tag", null) + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.ApiKey", b => + { + b.HasOne("Orbit.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.GoalProgressLog", b => + { + b.HasOne("Orbit.Domain.Entities.Goal", null) + .WithMany("ProgressLogs") + .HasForeignKey("GoalId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Habit", b => + { + b.HasOne("Orbit.Domain.Entities.Habit", null) + .WithMany("Children") + .HasForeignKey("ParentHabitId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.HabitLog", b => + { + b.HasOne("Orbit.Domain.Entities.Habit", null) + .WithMany("Logs") + .HasForeignKey("HabitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.PushSubscription", b => + { + b.HasOne("Orbit.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.StreakFreeze", b => + { + b.HasOne("Orbit.Domain.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Goal", b => + { + b.Navigation("ProgressLogs"); + }); + + modelBuilder.Entity("Orbit.Domain.Entities.Habit", b => + { + b.Navigation("Children"); + + b.Navigation("Logs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.cs b/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.cs new file mode 100644 index 00000000..a08e142b --- /dev/null +++ b/src/Orbit.Infrastructure/Migrations/20260401005230_RemoveMissionsAndTours.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Orbit.Infrastructure.Migrations +{ + /// + public partial class RemoveMissionsAndTours : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CompletedTours", + table: "Users"); + + migrationBuilder.DropColumn( + name: "HasDismissedMissions", + table: "Users"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CompletedTours", + table: "Users", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "HasDismissedMissions", + table: "Users", + type: "boolean", + nullable: false, + defaultValue: false); + } + } +} diff --git a/src/Orbit.Infrastructure/Migrations/OrbitDbContextModelSnapshot.cs b/src/Orbit.Infrastructure/Migrations/OrbitDbContextModelSnapshot.cs index 4fa1a4f0..be1bda9f 100644 --- a/src/Orbit.Infrastructure/Migrations/OrbitDbContextModelSnapshot.cs +++ b/src/Orbit.Infrastructure/Migrations/OrbitDbContextModelSnapshot.cs @@ -575,9 +575,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ColorScheme") .HasColumnType("text"); - b.Property("CompletedTours") - .HasColumnType("text"); - b.Property("CreatedAtUtc") .HasColumnType("timestamp with time zone"); @@ -600,9 +597,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("HasCompletedOnboarding") .HasColumnType("boolean"); - b.Property("HasDismissedMissions") - .HasColumnType("boolean"); - b.Property("HasImportedCalendar") .HasColumnType("boolean"); diff --git a/tests/Orbit.Application.Tests/Commands/Profile/ProfileCommandHandlerTests.cs b/tests/Orbit.Application.Tests/Commands/Profile/ProfileCommandHandlerTests.cs index da8eafb3..46d6ab7b 100644 --- a/tests/Orbit.Application.Tests/Commands/Profile/ProfileCommandHandlerTests.cs +++ b/tests/Orbit.Application.Tests/Commands/Profile/ProfileCommandHandlerTests.cs @@ -141,39 +141,4 @@ public async Task CompleteOnboarding_Valid_UpdatesAndSaves() await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } - // ----- DismissMissions ----- - - [Fact] - public async Task DismissMissions_Valid_UpdatesAndSaves() - { - var user = CreateTestUser(); - SetupUserFound(user); - - var handler = new DismissMissionsCommandHandler(_userRepo, _unitOfWork); - var command = new DismissMissionsCommand(UserId); - - var result = await handler.Handle(command, CancellationToken.None); - - result.IsSuccess.Should().BeTrue(); - user.HasDismissedMissions.Should().BeTrue(); - await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); - } - - // ----- MarkTourCompleted ----- - - [Fact] - public async Task MarkTourCompleted_Valid_UpdatesAndSaves() - { - var user = CreateTestUser(); - SetupUserFound(user); - - var handler = new MarkTourCompletedCommandHandler(_userRepo, _unitOfWork); - var command = new MarkTourCompletedCommand(UserId, "habits"); - - var result = await handler.Handle(command, CancellationToken.None); - - result.IsSuccess.Should().BeTrue(); - user.CompletedTours.Should().Contain("habits"); - await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); - } } diff --git a/tests/Orbit.Domain.Tests/Entities/UserTests.cs b/tests/Orbit.Domain.Tests/Entities/UserTests.cs index f5432f10..e13e4d66 100644 --- a/tests/Orbit.Domain.Tests/Entities/UserTests.cs +++ b/tests/Orbit.Domain.Tests/Entities/UserTests.cs @@ -186,27 +186,6 @@ public void SetTimeZone_Invalid_ReturnsFailure() result.Error.Should().Contain("Invalid timezone"); } - [Fact] - public void MarkTourCompleted_AddsTour() - { - var user = CreateValidUser(); - - user.MarkTourCompleted("today"); - - user.CompletedTours.Should().Contain("today"); - } - - [Fact] - public void MarkTourCompleted_Duplicate_NoDuplicates() - { - var user = CreateValidUser(); - - user.MarkTourCompleted("today"); - user.MarkTourCompleted("today"); - - user.CompletedTours!.Split(',').Where(t => t == "today").Should().HaveCount(1); - } - [Fact] public void SetStripeSubscription_SetsPlanToPro() {