Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@
"!src/apps/deposit/**",
"!src/apps/leaderboard/",
"!src/apps/leaderboard/**",
"!src/apps/blitz/",
"!src/apps/blitz/**",
"public/" // Ignore public folder
],
"settings": {
Expand Down
50 changes: 50 additions & 0 deletions src/apps/blitz/components/RandomAvatar/RandomAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Avatar from 'boring-avatars';

// types
import { AvatarVariantType } from '../../../../types';

type RandomAvatarProps = {
name: string;
variant?: string;
isRandomVariant?: boolean;
};

const RandomAvatar = ({
name,
variant,
isRandomVariant,
}: RandomAvatarProps) => {
const variants: AvatarVariantType[] = [
'marble',
'beam',
'pixel',
'sunset',
'ring',
'bauhaus',
];

const randomVariant: AvatarVariantType =
variants[Math.floor(Math.random() * variants.length)];

const avatarVariant = () => {
if (isRandomVariant && !variant) {
return randomVariant;
}
if (variant) {
return variant as AvatarVariantType;
}
return 'marble';
};

return (
<Avatar
name={name}
variant={avatarVariant()}
className="rounded-md"
square
data-testid="random-avatar"
/>
);
};

export default RandomAvatar;
84 changes: 84 additions & 0 deletions src/apps/blitz/components/TimeClock/TimeClock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-disable react/no-unstable-nested-components */
import { differenceInMinutes, differenceInSeconds, isPast } from 'date-fns';
import { useEffect, useState } from 'react';

const DUMMY_END_DATE = new Date(2025, 1, 5, 18, 10, 0); // Move outside component

type TimeClockProps = { classname?: string };

const TimeClock = ({ classname }: TimeClockProps) => {
const [isDesktop, setIsDesktop] = useState<boolean>(window.innerWidth > 1023);

const calculateTimeLeft = () => {
const now = new Date();
const minutes = Math.max(0, differenceInMinutes(DUMMY_END_DATE, now));
const seconds = Math.max(0, differenceInSeconds(DUMMY_END_DATE, now) % 60);
return { minutes, seconds };
};

const [timeRemaining, setTimeRemaining] = useState(calculateTimeLeft);

useEffect(() => {
const interval = setInterval(() => {
setTimeRemaining(calculateTimeLeft());
}, 1000);

return () => clearInterval(interval);
}, []);

useEffect(() => {
const handleResize = () => {
setIsDesktop(window.innerWidth > 1023);
};

window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
};
}, []);

const formattedMinutes = timeRemaining.minutes.toString().padStart(2, '0');
const formattedSeconds = timeRemaining.seconds.toString().padStart(2, '0');

const timeMap = Array.from(formattedMinutes + formattedSeconds);

const DigitComponent = ({ time }: { time: string }) => {
return (
<div className="flex desktop:h-[52px] desktop:w-[52px] tablet:w-9 tablet:h-9 p-[1px] bg-gradient-to-t from-[#87344D] to-[#D8748E] rounded-[10px]">
<div className="flex w-full h-full rounded-[10px] items-center justify-center bg-gradient-to-t from-[#992041] to-[#FF366C]">
<p className="text-white desktop:text-[40px] tablet:text-2xl">
{isPast(DUMMY_END_DATE) ? 0 : time}
</p>
</div>
</div>
);
};

return (
<div
className={`flex w-fit bg-container_grey rounded-2xl p-4 pb-2.5 gap-5 ${classname}`}
>
<div className="flex desktop:flex-col tablet:flex items-center tablet:gap-1.5">
<div className="flex gap-1.5">
<DigitComponent time={timeMap[0]} />
<DigitComponent time={timeMap[1]} />
</div>
<p className="mt-2.5 uppercase text-light_grey desktop:text-xl tablet:text-base font-extralight leading-5">
{isDesktop ? 'minutes' : 'min'}
</p>
</div>
<div className="flex desktop:flex-col tablet:flex items-center tablet:gap-1.5">
<div className="flex gap-1.5">
<DigitComponent time={timeMap[2]} />
<DigitComponent time={timeMap[3]} />
</div>
<p className="mt-2.5 uppercase text-light_grey desktop:text-xl tablet:text-base font-extralight leading-5">
{isDesktop ? 'seconds' : 'sec'}
</p>
</div>
</div>
);
};

export default TimeClock;
37 changes: 37 additions & 0 deletions src/apps/blitz/components/TokenPriceTime/TokenPriceTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import TokenPotIcon from '../../images/token_pot_icon.svg';
import TimeClock from '../TimeClock/TimeClock';
import TokenPriceUpdate from '../TokenPriceUpdate/TokenPriceUpdate';
import Body from '../Typography/Body';

const TokenPriceTime = () => {
const DUMMY_TOKEN_PRICE = '120,582.08';
const DUMMY_TOKEN_AMOUNT = 3515;
const DUMMY_TOKEN_SYMBOL = 'RBTC';
const DUMMY_TOKEN_WON_CLAIMABLE = 562;

return (
<div className="flex flex-col w-full items-center">
<div className="flex flex-col relative w-full items-center desktop:mb-10 tablet:mb-8">
<TokenPriceUpdate classname="absolute top-0 -translate-y-1/2" />
<div className="desktop:w-full tablet:w-fit desktop:px-[44px] tablet:px-8 desktop:pt-[55px] tablet:pt-[40px] desktop:pb-[88px] tablet:pb-[60px] rounded-[20px] bg-gradient-to-t from-[#27262F4D] via-[#27262F] to-[#27262F4D]">
<h1 className="desktop:text-[80px] tablet:text-5xl font-light text-center">
${DUMMY_TOKEN_PRICE}
</h1>
</div>
<TimeClock classname="absolute bottom-0 translate-y-1/4" />
</div>
<div className="flex w-fit bg-container_grey rounded-[10px] px-6 py-3.5 gap-2.5 mb-2">
<img src={TokenPotIcon} alt="token-pot-icon" className="w-5 h-5" />
<Body className="font-normal">
{DUMMY_TOKEN_AMOUNT} {DUMMY_TOKEN_SYMBOL} in the Pot!
</Body>
</div>
<Body className="font-normal">
{DUMMY_TOKEN_WON_CLAIMABLE} {DUMMY_TOKEN_SYMBOL} Won{' '}
<span className="text-percentage_green underline">Claim Now!</span>
</Body>
</div>
);
};

export default TokenPriceTime;
24 changes: 24 additions & 0 deletions src/apps/blitz/components/TokenPriceUpdate/TokenPriceUpdate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import RandomToken from '../../images/randomToken.png';

type TokenPriceUpdateProps = { classname?: string };

const TokenPriceUpdate = ({ classname }: TokenPriceUpdateProps) => {
const DUMMY_TIME = '3:15pm';
return (
<div
className={`flex w-fit bg-container_grey rounded-2xl desktop:py-4 tablet:py-3 desktop:px-7 tablet:px-[26px] justify-center items-center gap-2 ${classname}`}
>
<img
src={RandomToken}
alt="token-icon"
className="desktop:h-8 desktop:w-8 tablet:h-6 tablet:w-6"
/>
<h2 className="desktop:text-[32px] tablet:text-base">
Token price at{' '}
<span className="text-percentage_green">{DUMMY_TIME}</span>
</h2>
</div>
);
};

export default TokenPriceUpdate;
12 changes: 12 additions & 0 deletions src/apps/blitz/components/Typography/Body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReactNode } from 'react';

type BodyProps = {
children: ReactNode;
className?: string;
};

const Body = ({ children, className }: BodyProps) => {
return <p className={`text-base font-medium ${className}`}>{children}</p>;
};

export default Body;
12 changes: 12 additions & 0 deletions src/apps/blitz/components/Typography/BodySmall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReactNode } from 'react';

type BodySmallProps = {
children: ReactNode;
className?: string;
};

const BodySmall = ({ children, className }: BodySmallProps) => {
return <p className={`text-sm font-medium ${className}`}>{children}</p>;
};

export default BodySmall;
30 changes: 30 additions & 0 deletions src/apps/blitz/components/Typography/tests/Body.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import renderer, { ReactTestRendererJSON } from 'react-test-renderer';

// components
import Body from '../Body';

describe('<Body />', () => {
it('renders correctly', () => {
const tree = renderer
.create(
<>
<Body>Some regular text.</Body>
<Body className="text-red-500">Some red text</Body>
<Body className="text-[23px]">Some text with font size 23px</Body>
</>
)
.toJSON();

expect(tree).toMatchSnapshot();

const treeElements = tree as ReactTestRendererJSON[];

expect(treeElements[0].children?.length).toBe(1);
expect(treeElements[0].children?.[0]).toBe('Some regular text.');
expect(treeElements[0].type).toBe('p');
expect(treeElements[0].props.className).toContain('text-base');
expect(treeElements[0].props.className).toContain('font-medium');
expect(treeElements[1].props.className).toContain('text-red-500');
expect(treeElements[2].props.className).toContain('text-[23px]');
});
});
32 changes: 32 additions & 0 deletions src/apps/blitz/components/Typography/tests/BodySmall.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import renderer, { ReactTestRendererJSON } from 'react-test-renderer';

// components
import BodySmall from '../BodySmall';

describe('<BodySmall />', () => {
it('renders correctly', () => {
const tree = renderer
.create(
<>
<BodySmall>Some small text.</BodySmall>
<BodySmall className="text-red-500">Some red small text</BodySmall>
<BodySmall className="text-[7px]">
Some small text with font size 7px
</BodySmall>
</>
)
.toJSON();

expect(tree).toMatchSnapshot();

const treeElements = tree as ReactTestRendererJSON[];

expect(treeElements[0].children?.length).toBe(1);
expect(treeElements[0].children?.[0]).toBe('Some small text.');
expect(treeElements[0].type).toBe('p');
expect(treeElements[0].props.className).toContain('text-sm');
expect(treeElements[0].props.className).toContain('font-medium');
expect(treeElements[1].props.className).toContain('text-red-500');
expect(treeElements[2].props.className).toContain('text-[7px]');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Body /> renders correctly 1`] = `
[
<p
className="text-base font-medium undefined"
>
Some regular text.
</p>,
<p
className="text-base font-medium text-red-500"
>
Some red text
</p>,
<p
className="text-base font-medium text-[23px]"
>
Some text with font size 23px
</p>,
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<BodySmall /> renders correctly 1`] = `
[
<p
className="text-sm font-medium undefined"
>
Some small text.
</p>,
<p
className="text-sm font-medium text-red-500"
>
Some red small text
</p>,
<p
className="text-sm font-medium text-[7px]"
>
Some small text with font size 7px
</p>,
]
`;
Loading