From 3644bdd3a01ad710d5c47dd7ea7cd16d602891e1 Mon Sep 17 00:00:00 2001 From: Tahmid Ahmed Date: Sat, 1 Aug 2026 23:08:41 -0400 Subject: [PATCH 1/3] Update Achievement & AchievementDto to include leaderboardId --- .../db/models/achievements/Achievement.java | 3 +++ .../achievements/AchievementRepository.java | 2 ++ .../achievements/AchievementSqlRepository.java | 17 ++++++++++++----- .../common/dto/achievement/AchievementDto.java | 4 ++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/models/achievements/Achievement.java b/src/main/java/org/patinanetwork/codebloom/common/db/models/achievements/Achievement.java index 950504952..70a09d814 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/models/achievements/Achievement.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/models/achievements/Achievement.java @@ -23,6 +23,9 @@ public class Achievement { @NotNullColumn private String userId; + @NotNullColumn + private String leaderboardId; + @NotNullColumn private AchievementPlaceEnum place; diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepository.java b/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepository.java index dbf7e7d23..9075c47cb 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepository.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepository.java @@ -15,6 +15,7 @@ public interface AchievementRepository { *
  • title *
  • description *
  • isActive + *
  • leaderboardId * */ void createAchievement(Achievement achievement); @@ -29,6 +30,7 @@ public interface AchievementRepository { *
  • description *
  • isActive *
  • deletedAt + *
  • leaderboardId * * * @return updated achievement if successful diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementSqlRepository.java b/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementSqlRepository.java index 09bffe052..0bed46828 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementSqlRepository.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementSqlRepository.java @@ -26,6 +26,7 @@ public class AchievementSqlRepository implements AchievementRepository { var title = rs.getString("title"); var description = rs.getString("description"); var isActive = rs.getBoolean("isActive"); + var leaderboardId = rs.getString("leaderboardId"); var createdAt = StandardizedOffsetDateTime.normalize(rs.getObject("createdAt", OffsetDateTime.class)); OffsetDateTime deletedAt = StandardizedOffsetDateTime.normalize(rs.getObject("deletedAt", OffsetDateTime.class)); @@ -34,6 +35,7 @@ public class AchievementSqlRepository implements AchievementRepository { .userId(userId) .place(place) .leaderboard(leaderboard) + .leaderboardId(leaderboardId) .title(title) .description(description) .isActive(isActive) @@ -53,9 +55,9 @@ public void createAchievement(final Achievement achievement) { achievement.setId(UUID.randomUUID().toString()); String sql = """ INSERT INTO "Achievement" - (id, "userId", place, leaderboard, title, description, "isActive", "deletedAt") + (id, "userId", place, leaderboard, title, description, "isActive", "deletedAt", "leaderboardId") VALUES - (:id, :userId, :place, :leaderboard, :title, :description, :isActive, :deletedAt) + (:id, :userId, :place, :leaderboard, :title, :description, :isActive, :deletedAt, :leaderboardId) RETURNING "createdAt" """; @@ -71,6 +73,7 @@ public void createAchievement(final Achievement achievement) { .map(Enum::name) .orElse(null), Types.OTHER) + .param("leaderboardId", UUID.fromString(achievement.getLeaderboardId())) .param("title", achievement.getTitle()) .param("description", achievement.getDescription()) .param("isActive", achievement.isActive()) @@ -94,7 +97,8 @@ public Achievement updateAchievement(final Achievement achievement) { title = :title, description = :description, "isActive" = :isActive, - "deletedAt" = :deletedAt + "deletedAt" = :deletedAt, + "leaderboardId" = :leaderboardId WHERE id = :id """; @@ -108,6 +112,7 @@ public Achievement updateAchievement(final Achievement achievement) { .map(Enum::name) .orElse(null), Types.OTHER) + .param("leaderboardId", achievement.getLeaderboardId()) .param("title", achievement.getTitle()) .param("description", achievement.getDescription()) .param("isActive", achievement.isActive()) @@ -150,7 +155,8 @@ public Achievement getAchievementById(final String id) { description, "isActive", "createdAt", - "deletedAt" + "deletedAt", + "leaderboardId" FROM "Achievement" WHERE @@ -178,7 +184,8 @@ public List getAchievementsByUserId(final String userId) { description, "isActive", "createdAt", - "deletedAt" + "deletedAt", + "leaderboardId" FROM "Achievement" WHERE diff --git a/src/main/java/org/patinanetwork/codebloom/common/dto/achievement/AchievementDto.java b/src/main/java/org/patinanetwork/codebloom/common/dto/achievement/AchievementDto.java index 3d12f01fc..73f15a959 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/dto/achievement/AchievementDto.java +++ b/src/main/java/org/patinanetwork/codebloom/common/dto/achievement/AchievementDto.java @@ -30,6 +30,9 @@ public class AchievementDto { @Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true) private Tag leaderboard; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private String leaderboardId; + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) private String title; @@ -52,6 +55,7 @@ public static AchievementDto fromAchievement(final Achievement achievement) { .leaderboard(achievement.getLeaderboard()) .place(achievement.getPlace()) .title(achievement.getTitle()) + .leaderboardId(achievement.getLeaderboardId()) .description(achievement.getDescription()) .isActive(achievement.isActive()) .createdAt(achievement.getCreatedAt()) From 5956d5a4d3f28be077bfdcfa0d57c579d4bbf61a Mon Sep 17 00:00:00 2001 From: Tahmid Ahmed Date: Sun, 2 Aug 2026 00:11:32 -0400 Subject: [PATCH 2/3] fix tests --- .../db/repos/achievements/AchievementRepositoryTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepositoryTest.java b/src/test/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepositoryTest.java index 690d76d0e..5c7935add 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepositoryTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/db/repos/achievements/AchievementRepositoryTest.java @@ -22,6 +22,8 @@ public class AchievementRepositoryTest extends BaseRepositoryTest { private Achievement testAchievement; private Achievement deletableAchievement; private String mockUserId = "ed3bfe18-e42a-467f-b4fa-07e8da4d2555"; + // Not expired leaderboard + private String mockLeaderboardId = "39bc2def-669f-4383-8ea3-7202efd613f2"; @Autowired public AchievementRepositoryTest(final AchievementRepository repo) { @@ -34,6 +36,7 @@ void createAchievement() { .userId(mockUserId) .place(AchievementPlaceEnum.ONE) .leaderboard(null) + .leaderboardId(mockLeaderboardId) .title("Test Achievement") .description("Integration test achievement") .isActive(true) @@ -78,6 +81,7 @@ void testUpdateAchievement() { .userId(testAchievement.getUserId()) .place(AchievementPlaceEnum.THREE) .leaderboard(Tag.Patina) + .leaderboardId(mockLeaderboardId) .title("Updated Title") .description("Updated Description") .isActive(false) @@ -101,6 +105,7 @@ void testDeleteAchievementById() { .userId(mockUserId) .place(AchievementPlaceEnum.ONE) .leaderboard(null) + .leaderboardId(mockLeaderboardId) .title("Deletable Achievement") .description("Should be deleted") .isActive(true) From 585338a138feacf5d771256937fea618ef552e13 Mon Sep 17 00:00:00 2001 From: Tahmid Ahmed Date: Sat, 1 Aug 2026 23:13:22 -0400 Subject: [PATCH 3/3] Update achievement to link to attached leaderboard (and with specific filter enabled if not global) --- js/src/components/ui/tags/UserAchievement.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/js/src/components/ui/tags/UserAchievement.tsx b/js/src/components/ui/tags/UserAchievement.tsx index 315397ccc..1124ca2e3 100644 --- a/js/src/components/ui/tags/UserAchievement.tsx +++ b/js/src/components/ui/tags/UserAchievement.tsx @@ -5,6 +5,7 @@ import { ApiUtils } from "@/lib/api/utils"; import { Image, Tooltip, Box, Text, Stack, Divider } from "@mantine/core"; import dayjs from "dayjs"; import { useMemo } from "react"; +import { Link } from "react-router-dom"; type AchievementDto = Api<"AchievementDto">; interface UserAchievementProps { @@ -80,7 +81,11 @@ function GlobalTrophyBadge({ achievement }: AchievementBadgeProps) { withArrow position="top" > - + 🏆 @@ -124,7 +129,11 @@ function LeaderboardAchievementBadge({ withArrow position="top" > - +