Skip to content

Commit 5d8976c

Browse files
committed
feat: add leaderboard rank calculations to entries table
Introduce leaderboard rank calculations to enhance the leaderboard's entries table functionality. Import the leaderboard rank calculations handler and incorporate it into the application. Update the entries table component to display user rankings correctly, leveraging tracked properties for state management. Implement server endpoints for retrieving and creating leaderboard rank calculations in the Mirage backend.
1 parent 923284d commit 5d8976c

File tree

5 files changed

+60
-6
lines changed

5 files changed

+60
-6
lines changed

app/components/leaderboard-page/entries-table.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
{{else if this.shouldShowSurroundingEntries}}
3232
<LeaderboardPage::EntriesTable::FillerRow @text="... other users ..." />
3333

34-
{{#each this.sortedSurroundingEntries as |entry|}}
35-
<LeaderboardPage::EntriesTable::Row @entry={{entry}} @rankText={{"#~5000"}} />
34+
{{#each this.sortedSurroundingEntriesWithRanks as |entryWithRank|}}
35+
<LeaderboardPage::EntriesTable::Row @entry={{entryWithRank.entry}} @rankText={{concat "#" entryWithRank.rank}} />
3636
{{/each}}
3737
{{/if}}
3838
</tbody>

app/components/leaderboard-page/entries-table.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import Component from '@glimmer/component';
22
import type AuthenticatorService from 'codecrafters-frontend/services/authenticator';
33
import type LanguageModel from 'codecrafters-frontend/models/language';
44
import type LeaderboardEntryModel from 'codecrafters-frontend/models/leaderboard-entry';
5+
import type LeaderboardRankCalculationModel from 'codecrafters-frontend/models/leaderboard-rank-calculation';
56
import type Store from '@ember-data/store';
67
import { inject as service } from '@ember/service';
7-
import { tracked } from '@glimmer/tracking';
88
import { task } from 'ember-concurrency';
9+
import { tracked } from '@glimmer/tracking';
910

1011
interface Signature {
1112
Element: HTMLDivElement;
@@ -21,6 +22,7 @@ export default class LeaderboardPageEntriesTable extends Component<Signature> {
2122
@service declare store: Store;
2223

2324
@tracked surroundingEntries: LeaderboardEntryModel[] = [];
25+
@tracked userRankCalculation: LeaderboardRankCalculationModel | null = null;
2426

2527
constructor(owner: unknown, args: Signature['Args']) {
2628
super(owner, args);
@@ -50,6 +52,17 @@ Harder stages have higher scores assigned to them.
5052
return !this.userIsInTopLeaderboardEntries && this.surroundingEntries.length > 0;
5153
}
5254

55+
get userEntryIndexInSurroundingEntries() {
56+
return this.sortedSurroundingEntries.findIndex((entry) => entry.user.id === this.authenticator.currentUserId);
57+
}
58+
59+
get sortedSurroundingEntriesWithRanks() {
60+
return this.sortedSurroundingEntries.map((entry, index) => ({
61+
entry: entry,
62+
rank: this.userRankCalculation!.rank + (index - this.userEntryIndexInSurroundingEntries),
63+
}));
64+
}
65+
5366
get sortedSurroundingEntries() {
5467
return this.surroundingEntries.filter((entry) => !entry.isBanned).sort((a, b) => b.score - a.score);
5568
}
@@ -74,6 +87,26 @@ Harder stages have higher scores assigned to them.
7487
user_id: this.authenticator.currentUserId, // Only used in tests since mirage doesn't have auth context
7588
filter_type: 'around_me',
7689
})) as unknown as LeaderboardEntryModel[];
90+
91+
const userRankCalculations = (await this.store.query('leaderboard-rank-calculation', {
92+
include: 'user',
93+
leaderboard_id: this.args.language.leaderboard!.id,
94+
user_id: this.authenticator.currentUserId, // Only used in tests since mirage doesn't have auth context
95+
})) as unknown as LeaderboardRankCalculationModel[];
96+
97+
this.userRankCalculation = userRankCalculations[0] || null;
98+
99+
// TODO: Also look at "outdated" user rank calculations?
100+
if (!this.userRankCalculation) {
101+
this.userRankCalculation = await this.store
102+
.createRecord('leaderboard-rank-calculation', {
103+
leaderboard: this.args.language.leaderboard!,
104+
user: this.authenticator.currentUser!,
105+
})
106+
.save();
107+
}
108+
109+
console.log(this.userRankCalculation!.rank);
77110
}
78111
});
79112
}

app/models/user.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import AffiliateEarningsPayoutModel from 'codecrafters-frontend/models/affiliate-earnings-payout';
12
import AffiliateLinkModel from 'codecrafters-frontend/models/affiliate-link';
23
import AffiliateReferralModel from 'codecrafters-frontend/models/affiliate-referral';
34
import BadgeAwardModel from 'codecrafters-frontend/models/badge-award';
45
import ConceptEngagementModel from 'codecrafters-frontend/models/concept-engagement';
5-
import config from 'codecrafters-frontend/config/environment';
66
import CourseExtensionIdeaVoteModel from 'codecrafters-frontend/models/course-extension-idea-vote';
77
import CourseIdeaVoteModel from 'codecrafters-frontend/models/course-idea-vote';
88
import CourseLanguageRequestModel from 'codecrafters-frontend/models/course-language-request';
@@ -16,15 +16,16 @@ import GitHubAppInstallationModel from 'codecrafters-frontend/models/github-app-
1616
import InstitutionMembershipGrantApplicationModel from 'codecrafters-frontend/models/institution-membership-grant-application';
1717
import InstitutionMembershipGrantModel from 'codecrafters-frontend/models/institution-membership-grant';
1818
import InvoiceModel from 'codecrafters-frontend/models/invoice';
19+
import LeaderboardRankCalculationModel from './leaderboard-rank-calculation';
1920
import Model, { attr, hasMany } from '@ember-data/model';
21+
import PromotionalDiscountModel from 'codecrafters-frontend/models/promotional-discount';
2022
import ReferralActivationModel from 'codecrafters-frontend/models/referral-activation';
21-
import AffiliateEarningsPayoutModel from 'codecrafters-frontend/models/affiliate-earnings-payout';
2223
import ReferralLinkModel from 'codecrafters-frontend/models/referral-link';
2324
import RepositoryModel from 'codecrafters-frontend/models/repository';
24-
import PromotionalDiscountModel from 'codecrafters-frontend/models/promotional-discount';
2525
import SubscriptionModel from 'codecrafters-frontend/models/subscription';
2626
import TeamMembershipModel from 'codecrafters-frontend/models/team-membership';
2727
import UserProfileEventModel from 'codecrafters-frontend/models/user-profile-event';
28+
import config from 'codecrafters-frontend/config/environment';
2829
import { collectionAction, memberAction } from 'ember-api-actions';
2930
import { inject as service } from '@ember/service';
3031

@@ -70,6 +71,7 @@ export default class UserModel extends Model {
7071
@hasMany('institution-membership-grant', { async: false, inverse: 'user' })
7172
institutionMembershipGrants!: InstitutionMembershipGrantModel[];
7273

74+
@hasMany('leaderboard-rank-calculation', { async: false, inverse: 'user' }) leaderboardRankCalculations!: LeaderboardRankCalculationModel[];
7375
@hasMany('referral-activation', { async: false, inverse: 'customer' }) referralActivationsAsCustomer!: ReferralActivationModel[];
7476
@hasMany('referral-activation', { async: false, inverse: 'referrer' }) referralActivationsAsReferrer!: ReferralActivationModel[];
7577
@hasMany('affiliate-earnings-payout', { async: false, inverse: 'user' }) affiliateEarningsPayouts!: AffiliateEarningsPayoutModel[];

mirage/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import institutionMembershipGrantApplications from './handlers/institution-membe
4343
import institutions from './handlers/institutions';
4444
import languages from './handlers/languages';
4545
import leaderboardEntries from './handlers/leaderboard-entries';
46+
import leaderboardRankCalculations from './handlers/leaderboard-rank-calculations';
4647
import logstreams from './handlers/logstreams';
4748
import onboardingSurveys from './handlers/onboarding-surveys';
4849
import perks from './handlers/perks';
@@ -181,6 +182,7 @@ function routes() {
181182
institutions(this);
182183
languages(this);
183184
leaderboardEntries(this);
185+
leaderboardRankCalculations(this);
184186
logstreams(this);
185187
onboardingSurveys(this);
186188
perks(this);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default function (server) {
2+
server.get('/leaderboard-rank-calculations', function (schema, request) {
3+
if (!request.queryParams.leaderboard_id) {
4+
throw new Error('No leaderboard ID provided');
5+
}
6+
7+
return schema.leaderboardRankCalculations.all().filter((calculation) => calculation.leaderboard.id === request.queryParams.leaderboard_id);
8+
});
9+
10+
server.post('/leaderboard-rank-calculations', function (schema, request) {
11+
const attrs = this.normalizedRequestAttrs();
12+
attrs.calculatedAt = new Date();
13+
attrs.rank = 37812;
14+
15+
return schema.leaderboardRankCalculations.create(attrs);
16+
});
17+
}

0 commit comments

Comments
 (0)