From 2a61503456706b33e277ecc0796a0aefd95d9d03 Mon Sep 17 00:00:00 2001 From: MarkMelior Date: Mon, 15 Jul 2024 19:24:27 +0300 Subject: [PATCH] add hook `useMouse()`, add glowing-line --- app/[lang]/layout.tsx | 8 +- app/[lang]/not-found.tsx | 12 +-- app/[lang]/page.tsx | 17 ++-- .../projects/[category]/[name]/loading.tsx | 5 ++ projects/best-practice/app-router-auth/en.mdx | 1 - projects/best-practice/app-router-auth/ru.mdx | 3 +- public/images/melior-fly.png | Bin 0 -> 9579 bytes public/images/melior-graffiti.png | Bin 0 -> 23503 bytes src/shared/assets/icon/Logo.jsx | 20 ----- src/shared/assets/icon/Logo.tsx | 74 ++++++++++++++++++ src/shared/const/fonts.tsx | 4 + .../model/data.tsx => const/stack-data.tsx} | 4 +- src/shared/hooks/index.ts | 3 +- src/shared/hooks/useMouse/useMouse.tsx | 60 ++++++++++++++ src/shared/ui/code-block/code-block.tsx | 13 ++- src/shared/ui/code-block/code.tsx | 37 ++------- .../ui/glowing-box/glowing-box.module.scss | 46 +++++++++++ src/shared/ui/glowing-box/glowing-box.tsx | 52 ++++++++++++ .../ui/glowing-box/glowing-line.module.scss | 8 ++ src/shared/ui/glowing-box/glowing-line.tsx | 28 +++++++ .../ui/glowing-box/model/glowing-settings.ts | 26 ++++++ src/shared/ui/index.ts | 6 +- src/shared/ui/stack-buttons/stack-buttons.tsx | 10 +-- src/widgets/footer/footer-github-link.tsx | 15 ++-- src/widgets/footer/footer.module.scss | 2 +- src/widgets/footer/footer.tsx | 13 ++- src/widgets/header/header.tsx | 2 +- src/widgets/navbar/navbar.tsx | 3 +- 28 files changed, 367 insertions(+), 105 deletions(-) create mode 100644 app/[lang]/projects/[category]/[name]/loading.tsx create mode 100644 public/images/melior-fly.png create mode 100644 public/images/melior-graffiti.png delete mode 100644 src/shared/assets/icon/Logo.jsx create mode 100644 src/shared/assets/icon/Logo.tsx create mode 100644 src/shared/const/fonts.tsx rename src/shared/{ui/stack-buttons/model/data.tsx => const/stack-data.tsx} (91%) create mode 100644 src/shared/hooks/useMouse/useMouse.tsx create mode 100644 src/shared/ui/glowing-box/glowing-box.module.scss create mode 100644 src/shared/ui/glowing-box/glowing-box.tsx create mode 100644 src/shared/ui/glowing-box/glowing-line.module.scss create mode 100644 src/shared/ui/glowing-box/glowing-line.tsx create mode 100644 src/shared/ui/glowing-box/model/glowing-settings.ts diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index eaaf7b0..c08e6e1 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -1,14 +1,12 @@ import { ClientProviders } from '@/app/providers/client-providers'; import '@/app/styles/index.scss'; import { i18n, Locale } from '@/shared/config'; +import { FontDefault } from '@/shared/const/fonts'; import { Light, PageLoader } from '@/shared/ui'; import { Footer, Navbar, Sidebar } from '@/widgets'; import type { Metadata } from 'next'; -import { Inter } from 'next/font/google'; import { Suspense } from 'react'; -const inter = Inter({ subsets: ['latin'] }); - export const metadata: Metadata = { title: 'Simple App | Mark Melior', description: `Small and modern pet-projects. Hi, I'am Mark Melior - Frontend developer.`, @@ -29,12 +27,12 @@ export default async function RootLayout({ }: Readonly) { return ( - + - + } diff --git a/app/[lang]/not-found.tsx b/app/[lang]/not-found.tsx index f1b7e89..d1c0e8c 100644 --- a/app/[lang]/not-found.tsx +++ b/app/[lang]/not-found.tsx @@ -1,11 +1,7 @@ +import { getDictionary } from '@/shared/config'; + export default async function NotFound() { -// { -// params: { lang }, -// }: { -// params: { lang: Locale }; -// } - // const dictionary = await getDictionary(lang); + const dictionary = await getDictionary(); - return <>Not fount page.; - // return <>{dictionary['ui']['page-not-found']}; + return <>{dictionary['ui']['page-not-found']}; } diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx index ec90e2d..0b12da0 100644 --- a/app/[lang]/page.tsx +++ b/app/[lang]/page.tsx @@ -1,22 +1,15 @@ -import { Locale } from '@/shared/config'; +import { getDictionary } from '@/shared/config'; +import { StackButtons } from '@/shared/ui'; import { Header } from '@/widgets'; -import { getDictionary } from '../../src/shared/config/i18n/dictionaries'; -export default async function Home({ - params: { lang }, -}: { - params: { lang: Locale }; -}) { - // const lang = await getLang(); - // const HomePage = dynamic(() => import(`./home-${lang}.mdx`)); - - const dictionary = await getDictionary(lang); +export default async function Home() { + const dictionary = await getDictionary(); const { description, note, title } = dictionary['home-page']; return ( <>
- {/* */} + ); } diff --git a/app/[lang]/projects/[category]/[name]/loading.tsx b/app/[lang]/projects/[category]/[name]/loading.tsx new file mode 100644 index 0000000..fdd32a4 --- /dev/null +++ b/app/[lang]/projects/[category]/[name]/loading.tsx @@ -0,0 +1,5 @@ +import { Header } from '@/widgets'; + +export default async function ProjectLoading() { + return
; +} diff --git a/projects/best-practice/app-router-auth/en.mdx b/projects/best-practice/app-router-auth/en.mdx index 553e79b..d7384f6 100644 --- a/projects/best-practice/app-router-auth/en.mdx +++ b/projects/best-practice/app-router-auth/en.mdx @@ -1,5 +1,4 @@ --- -note: 'Auth' title: 'Next.js: Authentication' description: 'Best practices for Server Components, Actions, Middleware' tags: ['TypeScript', 'Cookie', 'SSR', 'Prisma'] diff --git a/projects/best-practice/app-router-auth/ru.mdx b/projects/best-practice/app-router-auth/ru.mdx index 67fac1c..47c5f14 100644 --- a/projects/best-practice/app-router-auth/ru.mdx +++ b/projects/best-practice/app-router-auth/ru.mdx @@ -1,5 +1,4 @@ --- -note: 'Auth' title: 'Next.js: Аутентификация' description: 'Рекомендации по использованию серверных компонентов, Actions, Middleware' tags: ['TypeScript', 'Cookie', 'SSR', 'Prisma'] @@ -12,7 +11,7 @@ tags: ['TypeScript', 'Cookie', 'SSR', 'Prisma'] Получение пользовательских данных: `const user = await getUser();`. Это очень просто :) - привет -- парень, "тест` хорош! +- парень, `тест` хорош! - у меня все хорошо тест)) diff --git a/public/images/melior-fly.png b/public/images/melior-fly.png new file mode 100644 index 0000000000000000000000000000000000000000..ed6ed7750ded48461ffb27ddb7a048ba7ce03cdc GIT binary patch literal 9579 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 literal 0 HcmV?d00001 diff --git a/src/shared/assets/icon/Logo.jsx b/src/shared/assets/icon/Logo.jsx deleted file mode 100644 index a2d56dd..0000000 --- a/src/shared/assets/icon/Logo.jsx +++ /dev/null @@ -1,20 +0,0 @@ -export const Logo = ({ ...props }) => ( - - - - -); diff --git a/src/shared/assets/icon/Logo.tsx b/src/shared/assets/icon/Logo.tsx new file mode 100644 index 0000000..6e11c9e --- /dev/null +++ b/src/shared/assets/icon/Logo.tsx @@ -0,0 +1,74 @@ +import Image from 'next/image'; +import { FC } from 'react'; + +interface Props extends React.SVGProps { + variant?: 'graffiti' | 'text' | 'fly'; +} + +export const Logo: FC = ({ variant, ...props }) => { + switch (variant) { + case 'text': + return ( + + + + + + + + + + + + ); + case 'fly': + return ( + logo + ); + case 'graffiti': + return ( + logo + ); + default: + return ( + + + + + ); + } +}; diff --git a/src/shared/const/fonts.tsx b/src/shared/const/fonts.tsx new file mode 100644 index 0000000..49b11ee --- /dev/null +++ b/src/shared/const/fonts.tsx @@ -0,0 +1,4 @@ +import { Inter, JetBrains_Mono } from 'next/font/google'; + +export const FontDefault = Inter({ subsets: ['latin'] }); +export const FontCode = JetBrains_Mono({ subsets: ['latin'] }); diff --git a/src/shared/ui/stack-buttons/model/data.tsx b/src/shared/const/stack-data.tsx similarity index 91% rename from src/shared/ui/stack-buttons/model/data.tsx rename to src/shared/const/stack-data.tsx index 6891e86..4cbfd1f 100644 --- a/src/shared/ui/stack-buttons/model/data.tsx +++ b/src/shared/const/stack-data.tsx @@ -15,13 +15,13 @@ export type StackVariants = | 'Markdown' | 'Prisma'; -interface ButtonProps { +interface StackProps { icon: JSX.Element; colorText?: string; color?: string; } -export const StackButtonData: Record = { +export const StackData: Record = { TypeScript: { icon: , color: 'bg-blue-500/10', diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index e8da4ab..38e0c4a 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -1,4 +1,5 @@ import { useCopy } from './useCopy/useCopy'; import { MessageProvider, useMessage } from './useMessage/useMessage'; +import { useMouse } from './useMouse/useMouse'; -export { MessageProvider, useCopy, useMessage }; +export { MessageProvider, useCopy, useMessage, useMouse }; diff --git a/src/shared/hooks/useMouse/useMouse.tsx b/src/shared/hooks/useMouse/useMouse.tsx new file mode 100644 index 0000000..17d54aa --- /dev/null +++ b/src/shared/hooks/useMouse/useMouse.tsx @@ -0,0 +1,60 @@ +'use client'; + +import { MutableRefObject, useEffect, useRef, useState } from 'react'; + +interface MouseState { + x: number; + y: number; + elementX: number; + elementY: number; + elementPositionX: number; + elementPositionY: number; +} + +export const useMouse = (): [ + MouseState, + MutableRefObject, +] => { + const [state, setState] = useState({ + x: 0, + y: 0, + elementX: 0, + elementY: 0, + elementPositionX: 0, + elementPositionY: 0, + }); + + const ref = useRef(null); + + useEffect(() => { + function handleMouseMove(event: MouseEvent) { + const { clientX, clientY } = event; + const element = ref.current; + + if (element) { + const rect = element.getBoundingClientRect(); + const elementX = clientX - rect.left; + const elementY = clientY - rect.top; + const elementPositionX = rect.left + window.scrollX; + const elementPositionY = rect.top + window.scrollY; + + setState({ + x: clientX, + y: clientY, + elementX, + elementY, + elementPositionX, + elementPositionY, + }); + } + } + + window.addEventListener('mousemove', handleMouseMove); + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + }; + }, []); + + return [state, ref]; +}; diff --git a/src/shared/ui/code-block/code-block.tsx b/src/shared/ui/code-block/code-block.tsx index 5dd34d4..7f87db2 100644 --- a/src/shared/ui/code-block/code-block.tsx +++ b/src/shared/ui/code-block/code-block.tsx @@ -1,12 +1,12 @@ 'use client'; import { Dictionary } from '@/shared/config'; +import { FontCode, FontDefault } from '@/shared/const/fonts'; import { cn, gitHubRepoLink } from '@/shared/lib'; import { GitHubPath } from '@/shared/types/github-path'; import { Theme } from '@/shared/types/theme'; import { Button, Link, Tooltip } from '@nextui-org/react'; import { useTheme } from 'next-themes'; -import { Inter, JetBrains_Mono } from 'next/font/google'; import { FC, useCallback, useEffect, useState } from 'react'; import { LuEye } from 'react-icons/lu'; import { TbFileUnknown } from 'react-icons/tb'; @@ -15,8 +15,8 @@ import { atomOneDark, atomOneLight, } from 'react-syntax-highlighter/dist/cjs/styles/hljs'; +import { StackData, StackVariants } from '../../const/stack-data'; import { CopyButton } from '../copy-button/copy-button'; -import { StackButtonData, StackVariants } from '../stack-buttons/model/data'; import './code-block.scss'; interface CodeBlockProps { @@ -31,9 +31,6 @@ interface CodeBlockProps { dict: Dictionary['ui']; } -const inter = Inter({ subsets: ['latin'] }); -const jetBrains_Mono = JetBrains_Mono({ subsets: ['latin'] }); - export const CodeBlock: FC = ({ text, fileName, @@ -99,13 +96,13 @@ export const CodeBlock: FC = ({
{!hideHeader && (
- {StackButtonData[language]?.icon || } + {StackData[language]?.icon || } {fileName ? fileName : language} {buttons()}
@@ -127,7 +124,7 @@ export const CodeBlock: FC = ({ 'border-0': hideHeader, })} codeTagProps={{ - className: cn('text-[0.85rem]', jetBrains_Mono.className), + className: cn('text-[0.85rem]', FontCode.className), }} > {isExpanded || !isLong diff --git a/src/shared/ui/code-block/code.tsx b/src/shared/ui/code-block/code.tsx index 40b782f..30f9374 100644 --- a/src/shared/ui/code-block/code.tsx +++ b/src/shared/ui/code-block/code.tsx @@ -1,53 +1,32 @@ 'use client'; import { Dictionary } from '@/shared/config'; +import { FontDefault } from '@/shared/const/fonts'; import { useCopy } from '@/shared/hooks'; +import { cn } from '@/shared/lib'; import { Button, Tooltip } from '@nextui-org/react'; import { FC } from 'react'; -import { StackVariants } from '../stack-buttons/model/data'; interface CodeProps { text: string; - language?: StackVariants; dict: Dictionary['ui']; } -export const Code: FC = ({ - text, - dict, - language = 'TypeScript', -}) => { +export const Code: FC = ({ text, dict }) => { const { handleCopy } = useCopy(); - // const [mounted, setMounted] = useState(false); - // const { theme } = useTheme(); - - // useEffect(() => { - // setMounted(true); - // }, []); return ( ); diff --git a/src/shared/ui/glowing-box/glowing-box.module.scss b/src/shared/ui/glowing-box/glowing-box.module.scss new file mode 100644 index 0000000..9a43241 --- /dev/null +++ b/src/shared/ui/glowing-box/glowing-box.module.scss @@ -0,0 +1,46 @@ +.glowing-iconbox { + position: relative; + + &:hover::after { + opacity: var(--spotlight-border-strength-hover); + } + + &:hover::before { + opacity: var(--spotlight-bg-strength-hover); + } + + &::before, + &::after { + content: ""; + border-radius: inherit; + height: 100%; + position: absolute; + left: 0; + top: 0; + transition: opacity .5s; + width: 100%; + pointer-events: none; + will-change: background; + } + + &::before { + opacity: var(--spotlight-bg-strength); + z-index: 3; + background: radial-gradient(var(--spotlight-bg-size) circle at var(--mouse-x) var(--mouse-y), #488bff, transparent 40%); + } + + &::after { + opacity: var(--spotlight-border-strength); + z-index: 1; + background: radial-gradient(var(--spotlight-border-size) circle at var(--mouse-x) var(--mouse-y), + rgba(57, 196, 255, 1) 0%, + rgba(227, 57, 255, 1) 50%, + rgba(0, 0, 0, 0) 100%, transparent 40%); + // background: radial-gradient(var(--spotlight-border-size) circle at var(--mouse-x) var(--mouse-y), + // rgba(57, 196, 255, 1) 0%, + // rgba(57, 101, 255, 1) 25%, + // rgba(227, 57, 255, 1) 50%, + // rgba(0, 0, 0, 0) 75%, + // rgba(0, 0, 0, 0) 100%, transparent 40%); + } +} \ No newline at end of file diff --git a/src/shared/ui/glowing-box/glowing-box.tsx b/src/shared/ui/glowing-box/glowing-box.tsx new file mode 100644 index 0000000..48194d9 --- /dev/null +++ b/src/shared/ui/glowing-box/glowing-box.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { useMouse } from '@/shared/hooks'; +import { cn } from '@/shared/lib'; +import { FC } from 'react'; +import cls from './glowing-box.module.scss'; +import { glowingStyle } from './model/glowing-settings'; + +interface GlowingBoxClassnames { + border?: string; + background?: string; + common?: string; +} + +interface GlowingBoxProps { + children: React.ReactNode; + classNames?: GlowingBoxClassnames; + className?: string; +} + +export const GlowingBox: FC = ({ children, classNames }) => { + const [mousePosition, ref] = useMouse(); + + const style = { + '--mouse-x': `${mousePosition.elementX}px`, + '--mouse-y': `${mousePosition.elementY}px`, + ...glowingStyle, + } as React.CSSProperties; + + return ( +
+
+ {children} +
+
+ ); +}; diff --git a/src/shared/ui/glowing-box/glowing-line.module.scss b/src/shared/ui/glowing-box/glowing-line.module.scss new file mode 100644 index 0000000..05a273c --- /dev/null +++ b/src/shared/ui/glowing-box/glowing-line.module.scss @@ -0,0 +1,8 @@ +.glowing-line { + position: absolute; + width: 100%; + height: 1px; + background: radial-gradient(var(--spotlight-line-size) circle at var(--mouse-x) var(--mouse-y), + theme('colors.primary-400') 0%, + rgba(0, 0, 0, 0) 70%, transparent 40%); +} \ No newline at end of file diff --git a/src/shared/ui/glowing-box/glowing-line.tsx b/src/shared/ui/glowing-box/glowing-line.tsx new file mode 100644 index 0000000..a88b26c --- /dev/null +++ b/src/shared/ui/glowing-box/glowing-line.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { useMouse } from '@/shared/hooks'; +import { cn } from '@/shared/lib'; +import { FC } from 'react'; +import cls from './glowing-line.module.scss'; + +interface GlowingBoxProps { + className?: string; +} + +export const GlowingLine: FC = ({ className }) => { + const [mousePosition, ref] = useMouse(); + + const style = { + '--mouse-x': `${mousePosition.elementX}px`, + '--mouse-y': `${mousePosition.elementY}px`, + '--spotlight-line-size': '400px', + } as React.CSSProperties; + + return ( +
+ ); +}; diff --git a/src/shared/ui/glowing-box/model/glowing-settings.ts b/src/shared/ui/glowing-box/model/glowing-settings.ts new file mode 100644 index 0000000..b78f8ec --- /dev/null +++ b/src/shared/ui/glowing-box/model/glowing-settings.ts @@ -0,0 +1,26 @@ +interface GlowingBoxSettings { + borderStrength?: number; + borderStrengthHover?: number; + bgStrength?: number; + bgStrengthHover?: number; + borderSize?: string; + bgSize?: string; +} + +export const glowingSettings: GlowingBoxSettings = { + borderStrength: 0.5, + borderStrengthHover: 1, + bgStrength: 0, + bgStrengthHover: 0.05, + borderSize: '500px', + bgSize: '700px', +}; + +export const glowingStyle = { + '--spotlight-border-strength': glowingSettings.borderStrength, + '--spotlight-border-strength-hover': glowingSettings.borderStrengthHover, + '--spotlight-bg-strength': glowingSettings.bgStrength, + '--spotlight-bg-strength-hover': glowingSettings.bgStrengthHover, + '--spotlight-border-size': glowingSettings.borderSize, + '--spotlight-bg-size': glowingSettings.bgSize, +} as React.CSSProperties; diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index 538cdc0..a4cb6e5 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -1,10 +1,12 @@ +import { StackVariants } from '../const/stack-data'; import { Code } from './code-block/code'; import { CodeBlock } from './code-block/code-block'; import { DownloadCvButton } from './download-cv-button/download-cv-button'; +import { GlowingBox } from './glowing-box/glowing-box'; +import { GlowingLine } from './glowing-box/glowing-line'; import { Light } from './light/light'; import { PageLoader } from './page-loader/page-loader'; import { SidebarNavigation } from './sidebar-navigation/sidebar-navigation'; -import { StackVariants } from './stack-buttons/model/data'; import { StackButtons } from './stack-buttons/stack-buttons'; import { Text } from './text/text'; @@ -12,6 +14,8 @@ export { Code, CodeBlock, DownloadCvButton, + GlowingBox, + GlowingLine, Light, PageLoader, SidebarNavigation, diff --git a/src/shared/ui/stack-buttons/stack-buttons.tsx b/src/shared/ui/stack-buttons/stack-buttons.tsx index 8026402..a13cedd 100644 --- a/src/shared/ui/stack-buttons/stack-buttons.tsx +++ b/src/shared/ui/stack-buttons/stack-buttons.tsx @@ -1,6 +1,6 @@ import { cn } from '@/shared/lib'; import { Button } from '@nextui-org/react'; -import { StackButtonData, StackVariants } from './model/data'; +import { StackData, StackVariants } from '../../const/stack-data'; interface StackButtonsProps { tags?: StackVariants[]; @@ -10,7 +10,7 @@ interface StackButtonsProps { export const StackButtons = ({ tags, - isColored = true, + isColored, className, }: StackButtonsProps) => { if (!tags) { @@ -25,11 +25,11 @@ export const StackButtons = ({ variant='flat' disableRipple size='sm' - startContent={StackButtonData[tag]?.icon} + startContent={StackData[tag]?.icon} className={`${ - (isColored && StackButtonData[tag]?.color) || 'bg-default-500/10' + (isColored && StackData[tag]?.color) || 'bg-default-500/10' } ${ - (isColored && StackButtonData[tag]?.colorText) || 'text-default-700' + (isColored && StackData[tag]?.colorText) || 'text-default-700' } cursor-default`} > {tag} diff --git a/src/widgets/footer/footer-github-link.tsx b/src/widgets/footer/footer-github-link.tsx index 4e01917..9de4d9d 100644 --- a/src/widgets/footer/footer-github-link.tsx +++ b/src/widgets/footer/footer-github-link.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Dictionary, Link, Locale } from '@/shared/config'; +import { Dictionary, i18n, Link, Locale } from '@/shared/config'; import { usePathname } from 'next/navigation'; import { FC } from 'react'; @@ -9,17 +9,18 @@ interface FooterGithubLinkProps { lang: Locale; } -export const FooterGithubLink: FC = async ({ - dict, - lang, -}) => { +export const FooterGithubLink: FC = ({ dict, lang }) => { const pathname = usePathname(); - if (pathname === '/') return null; + if ( + pathname === '/' || + i18n.locales.includes(pathname.split('/')[1] as Locale) + ) + return null; const hrefGithub = `https://github.com/MarkMelior/simple-app/blob/master${pathname}/${lang}.mdx`; - const hrefHomePage = `https://github.com/MarkMelior`; + // const hrefHomePage = `https://github.com/MarkMelior/simple-app/blob/master/app/[lang]/page.tsx`; return ( { return (
+ {/* */}
-
+ +

{dict['footer-copyright']}

@@ -26,6 +36,7 @@ export const Footer = async () => {

+ {/* */}
); }; diff --git a/src/widgets/header/header.tsx b/src/widgets/header/header.tsx index 926dad9..77af34b 100644 --- a/src/widgets/header/header.tsx +++ b/src/widgets/header/header.tsx @@ -39,7 +39,7 @@ export const Header: React.FC = ({

{description}

- +
); }; diff --git a/src/widgets/navbar/navbar.tsx b/src/widgets/navbar/navbar.tsx index 683a3dc..a0609f8 100644 --- a/src/widgets/navbar/navbar.tsx +++ b/src/widgets/navbar/navbar.tsx @@ -2,7 +2,7 @@ import { getProjects } from '@/entity/project'; import { Burger, LocaleSwitcher, ThemeSwitcher } from '@/features'; import { Logo } from '@/shared/assets/icon/Logo'; import { getDictionary, Link } from '@/shared/config'; -import { DownloadCvButton, SidebarNavigation } from '@/shared/ui'; +import { DownloadCvButton, GlowingLine, SidebarNavigation } from '@/shared/ui'; import { Button } from '@nextui-org/react'; import { BsGithub } from 'react-icons/bs'; import cls from './navbar.module.scss'; @@ -69,6 +69,7 @@ export const Navbar = async () => { + ); };