Skip to content

Commit 750f912

Browse files
authored
Merge pull request #3336 from codecrafters-io/enhance-gifts-redeem-page-styling
feat(gifts): enhance redeem page styling and update route metadata
2 parents 2ac5bc4 + ece6f7c commit 750f912

File tree

12 files changed

+199
-50
lines changed

12 files changed

+199
-50
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<div
2+
class="relative bg-gradient-to-br from-gray-850 to-gray-950 rounded-lg border border-white/8 mb-6 w-full max-w-2xs flex flex-col justify-center items-center overflow-hidden gift-card"
3+
>
4+
<div class="noisy-overlay absolute top-0 left-0 right-0 bottom-0 pointer-events-none"></div>
5+
6+
{{! Cutout wide slot !}}
7+
<div class="absolute top-0 left-0 right-0 h-10 flex items-end justify-center">
8+
<div class="w-20 h-4 bg-gray-950 rounded-full border border-white/10">
9+
</div>
10+
</div>
11+
12+
{{! Cutout top circle !}}
13+
<div class="absolute top-0 left-0 right-0 h-10 flex items-end justify-center">
14+
<div class="w-5 h-5 bg-gray-950 rounded-full border-t border-white/10 mb-px">
15+
</div>
16+
</div>
17+
18+
{{! <div class="absolute top-0 right-0 py-4 px-4">
19+
<span class="text-2xl text-gradient-to-b from-white to-gray-300 leading-7 font-bold">3</span><span
20+
class="text-xl text-gradient-to-b from-white to-gray-300 leading-7 font-bold"
21+
>mo</span>
22+
</div> }}
23+
24+
<div class="flex flex-col items-center px-6 pt-18 pb-8 w-full">
25+
<img src={{this.logoImage}} alt="CodeCrafters" class="w-16 h-16 mb-2" />
26+
27+
<span class="text-gradient-to-b from-white to-gray-300 text-2xl leading-9 font-bold mb-2">
28+
CodeCrafters
29+
</span>
30+
31+
<span class="text-gradient-to-b from-gray-400 to-gray-500 leading-5 uppercase text-sm font-medium tracking-wide text-center text-balance mb-6">
32+
Train your engineering skills like a pro athlete
33+
</span>
34+
35+
<span class="text-gradient-to-b from-white to-gray-300 text-2xl leading-9 font-bold">
36+
3 months
37+
</span>
38+
39+
<span class="text-gradient-to-b from-gray-400 to-gray-500 leading-5 uppercase text-xs font-medium tracking-wide text-center text-balance mt-0.5">
40+
All-access
41+
</span>
42+
</div>
43+
44+
<div class="bg-gray-950 border-t border-white/10 w-full">
45+
<div class="text-gray-400 text-xxs font-medium tracking-wide text-center text-balance p-2 uppercase">
46+
Expiry: 25 JUL '26
47+
</div>
48+
</div>
49+
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Component from '@glimmer/component';
2+
import logoImage from '/assets/images/logo/logomark-color.svg';
3+
4+
interface Signature {
5+
Element: HTMLDivElement;
6+
}
7+
8+
export default class RedeemGiftPageGiftCard extends Component<Signature> {
9+
logoImage = logoImage;
10+
}
11+
12+
declare module '@glint/environment-ember-loose/registry' {
13+
export default interface Registry {
14+
'RedeemGiftPage::GiftCard': typeof RedeemGiftPageGiftCard;
15+
}
16+
}

app/controllers/gifts/redeem.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
import BYOXBanner from '/assets/images/affiliate-program-features/byox-banner.svg';
2+
import BYOXBannerMobile from '/assets/images/affiliate-program-features/byox-banner-mobile.svg';
13
import Controller from '@ember/controller';
4+
import type AuthenticatorService from 'codecrafters-frontend/services/authenticator';
5+
import type RouterService from '@ember/routing/router-service';
26
import type { ModelType } from 'codecrafters-frontend/routes/gifts/redeem';
3-
import { inject as service } from '@ember/service';
47
import { action } from '@ember/object';
8+
import { inject as service } from '@ember/service';
59
import { tracked } from '@glimmer/tracking';
6-
import type RouterService from '@ember/routing/router-service';
7-
import type AuthenticatorService from 'codecrafters-frontend/services/authenticator';
810

911
export default class GiftsRedeemController extends Controller {
12+
BYOXBanner = BYOXBanner;
13+
BYOXBannerMobile = BYOXBannerMobile;
14+
1015
declare model: ModelType;
1116

1217
@service declare router: RouterService;

app/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
{{content-for "head-footer"}}
2222

2323
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&display=swap" rel="stylesheet">
24+
<link href="https://fonts.googleapis.com/css2?family=Shadows+Into+Light+Two&display=swap" rel="stylesheet">
25+
<link href="https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap" rel="stylesheet">
26+
2427
<link href="https://fonts.cdnfonts.com/css/monaco" rel="stylesheet">
2528

2629
<!-- HelpScout Beacon -->

app/routes/gifts/redeem.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import Route from '@ember/routing/route';
1+
import BaseRoute from 'codecrafters-frontend/utils/base-route';
22
import type Store from '@ember-data/store';
33
import { inject as service } from '@ember/service';
44
import RouterService from '@ember/routing/router-service';
55
import MembershipGiftModel from 'codecrafters-frontend/models/membership-gift';
6+
import RouteInfoMetadata, { RouteColorScheme } from 'codecrafters-frontend/utils/route-info-metadata';
67

78
export type ModelType = MembershipGiftModel;
89

9-
export default class GiftsRedeemRoute extends Route {
10+
export default class GiftsRedeemRoute extends BaseRoute {
1011
@service declare store: Store;
1112
@service declare router: RouterService;
1213

@@ -16,6 +17,10 @@ export default class GiftsRedeemRoute extends Route {
1617
}
1718
}
1819

20+
buildRouteInfoMetadata() {
21+
return new RouteInfoMetadata({ allowsAnonymousAccess: true, colorScheme: RouteColorScheme.Dark });
22+
}
23+
1924
async model(params: { secret_token: string }): Promise<ModelType | undefined> {
2025
const membershipGift = (
2126
(await this.store.query('membership-gift', { secret_token: params.secret_token })) as unknown as MembershipGiftModel[]

app/styles/app.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
@import url('pages/code-walkthrough-page.css');
1515
@import url('pages/course-stage-solution-page.css');
1616

17+
:root {
18+
/* --font-cursive: 'Shadows Into Light Two', cursive; */
19+
--font-cursive: 'Indie Flower', cursive;
20+
}
21+
1722
body {
1823
font-family: Montserrat, sans-serif; /* Required for fonts to work on Percy */
1924
}
@@ -239,3 +244,29 @@ body {
239244
.ember-popover[x-placement^='left'] .ember-popover-arrow {
240245
border-left-color: #3a3c47;
241246
}
247+
248+
.gift-card {
249+
animation: rotate-in 0.75s ease;
250+
transform-origin: center;
251+
}
252+
253+
@keyframes rotate-in {
254+
0% {
255+
transform: rotateY(-45deg) scale(0.8);
256+
opacity: 0;
257+
}
258+
259+
100% {
260+
transform: rotateY(0deg) scale(1);
261+
opacity: 1;
262+
}
263+
}
264+
265+
.noisy-overlay {
266+
background-image: url('/assets/images/bkg-noise.webp');
267+
background-repeat: repeat;
268+
background-size: 200px 200px;
269+
mix-blend-mode: normal;
270+
opacity: 0.035;
271+
pointer-events: none;
272+
}

app/templates/gifts/redeem.hbs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,78 @@
1-
<div class="max-w-2xl mx-auto px-4 py-8">
2-
<h1 class="text-2xl font-bold mb-6">Claim Your Gift</h1>
1+
<div class="bg-github-green-dot-wall">
2+
<div class="container mx-auto lg:max-w-(--breakpoint-xl) px-4 py-16">
3+
<div class="flex flex-col justify-center items-center mb-20">
4+
<h1 class="text-3xl sm:text-4xl leading-10 text-gradient-to-b from-white to-gray-300 font-bold mb-12 text-pretty text-center">
5+
Redeem Gift Card
6+
</h1>
37

4-
<div data-test-gift-details-container class="bg-white rounded-lg shadow-md p-6 mb-6">
5-
<div class="mb-4">
6-
<h2 class="text-lg font-semibold">Gift Details</h2>
7-
<p class="text-gray-600">You've received a CodeCrafters membership gift!</p>
8-
</div>
8+
<RedeemGiftPage::GiftCard />
9+
10+
{{#if @model.giftMessage}}
11+
<div class="px-4 py-2 mb-6 flex flex-col items-center max-w-3xs" data-test-gift-message-container>
12+
<div class="text-gray-500 uppercase text-xs font-medium tracking-wide mb-6 text-center">
13+
Message from sender:
14+
</div>
15+
16+
<div
17+
class="text-gray-100 font-(family-name:--font-cursive) text-xl whitespace-pre-wrap text-center text-pretty"
18+
>{{@model.giftMessage}}</div>
19+
</div>
20+
{{/if}}
21+
22+
<PrimaryButton @size="large" data-test-redeem-button @isDisabled={{this.redeemButtonIsDisabled}} {{on "click" this.handleRedeemButtonClick}}>
23+
<div class="flex items-center gap-2.5">
24+
{{#if this.currentUserIsAnonymous}}
25+
{{svg-jar "github" class="fill-current w-6 transform transition-all"}}
26+
{{/if}}
27+
28+
{{#if this.isRedeemingGift}}
29+
Redeeming...
30+
{{else}}
31+
Redeem Gift Card
32+
{{/if}}
33+
</div>
34+
35+
{{#if this.currentUserIsAnonymous}}
36+
<EmberTooltip @text="Click to login via GitHub" />
37+
{{else if this.currentUserCanAccessMembershipBenefits}}
38+
<EmberTooltip @text="You already have full access to CodeCrafters. You can claim this gift once your membership expires." />
39+
{{/if}}
40+
</PrimaryButton>
941

10-
<div class="mb-4">
11-
<p class="text-gray-600" data-test-gift-message>{{@model.giftMessage}}</p>
42+
<div class="text-xs text-gray-400 text-center text-pretty mt-2">
43+
No credit card required.
44+
</div>
1245
</div>
1346

14-
<div class="mb-6">
15-
<p class="font-medium">Validity Period:</p>
16-
<p class="text-gray-600" data-test-validity-period>
17-
This gift is valid for
18-
{{@model.validityInDays}}
19-
days.
47+
<div class="space-y-6 text-center my-8 max-w-[640px] mx-auto">
48+
<h2
49+
class="text-3xl leading-9 md:text-4xl md:leading-10 text-gradient-to-b from-gray-950 to-gray-700 dark:from-white dark:to-gray-300 font-bold"
50+
>
51+
Trusted by senior engineers at
52+
<span class="italic font-extrabold">the world's best companies</span>
53+
</h2>
54+
<p class="text-sm md:text-lg text-gray-900 dark:text-gray-300">
55+
Discover how core contributors to Docker and Next.js practice writing code at the edge of their abilities — as well as senior engineers at
56+
companies like Apple, NVIDIA, and Stripe.
2057
</p>
2158
</div>
2259

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}}
60+
<h2 class="text-gray-400 dark:text-gray-500 uppercase text-center text-xs mb-5">Trusted by engineers at top companies</h2>
61+
<AffiliateLinkPage::LogoCloud />
2762

28-
{{#if this.isRedeemingGift}}
29-
Redeeming...
30-
{{else}}
31-
Redeem Gift
32-
{{/if}}
63+
<img src={{this.BYOXBanner}} alt="Build Your Own X GitHub Banner" class="hidden sm:block sm:w-4/5 mx-auto mt-16" />
64+
<img src={{this.BYOXBannerMobile}} alt="Build Your Own X GitHub Banner" class="block sm:hidden w-full mt-20" />
3365

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}}
39-
</PrimaryButton>
66+
<h2
67+
class="text-2xl leading-10 md:text-3xl text-gradient-to-b from-gray-950 to-gray-700 dark:from-white dark:to-gray-300 font-bold text-center mt-16"
68+
>
69+
Hear it from our members
70+
</h2>
71+
72+
<div class="text-gray-500 dark:text-gray-400 text-sm text-center mt-2">
73+
Engineers at top teams love The CodeCrafters Way<sup>TM</sup>.
74+
</div>
75+
76+
<TestimonialList class="mt-10" />
4077
</div>
4178
</div>
34.9 KB
Loading

tests/acceptance/redeem-gift-page/redeem-test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ module('Acceptance | redeem-gift-page | redeem', function (hooks) {
2020
});
2121

2222
await redeemGiftPage.visit({ secret_token: 'xyz' });
23-
assert.notOk(redeemGiftPage.giftDetailsContainer.redeemButton.isDisabled, 'Redeem button should be enabled for anonymous users');
23+
assert.notOk(redeemGiftPage.redeemButton.isDisabled, 'Redeem button should be enabled for anonymous users');
2424

25-
await redeemGiftPage.giftDetailsContainer.redeemButton.hover();
25+
await redeemGiftPage.redeemButton.hover();
2626
assertTooltipContent(assert, { contentString: 'Click to login via GitHub' });
2727
});
2828

@@ -40,9 +40,9 @@ module('Acceptance | redeem-gift-page | redeem', function (hooks) {
4040
});
4141

4242
await redeemGiftPage.visit({ secret_token: 'xyz' });
43-
assert.ok(redeemGiftPage.giftDetailsContainer.redeemButton.isDisabled, 'Redeem button should be disabled for users with existing membership');
43+
assert.ok(redeemGiftPage.redeemButton.isDisabled, 'Redeem button should be disabled for users with existing membership');
4444

45-
await redeemGiftPage.giftDetailsContainer.redeemButton.hover();
45+
await redeemGiftPage.redeemButton.hover();
4646

4747
assertTooltipContent(assert, {
4848
contentString: 'You already have full access to CodeCrafters. You can claim this gift once your membership expires.',

tests/acceptance/redeem-gift-page/view-test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ module('Acceptance | redeem-gift-page | view', function (hooks) {
1414
test('displays gift details', async function (assert) {
1515
this.server.schema.membershipGifts.create({
1616
secretToken: 'xyz',
17-
giftMessage: 'Happy Birthday! Enjoy your CodeCrafters membership.',
17+
giftMessage: 'Happy Birthday! Enjoy your CodeCrafters membership.\n\n— Paul',
1818
validityInDays: 365,
1919
purchasedAt: new Date(),
2020
claimedAt: null,
2121
});
2222

2323
await redeemGiftPage.visit({ secret_token: 'xyz' });
24-
25-
assert.strictEqual(redeemGiftPage.giftDetailsContainer.giftMessage, 'Happy Birthday! Enjoy your CodeCrafters membership.');
24+
assert.strictEqual(redeemGiftPage.giftMessageContainer.text, 'Message from sender: Happy Birthday! Enjoy your CodeCrafters membership. — Paul');
2625
});
2726
});

0 commit comments

Comments
 (0)