Initial commit: Tapalka monorepo (bot, front, strapi)

This commit is contained in:
2026-05-21 11:54:44 +07:00
commit 9ec9485940
208 changed files with 58314 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
.friend {
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF0D;
border-radius: 1rem;
padding: 1rem;
height: 74px;
}
.friendContent {
display: flex;
align-items: center;
gap: 12px;
}
.friendData {
display: flex;
flex-direction: column;
}
.friendReferrals {
display: flex;
align-items: center;
gap: 3px;
}
.friendBalance span {
font-family: var(--font-manrope);
font-weight: 600;
font-size: 18px;
line-height: 24px;
color: white;
}

View File

@@ -0,0 +1,43 @@
'use client';
import styles from '@/components/friends/Friend.module.css';
import Image from 'next/image';
import { IPlayer } from '@/interfaces/player.type';
import { toFixed } from '@/utils/FormatNumber';
type Props = {
player: IPlayer;
};
const Friend = ({ player }: Props) => {
return (
<div className={styles.friend}>
<div className={styles.friendContent}>
<div className={styles.friendAvatar}>
<Image
src={'/icons/friend-icon.svg'}
alt={'friend avatar'}
height={42}
width={42}
/>
</div>
<div className={styles.friendData}>
<span>{player?.attributes.telegram_nick}</span>
<div className={styles.friendReferrals}>
<Image
src={'/icons/referrals-icon.svg'}
alt={'referrals icon'}
width={14}
height={14}
/>
<span>{player?.attributes.my_referrals?.data?.length || 0}</span>
</div>
</div>
</div>
<div className={styles.friendBalance}>
<span>{toFixed(player?.attributes.balance)}</span>
</div>
</div>
);
};
export default Friend;

View File

@@ -0,0 +1,113 @@
.friends {
background: #101010;
height: 100vh;
overflow-y: auto;
padding-bottom: 90px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
}
.friendsTitle {
align-items: flex-start;
padding-top: 45px;
padding-left: 1.5rem;
font-weight: 600;
font-size: 19px;
color: white;
line-height: 21px;
font-family: var(--font-manrope);
}
.inviteBlock {
width: min(343px, 100%);
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin-top: 1rem;
padding: 20px 16px;
border-radius: 1.5rem;
background: linear-gradient(81.19deg, rgba(66, 187, 71, 0.06) 0%, rgba(66, 187, 71, 0.2) 100%), #0E0E10;
border: 1px solid #18181A;
}
.rewardInvite {
display: flex;
align-items: center;
flex-direction: row-reverse;
gap: 3px;
font-family: var(--font-clash);
}
.rewardInvite span {
font-size: 42px;
font-weight: 600;
line-height: 51px;
background: linear-gradient(180deg, #42BB47 4.41%, #33A137 61.59%, #42BB47 101.32%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.inviteButtonBlock {
width: 100%;
}
.inviteButtonBlock button {
width: 100%;
height: 46px;
border-radius: 40px;
color: white;
background: #42BB47;
border: none;
font-weight: 600;
font-size: 16px;
line-height: 21px;
font-family: var(--font-manrope);
cursor: pointer;
}
.infoInvite {
text-align: center;
font-family: var(--font-manrope);
}
.infoInvite span {
font-size: 12px;
font-weight: 600;
line-height: 16px;
color: #BBBBBB;
}
.friendsList {
display: flex;
flex-direction: column;
padding-top: 1rem;
margin-top: 7px;
gap: 5px;
padding-bottom: 7px;
}
.friendsListBlock {
width: min(375px, 100%);
margin-top: 2rem;
background: #0E0E10;
padding: 20px 16px;
border: 1px solid #18181A;
border-radius: 1.5rem;
}
.friendsListHeader {
display: flex;
justify-content: space-between;
font-size: 19px;
color: white;
font-weight: 600;
font-family: var(--font-manrope);
line-height: 19px;
}

View File

@@ -0,0 +1,67 @@
'use client';
import styles from '@/components/friends/Friends.module.css';
import Image from 'next/image';
import { FC } from 'react';
import Friend from '@/components/friends/Friend';
import CopyLinkToast from '@/components/ui/copy-link-toast/CopyLinkToast';
import { usePlayerStore } from '@/store/player.state';
interface IFriends {
isVisible: boolean;
referral_fee: number | undefined;
friendsAndReferralsCount: { friendsWord: string; referralWord: string };
handleCopyInviteLink: () => Promise<void>;
handleCloseToastLink: () => void;
}
const Friends: FC<IFriends> = ({
isVisible,
referral_fee,
friendsAndReferralsCount,
handleCopyInviteLink,
handleCloseToastLink,
}) => {
const { player } = usePlayerStore();
const referrals = player?.attributes.my_referrals.data;
return (
<div className={styles.friends}>
<div className={styles.friendsTitle}>
<span>Друзья</span>
</div>
<div className={styles.content}>
<div className={styles.inviteBlock}>
<div className={styles.rewardInvite}>
<span>{referral_fee || 0}</span>
<Image
src={'/icons/robucks-icon.svg'}
alt={'robucks icon'}
width={32}
height={32}
/>
</div>
<div className={styles.inviteButtonBlock}>
<button onClick={handleCopyInviteLink}>Пригласи друга</button>
</div>
<div className={styles.infoInvite}>
<span>
Получай 10% от твоих друзей +5% от друзей реферала +2.5% от их рефералов
</span>
</div>
</div>
<div className={styles.friendsListBlock}>
<div className={styles.friendsListHeader}>
<span>{friendsAndReferralsCount.friendsWord}</span>
<span>{friendsAndReferralsCount.referralWord}</span>
</div>
<div className={styles.friendsList}>
{referrals?.map(referral => <Friend key={referral.id} player={referral} />)}
</div>
</div>
<CopyLinkToast isVisible={isVisible} handleCloseToastLink={handleCloseToastLink} />
</div>
</div>
);
};
export default Friends;

View File

@@ -0,0 +1,50 @@
'use client';
import Friends from '@/components/friends/Friends';
import { useCallback, useEffect, useState } from 'react';
import { CalculateReferralsCount } from '@/utils/CalculateReferralsCount';
import { updateFriendAndReferralCounts } from '@/utils/UpdateFriendAndReferralCount';
import { usePlayerStore } from '@/store/player.state';
const FriendsContainer = ({ botLink }: { botLink: string }) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
const [userData, setUserData] = useState<{ id: number; username: string } | null>(null);
const { player } = usePlayerStore();
useEffect(() => {
const initDataUnsafe = window.Telegram?.WebApp?.initDataUnsafe;
if (!initDataUnsafe?.user) {
return;
}
setUserData(initDataUnsafe.user);
}, []);
const handleCopyInviteLink = useCallback(async () => {
setIsVisible(true);
setTimeout(() => {
setIsVisible(false);
}, 3000);
return await navigator.clipboard.writeText(`${botLink}ref_${userData?.id}`);
}, [userData, botLink]);
const handleCloseToastLink = useCallback(() => {
setIsVisible(false);
}, []);
return (
<Friends
isVisible={isVisible}
referral_fee={player?.attributes.referral_fee}
friendsAndReferralsCount={updateFriendAndReferralCounts(
player?.attributes.my_referrals?.data?.length || 0,
CalculateReferralsCount(player),
)}
handleCopyInviteLink={handleCopyInviteLink}
handleCloseToastLink={handleCloseToastLink}
/>
);
};
export default FriendsContainer;