diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 9abe5b6..b5c1d79 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -2,12 +2,12 @@ import { ClientProviders } from '@/app/providers/ClientProviders'; import { ServerProviders } from '@/app/providers/ServerProviders'; import '@/app/styles/index.scss'; import { Notification } from '@/entities/Notification'; +import { ScrollUp } from '@/features/ScrollUp'; import { locales } from '@/shared/config/i18n/config'; import { SpaceCanvas } from '@/shared/ui/SpaceCanvas'; import { Footer } from '@/widgets/Footer'; import { Navbar } from '@/widgets/Navbar'; import { PageLoader } from '@/widgets/PageLoader'; -import { ScrollUp } from '@/widgets/ScrollUp'; import { Metadata } from 'next'; import { getTranslations, unstable_setRequestLocale } from 'next-intl/server'; import { Inter } from 'next/font/google'; @@ -16,8 +16,8 @@ import { ReactNode, Suspense } from 'react'; const inter = Inter({ subsets: ['latin'] }); type LocaleLayoutProps = { - children: ReactNode, - params: { locale: string }, + children: ReactNode; + params: { locale: string }; }; export function generateStaticParams() { diff --git a/package-lock.json b/package-lock.json index 5662f84..4fdf5e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "autoprefixer": "^10.4.18", "clsx": "^2.1.0", "color2k": "^2.0.3", + "crypto": "^1.0.1", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", @@ -15261,6 +15262,13 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "dev": true + }, "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", diff --git a/package.json b/package.json index 2922dd2..490de3d 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "autoprefixer": "^10.4.18", "clsx": "^2.1.0", "color2k": "^2.0.3", + "crypto": "^1.0.1", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", diff --git a/src/app/providers/ClientProviders.tsx b/src/app/providers/ClientProviders.tsx index c8767ff..81089e8 100644 --- a/src/app/providers/ClientProviders.tsx +++ b/src/app/providers/ClientProviders.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Theme } from '@/shared/types'; +import { Theme } from '@/shared/const/theme'; import { NextUIProvider } from '@nextui-org/react'; import { ThemeProvider } from 'next-themes'; import { ReactNode } from 'react'; diff --git a/src/app/styles/index.scss b/src/app/styles/index.scss index a1d4658..556c9cc 100644 --- a/src/app/styles/index.scss +++ b/src/app/styles/index.scss @@ -6,9 +6,16 @@ @import "swiper"; @import "nextui"; +html { + font-size: 16px; + + @media (max-width: theme('screens.sm')) { + font-size: 14px; + } +} + body { font-family: var(--font-family-main); - font-size: 16px; font-weight: 300; line-height: 1.5; isolation: isolate; diff --git a/src/app/styles/variables.scss b/src/app/styles/variables.scss index af3642d..020b249 100644 --- a/src/app/styles/variables.scss +++ b/src/app/styles/variables.scss @@ -42,7 +42,7 @@ // Others --position-bottom: 45px; - --position-top: 45px; + --position-top: calc(var(--navbar-height) + 10px); @media (max-width: theme('screens.sm')) { --position-bottom: calc(var(--navbar-height-mobile) + 10px); diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..5bedfd4 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,68 @@ +import { DataCardProps } from './shared/ui/Card'; + +export const cardData: DataCardProps[] = [ + { + id: 1, + src: '/', + links: [ + { + src: 'exampleLink1', + market: 'ozon', + }, + { + src: 'exampleLink2', + market: 'yandex', + }, + ], + images: ['cat.png', 'cat.png', 'cat.png'], + title: 'Example Title 1', + rating: 4.5, + reviewCount: 100, + currency: 'RUB', + price: 50, + oldPrice: 60, + }, + { + id: 2, + src: '/', + links: [ + { + src: 'exampleLink1', + market: 'ozon', + }, + { + src: 'exampleLink2', + market: 'yandex', + }, + ], + images: ['cat.png', 'cat.png', 'cat.png'], + title: 'Example Title 2', + rating: 4.5, + reviewCount: 100, + currency: 'RUB', + price: 50, + oldPrice: 60, + }, + { + id: 3, + src: '/', + links: [ + { + src: 'exampleLink1', + market: 'ozon', + }, + { + src: 'exampleLink2', + market: 'yandex', + }, + ], + images: ['cat.png', 'cat.png', 'cat.png'], + title: + 'Example Title Lorem Ipsum Dolor Sit Amet Consectetur Adipiscing Elit', + rating: 4.5, + reviewCount: 100, + currency: 'RUB', + price: 50, + oldPrice: 60, + }, +]; diff --git a/src/entities/Notification/Notification.module.scss b/src/entities/Notification/Notification.module.scss index 0573b0d..03d724f 100644 --- a/src/entities/Notification/Notification.module.scss +++ b/src/entities/Notification/Notification.module.scss @@ -27,11 +27,12 @@ backdrop-filter: none; } + @media (max-width: theme('screens.md')) { + @include background(1); + } + @media (max-width: theme('screens.sm')) { width: calc(100% - 10px * 2); - - @apply text-sm; - @include background(1); } button { diff --git a/src/widgets/ScrollUp/ScrollUp.module.scss b/src/features/ScrollUp/ScrollUp.module.scss similarity index 95% rename from src/widgets/ScrollUp/ScrollUp.module.scss rename to src/features/ScrollUp/ScrollUp.module.scss index 5a35e19..44870d7 100644 --- a/src/widgets/ScrollUp/ScrollUp.module.scss +++ b/src/features/ScrollUp/ScrollUp.module.scss @@ -8,7 +8,7 @@ opacity: 0; - @media (max-width: theme('screens.sm')) { + @media (max-width: theme('screens.md')) { top: var(--position-top); right: 10px; bottom: auto; diff --git a/src/widgets/ScrollUp/ScrollUp.tsx b/src/features/ScrollUp/ScrollUp.tsx similarity index 96% rename from src/widgets/ScrollUp/ScrollUp.tsx rename to src/features/ScrollUp/ScrollUp.tsx index 0fef758..3dd8ad1 100644 --- a/src/widgets/ScrollUp/ScrollUp.tsx +++ b/src/features/ScrollUp/ScrollUp.tsx @@ -49,7 +49,7 @@ export const ScrollUp: FC = ({ className = '' }) => { 'rounded-full', )} > - + {t('top')} ); diff --git a/src/widgets/ScrollUp/index.ts b/src/features/ScrollUp/index.ts similarity index 100% rename from src/widgets/ScrollUp/index.ts rename to src/features/ScrollUp/index.ts diff --git a/src/shared/assets/icon/Mail.jsx b/src/shared/assets/icon/Mail.jsx index 4f548f4..63ebc82 100644 --- a/src/shared/assets/icon/Mail.jsx +++ b/src/shared/assets/icon/Mail.jsx @@ -3,18 +3,26 @@ export const MailIcon = ({ ...props }) => ( ); diff --git a/src/shared/assets/icon/Password.jsx b/src/shared/assets/icon/Password.jsx new file mode 100644 index 0000000..69f1ec8 --- /dev/null +++ b/src/shared/assets/icon/Password.jsx @@ -0,0 +1,20 @@ +export const PasswordIcon = ({ + color = 'var(--color-main-inverted)', + ...props +}) => ( + + + +); diff --git a/src/shared/assets/icon/User.jsx b/src/shared/assets/icon/User.jsx new file mode 100644 index 0000000..a5af2ea --- /dev/null +++ b/src/shared/assets/icon/User.jsx @@ -0,0 +1,23 @@ +export const UserIcon = ({ + color = 'var(--color-main-inverted)', + ...props +}) => ( + + + + +); diff --git a/src/shared/const/localstorage.ts b/src/shared/const/localstorage.ts new file mode 100644 index 0000000..15dde23 --- /dev/null +++ b/src/shared/const/localstorage.ts @@ -0,0 +1,4 @@ +export enum LocalstorageKeys { + LIKED = 'liked-products-id', + HISTORY = 'history-products-id', +} diff --git a/src/shared/const/market.ts b/src/shared/const/market.ts new file mode 100644 index 0000000..8cf808b --- /dev/null +++ b/src/shared/const/market.ts @@ -0,0 +1,14 @@ +export type MarketType = 'ozon' | 'yandex'; + +export const Market = { + ozon: { + name: 'Ozon', + color: 'var(--color-ozon-rgb)', + image: 'ozon.svg', + }, + yandex: { + name: 'Yandex Market', + color: 'var(--color-yandex-rgb)', + image: 'yandex-market.svg', + }, +}; diff --git a/src/shared/const/theme.ts b/src/shared/const/theme.ts new file mode 100644 index 0000000..776b177 --- /dev/null +++ b/src/shared/const/theme.ts @@ -0,0 +1,4 @@ +export enum Theme { + LIGHT = 'light', + DARK = 'dark', +} diff --git a/src/shared/lib/.deprecated/useElementColor.tsx b/src/shared/lib/.deprecated/useElementColor.tsx new file mode 100644 index 0000000..697de22 --- /dev/null +++ b/src/shared/lib/.deprecated/useElementColor.tsx @@ -0,0 +1,28 @@ +// import React, { useEffect, useState } from 'react'; + +// interface ElementColorHookOptions { +// ref: React.RefObject; +// disableRipple: boolean; +// } + +// function getElementColor(element: HTMLElement | null): string | null { +// if (!element) return null; +// const computedStyle = window.getComputedStyle(element); +// return computedStyle.color; +// } + +// export function useElementColor({ +// ref, +// disableRipple, +// }: ElementColorHookOptions): string | null { +// const [currentColor, setCurrentColor] = useState(null); + +// useEffect(() => { +// if (!disableRipple && ref.current) { +// const color = getElementColor(ref.current); +// setCurrentColor(color); +// } +// }, [ref, disableRipple]); + +// return currentColor; +// } diff --git a/src/shared/lib/hooks/deprecated/useSlider.tsx b/src/shared/lib/.deprecated/useSlider.tsx similarity index 100% rename from src/shared/lib/hooks/deprecated/useSlider.tsx rename to src/shared/lib/.deprecated/useSlider.tsx diff --git a/src/shared/lib/addStorageData.tsx b/src/shared/lib/addStorageData.tsx deleted file mode 100644 index 346af11..0000000 --- a/src/shared/lib/addStorageData.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; - -/* eslint-disable react-hooks/rules-of-hooks */ -import { useEffect, useState } from 'react'; -import { StorageDataProps } from './getStorageData'; - -export const addStorageData = (id: number, item: StorageDataProps) => { - const [isLiked, setIsLiked] = useState(false); - - useEffect(() => { - const likedProducts = JSON.parse(localStorage.getItem(item) || '[]'); - setIsLiked(likedProducts.includes(id)); - }, [id, item]); - - const toggleLike = (e: MouseEvent) => { - e.preventDefault(); - - const likedProducts = JSON.parse(localStorage.getItem(item) || '[]'); - const productId = id; - - if (!isLiked) { - likedProducts.push(productId); - } else { - const index = likedProducts.indexOf(productId); - if (index !== -1) { - likedProducts.splice(index, 1); - } - } - - localStorage.setItem(item, JSON.stringify(likedProducts)); - setIsLiked(!isLiked); - }; - - return { isLiked, toggleLike }; -}; diff --git a/src/shared/lib/getStorageData.tsx b/src/shared/lib/getStorageData.tsx deleted file mode 100644 index 0e0a8e6..0000000 --- a/src/shared/lib/getStorageData.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export type StorageDataProps = 'likedProducts' | 'historyProducts'; - -export const getStorageData = (data: any, item: StorageDataProps) => { - const isFavorite = (id: number) => { - const favorites: number[] = JSON.parse(localStorage.getItem(item) || '[]'); - return favorites.includes(id); - }; - - const filteredData = data.filter((el: { id: number }) => isFavorite(el.id)); - - if (!filteredData.length) return false; - - return filteredData; -}; diff --git a/src/shared/lib/hooks/index.ts b/src/shared/lib/hooks/index.ts new file mode 100644 index 0000000..b539756 --- /dev/null +++ b/src/shared/lib/hooks/index.ts @@ -0,0 +1,4 @@ +export { useCurrency } from './useCurrency/useCurrency'; +export type { Currency } from './useCurrency/useCurrency'; +export { getStorageData } from './useStorageData/getStorageData'; +export { useStorageData } from './useStorageData/useStorageData'; diff --git a/src/shared/lib/hooks/useCurrency/useCurrency.tsx b/src/shared/lib/hooks/useCurrency/useCurrency.tsx new file mode 100644 index 0000000..fdf45ed --- /dev/null +++ b/src/shared/lib/hooks/useCurrency/useCurrency.tsx @@ -0,0 +1,37 @@ +/* eslint-disable prettier/prettier */ +export type Currency = 'RUB' | 'USD' | 'EUR'; + +export type Rates = { + [key in Currency]: number; +}; + +interface Options { + rates?: Rates; + precision?: number; // Определяет количество десятичных знаков после запятой +} + +const exchangeRates: Rates = { + RUB: 1, + USD: 0.013, + EUR: 0.012, +}; + +export const useCurrency = ( + number: number, + to: Currency, + { rates = exchangeRates, precision = 0 }: Options, +): string => { + if (!(to in rates)) { + throw new Error('Unsupported currency'); + } + + const targetPrice = number * rates[to]; + + const formattedCurrency = new Intl.NumberFormat('ru-RU', { + style: 'currency', + currency: to, + minimumFractionDigits: precision, + }).format(targetPrice); + + return formattedCurrency; +}; diff --git a/src/shared/lib/hooks/useStorageData/getStorageData.tsx b/src/shared/lib/hooks/useStorageData/getStorageData.tsx new file mode 100644 index 0000000..cf16625 --- /dev/null +++ b/src/shared/lib/hooks/useStorageData/getStorageData.tsx @@ -0,0 +1,22 @@ +import { LocalstorageKeys } from '@/shared/types'; +import { DataCardProps } from '@/shared/ui/Card'; + +export const getStorageData = ( + data: DataCardProps[], + item: LocalstorageKeys, + max?: number, +) => { + const isAdded = (id: number) => { + const favorites: number[] = JSON.parse(localStorage.getItem(item) || '[]'); + return favorites.includes(id); + }; + + const filteredData: DataCardProps[] = data + .filter((el: { id: number }) => isAdded(el.id)) + .reverse() + .slice(0, max); + + if (!filteredData.length) return false; + + return filteredData; +}; diff --git a/src/shared/lib/hooks/useStorageData/useStorageData.tsx b/src/shared/lib/hooks/useStorageData/useStorageData.tsx new file mode 100644 index 0000000..30cecd8 --- /dev/null +++ b/src/shared/lib/hooks/useStorageData/useStorageData.tsx @@ -0,0 +1,80 @@ +'use client'; + +/* eslint-disable react-hooks/rules-of-hooks */ +import { LocalstorageKeys } from '@/shared/types'; +import { DataCardProps } from '@/shared/ui/Card'; +import { MouseEvent, useEffect, useState } from 'react'; + +export const useStorageData = (data: DataCardProps, item: LocalstorageKeys) => { + const [isAdded, setIsAdded] = useState(false); + + useEffect(() => { + const products = JSON.parse(localStorage.getItem(item) || '[]'); + setIsAdded(products.includes(data.id)); + }, [data.id, item]); + + const toggle = (e: MouseEvent) => { + e.preventDefault(); + + const products = JSON.parse(localStorage.getItem(item) || '[]'); + + if (!isAdded) { + products.push(data.id); + } else { + const index = products.indexOf(data.id); + if (index !== -1) { + products.splice(index, 1); + } + } + + localStorage.setItem(item, JSON.stringify(products)); + setIsAdded(!isAdded); + }; + + const add = (e: MouseEvent) => { + e.preventDefault(); + + const products = JSON.parse(localStorage.getItem(item) || '[]'); + + if (!isAdded) { + products.push(data.id); + } + + localStorage.setItem(item, JSON.stringify(products)); + setIsAdded(true); + }; + + const remove = (e: MouseEvent) => { + e.preventDefault(); + + const products = JSON.parse(localStorage.getItem(item) || '[]'); + + const index = products.indexOf(data.id); + if (index !== -1) { + products.splice(index, 1); + } + + localStorage.setItem(item, JSON.stringify(products)); + setIsAdded(false); + }; + + // const getData = (max?: number) => { + // const isAdded = (id: number) => { + // const favorites: number[] = JSON.parse( + // localStorage.getItem(item) || '[]', + // ); + // return favorites.includes(id); + // }; + + // const filteredData = data + // .filter((el: { id: number }) => isAdded(el.id)) + // .reverse() + // .slice(0, max); + + // if (!filteredData.length) return false; + + // return filteredData; + // }; + + return { isAdded, toggle, add, remove }; +}; diff --git a/src/shared/lib/numberToCurrency.tsx b/src/shared/lib/numberToCurrency.tsx deleted file mode 100644 index ce7d7f0..0000000 --- a/src/shared/lib/numberToCurrency.tsx +++ /dev/null @@ -1,19 +0,0 @@ -export const numberToCurrency = (number: number, currency = 'RUB'): string => { - const exchangeRates: { [key: string]: number } = { - RUB: 1, - USD: 0.013, - EUR: 0.012, - }; - - if (!(currency in exchangeRates)) { - throw new Error('Unsupported currency'); - } - - const targetPrice = number * exchangeRates[currency]; - - return new Intl.NumberFormat('ru-RU', { - style: 'currency', - currency, - minimumFractionDigits: 0, // Определяет количество десятичных знаков после запятой - }).format(targetPrice); -}; diff --git a/src/shared/lib/useElementColor.tsx b/src/shared/lib/useElementColor.tsx deleted file mode 100644 index 851024a..0000000 --- a/src/shared/lib/useElementColor.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { useEffect, useState } from 'react'; - -interface ElementColorHookOptions { - ref: React.RefObject; - disableRipple: boolean; -} - -function getElementColor(element: HTMLElement | null): string | null { - if (!element) return null; - const computedStyle = window.getComputedStyle(element); - return computedStyle.color; -} - -export function useElementColor({ - ref, - disableRipple, -}: ElementColorHookOptions): string | null { - const [currentColor, setCurrentColor] = useState(null); - - useEffect(() => { - if (!disableRipple && ref.current) { - const color = getElementColor(ref.current); - setCurrentColor(color); - } - }, [ref, disableRipple]); - - return currentColor; -} diff --git a/src/shared/types.ts b/src/shared/types.ts index 5df6cac..89d0693 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1,28 +1 @@ -export type Size = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full'; - export type clsxMods = Record; - -export enum Theme { - LIGHT = 'light', - DARK = 'dark', -} - -// export enum Market { -// OZON = 'Ozon', -// YANDEX = 'Yandex Market', -// } - -export type MarketType = 'ozon' | 'yandex'; - -export const Market = { - ozon: { - name: 'Ozon', - color: 'var(--color-ozon-rgb)', - image: 'ozon.svg', - }, - yandex: { - name: 'Yandex Market', - color: 'var(--color-yandex-rgb)', - image: 'yandex-market.svg', - }, -}; diff --git a/src/shared/ui/Avatar/Avatar.module.scss b/src/shared/ui/Avatar/Avatar.module.scss index 8964fe8..35bbe24 100644 --- a/src/shared/ui/Avatar/Avatar.module.scss +++ b/src/shared/ui/Avatar/Avatar.module.scss @@ -1,9 +1,9 @@ .avatar { min-width: fit-content; - box-shadow: 0 0 0 2px rgb(var(--color-main-rgb), 1), 0 0 0 4px #3a3641; + box-shadow: 0 0 0 0.125rem rgb(var(--color-main-rgb), 1), 0 0 0 4px #3a3641; &:hover, &[aria-expanded="true"] { - box-shadow: 0 0 0 2px rgb(var(--color-main-rgb), 1), 0 0 0 4px theme('colors.primary'); + box-shadow: 0 0 0 0.125rem rgb(var(--color-main-rgb), 1), 0 0 0 4px theme('colors.primary'); } } \ No newline at end of file diff --git a/src/shared/ui/Blackhole/Blackhole.module.scss b/src/shared/ui/Blackhole/Blackhole.module.scss index 0b280b8..e430841 100644 --- a/src/shared/ui/Blackhole/Blackhole.module.scss +++ b/src/shared/ui/Blackhole/Blackhole.module.scss @@ -5,10 +5,10 @@ .video { position: absolute; - top: -153px; + top: -81px; // -153px z-index: var(--z-index-blackhole); width: 100%; - height: 745px; + height: 600px; // 745px overflow: visible; object-fit: cover; mix-blend-mode: lighten; @@ -18,10 +18,6 @@ [class="light"] & { display: none; } - - @media (max-width: theme('screens.md')) { - height: 600px; - } } @keyframes fadeInBlackhole { diff --git a/src/shared/ui/Button/Button.module.scss b/src/shared/ui/Button/Button.module.scss index cc447d5..bf22a84 100644 --- a/src/shared/ui/Button/Button.module.scss +++ b/src/shared/ui/Button/Button.module.scss @@ -102,7 +102,7 @@ .layer { height: fit-content; - padding: 5px !important; + padding: 0.313rem !important; font-weight: 400; background: rgb(var(--color-main-inverted-rgb), .025); transition: none; @@ -338,7 +338,7 @@ } .squareItem { - $border-radius: 12px; + $border-radius: 0.75rem; position: absolute; padding: 1px; @@ -363,7 +363,7 @@ z-index: 2; width: calc(100% + 8px); height: calc(100% + 8px); - border-radius: calc($border-radius + 4px); + border-radius: calc($border-radius + 0.25rem); &::before { opacity: .3; @@ -374,7 +374,7 @@ z-index: 1; width: calc(100% + 16px); height: calc(100% + 16px); - border-radius: calc($border-radius + 8px); + border-radius: calc($border-radius + 0.5rem); &::before { opacity: .2; @@ -384,7 +384,7 @@ &:nth-child(3) { width: calc(100% + 24px); height: calc(100% + 24px); - border-radius: calc($border-radius + 12px); + border-radius: calc($border-radius + 0.75rem); &::before { opacity: .1; diff --git a/src/shared/ui/Card/ui/Card/Card.module.scss b/src/shared/ui/Card/ui/Card/Card.module.scss index f99f5be..4278c03 100644 --- a/src/shared/ui/Card/ui/Card/Card.module.scss +++ b/src/shared/ui/Card/ui/Card/Card.module.scss @@ -128,28 +128,28 @@ } } -.indicator { - position: relative; - width: 18px; - height: 18px; - background: rgba(var(--indicator-color-rgb), .2); - - @apply rounded-full; - - &::after { - position: absolute; - top: 50%; - left: 50%; - display: block; - width: 6px; - height: 6px; - content: ""; - background: rgba(var(--indicator-color-rgb), 1); - transform: translate(-50%, -50%); - - @apply rounded-full; - } -} +// .indicator { +// position: relative; +// width: 18px; +// height: 18px; +// background: rgba(var(--indicator-color-rgb), .2); + +// @apply rounded-full; + +// &::after { +// position: absolute; +// top: 50%; +// left: 50%; +// display: block; +// width: 6px; +// height: 6px; +// content: ""; +// background: rgba(var(--indicator-color-rgb), 1); +// transform: translate(-50%, -50%); + +// @apply rounded-full; +// } +// } .tooltip { background: var(--bg-noise), var(--color-background); @@ -206,4 +206,8 @@ @apply rounded-full; @apply text-xs; @apply font-semibold; + + @media (max-width: theme('screens.sm')) { + font-size: .6rem; + } } \ No newline at end of file diff --git a/src/shared/ui/Card/ui/Card/Card.tsx b/src/shared/ui/Card/ui/Card/Card.tsx index 148f720..c76b5c6 100644 --- a/src/shared/ui/Card/ui/Card/Card.tsx +++ b/src/shared/ui/Card/ui/Card/Card.tsx @@ -7,13 +7,14 @@ import { StarIcon } from '@/shared/assets/icon/Star'; import { PathnamesKeys } from '@/shared/config/i18n/config'; import { Link } from '@/shared/config/i18n/navigation'; import { MediaSize } from '@/shared/config/mediaQuery/sizes'; -import { addStorageData } from '@/shared/lib/addStorageData'; -import { numberToCurrency } from '@/shared/lib/numberToCurrency'; -import { Market, MarketType } from '@/shared/types'; +import { LocalstorageKeys } from '@/shared/const/localstorage'; +import { Market, MarketType } from '@/shared/const/market'; +import { Currency, useStorageData } from '@/shared/lib/hooks'; import { Button } from '@/shared/ui/Button'; import { Tooltip } from '@nextui-org/react'; +import crypto from 'crypto'; import Image from 'next/image'; -import { FC, MouseEvent, useRef } from 'react'; +import { FC, MouseEvent, memo, useRef } from 'react'; import { useMediaQuery } from 'react-responsive'; import 'swiper/css/pagination'; import { Pagination } from 'swiper/modules'; @@ -33,6 +34,7 @@ export interface DataCardProps { title: string; rating: number; reviewCount: number; + currency: Currency; price: number; oldPrice?: number; } @@ -41,9 +43,10 @@ export interface CardProps { data: DataCardProps; } -export const Card: FC = ({ data }) => { +export const Card: FC = memo(({ data }) => { const swiperRef = useRef(null); const isPhone = useMediaQuery({ maxWidth: MediaSize.MD }); + const saltPagination = crypto.randomBytes(2).toString('hex'); const handleMouseMove = (e: MouseEvent) => { const sliderLength = swiperRef.current?.swiper.slides.length; @@ -62,7 +65,7 @@ export const Card: FC = ({ data }) => { const pagination = { clickable: true, - el: `[data-slider-dots="${data.id}"]`, + el: `[data-slider-dots="${data.id}-${saltPagination}"]`, bulletClass: cls.bullet, bulletActiveClass: cls.bulletActive, renderBullet(index: number, className: string) { @@ -72,11 +75,8 @@ export const Card: FC = ({ data }) => { const images = data.images.slice(0, 5); - const { isLiked, toggleLike } = addStorageData(data.id, 'likedProducts'); - const { toggleLike: toggleHistory } = addStorageData( - data.id, - 'historyProducts', - ); + const { isAdded, toggle } = useStorageData(data, LocalstorageKeys.LIKED); + const { add: addHistory } = useStorageData(data, LocalstorageKeys.HISTORY); return ( @@ -108,7 +108,7 @@ export const Card: FC = ({ data }) => { onClick={(e) => { e.preventDefault(); window.open(data.links[0].src, '_blank'); - toggleHistory(e); + addHistory(e); }} > Купить @@ -156,36 +156,36 @@ export const Card: FC = ({ data }) => { {pagination && ( - + )} {data.title} - - кэшбэк ~{numberToCurrency((data.price / 100) * 5)} - + кэшбэк ~{(data.price / 100) * 5}
- {numberToCurrency(data.price)} + {data.price} {data.oldPrice && ( - - {numberToCurrency(data.oldPrice)} - + {data.oldPrice} )}
{/* */}
); -}; +}); diff --git a/src/shared/ui/Card/ui/CardWide/CardWide.tsx b/src/shared/ui/Card/ui/CardWide/CardWide.tsx index cc2f3bf..afe172c 100644 --- a/src/shared/ui/Card/ui/CardWide/CardWide.tsx +++ b/src/shared/ui/Card/ui/CardWide/CardWide.tsx @@ -1,4 +1,3 @@ -import { numberToCurrency } from '@/shared/lib/numberToCurrency'; import cn from 'clsx'; import { useTranslations } from 'next-intl'; import Image from 'next/image'; @@ -33,8 +32,6 @@ export const CardWide: FC = ({ hot, }) => { const t = useTranslations('CardWide'); - const formattedPrice = numberToCurrency(price); - const formattedOldPrice = oldPrice ? numberToCurrency(oldPrice) : undefined; return (
@@ -81,10 +78,8 @@ export const CardWide: FC = ({ )}
- {formattedOldPrice && ( - {formattedOldPrice} - )} - {formattedPrice} + {oldPrice && {oldPrice}} + {price}
diff --git a/src/shared/ui/Card/ui/Cards/Cards.tsx b/src/shared/ui/Card/ui/Cards/Cards.tsx index af90b7d..8c744f1 100644 --- a/src/shared/ui/Card/ui/Cards/Cards.tsx +++ b/src/shared/ui/Card/ui/Cards/Cards.tsx @@ -6,11 +6,11 @@ import { Card, DataCardProps } from '../Card/Card'; import cls from './Cards.module.scss'; export interface CardsProps { - data: DataCardProps[]; + data: DataCardProps[] | false; } export const Cards: FC = ({ data }) => { - if (!data.length) return null; + if (!data) return null; return (
diff --git a/src/shared/ui/HeadingSlide/SlideHeading.module.scss b/src/shared/ui/HeadingSlide/SlideHeading.module.scss index c509d88..eb598d7 100644 --- a/src/shared/ui/HeadingSlide/SlideHeading.module.scss +++ b/src/shared/ui/HeadingSlide/SlideHeading.module.scss @@ -28,11 +28,12 @@ .left, .right { - font-size: 96px; - font-weight: 700; text-transform: uppercase; + @apply text-8xl; + @apply font-bold; + @media (max-width: theme('screens.sm')) { - font-size: 72px; + @apply text-6xl; } } \ No newline at end of file diff --git a/src/shared/ui/SpaceCanvas/SpaceCanvas.tsx b/src/shared/ui/SpaceCanvas/SpaceCanvas.tsx index ac8e595..b4d3cf3 100644 --- a/src/shared/ui/SpaceCanvas/SpaceCanvas.tsx +++ b/src/shared/ui/SpaceCanvas/SpaceCanvas.tsx @@ -1,7 +1,7 @@ 'use client'; import { MediaSize } from '@/shared/config/mediaQuery/sizes'; -import { Theme } from '@/shared/types'; +import { Theme } from '@/shared/const/theme'; import { PointMaterial, Points } from '@react-three/drei'; import { Canvas, useFrame } from '@react-three/fiber'; import cn from 'clsx'; diff --git a/src/views/FavoritesPage/FavoritesPage.tsx b/src/views/FavoritesPage/FavoritesPage.tsx index 8ddf75d..86f4774 100644 --- a/src/views/FavoritesPage/FavoritesPage.tsx +++ b/src/views/FavoritesPage/FavoritesPage.tsx @@ -1,22 +1,24 @@ 'use client'; /* eslint-disable i18next/no-literal-string */ +import { cardData } from '@/db'; import { GiftIcon } from '@/shared/assets/icon/Gift'; import { Link } from '@/shared/config/i18n/navigation'; -import { getStorageData } from '@/shared/lib/getStorageData'; +import { LocalstorageKeys } from '@/shared/const/localstorage'; +import { getStorageData } from '@/shared/lib/hooks'; import { Button } from '@/shared/ui/Button'; import { Cards } from '@/shared/ui/Card'; import { TopPage } from '@/widgets/TopPage'; import { Image } from '@nextui-org/react'; import cn from 'clsx'; import { FC } from 'react'; -import { cardData } from '../ShopPage/ShopPage'; import cls from './FavoritesPage.module.scss'; export const FavoritesPage: FC = () => { // const t = useTranslations('Favorites'); - const filteredData = getStorageData(cardData, 'likedProducts'); + const filteredData = getStorageData(cardData, LocalstorageKeys.LIKED); + const historyData = getStorageData(cardData, LocalstorageKeys.HISTORY); return (
@@ -40,7 +42,7 @@ export const FavoritesPage: FC = () => { } /> - {!filteredData && ( + {!filteredData ? ( <>
@@ -59,6 +61,11 @@ export const FavoritesPage: FC = () => {
+ ) : ( +
+

Вы недавно смотрели

+ +
)}
); diff --git a/src/views/ShopPage/ShopPage.module.scss b/src/views/ShopPage/ShopPage.module.scss index e504e39..324622a 100644 --- a/src/views/ShopPage/ShopPage.module.scss +++ b/src/views/ShopPage/ShopPage.module.scss @@ -8,7 +8,6 @@ display: grid; grid-template-columns: 300px 1fr; gap: 30px; - margin-top: 80px; @media (max-width: theme('screens.lg')) { grid-template-columns: 250px 1fr; diff --git a/src/views/ShopPage/ShopPage.tsx b/src/views/ShopPage/ShopPage.tsx index d58ed32..a4b20d7 100644 --- a/src/views/ShopPage/ShopPage.tsx +++ b/src/views/ShopPage/ShopPage.tsx @@ -1,8 +1,9 @@ 'use client'; /* eslint-disable i18next/no-literal-string */ +import { cardData } from '@/db'; import { Button } from '@/shared/ui/Button'; -import { Cards, DataCardProps } from '@/shared/ui/Card'; +import { Cards } from '@/shared/ui/Card'; import { Input } from '@/shared/ui/Input'; import { NavigationPanel } from '@/widgets/NavigationPanel'; import { TopPage } from '@/widgets/TopPage'; @@ -11,70 +12,6 @@ import cn from 'clsx'; import { FC, useState } from 'react'; import cls from './ShopPage.module.scss'; -export const cardData: DataCardProps[] = [ - { - id: 1, - src: '/', - links: [ - { - src: 'exampleLink1', - market: 'ozon', - }, - { - src: 'exampleLink2', - market: 'yandex', - }, - ], - images: ['cat.png', 'cat.png', 'cat.png'], - title: 'Example Title 1', - rating: 4.5, - reviewCount: 100, - price: 50, - oldPrice: 60, - }, - { - id: 2, - src: '/', - links: [ - { - src: 'exampleLink1', - market: 'ozon', - }, - { - src: 'exampleLink2', - market: 'yandex', - }, - ], - images: ['cat.png', 'cat.png', 'cat.png'], - title: 'Example Title 2', - rating: 4.5, - reviewCount: 100, - price: 50, - oldPrice: 60, - }, - { - id: 3, - src: '/', - links: [ - { - src: 'exampleLink1', - market: 'ozon', - }, - { - src: 'exampleLink2', - market: 'yandex', - }, - ], - images: ['cat.png', 'cat.png', 'cat.png'], - title: - 'Example Title Lorem Ipsum Dolor Sit Amet Consectetur Adipiscing Elit', - rating: 4.5, - reviewCount: 100, - price: 50, - oldPrice: 60, - }, -]; - export const ShopPage: FC = () => { // const t = useTranslations('ShopPage'); const minPrice = 0; diff --git a/src/widgets/Advantages/Advantages.module.scss b/src/widgets/Advantages/Advantages.module.scss index 20f4e21..387becc 100644 --- a/src/widgets/Advantages/Advantages.module.scss +++ b/src/widgets/Advantages/Advantages.module.scss @@ -51,8 +51,8 @@ // } .icon { - width: 64px; - height: 64px; + width: 4rem; + height: 4rem; img { opacity: 1 !important; diff --git a/src/widgets/BestProduct/BestProduct.module.scss b/src/widgets/BestProduct/BestProduct.module.scss index eef5840..4d2aeda 100644 --- a/src/widgets/BestProduct/BestProduct.module.scss +++ b/src/widgets/BestProduct/BestProduct.module.scss @@ -86,8 +86,8 @@ .bullet { position: relative; - width: 18px; - height: 18px; + width: 1.125rem; + height: 1.125rem; cursor: pointer; &::after { @@ -95,8 +95,8 @@ top: 50%; left: 50%; display: inline-block; - width: 6px; - height: 6px; + width: 0.375rem; + height: 0.375rem; content: ''; background: var(--color-main-inverted); opacity: .1; diff --git a/src/widgets/Footer/Footer.tsx b/src/widgets/Footer/Footer.tsx index dff9ebc..a967896 100644 --- a/src/widgets/Footer/Footer.tsx +++ b/src/widgets/Footer/Footer.tsx @@ -1,6 +1,5 @@ import { GmailIcon } from '@/shared/assets/icon/Gmail'; import { TelegramIcon } from '@/shared/assets/icon/Telegram'; -import { Links } from '@/shared/const/links'; import { Logo } from '@/shared/ui/Logo'; import { Wave } from '@/shared/ui/Wave'; import cn from 'clsx'; @@ -8,6 +7,7 @@ import { useTranslations } from 'next-intl'; import Link from 'next/link'; import { FC } from 'react'; import cls from './Footer.module.scss'; +import { Links } from './data'; interface FooterProps { className?: string; @@ -22,7 +22,7 @@ export const Footer: FC = ({ className = '' }) => {