diff --git a/app/layout.tsx b/app/layout.tsx index c000576..2e86dae 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,6 +3,7 @@ import { ServerProviders } from '@/app/providers/ServerProviders'; import '@/app/styles/index.scss'; import { Notification } from '@/entities/Notification'; import { ScrollUp } from '@/features/ScrollUp'; +import { BookmarkIcon } from '@/shared/assets/icon/Bookmark'; import { SpaceCanvas } from '@/shared/ui/SpaceCanvas'; import { Footer } from '@/widgets/Footer'; import { Navbar } from '@/widgets/Navbar'; @@ -34,7 +35,7 @@ export default function LocaleLayout({ children }: LocaleLayoutProps) { {children} } /> diff --git a/src/app/styles/themes/dark.scss b/src/app/styles/themes/dark.scss index 6e80173..9d4f778 100644 --- a/src/app/styles/themes/dark.scss +++ b/src/app/styles/themes/dark.scss @@ -11,11 +11,11 @@ --color-text: 255, 255, 255; // Light ellipse - --light-color-ellipse-1: rgb(90, 186, 255, 30%); + --light-color-ellipse-1: rgb(90, 186, 255, 35%); --light-blur-ellipse-1: blur(400px); - --light-color-ellipse-2: rgb(255, 90, 209, 30%); + --light-color-ellipse-2: rgb(255, 90, 209, 5%); --light-blur-ellipse-2: blur(600px); - --light-color-ellipse-3: rgb(255, 90, 209, 30%); + --light-color-ellipse-3: rgb(255, 90, 209, 5%); --light-blur-ellipse-3: blur(400px); // @media (max-width: $display-desktop) { diff --git a/src/db.ts b/src/db.ts index 1a1fc97..e6aad00 100644 --- a/src/db.ts +++ b/src/db.ts @@ -13,7 +13,14 @@ export const productData: ProductDataProps[] = [ creativity: 5, filter: ['joke'], characteristics: { - 'Характеристика 1': 'Значение характеристики 1', + Версия: ['global', 'Ростест (EAC)'], + Конфигурация: ['4/128 ГБ', '6/128 ГБ', '8/256 ГБ'], + 'Коротко о товаре': { + Емкость: '20000 мА·ч (74 Вт·ч)', + Вес: '438 г', + Фото: '3 камеры, основная 50 МП', + Процессор: 'Qualcomm Snapdragon 685', + }, }, markets: [ { @@ -25,6 +32,15 @@ export const productData: ProductDataProps[] = [ price: 13999, oldPrice: 21947, }, + { + market: 'yandex', + link: 'https://www.ozon.ru/product/logitech-naushniki-provodnye-s-mikrofonom-3-5-mm-chernyy-1353218032/', + rating: 4.5, + reviewCount: 68, + currency: 'RUB', + price: 13578, + oldPrice: 19653, + }, ], }, { @@ -34,7 +50,7 @@ export const productData: ProductDataProps[] = [ creativity: 5, filter: ['female', 'love'], characteristics: { - 'Характеристика 1': 'Значение характеристики 1', + 'Характеристика 1': ['Значение характеристики 1'], }, markets: [ { @@ -55,7 +71,7 @@ export const productData: ProductDataProps[] = [ creativity: 5, filter: ['kid'], characteristics: { - 'Характеристика 1': 'Значение характеристики 1', + 'Характеристика 1': ['Значение характеристики 1'], }, markets: [ { @@ -76,7 +92,7 @@ export const productData: ProductDataProps[] = [ creativity: 5, filter: ['year'], characteristics: { - 'Характеристика 1': 'Значение характеристики 1', + 'Характеристика 1': ['Значение характеристики 1'], }, markets: [ { diff --git a/src/entities/Notification/Notification.module.scss b/src/entities/Notification/Notification.module.scss index 03d724f..5c66d1a 100644 --- a/src/entities/Notification/Notification.module.scss +++ b/src/entities/Notification/Notification.module.scss @@ -2,7 +2,6 @@ .wrapper { position: fixed; - bottom: var(--position-bottom); left: 50%; z-index: var(--z-index-notification); display: flex; @@ -13,10 +12,10 @@ max-width: 1320px; padding: 15px 30px; overflow: hidden; + user-select: none; backdrop-filter: var(--blur-md); border: solid 1px var(--color-border); transform: translateX(-50%); - animation: fadeIn .3s ease-out forwards; @include background(.3); @apply rounded-xl; @@ -41,7 +40,68 @@ } } -@keyframes fadeIn { +.content { + display: flex; + gap: 10px; + align-items: center; +} + +.startContent { + max-width: 24px; + max-height: 24px; +} + +.progress { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 1px; + + &>div>div { + animation: progress var(--animation-duration-notification) linear forwards; + } +} + +.top { + top: 0; + animation: fadeInFromTop .3s ease-out forwards; +} + +.bottom { + bottom: var(--position-bottom); + animation: fadeInFromBottom .3s ease-out forwards; +} + +.closing { + animation: fadeOutFromBottom var(--animation-close-duration-notification) ease-out forwards; +} + +.closing.top { + animation: fadeOutFromTop var(--animation-close-duration-notification) ease-out forwards; +} + +.swipeLeft { + animation: swipeLeft var(--animation-close-duration-notification) ease-out forwards !important; +} + +.swipeRight { + animation: swipeRight var(--animation-close-duration-notification) ease-out forwards !important; +} + +@keyframes fadeInFromTop { + 0% { + opacity: 0; + transform: translate(-50%, 0); + } + + 100% { + opacity: 1; + transform: translate(-50%, 100px); + } +} + +@keyframes fadeInFromBottom { 0% { opacity: 0; transform: translate(-50%, 100px); @@ -53,11 +113,19 @@ } } -.closing { - animation: fadeOut var(--animation-close-duration-notification) ease-out forwards; +@keyframes fadeOutFromTop { + 0% { + opacity: 1; + transform: translate(-50%, 100px); + } + + 100% { + opacity: 0; + transform: translate(-50%, 0); + } } -@keyframes fadeOut { +@keyframes fadeOutFromBottom { 0% { opacity: 1; transform: translate(-50%, 0); @@ -69,14 +137,6 @@ } } -.swipeLeft { - animation: swipeLeft var(--animation-close-duration-notification) ease-out forwards; -} - -.swipeRight { - animation: swipeRight var(--animation-close-duration-notification) ease-out forwards; -} - @keyframes swipeLeft { 0% { opacity: 1; @@ -101,32 +161,6 @@ } } -.icon { - opacity: .5; - - &svg *[fill] { - fill: var(--color-main-inverted); - } -} - -.content { - display: flex; - gap: 10px; - align-items: center; -} - -.progress { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 1px; - - &>div>div { - animation: progress var(--animation-duration-notification) linear forwards; - } -} - @keyframes progress { 0% { transform: translateX(0%); diff --git a/src/entities/Notification/Notification.tsx b/src/entities/Notification/Notification.tsx index 9e04d79..d1104fb 100644 --- a/src/entities/Notification/Notification.tsx +++ b/src/entities/Notification/Notification.tsx @@ -1,6 +1,5 @@ 'use client'; -import { BookmarkIcon } from '@/shared/assets/icon/Bookmark'; import { CrossIcon } from '@/shared/assets/icon/Cross'; import { Button } from '@/shared/ui/Button'; import { Progress } from '@nextui-org/react'; @@ -20,10 +19,11 @@ interface NotificationProps { message: string; duration?: number; animationCloseDuration?: number; - closable?: boolean; onClose?: () => void; onCancel?: () => void; - icon?: string; + startContent?: JSX.Element; + placement?: 'top' | 'bottom'; + closeOnClick?: boolean; } export const Notification: FC = memo( @@ -31,10 +31,11 @@ export const Notification: FC = memo( message, duration = 7000, animationCloseDuration = 300, - closable = true, onClose, onCancel, - icon, + startContent, + closeOnClick, + placement = 'bottom', }) => { const [visible, setVisible] = useState(true); const [closing, setClosing] = useState(false); @@ -101,6 +102,8 @@ export const Notification: FC = memo( [cls.closing]: closing, [cls.swipeLeft]: swipeClose && touchEndX < touchStartX && !closing, [cls.swipeRight]: swipeClose && touchEndX > touchStartX && !closing, + [cls.top]: placement === 'top', + [cls.bottom]: placement === 'bottom', })} style={ { @@ -111,6 +114,7 @@ export const Notification: FC = memo( onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} onTouchEnd={handleTouchEnd} + onClick={closeOnClick ? handleClose : undefined} > = memo( color='success' />
- {icon && } +
{startContent}

{message}

- {closable && ( + {!closeOnClick && ( diff --git a/src/shared/assets/icon/Copy.jsx b/src/shared/assets/icon/Copy.jsx new file mode 100644 index 0000000..178e792 --- /dev/null +++ b/src/shared/assets/icon/Copy.jsx @@ -0,0 +1,26 @@ +export const CopyIcon = ({ + color = 'var(--color-main-inverted)', + ...props +}) => ( + + + + +); diff --git a/src/shared/types/product.ts b/src/shared/types/product.ts index b1fb14f..21cd467 100644 --- a/src/shared/types/product.ts +++ b/src/shared/types/product.ts @@ -7,7 +7,7 @@ export interface ProductDataProps { title: string; creativity: number; filter: FilterSortProps[]; - characteristics: Record; + characteristics: Record>; markets: MarketsProductData[]; } diff --git a/src/shared/ui/Button/Button.module.scss b/src/shared/ui/Button/Button.module.scss index 7f4faf7..0e6d530 100644 --- a/src/shared/ui/Button/Button.module.scss +++ b/src/shared/ui/Button/Button.module.scss @@ -32,6 +32,7 @@ img, svg { opacity: .5; + transition: all .2s ease !important; } &:hover { diff --git a/src/views/ProductPage/ProductPage.module.scss b/src/views/ProductPage/ProductPage.module.scss index baabcb2..8a1fa9b 100644 --- a/src/views/ProductPage/ProductPage.module.scss +++ b/src/views/ProductPage/ProductPage.module.scss @@ -1,12 +1,9 @@ +@import '@/app/styles/utils'; + .wrapper { margin-top: 50px; } -.content { - display: flex; - gap: 50px; -} - .product { display: flex; flex-direction: column; @@ -24,7 +21,29 @@ } } -.links { +.productWrapper { + display: flex; + gap: 50px; +} + +.cards { + display: flex; + flex-direction: column; + gap: 30px; + margin-top: 100px; + + h3 { + @apply text-xl; + @apply font-semibold; + } +} + +.linksAndCharacteristics { + display: flex; + gap: 50px; width: 100%; - max-width: 330px; + + @media (max-width: theme('screens.xl')) { + flex-direction: column; + } } \ No newline at end of file diff --git a/src/views/ProductPage/ProductPage.tsx b/src/views/ProductPage/ProductPage.tsx index 1a74d2b..dfb7df2 100644 --- a/src/views/ProductPage/ProductPage.tsx +++ b/src/views/ProductPage/ProductPage.tsx @@ -1,6 +1,11 @@ import { productData } from '@/db'; import { Markets } from '@/shared/const'; import { ProductDataProps } from '@/shared/types/product'; +import { Cards } from '@/shared/ui/Card'; +import { Light } from '@/shared/ui/Light'; +import { Characteristics } from '@/widgets/Characteristics'; +import { ImageCarousel } from '@/widgets/ImageCarousel'; +import { LinksPanel } from '@/widgets/LinksPanel'; import { Tooltip } from '@nextui-org/react'; import { ProductPageProps } from 'app/product/[id]/page'; import cn from 'clsx'; @@ -10,52 +15,58 @@ import cls from './ProductPage.module.scss'; export const ProductPage: FC = ({ params }) => { const productId = Number(params.id.split('-').reverse()[0]); - const product: ProductDataProps | undefined = productData.find( (product) => product.id === productId, ); - - if (!product) { - return
Product not found
; - } + if (!product) return
Product not found
; return (
-
-
-
- - test - - Статистика с {Markets[product.markets[0].market].name} - -
- } - className={cls.tooltip} - > - test - -

{product.title}

+ +
+
+ + test + + Информация получена с{' '} + {Markets[product.markets[0].market].name} + +
+ } + className={cls.tooltip} + > + test + +

{product.title}

+
+
+ +
+ +
-
+
+
+

Смотрите также

+
); diff --git a/src/views/ShopPage/ShopPage.tsx b/src/views/ShopPage/ShopPage.tsx index 9b609f9..da6f016 100644 --- a/src/views/ShopPage/ShopPage.tsx +++ b/src/views/ShopPage/ShopPage.tsx @@ -2,13 +2,12 @@ import { productData } from '@/db'; import { Blackhole } from '@/shared/ui/Blackhole'; -import { Button } from '@/shared/ui/Button'; import { Cards } from '@/shared/ui/Card'; import { NavigationPanel } from '@/widgets/NavigationPanel'; import { Sorts } from '@/widgets/Sorts'; import { getSortSearchparams } from '@/widgets/Sorts/model/features/getSortSearchparams'; import { TopPage } from '@/widgets/TopPage'; -import { Image, Textarea } from '@nextui-org/react'; +import { Image } from '@nextui-org/react'; import cn from 'clsx'; import { FC, useMemo } from 'react'; import cls from './ShopPage.module.scss'; @@ -95,7 +94,7 @@ export const ShopPage: FC = () => {
-
+ {/*