[AB-xxx] adding rank to leaderboard page

changing design of leaderboard page
fixing role not having an id
This commit is contained in:
Sheldan
2024-03-26 22:13:18 +01:00
parent e91becee0d
commit 675da8d5d8
7 changed files with 54 additions and 46 deletions

View File

@@ -44,11 +44,12 @@ public class LeaderboardController {
Pageable pageable) { Pageable pageable) {
AServer server = serverManagementService.loadServer(serverId); AServer server = serverManagementService.loadServer(serverId);
Guild guild = guildService.getGuildById(serverId); Guild guild = guildService.getGuildById(serverId);
return userExperienceManagementService.loadAllUsersPaginated(server, pageable) Page<AUserExperience> allElements = userExperienceManagementService.loadAllUsersPaginated(server, pageable);
.map(userExperience -> convertFromUser(guild, userExperience)); return allElements
.map(userExperience -> convertFromUser(guild, userExperience, pageable, allElements));
} }
private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience) { private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience, Pageable pageable, Page<AUserExperience> page) {
Long userId = aUserExperience.getUser().getUserReference().getId(); Long userId = aUserExperience.getUser().getUserReference().getId();
Member member = guild.getMember(UserSnowflake.fromId(userId)); Member member = guild.getMember(UserSnowflake.fromId(userId));
AExperienceRole experienceRole = aUserExperience.getCurrentExperienceRole(); AExperienceRole experienceRole = aUserExperience.getCurrentExperienceRole();
@@ -70,6 +71,7 @@ public class LeaderboardController {
.id(userId) .id(userId)
.messages(aUserExperience.getMessageCount()) .messages(aUserExperience.getMessageCount())
.level(aUserExperience.getLevelOrDefault()) .level(aUserExperience.getLevelOrDefault())
.rank((int) pageable.getOffset() + page.getContent().indexOf(aUserExperience) + 1)
.experience(aUserExperience.getExperience()) .experience(aUserExperience.getExperience())
.role(roleDisplay) .role(roleDisplay)
.member(userDisplay) .member(userDisplay)

View File

@@ -11,6 +11,7 @@ import lombok.Getter;
public class UserExperienceDisplay { public class UserExperienceDisplay {
private UserDisplay member; private UserDisplay member;
private Long id; private Long id;
private Integer rank;
private Integer level; private Integer level;
private Long experience; private Long experience;
private Long messages; private Long messages;

View File

@@ -18,7 +18,8 @@ public class RoleDisplay {
public static RoleDisplay fromRole(Role role) { public static RoleDisplay fromRole(Role role) {
RoleDisplayBuilder builder = builder() RoleDisplayBuilder builder = builder()
.name(role.getName()); .name(role.getName())
.id(role.getIdLong());
Color roleColor = role.getColor(); Color roleColor = role.getColor();
if(roleColor != null) { if(roleColor != null) {
builder.r(roleColor.getRed()). builder.r(roleColor.getRed()).

View File

@@ -6,6 +6,7 @@ export const ExperienceConfigDisplay = ({serverId}: { serverId: bigint }) => {
const [roles, setRoles] = useState<ExperienceRole[]>([]) const [roles, setRoles] = useState<ExperienceRole[]>([])
const [hasError, setError] = useState(false) const [hasError, setError] = useState(false)
const [isOpen, setOpen] = useState(false)
async function loadConfig() { async function loadConfig() {
try { try {
const configResponse = await fetch(`/experience/v1/leaderboards/${serverId}/config`) const configResponse = await fetch(`/experience/v1/leaderboards/${serverId}/config`)
@@ -23,37 +24,39 @@ export const ExperienceConfigDisplay = ({serverId}: { serverId: bigint }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
},[]) },[])
function toggleOpen() {
setOpen(!isOpen)
}
return ( return (
<> <>
{!hasError ? {!hasError ?
<div className="py-10"> <div className="bg-gray-800 p-4 mx-auto w-4/5 rounded-lg">
<h2 className="text-4xl font-extrabold leading-none tracking-tight text-white">Role <button className="w-full flex justify-between items-center p-2 px-4" onClick={toggleOpen}>
config</h2> <h2 className="text-xl font-extrabold leading-none tracking-tight text-gray-100">Role config</h2>
<table className="w-full text-gray-400"> <div>
<thead <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" className={`w-6 h-6 stroke-white stroke-2 duration-300 ${isOpen ? "rotate-180" : ""}`}>
className="text-xs uppercase bg-gray-700 text-gray-400"> <path strokeLinecap="round" strike-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
<tr> </svg>
<th className="px-6 py-3 w-1/2">Role</th> </div>
<th className="px-6 py-3 w-1/8">Level</th> </button>
</tr> <div className={`grid grid-cols-1 ${isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'} overflow-hidden duration-300`}>
</thead> <div className="w-full gap-3 min-h-0 overflow-hidden flex flex-col items-start">
<tbody>
{roles.map(role => {roles.map(role =>
<tr key={role.role.id} className="border-b bg-gray-800 border-gray-700"> <div key={role.role.id} className="border-gray-700 flex gap-2 p-2 px-4 items-center flex-row-reverse">
<td className="px-6 py-4"> <p>
<RoleDisplay role={role.role}/> <RoleDisplay role={role.role}/>
</td> </p>
<td className="px-6 py-4 text-center"> <p className="font-bold text-gray-200">
{role.level} {role.level}
</td> </p>
</tr>)} </div>)}
</tbody> </div>
</table>
{roles.length === 0 ? {roles.length === 0 ?
<div className="w-full flex items-center justify-center"> <div className="w-full flex items-center justify-center">
<span className="text-gray-400">No roles</span> <span className="text-gray-400">No roles</span>
</div> : ''} </div> : ''}
</div>
</div> </div>
: ''} : ''}
</> </>

View File

@@ -51,7 +51,7 @@ export function Leaderboard({serverId}: { serverId: bigint }) {
function loadMore() { function loadMore() {
loadLeaderboard(pageCount + 1, pageSize) loadLeaderboard(pageCount + 1, pageSize)
} }
let loadMoreButton = <button className="w-full bg-gray-500 hover:bg-gray-700 text-white" onClick={loadMore}>Load more</button>; let loadMoreButton = <button className="w-full h-10 bg-gray-500 hover:bg-gray-700 text-white mt-4" onClick={loadMore}>Load more</button>;
return ( return (
<> <>
{!hasError ? {!hasError ?
@@ -67,42 +67,42 @@ export function Leaderboard({serverId}: { serverId: bigint }) {
alt="Icon" alt="Icon"
className="w-24"/> className="w-24"/>
: ''} : ''}
<h1 className="text-4xl font-extrabold leading-none tracking-tight md:text-5xl lg:text-6xl text-white">{'Leaderboard for ' + guildInfo.name}</h1> <h1 className="text-4xl font-extrabold leading-none tracking-tight md:text-5xl lg:text-6xl text-white px-5">{guildInfo.name + ' Leaderboard'}</h1>
</div> </div>
</div> </div>
<div className="flex"> <div className="flex flex-col">
<div className="text-sm text-left w-3/4 "> <div>
<ExperienceConfigDisplay serverId={serverId}/>
</div>
<div className="text-sm text-left w-full mt-4">
<table className="w-full text-gray-400"> <table className="w-full text-gray-400">
<thead <thead
className="text-xs uppercase bg-gray-700 text-gray-400"> className="text-xs uppercase bg-gray-700 text-gray-400">
<tr> <tr>
<th scope="col" className="px-6 py-3 w-1/3"> <th scope="col" className="px-2 py-3 w-5">
Rank
</th>
<th scope="col" className="px-6 py-3 w-1/2">
Member Member
</th> </th>
<th scope="col" className="px-6 py-3 w-1/6 text-center"> <th scope="col" className="px-6 py-3 w-1/4 text-center">
Experience Experience
</th> </th>
<th scope="col" className="px-6 py-3 w-1/6 text-center"> <th scope="col" className="px-6 py-3 w-1/4 text-center">
Messages Messages
</th> </th>
<th scope="col" className="px-6 py-3 w-1/6 text-center"> <th scope="col" className="px-6 py-3 w-5 text-center">
Level Level
</th> </th>
<th scope="col" className="px-6 py-3 w-1/3 text-center">
Role
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{members.map(member => <LeaderboardEntry key={member.id} member={member}/>)} {members.map((member, index) => <LeaderboardEntry key={member.id} index={index} member={member}/>)}
</tbody> </tbody>
</table> </table>
{hasMore ? loadMoreButton : ''} {hasMore ? loadMoreButton : ''}
</div> </div>
<div className="w-1/4 px-3">
<ExperienceConfigDisplay serverId={serverId}/>
</div>
</div> </div>
</> </>
: <ErrorDisplay/>} : <ErrorDisplay/>}

View File

@@ -1,8 +1,7 @@
import {ExperienceMember} from "../data/leaderboard"; import {ExperienceMember} from "../data/leaderboard";
import {RoleDisplay} from "./RoleDisplay";
import createStyle from "../utils/styleUtils"; import createStyle from "../utils/styleUtils";
export const LeaderboardEntry = ({member}: { member: ExperienceMember }) => { export const LeaderboardEntry = ({member, index}: { member: ExperienceMember, index: number }) => {
const userHasRole = member.role !== null; const userHasRole = member.role !== null;
const memberExists = member.member !== null; const memberExists = member.member !== null;
const nameColor = userHasRole ? createStyle(member.role!) : '' const nameColor = userHasRole ? createStyle(member.role!) : ''
@@ -13,7 +12,11 @@ export const LeaderboardEntry = ({member}: { member: ExperienceMember }) => {
</> : <>{member.id}</>; </> : <>{member.id}</>;
return ( return (
<> <>
<tr className="border-b bg-gray-800 border-gray-700"> <tr className={`${index % 2 === 0 ? "bg-gray-800" : "bg-gray-600"}`}>
<td
className="text-center">
{member.rank}
</td>
<td <td
className="px-2 py-4 font-medium whitespace-nowrap text-white flex items-center gap-3"> className="px-2 py-4 font-medium whitespace-nowrap text-white flex items-center gap-3">
{memberDisplay} {memberDisplay}
@@ -27,9 +30,6 @@ export const LeaderboardEntry = ({member}: { member: ExperienceMember }) => {
<td className="px-6 py-4 text-center"> <td className="px-6 py-4 text-center">
{member.level.toString()} {member.level.toString()}
</td> </td>
<td className="px-6 py-4 text-center">
{userHasRole ? <RoleDisplay role={member.role!}/> : 'No role'}
</td>
</tr> </tr>
</> </>

View File

@@ -1,5 +1,6 @@
export interface ExperienceMember { export interface ExperienceMember {
experience: bigint; experience: bigint;
rank: number;
id: bigint; id: bigint;
level: number; level: number;
messages: bigint; messages: bigint;