Initial commit: Tapalka monorepo (bot, front, strapi)
This commit is contained in:
155
CMyTapper/robucks-front/components/shop/Shop.module.css
Normal file
155
CMyTapper/robucks-front/components/shop/Shop.module.css
Normal file
@@ -0,0 +1,155 @@
|
||||
.shop {
|
||||
background: #101010;
|
||||
height: calc(100vh - 82px);
|
||||
font-family: var(--font-manrope);
|
||||
overflow-y: auto;
|
||||
padding-bottom: 82px;
|
||||
}
|
||||
|
||||
.item_img {
|
||||
--size: 32px;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
max-width: var(--size);
|
||||
max-height: var(--size);
|
||||
}
|
||||
|
||||
.item_img_sm {
|
||||
--size: 18px;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
max-width: var(--size);
|
||||
max-height: var(--size);
|
||||
}
|
||||
|
||||
.item_img > img, .item_img_sm > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 45px;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.shop.overflow {
|
||||
padding-bottom: 90px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.itemsAll {
|
||||
color: #42BB47;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.header span {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 19px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.exchangeRobucks {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.items {
|
||||
min-height: 155px;
|
||||
border-radius: 1.5rem;
|
||||
padding: 20px 16px;
|
||||
background: #0E0E10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.itemsHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
background: #FFFFFF0D;
|
||||
height: 76px;
|
||||
border-radius: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.shopItem {
|
||||
cursor: pointer;
|
||||
background: #FFFFFF0D;
|
||||
height: 76px;
|
||||
border-radius: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.shopItemPrice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.itemInfo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.itemInfoTitle span {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.itemInfoContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.upgrades {
|
||||
margin-top: 1rem;
|
||||
border-radius: 1.5rem;
|
||||
background: #0E0E10;
|
||||
border: 1px solid #18181A;
|
||||
padding: 20px 16px;
|
||||
}
|
||||
|
||||
.upgradesTitle span {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 19px;
|
||||
line-height: 20.9px;
|
||||
}
|
||||
|
||||
.upgradesContent {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.upgradesContent span {
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
.shopItems {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding-top: 0.8rem;
|
||||
}
|
||||
84
CMyTapper/robucks-front/components/shop/Shop.tsx
Normal file
84
CMyTapper/robucks-front/components/shop/Shop.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
'use client';
|
||||
|
||||
import styles from '@/components/shop/Shop.module.css';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import ModalExchangeContainer from '@/components/ui/modal-exchange/ModalExchangeContainer';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import ShopItem from '@/components/shop/ShopItem';
|
||||
import { IHandleOpen } from '@/components/shop/ShopContainer';
|
||||
import { useShopItemsStore } from '@/store/shopItems.state';
|
||||
import { usePlayerStore } from '@/store/player.state';
|
||||
import { toFixed } from '@/utils/FormatNumber';
|
||||
|
||||
interface IShop {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
handleOpen: (data: IHandleOpen) => void;
|
||||
}
|
||||
|
||||
const Shop: FC<IShop> = ({ isOpen, handleOpen, handleClose }) => {
|
||||
const shopBlockRef = useRef<HTMLDivElement>(null);
|
||||
const [isOverflow, setIsOverflow] = useState(false);
|
||||
|
||||
const { shopItems } = useShopItemsStore();
|
||||
const { player } = usePlayerStore();
|
||||
|
||||
useEffect(() => {
|
||||
const checkOverflow = () => {
|
||||
if (shopBlockRef.current) {
|
||||
setIsOverflow(
|
||||
shopBlockRef.current.scrollHeight > shopBlockRef.current.clientHeight,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
checkOverflow();
|
||||
|
||||
window.addEventListener('resize', checkOverflow);
|
||||
|
||||
return () => window.removeEventListener('resize', checkOverflow);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`${styles.shop} ${isOverflow ? styles.overflow : ''}`} ref={shopBlockRef}>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.header}>
|
||||
<span>Магазин</span>
|
||||
</div>
|
||||
<div className={styles.items}>
|
||||
<div className={styles.itemsHeader}>
|
||||
<span>Товары</span>
|
||||
<span className={styles.itemsAll}>Все</span>
|
||||
</div>
|
||||
<div className={styles.shopItems}>
|
||||
{shopItems &&
|
||||
shopItems.map(shopItem => (
|
||||
<ShopItem
|
||||
key={shopItem.id}
|
||||
shopItem={shopItem}
|
||||
handleOpen={handleOpen}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.upgrades}>
|
||||
<div className={styles.upgradesTitle}>
|
||||
<span>Улучшения</span>
|
||||
</div>
|
||||
<div className={styles.upgradesContent}>
|
||||
<span>Уже скоро...</span>
|
||||
</div>
|
||||
</div>
|
||||
<ModalExchangeContainer
|
||||
isOpen={isOpen}
|
||||
balance={toFixed(player?.attributes.balance)}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
<ToastContainer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Shop;
|
||||
42
CMyTapper/robucks-front/components/shop/ShopContainer.tsx
Normal file
42
CMyTapper/robucks-front/components/shop/ShopContainer.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import Shop from '@/components/shop/Shop';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useModalStore } from '@/store/modal.state';
|
||||
|
||||
export interface IHandleOpen {
|
||||
image?: string;
|
||||
isDecimal?: boolean;
|
||||
priceItem?: number;
|
||||
shopItem?: number;
|
||||
}
|
||||
|
||||
const ShopContainer = () => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
||||
const { setShopItemId, setPriceItem, setImage, setIsDecimal } = useModalStore();
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleOpen = useCallback((data: IHandleOpen) => {
|
||||
if (data.image && data.priceItem && data.shopItem && data.isDecimal !== undefined) {
|
||||
setIsDecimal(data.isDecimal);
|
||||
setImage(data.image);
|
||||
setPriceItem(data.priceItem);
|
||||
setShopItemId(data.shopItem);
|
||||
setIsOpen(true);
|
||||
} else {
|
||||
setIsOpen(true);
|
||||
setIsDecimal(true);
|
||||
setImage('/icons/small-coin-icon.svg');
|
||||
setPriceItem(0);
|
||||
setShopItemId(null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <Shop isOpen={isOpen} handleClose={handleClose} handleOpen={handleOpen} />;
|
||||
};
|
||||
|
||||
export default ShopContainer;
|
||||
63
CMyTapper/robucks-front/components/shop/ShopItem.tsx
Normal file
63
CMyTapper/robucks-front/components/shop/ShopItem.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
'use client';
|
||||
|
||||
import { FC } from 'react';
|
||||
import { IShopItem } from '@/api/shop-item.api';
|
||||
import styles from '@/components/shop/Shop.module.css';
|
||||
import Image from 'next/image';
|
||||
import { IHandleOpen } from '@/components/shop/ShopContainer';
|
||||
|
||||
interface IShopItemProps {
|
||||
shopItem: IShopItem;
|
||||
handleOpen: (data: IHandleOpen) => void;
|
||||
}
|
||||
|
||||
const ShopItem: FC<IShopItemProps> = ({ shopItem, handleOpen }) => {
|
||||
return (
|
||||
<div
|
||||
className={styles.item}
|
||||
onClick={() =>
|
||||
handleOpen({
|
||||
image: `${process.env.NEXT_PUBLIC_STRAPI || ''}${shopItem.attributes.icon.data.attributes.url}`,
|
||||
isDecimal: shopItem.attributes.isDecimal,
|
||||
priceItem: shopItem.attributes.price,
|
||||
shopItem: shopItem.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
{shopItem?.attributes?.icon?.data && (
|
||||
<div className={styles.item_img}>
|
||||
<img
|
||||
loading={'lazy'}
|
||||
src={`${process.env.NEXT_PUBLIC_STRAPI || ''}${shopItem.attributes.icon.data.attributes.url}`}
|
||||
alt={'shop item icon'}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.itemInfo}>
|
||||
<div className={styles.itemInfoTitle}>
|
||||
<span>{shopItem.attributes.name}</span>
|
||||
</div>
|
||||
<div className={styles.itemInfoContent}>
|
||||
<span>{shopItem.attributes.price}</span>
|
||||
<Image
|
||||
src={'/icons/small-robucks-icon.svg'}
|
||||
alt={'small robucks'}
|
||||
width={18}
|
||||
height={18}
|
||||
/>
|
||||
<span>=</span>
|
||||
<span>1</span>
|
||||
<div className={styles.item_img_sm}>
|
||||
<img
|
||||
loading={'lazy'}
|
||||
src={`${process.env.NEXT_PUBLIC_STRAPI || ''}${shopItem.attributes.icon.data.attributes.url}`}
|
||||
alt={'small coin icon'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShopItem;
|
||||
Reference in New Issue
Block a user