feat(admin): Tierliste zeigt alle erreichten Raenge
found.md: Admin -> Tiere zeigte nur den hoechsten Rang (Cache referralRankId). Die Liste laedt jetzt alle ReferralRankAchievements des Owners (nach Schwelle) und zeigt sie als Symbole, unabhaengig vom Vorteil NAME_BADGE; entzogene Raenge ausgegraut, Tooltip mit 'manuell vergeben' / 'entzogen'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2bd4b2d973
commit
dbc416c43c
3 files changed
+46
-2
No files matched your search
@@ -9,7 +9,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { PetAvatar } from "@/components/ui/pet-avatar";
|
||||
import { ReferralRankBadge } from "@/components/referrals/ReferralRankBadge";
|
||||
import { AchievedRankBadges } from "@/components/admin/pets/AchievedRankBadges";
|
||||
import { PetDetailSheet } from "@/components/admin/pets/PetDetailSheet";
|
||||
|
||||
type Filter = "all" | "verified" | "founding" | "ranked" | "bannedOwner";
|
||||
@@ -135,7 +135,7 @@ export default function AdminPetsPage() {
|
||||
</span>
|
||||
{pet.isVerified && <BadgeCheck className="h-3.5 w-3.5 fill-blue-500 text-background" aria-label="Verifiziert" />}
|
||||
{pet.isFoundingMember && <Sprout className="h-3.5 w-3.5 text-emerald-600" aria-label="Gründungsmitglied" />}
|
||||
<ReferralRankBadge owner={pet.owner} />
|
||||
<AchievedRankBadges achievements={pet.owner.rankAchievements} />
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-muted-foreground">{pet.species.name}</td>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { useReferralRanks } from "@/hooks/useReferralRanks";
|
||||
import { ReferralIcon } from "@/components/referrals/ReferralRankBadge";
|
||||
|
||||
type Achievement = { rankId: string; revokedAt: Date | string | null; grantedManually: boolean };
|
||||
|
||||
/**
|
||||
* Admin → Tiere: alle Ränge, die der Owner je erreicht hat (found.md — vorher
|
||||
* nur der höchste). Unabhängig vom Vorteil NAME_BADGE, weil das hier eine
|
||||
* Übersicht fürs Team ist. Entzogene Ränge bleiben ausgegraut sichtbar.
|
||||
*/
|
||||
export function AchievedRankBadges({ achievements }: { achievements: Achievement[] }) {
|
||||
const { byId } = useReferralRanks();
|
||||
const items = achievements.flatMap((achievement) => {
|
||||
const rank = byId.get(achievement.rankId);
|
||||
return rank ? [{ achievement, rank }] : [];
|
||||
});
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<span className="inline-flex shrink-0 items-center gap-0.5">
|
||||
{items.map(({ achievement, rank }) => {
|
||||
const isRevoked = !!achievement.revokedAt;
|
||||
const status = isRevoked ? " · entzogen" : achievement.grantedManually ? " · manuell vergeben" : "";
|
||||
return (
|
||||
<span key={rank.id} title={`${rank.name}${status}`} className={isRevoked ? "opacity-30" : undefined}>
|
||||
<ReferralIcon
|
||||
icon={rank.icon}
|
||||
className="h-3.5 w-3.5"
|
||||
style={{ color: rank.color, fill: isRevoked ? "none" : rank.color }}
|
||||
aria-label={`${rank.name}${status}`}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -97,6 +97,11 @@ export const petAdminRouter = router({
|
||||
sponsorSince: true,
|
||||
sponsorPeriodEnd: true,
|
||||
ban: { select: { expiresAt: true } },
|
||||
// found.md: die Liste zeigt alle erreichten Ränge, nicht nur den höchsten (Cache referralRankId).
|
||||
rankAchievements: {
|
||||
select: { rankId: true, revokedAt: true, grantedManually: true },
|
||||
orderBy: { rank: { threshold: "asc" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
_count: { select: { posts: true, followers: true } },
|
||||
|
||||
Reference in new issue
Block a user