Skip to content

Commit b3cb3ee

Browse files
authored
Merge pull request #3143 from codecrafters-io/enhance-redeem-button-functionality
feat(gifts): enhance redeem button functionality and add tests
2 parents 7c4faf8 + 2e281a1 commit b3cb3ee

File tree

6 files changed

+131
-9
lines changed

6 files changed

+131
-9
lines changed

app/controllers/gifts/redeem.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,53 @@
11
import Controller from '@ember/controller';
22
import type { ModelType } from 'codecrafters-frontend/routes/gifts/redeem';
3+
import { inject as service } from '@ember/service';
4+
import { action } from '@ember/object';
5+
import { tracked } from '@glimmer/tracking';
6+
import type RouterService from '@ember/routing/router-service';
7+
import type AuthenticatorService from 'codecrafters-frontend/services/authenticator';
38

49
export default class GiftsRedeemController extends Controller {
510
declare model: ModelType;
11+
12+
@service declare router: RouterService;
13+
@service declare authenticator: AuthenticatorService;
14+
15+
@tracked isRedeemingGift = false;
16+
17+
get currentUserCanAccessMembershipBenefits() {
18+
return this.authenticator.currentUser && this.authenticator.currentUser.canAccessMembershipBenefits;
19+
}
20+
21+
get currentUserIsAnonymous() {
22+
return this.authenticator.isAnonymous;
23+
}
24+
25+
get redeemButtonIsDisabled() {
26+
// Button is disabled if user already has membership benefits
27+
return this.currentUserCanAccessMembershipBenefits || this.isRedeemingGift;
28+
}
29+
30+
@action
31+
async handleRedeemButtonClick() {
32+
if (this.currentUserIsAnonymous) {
33+
this.authenticator.initiateLogin();
34+
35+
return;
36+
}
37+
38+
if (this.redeemButtonIsDisabled) {
39+
return;
40+
}
41+
42+
this.isRedeemingGift = true;
43+
44+
try {
45+
await this.model.redeem({});
46+
this.router.transitionTo('catalog');
47+
} catch (error) {
48+
// TODO: Handle error appropriately
49+
this.isRedeemingGift = false;
50+
throw error;
51+
}
52+
}
653
}

app/models/membership-gift.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Model, { attr, belongsTo } from '@ember-data/model';
2+
import { memberAction } from 'ember-api-actions';
23
import type UserModel from 'codecrafters-frontend/models/user';
34

45
export default class MembershipGiftModel extends Model {
@@ -10,4 +11,11 @@ export default class MembershipGiftModel extends Model {
1011
@attr('string') declare giftMessage: string;
1112
@attr('number') declare validityInDays: number;
1213
@attr('string') declare secretToken: string;
14+
15+
declare redeem: (this: MembershipGiftModel, payload: unknown) => Promise<void>;
1316
}
17+
18+
MembershipGiftModel.prototype.redeem = memberAction({
19+
path: 'redeem',
20+
type: 'post',
21+
});

app/templates/gifts/redeem.hbs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,22 @@
2020
</p>
2121
</div>
2222

23-
<PrimaryButton data-test-claim-button {{on "click" (noop)}}>
24-
Claim Gift
23+
<PrimaryButton data-test-redeem-button @isDisabled={{this.redeemButtonIsDisabled}} {{on "click" this.handleRedeemButtonClick}}>
24+
{{#if this.currentUserIsAnonymous}}
25+
{{svg-jar "github" class="fill-current w-6 transform transition-all mr-3"}}
26+
{{/if}}
27+
28+
{{#if this.isRedeemingGift}}
29+
Redeeming...
30+
{{else}}
31+
Redeem Gift
32+
{{/if}}
33+
34+
{{#if this.currentUserIsAnonymous}}
35+
<EmberTooltip @text="Click to login via GitHub" />
36+
{{else if this.currentUserCanAccessMembershipBenefits}}
37+
<EmberTooltip @text="You already have full access to CodeCrafters. You can claim this gift once your membership expires." />
38+
{{/if}}
2539
</PrimaryButton>
2640
</div>
2741
</div>

tests/acceptance/concepts-test.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -876,10 +876,7 @@ module('Acceptance | concepts-test', function (hooks) {
876876
assert.ok(conceptsPage.conceptCards[3].draftLabel.isVisible, 'Draft label is visible for draft concepts');
877877

878878
await conceptsPage.conceptCards[3].draftLabel.hover();
879-
880-
assertTooltipContent(assert, {
881-
contentString: 'This concept is only visible to staff',
882-
});
879+
assertTooltipContent(assert, { contentString: 'This concept is only visible to staff' });
883880
});
884881

885882
test('draft concepts are visible to concept author', async function (assert) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import redeemGiftPage from 'codecrafters-frontend/tests/pages/redeem-gift-page';
2+
import testScenario from 'codecrafters-frontend/mirage/scenarios/test';
3+
import { module, test } from 'qunit';
4+
import { setupApplicationTest } from 'codecrafters-frontend/tests/helpers';
5+
import { signInAsSubscriber } from 'codecrafters-frontend/tests/support/authentication-helpers';
6+
import { assertTooltipContent } from 'ember-tooltips/test-support';
7+
8+
module('Acceptance | redeem-gift-page | redeem', function (hooks) {
9+
setupApplicationTest(hooks);
10+
11+
test('user cannot redeem a gift if they are not logged in', async function (assert) {
12+
testScenario(this.server);
13+
14+
this.server.schema.membershipGifts.create({
15+
secretToken: 'xyz',
16+
giftMessage: 'Happy Birthday! Enjoy your CodeCrafters membership.',
17+
validityInDays: 365,
18+
purchasedAt: new Date(),
19+
claimedAt: null,
20+
});
21+
22+
await redeemGiftPage.visit({ secret_token: 'xyz' });
23+
assert.notOk(redeemGiftPage.giftDetailsContainer.redeemButton.isDisabled, 'Redeem button should be enabled for anonymous users');
24+
25+
await redeemGiftPage.giftDetailsContainer.redeemButton.hover();
26+
assertTooltipContent(assert, { contentString: 'Click to login via GitHub' });
27+
});
28+
29+
test('user cannot redeem a gift if they already have a membership', async function (assert) {
30+
testScenario(this.server);
31+
32+
signInAsSubscriber(this.owner, this.server);
33+
34+
this.server.schema.membershipGifts.create({
35+
secretToken: 'xyz',
36+
giftMessage: 'Happy Birthday! Enjoy your CodeCrafters membership.',
37+
validityInDays: 365,
38+
purchasedAt: new Date(),
39+
claimedAt: null,
40+
});
41+
42+
await redeemGiftPage.visit({ secret_token: 'xyz' });
43+
assert.ok(redeemGiftPage.giftDetailsContainer.redeemButton.isDisabled, 'Redeem button should be disabled for users with existing membership');
44+
45+
await redeemGiftPage.giftDetailsContainer.redeemButton.hover();
46+
47+
assertTooltipContent(assert, {
48+
contentString: 'You already have full access to CodeCrafters. You can claim this gift once your membership expires.',
49+
});
50+
});
51+
});

tests/pages/redeem-gift-page.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { clickable, text, visitable } from 'ember-cli-page-object';
1+
import { clickable, text, visitable, hasClass, triggerable } from 'ember-cli-page-object';
22
import createPage from 'codecrafters-frontend/tests/support/create-page';
33

44
export default createPage({
55
visit: visitable('/gifts/redeem/:secret_token'),
66

77
giftDetailsContainer: {
8-
claimButton: { scope: '[data-test-claim-button]' },
8+
redeemButton: {
9+
scope: '[data-test-redeem-button]',
10+
isDisabled: hasClass('cursor-not-allowed'),
11+
hover: triggerable('mouseenter'),
12+
},
13+
914
giftMessage: text('[data-test-gift-message]'),
1015
scope: '[data-test-gift-details-container]',
1116
},
1217

13-
clickClaimButton: clickable('[data-test-claim-button]'),
18+
clickRedeemButton: clickable('[data-test-redeem-button]'),
1419
});

0 commit comments

Comments
 (0)