All files / src/app/student-page/page/dashboard/components sortAchievements.js

94.44% Statements 17/18
95.45% Branches 21/22
100% Functions 5/5
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58                                  32x 8x     24x 39x   6x 6x   6x 5x 4x 4x   39x   24x                       2x                 10x    
/**
 * EI-2966 — Achievements sorting + summary helper.
 *
 * Pure helper for the AchievementsCard widget.  Given the
 * `achievements` array returned by `GET /v1/student/dashboard/
 * achievements/fetch`, returns:
 *   - `sorted`: earned badges first (sorted by earned_at ASC, with
 *     null earned_at last among earned), then locked badges in the
 *     server-returned order.
 *   - `earnedCount` / `lockedCount` for the card's summary chip.
 *
 * The original server array order is preserved within each group so
 * the FE doesn't impose its own opinion on badge ordering beyond
 * "earned first".
 */
 
export function sortAchievements(achievements) {
    if (!Array.isArray(achievements) || achievements.length === 0) {
        return { sorted: [], earnedCount: 0, lockedCount: 0 };
    }
 
    const earned = achievements
        .filter((a) => a && a.earned)
        .sort((a, b) => {
            const aAt = a.earned_at || "";
            const bAt = b.earned_at || "";
            // Empty string sorts LAST among earned (so dateless-earned go after dated ones).
            if (!aAt && !bAt) return 0;
            if (!aAt) return 1;
            Iif (!bAt) return -1;
            return aAt.localeCompare(bAt);
        });
    const locked = achievements.filter((a) => a && !a.earned);
 
    return {
        sorted: [...earned, ...locked],
        earnedCount: earned.length,
        lockedCount: locked.length,
    };
}
 
/**
 * Map a server-side `icon` slug to a stable identifier used by the
 * FE for selecting the MUI icon component.  Kept as a separate
 * helper so the slug → component lookup table is unit-testable.
 */
export const ACHIEVEMENT_ICON_SLUGS = new Set([
    "rocket",
    "star",
    "trophy",
    "flame",
    "medal",
]);
 
export function isKnownIconSlug(slug) {
    return typeof slug === "string" && ACHIEVEMENT_ICON_SLUGS.has(slug);
}