├── public ├── locale │ ├── de │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── fr │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── id │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── it │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── pl │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── ru │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ ├── uk │ │ ├── common.json │ │ ├── contact.json │ │ ├── footer.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json │ └── en │ │ ├── contact.json │ │ ├── footer.json │ │ ├── common.json │ │ ├── header.json │ │ ├── about.json │ │ ├── skills.json │ │ └── works.json ├── works │ ├── cvet.png │ ├── ikota.png │ ├── kai.js.png │ ├── slova.png │ ├── dbh-docs.png │ ├── portfolio.png │ ├── splendid.png │ ├── profile-card.png │ ├── todo-tracker.png │ └── knrtu-kai-discord-bot.png ├── about │ └── domin.jpg ├── contacts │ └── kazan.jpg ├── favicon │ ├── favicon.ico │ ├── mstile-70x70.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── browserconfig.xml │ ├── site.webmanifest │ └── safari-pinned-tab.svg └── landing │ └── keyboard.glb ├── .eslintrc.json ├── src ├── components │ ├── ui │ │ ├── time │ │ │ ├── index.tsx │ │ │ └── component.tsx │ │ ├── card │ │ │ ├── index.tsx │ │ │ ├── component.tsx │ │ │ └── styles.tsx │ │ ├── carousel │ │ │ ├── index.tsx │ │ │ ├── state.ts │ │ │ └── component.tsx │ │ ├── modal │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ └── component.tsx │ │ └── menu │ │ │ ├── index.ts │ │ │ ├── styles.ts │ │ │ ├── state.ts │ │ │ ├── config.ts │ │ │ └── component.tsx │ ├── pages │ │ └── index │ │ │ ├── keyboard │ │ │ ├── index.tsx │ │ │ └── component.tsx │ │ │ ├── landing │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ └── component.tsx │ │ │ ├── loading │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ └── component.tsx │ │ │ ├── about │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ ├── config.tsx │ │ │ └── component.tsx │ │ │ ├── contact │ │ │ ├── index.tsx │ │ │ ├── config.tsx │ │ │ ├── styles.tsx │ │ │ └── component.tsx │ │ │ ├── skills │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ ├── config.tsx │ │ │ └── component.tsx │ │ │ └── works │ │ │ ├── index.tsx │ │ │ ├── styles.tsx │ │ │ ├── component.tsx │ │ │ └── config.tsx │ └── layout │ │ ├── drawer │ │ ├── index.tsx │ │ ├── styles.tsx │ │ └── component.tsx │ │ ├── mesh │ │ ├── index.tsx │ │ ├── styles.tsx │ │ └── component.tsx │ │ ├── footer │ │ ├── index.tsx │ │ ├── config.tsx │ │ ├── styles.tsx │ │ └── component.tsx │ │ ├── help │ │ ├── index.tsx │ │ ├── config.tsx │ │ ├── styles.tsx │ │ └── component.tsx │ │ ├── navbar │ │ ├── index.ts │ │ ├── state.ts │ │ ├── styles.ts │ │ └── component.tsx │ │ ├── translations │ │ ├── index.tsx │ │ ├── styles.tsx │ │ ├── config.tsx │ │ └── component.tsx │ │ └── header │ │ ├── index.tsx │ │ ├── config.tsx │ │ ├── state.ts │ │ ├── styles.tsx │ │ └── component.tsx ├── types │ ├── components │ │ ├── ui │ │ │ ├── card.d.ts │ │ │ ├── devicon.d.ts │ │ │ ├── translations.d.ts │ │ │ ├── menu.d.ts │ │ │ └── carousel.d.ts │ │ ├── layout │ │ │ ├── navbar.d.ts │ │ │ └── header.d.ts │ │ └── pages │ │ │ ├── contact.d.ts │ │ │ ├── about.d.ts │ │ │ ├── skills.d.ts │ │ │ ├── keyboard.d.ts │ │ │ └── works.d.ts │ ├── utils │ │ └── number.d.ts │ └── pages │ │ └── app.d.ts └── pages │ ├── _document.tsx │ ├── 404.tsx │ ├── index.tsx │ └── _app.tsx ├── .prettierrc.js ├── crowdin.yml ├── .github ├── dependabot.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── next-i18next.config.js ├── next-env.d.ts ├── next.config.js ├── ikota.config.js ├── .gitignore ├── tsconfig.json ├── LICENSE ├── package.json ├── docs ├── 3d-models.md └── carousel-page-OUTDATED.md ├── README.md └── CODE_OF_CONDUCT.md /public/locale/de/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/fr/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/id/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/it/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/pl/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/ru/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /public/locale/uk/common.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /public/locale/en/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Contact" 3 | } -------------------------------------------------------------------------------- /public/locale/fr/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Contact" 3 | } -------------------------------------------------------------------------------- /public/locale/id/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Kontak" 3 | } -------------------------------------------------------------------------------- /public/locale/it/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Contact" 3 | } -------------------------------------------------------------------------------- /public/locale/pl/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Kontakt" 3 | } -------------------------------------------------------------------------------- /public/locale/ru/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Связь" 3 | } -------------------------------------------------------------------------------- /public/locale/uk/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Зв'язок" 3 | } -------------------------------------------------------------------------------- /src/components/ui/time/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | -------------------------------------------------------------------------------- /public/locale/de/contact.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Kontaktinfos" 3 | } -------------------------------------------------------------------------------- /src/components/pages/index/keyboard/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@mantine/eslint-config/prettier.config'); 2 | -------------------------------------------------------------------------------- /public/locale/en/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. All Rights Reserved." 3 | } -------------------------------------------------------------------------------- /public/locale/fr/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. All Rights Reserved." 3 | } -------------------------------------------------------------------------------- /public/locale/id/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. All Rights Reserved." 3 | } -------------------------------------------------------------------------------- /public/locale/it/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. All Rights Reserved." 3 | } -------------------------------------------------------------------------------- /public/locale/ru/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Камиль С. Все Права Защищены." 3 | } -------------------------------------------------------------------------------- /public/locale/uk/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Каміл С. Всі права захищені." 3 | } -------------------------------------------------------------------------------- /public/works/cvet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/cvet.png -------------------------------------------------------------------------------- /src/components/ui/card/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /src/components/ui/carousel/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './state'; 3 | -------------------------------------------------------------------------------- /src/components/ui/modal/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /public/about/domin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/about/domin.jpg -------------------------------------------------------------------------------- /public/locale/de/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. Alle Rechte Vorbehalten." 3 | } -------------------------------------------------------------------------------- /public/locale/pl/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "footer-value": "© 2023 Kamil S. Wszelkie prawa zastrzeżone." 3 | } -------------------------------------------------------------------------------- /public/works/ikota.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/ikota.png -------------------------------------------------------------------------------- /public/works/kai.js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/kai.js.png -------------------------------------------------------------------------------- /public/works/slova.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/slova.png -------------------------------------------------------------------------------- /src/components/layout/drawer/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /src/components/layout/mesh/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /public/contacts/kazan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/contacts/kazan.jpg -------------------------------------------------------------------------------- /public/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/favicon.ico -------------------------------------------------------------------------------- /public/works/dbh-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/dbh-docs.png -------------------------------------------------------------------------------- /public/works/portfolio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/portfolio.png -------------------------------------------------------------------------------- /public/works/splendid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/splendid.png -------------------------------------------------------------------------------- /src/components/pages/index/landing/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /src/components/pages/index/loading/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | -------------------------------------------------------------------------------- /public/landing/keyboard.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/landing/keyboard.glb -------------------------------------------------------------------------------- /public/works/profile-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/profile-card.png -------------------------------------------------------------------------------- /public/works/todo-tracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/todo-tracker.png -------------------------------------------------------------------------------- /public/favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /public/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /public/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /public/favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /public/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /src/components/ui/menu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /public/works/knrtu-kai-discord-bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/works/knrtu-kai-discord-bot.png -------------------------------------------------------------------------------- /src/components/layout/footer/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /src/components/layout/help/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /src/components/layout/navbar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './state'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /public/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domin-mnd/portfolio/HEAD/public/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/components/layout/translations/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /src/components/pages/index/about/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | export * from './config'; 4 | -------------------------------------------------------------------------------- /src/components/pages/index/contact/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | export * from './config'; 4 | -------------------------------------------------------------------------------- /src/components/pages/index/skills/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /src/components/pages/index/works/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './config'; 3 | export * from './styles'; 4 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /public/locale/en/*.json 3 | translation: /public/locale/%two_letters_code%/%original_file_name% 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /src/components/layout/header/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './component'; 2 | export * from './styles'; 3 | export * from './config'; 4 | export * from './state'; 5 | -------------------------------------------------------------------------------- /src/components/layout/footer/config.tsx: -------------------------------------------------------------------------------- 1 | /** Footer text presented in the bottom part of the screen */ 2 | export const footerText: string = '© 2023 Kamil S. All Rights Reserved.'; 3 | -------------------------------------------------------------------------------- /public/locale/en/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta-title": "My portfolio", 3 | "meta-description": "My personal portfolio website consisting of my projects, skill stack and contact information." 4 | } -------------------------------------------------------------------------------- /next-i18next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | i18n: { 3 | defaultLocale: 'en', 4 | locales: ['de', 'en', 'fr', 'it', 'pl', 'id', 'ru', 'uk'], 5 | }, 6 | localePath: './public/locale', 7 | } -------------------------------------------------------------------------------- /public/locale/fr/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "English", 3 | "sections": { 4 | "landing": "Domin", 5 | "works": "Works", 6 | "skills": "Skills", 7 | "contact": "Contact" 8 | } 9 | } -------------------------------------------------------------------------------- /public/locale/id/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Bahasa", 3 | "sections": { 4 | "landing": "Domin", 5 | "works": "Karya", 6 | "skills": "Keahlian", 7 | "contact": "Kontak" 8 | } 9 | } -------------------------------------------------------------------------------- /public/locale/it/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "English", 3 | "sections": { 4 | "landing": "Domin", 5 | "works": "Works", 6 | "skills": "Skills", 7 | "contact": "Contact" 8 | } 9 | } -------------------------------------------------------------------------------- /public/locale/ru/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Русский", 3 | "sections": { 4 | "landing": "Домин", 5 | "works": "Проекты", 6 | "skills": "Навыки", 7 | "contact": "Связь" 8 | } 9 | } -------------------------------------------------------------------------------- /src/types/components/ui/card.d.ts: -------------------------------------------------------------------------------- 1 | /** Properties for section card */ 2 | interface CardProps { 3 | /** Title of the section displayed either on the side or on top */ 4 | title?: string; 5 | } 6 | -------------------------------------------------------------------------------- /public/locale/de/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Deutsch", 3 | "sections": { 4 | "landing": "Domin", 5 | "works": "Werke", 6 | "skills": "Fähigkeiten", 7 | "contact": "Kontaktinfos" 8 | } 9 | } -------------------------------------------------------------------------------- /public/locale/pl/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Polski", 3 | "sections": { 4 | "landing": "Domin", 5 | "works": "Projekty", 6 | "skills": "Umiejętności", 7 | "contact": "Kontakt" 8 | } 9 | } -------------------------------------------------------------------------------- /public/locale/uk/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Українська", 3 | "sections": { 4 | "landing": "Домін", 5 | "works": "Проєкти", 6 | "skills": "Навички", 7 | "contact": "Зв'язок" 8 | } 9 | } -------------------------------------------------------------------------------- /src/components/layout/help/config.tsx: -------------------------------------------------------------------------------- 1 | /** Crowdin translation link to redirect to on footer subtle click in dropdown */ 2 | export const translateLink: string = 'https://crowdin.com/project/domins-portfolio'; 3 | -------------------------------------------------------------------------------- /src/types/components/ui/devicon.d.ts: -------------------------------------------------------------------------------- 1 | /** A type for SVG props of a devicon icon used in stack */ 2 | interface DeviconProps extends React.SVGProps { 3 | /** A size of the icon */ 4 | size?: number | string; 5 | } 6 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/types/components/ui/translations.d.ts: -------------------------------------------------------------------------------- 1 | /** Translations component additional properties */ 2 | interface TranslationsProps { 3 | /** Extended Styles API of mantine */ 4 | classNames?: { 5 | /** Translation button that opens the menu */ 6 | button?: string; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document from 'next/document'; 2 | import { createGetInitialProps } from '@mantine/next'; 3 | 4 | const getInitialProps = createGetInitialProps(); 5 | 6 | export default class _Document extends Document { 7 | static getInitialProps = getInitialProps; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/ui/modal/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | overlay: { 5 | [theme.fn.smallerThan('md')]: { 6 | backdropFilter: 'none', 7 | }, 8 | }, 9 | })); 10 | -------------------------------------------------------------------------------- /src/components/ui/menu/styles.ts: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles( 4 | (theme: MantineTheme, { x, y }: { x: number; y: number }) => ({ 5 | menu: { 6 | top: y, 7 | left: x, 8 | }, 9 | }) 10 | ); 11 | -------------------------------------------------------------------------------- /src/components/ui/menu/state.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | export const useStore = create((set) => ({ 4 | opened: false, 5 | toggle: () => set((state) => ({ opened: !state.opened })), 6 | close: () => set(() => ({ opened: false })), 7 | open: () => set(() => ({ opened: true })), 8 | })); 9 | -------------------------------------------------------------------------------- /public/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #000000 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/layout/navbar/state.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | export const useStore = create((set) => ({ 4 | opened: false, 5 | toggle: () => set((state) => ({ opened: !state.opened })), 6 | close: () => set(() => ({ opened: false })), 7 | open: () => set(() => ({ opened: true })), 8 | })); 9 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const { i18n } = require('./next-i18next.config'); 2 | const withBundleAnalyzer = require('@next/bundle-analyzer')({ 3 | enabled: process.env.ANALYZE === 'true', 4 | }); 5 | 6 | module.exports = withBundleAnalyzer({ 7 | i18n, 8 | reactStrictMode: false, 9 | eslint: { 10 | ignoreDuringBuilds: true, 11 | }, 12 | }); -------------------------------------------------------------------------------- /src/types/components/ui/menu.d.ts: -------------------------------------------------------------------------------- 1 | /** Menu's zustand state */ 2 | interface MenuStore { 3 | /** Whether the menu is open or not */ 4 | opened: boolean; 5 | /** Toggle the open state of the menu */ 6 | toggle: () => void; 7 | /** Set the state to false */ 8 | close: () => void; 9 | /** Set the state to open */ 10 | open: () => void; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/components/layout/navbar.d.ts: -------------------------------------------------------------------------------- 1 | /** Navbar's zustand state */ 2 | interface NavbarStore { 3 | /** Whether navbar is open or not */ 4 | opened: boolean; 5 | /** Toggle the open state of navbar */ 6 | toggle: () => void; 7 | /** Set the state to false */ 8 | close: () => void; 9 | /** Set the state to open */ 10 | open: () => void; 11 | } 12 | -------------------------------------------------------------------------------- /src/components/layout/help/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | link: { 5 | color: theme.colors.blue[7], 6 | textDecoration: 'underline', 7 | cursor: 'pointer', 8 | 9 | ':hover': { 10 | textDecoration: 'none', 11 | }, 12 | }, 13 | })); 14 | -------------------------------------------------------------------------------- /src/components/pages/index/loading/styles.tsx: -------------------------------------------------------------------------------- 1 | import { MantineTheme, createStyles, keyframes } from '@mantine/core'; 2 | 3 | export const rotate = keyframes({ 4 | '100%': { transform: 'rotate(360deg);' }, 5 | }); 6 | 7 | export const useStyles = createStyles((theme: MantineTheme) => ({ 8 | animation: { 9 | animation: `${rotate} 1s linear infinite`, 10 | }, 11 | })); 12 | -------------------------------------------------------------------------------- /src/types/components/pages/contact.d.ts: -------------------------------------------------------------------------------- 1 | /** A social link used in the contact card */ 2 | interface Social { 3 | /** The name of the external source */ 4 | name: string; 5 | /** The link to the external source */ 6 | href: string; 7 | /** Username */ 8 | username?: string; 9 | /** The icon of the external source */ 10 | icon: import('@tabler/icons').TablerIcon; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/components/ui/carousel.d.ts: -------------------------------------------------------------------------------- 1 | /** Store state for Embla carousel */ 2 | interface CarouselStore { 3 | /** Embla API */ 4 | api: import('@mantine/carousel').Embla | null; 5 | /** Set Embla API */ 6 | setApi: (api: import('@mantine/carousel').Embla) => void; 7 | /** Scroll to a certain slide via the url provided by fetching tab config */ 8 | scrollViaURL: (url: string) => void; 9 | } 10 | -------------------------------------------------------------------------------- /ikota.config.js: -------------------------------------------------------------------------------- 1 | const ikotaMantine = require("@ikota/mantine"); 2 | 3 | /** 4 | * @type {import('ikota').IkotaConfig} 5 | */ 6 | module.exports = { 7 | componentPath: "components", 8 | useTypescript: true, 9 | addConfigFile: true, 10 | addIndexFile: true, 11 | preprocessor: "mantine", 12 | useLambdaSimplifier: false, 13 | trailingSpace: true, 14 | plugins: [ikotaMantine], 15 | other: { 16 | styleProps: true 17 | } 18 | } -------------------------------------------------------------------------------- /public/locale/en/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "English", 3 | "find-more-translations": { 4 | "content": "Find {{more}} translations...", 5 | "subtle": "more" 6 | }, 7 | "help-translate": { 8 | "content": "Help translate the {{portfolio}}...", 9 | "portfolio": "portfolio" 10 | }, 11 | "sections": { 12 | "landing": "Domin", 13 | "works": "Works", 14 | "skills": "Skills", 15 | "contact": "Contact" 16 | } 17 | } -------------------------------------------------------------------------------- /src/components/layout/translations/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | dropdown: { 5 | backgroundColor: theme.colors.gray[0], 6 | // backgroundColor: theme.fn.rgba(theme.colors.gray[1], 0.37), 7 | backdropFilter: 'blur(10px)', 8 | border: 'none', 9 | }, 10 | 11 | item: { 12 | ':hover': { 13 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 14 | }, 15 | }, 16 | })); 17 | -------------------------------------------------------------------------------- /src/types/utils/number.d.ts: -------------------------------------------------------------------------------- 1 | /** A type for enumerating an integer */ 2 | type Enumerate = Acc['length'] extends N 3 | ? Acc[number] 4 | : Enumerate; 5 | 6 | /** A type for finding a range of integers */ 7 | type IntRange = Exclude< 8 | Enumerate, // A range starting point 9 | Enumerate // A range ending point, excluding the starting point 10 | >; 11 | 12 | /** A safe type for a hex color */ 13 | type HEX = `#${string}`; 14 | -------------------------------------------------------------------------------- /public/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { LoadingOverlay } from '@mantine/core'; 2 | import { useRouter } from 'next/router'; 3 | import { ReactElement, useEffect } from 'react'; 4 | 5 | // A 404 page redirects to the home page 6 | // Added loading overlay to prevent 7 | // loading times during development environment 8 | // as well as disabling header usage 9 | export default function NotFound(): ReactElement { 10 | const { push } = useRouter(); 11 | useEffect(() => { 12 | push('/'); 13 | }); 14 | 15 | return ; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/ui/menu/config.ts: -------------------------------------------------------------------------------- 1 | export const cardLabel: string = 'Allow cookies'; 2 | 3 | export const description: string = [ 4 | 'So the deal is, we want to spy on you. We would like to know what did you have for todays', 5 | 'breakfast, where do you live, how much do you earn and like 50 other things. To view our', 6 | "landing page you will have to accept all cookies. That's all, and remember that we are", 7 | 'watching...', 8 | ].join(' '); 9 | 10 | export const defaultButton: string = 'Cookies preferences'; 11 | 12 | export const outlineButton: string = 'Accept all'; 13 | -------------------------------------------------------------------------------- /src/types/components/pages/about.d.ts: -------------------------------------------------------------------------------- 1 | /** About section properties */ 2 | interface About { 3 | /** A full name of the person */ 4 | name: [string, string, string]; 5 | /** A username of the person on the internet, used in About section, Landing section & Header component */ 6 | username?: string; 7 | /** A rounded avatar used in about section */ 8 | avatar: import('next/image').StaticImageData; 9 | /** A description of the person */ 10 | description: string; 11 | /** A location of the person */ 12 | location: string; 13 | /** A time zone */ 14 | timeZone: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/layout/footer/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | footer: { 5 | position: 'fixed', 6 | height: 62, 7 | maxWidth: '100%', 8 | width: '100%', 9 | bottom: 0, 10 | backdropFilter: 'blur(10px)', 11 | // Ignore holding footer 12 | pointerEvents: 'none', 13 | 14 | [theme.fn.smallerThan('xs')]: { 15 | height: 40, 16 | backdropFilter: 'unset', 17 | backgroundColor: theme.fn.rgba(theme.colors.gray[0], 0.87), 18 | }, 19 | }, 20 | })); 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | *.tsbuildinfo 36 | 37 | # intellij 38 | .idea 39 | .fleet -------------------------------------------------------------------------------- /src/components/layout/header/config.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@mantine/core'; 2 | 3 | /** An array of tabs displayed in header & mobile drawer */ 4 | export const tabs: Tab[] = [ 5 | { 6 | label: (username: string) => ( 7 | 8 | {username} 9 | 10 | ), 11 | href: '#', 12 | index: [0, 1], 13 | }, 14 | { 15 | label: 'Works', 16 | href: '#works', 17 | index: 2, 18 | }, 19 | { 20 | label: 'Skills', 21 | href: '#skills', 22 | index: 3, 23 | }, 24 | { 25 | label: 'Contact', 26 | href: '#contact', 27 | index: 4, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /src/components/pages/index/landing/styles.tsx: -------------------------------------------------------------------------------- 1 | import { MantineTheme, createStyles } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | canvas: { 5 | maxHeight: 760, 6 | 7 | [theme.fn.largerThan('sm')]: { 8 | height: '100vh !important', 9 | }, 10 | }, 11 | 12 | username: { 13 | position: 'absolute', 14 | pointerEvents: 'none', 15 | top: '50%', 16 | left: '50%', 17 | transform: 'translate(-50%, -50%)', 18 | fontSize: '5rem', 19 | fontWeight: 900, 20 | mixBlendMode: 'difference', 21 | color: 'white', 22 | textTransform: 'uppercase', 23 | }, 24 | })); 25 | -------------------------------------------------------------------------------- /src/components/layout/drawer/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | import { useStyles as cardStyles } from '@component/ui/card'; 3 | 4 | export const useStyles = createStyles((theme: MantineTheme) => ({ 5 | header: { 6 | justifyContent: 'center', 7 | backgroundColor: theme.colors.gray[0], 8 | }, 9 | 10 | content: { 11 | backgroundColor: theme.colors.gray[0], 12 | 13 | // Let translation modal ignore the hidden overflow 14 | overflow: 'unset', 15 | 16 | [`& .${cardStyles().classes.scrollArea}`]: { 17 | overflow: 'unset', 18 | }, 19 | }, 20 | 21 | title: { 22 | display: 'none', 23 | }, 24 | })); 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/types/pages/app.d.ts: -------------------------------------------------------------------------------- 1 | /** Extend the Next.js NextPage type to include a getLayout */ 2 | type NextPageWithLayout

= import('next').NextPage & { 3 | getLayout?: (page: ReactElement) => React.ReactNode; 4 | }; 5 | 6 | /** 7 | * These are used to extend the Next.js AppProps type to include a getLayout 8 | * function. This is used to wrap the page in a layout component. These types 9 | * are unused. 10 | * @see https://nextjs.org/docs/basic-features/layouts#per-page-layouts 11 | */ 12 | interface AppPropsWithLayout extends Omit { 13 | Component: import('next/app').AppProps['Component'] & { 14 | getLayout?: (page: ReactElement) => React.ReactNode; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /public/locale/pl/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "O mnie", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Znany też jako {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Mieszka w:", 11 | "location-value": "Kazań, Rosja", 12 | "description-value": "Ambitny, samouczący się 17-sto letni programista z dużą pasją do technologii, designu stron i rozwoju. Programuje od wieku 12 lat. Biegły w rozwoju baz danych, tworzeniu interfejsów i implementacji nowych funkcji na podstawie opinii użytkowników. Gotowy by się uczyć i pracować w zmieniających się warunkach. Biegły we wszystkich etapach tworzenia stron internetowych." 13 | } -------------------------------------------------------------------------------- /public/locale/uk/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Про мене", 3 | "full-name": { 4 | "first": "Каміль", 5 | "last": "Сахабутдінов", 6 | "middle": "Расімович" 7 | }, 8 | "nickname-label": "Відомий як {{nicknameValue}}", 9 | "nickname-value": "Домін", 10 | "location-label": "Розташування:", 11 | "location-value": "Казань, Росія", 12 | "description-value": "Амбіційний, 17-річний програміст-самоучка з великою пристрастю до технологій, вебдизайну та розробки. Займаюся програмуванням із 12 років. Володію навичками розробки баз даних, створення інтерфейсів і впровадження нових функцій на основі відгуків користувачів. Готовий вчитися і співпрацювати в умовах, що швидко змінюються. Фахівець у всіх аспектах розвиненої веб-розробки." 13 | } -------------------------------------------------------------------------------- /src/components/layout/header/state.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | import { useStore as useCarouselStore } from '@component/ui/carousel'; 3 | 4 | export const useStore = create((set) => ({ 5 | tab: '#', 6 | setTab: (tab: Hash) => set({ tab }), 7 | pushHash: (href: string) => { 8 | const hash = href.match(/#([a-z0-9]+)/gi); 9 | window.location.hash = hash ? hash[0] : ''; 10 | // Switch the tab 11 | // If the hash is #, it will be empty string 12 | set({ 13 | tab: (window.location.hash as Hash) || '#', 14 | }); 15 | // For a carousel, emitting this event will switch the slide 16 | // As it was declared in carousel 17 | useCarouselStore.getState().scrollViaURL(href); 18 | }, 19 | })); 20 | -------------------------------------------------------------------------------- /src/types/components/pages/skills.d.ts: -------------------------------------------------------------------------------- 1 | /** An object for a stack, is a child in skills array */ 2 | interface Stack { 3 | /** The name of the stack: language, framework, tech etc. */ 4 | name: string; 5 | /** A knowledge level used in the progress bar with the range of 1 to 100 */ 6 | knowledge: IntRange<1, 101>; 7 | /** Color of the stack, can only be hex */ 8 | color: HEX; 9 | /** A description of the stack/your knowledge/experience */ 10 | description?: string; 11 | /** A link to the technology */ 12 | href: string; 13 | /** An icon */ 14 | icon: React.FC; 15 | } 16 | 17 | /** Skills properties */ 18 | interface SkillsProps { 19 | /** An array of stacks used in skills section */ 20 | skills?: Stack[]; 21 | } 22 | -------------------------------------------------------------------------------- /public/locale/ru/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Обо мне", 3 | "full-name": { 4 | "first": "Камиль", 5 | "last": "Сахабутдинов", 6 | "middle": "Расимович" 7 | }, 8 | "nickname-label": "Известен как {{nicknameValue}}", 9 | "nickname-value": "Домин", 10 | "location-label": "Местоположение:", 11 | "location-value": "Казань, Россия", 12 | "description-value": "Амбициозный, 17-летний программист-самоучка с большой страстью к технологиям, веб-дизайну и разработке. Занимаюсь программированием с 12 лет. Владею навыками разработки баз данных, создания пользовательских интерфейсов и внедрения новых функций на основе отзывов пользователей. Готов учиться и сотрудничать в быстро меняющихся условиях. Специалист во всех аспектах продвинутой веб-разработки." 13 | } -------------------------------------------------------------------------------- /src/components/pages/index/about/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | columns: { 5 | gap: theme.spacing.md, 6 | 7 | [theme.fn.smallerThan('xs')]: { 8 | flexDirection: 'column', 9 | alignItems: 'center', 10 | }, 11 | }, 12 | 13 | information: { 14 | flexDirection: 'column', 15 | 16 | [theme.fn.smallerThan('xs')]: { 17 | flexDirection: 'row', 18 | alignItems: 'flex-end', 19 | gap: 16, 20 | }, 21 | }, 22 | 23 | avatar: { 24 | [theme.fn.smallerThan('xs')]: { 25 | width: 100, 26 | height: 100, 27 | 28 | minWidth: 'unset', // Removing default min-width 29 | }, 30 | }, 31 | })); 32 | -------------------------------------------------------------------------------- /public/locale/en/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "About", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Also known as {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Location:", 11 | "location-value": "Kazan, Russia", 12 | "description-value": "Ambitious, self-taught 17 years old software engineer with a great passion for technology, web design and development. Has been programming since the age of 12. Proficient in developing databases, creating user interfaces and implementing new features based on user feedback. Ready to learn and collaborate in rapidly changing environments and compositions. Adept in all stages of advanced web development." 13 | } -------------------------------------------------------------------------------- /public/locale/fr/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "About", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Also known as {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Location:", 11 | "location-value": "Kazan, Russia", 12 | "description-value": "Ambitious, self-taught 17 years old software engineer with a great passion for technology, web design and development. Has been programming since the age of 12. Proficient in developing databases, creating user interfaces and implementing new features based on user feedback. Ready to learn and collaborate in rapidly changing environments and compositions. Adept in all stages of advanced web development." 13 | } -------------------------------------------------------------------------------- /public/locale/it/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Chi sono", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Conosciuto anche come {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Location:", 11 | "location-value": "Kazan, Russia", 12 | "description-value": "Ambitious, self-taught 17 years old software engineer with a great passion for technology, web design and development. Has been programming since the age of 12. Proficient in developing databases, creating user interfaces and implementing new features based on user feedback. Ready to learn and collaborate in rapidly changing environments and compositions. Adept in all stages of advanced web development." 13 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": "src", 18 | "paths": { 19 | "@component/*": ["components/*"], 20 | "@public/*": ["../public/*"], 21 | } 22 | }, 23 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "components/mesh/module.js"], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /public/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/locale/id/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Tentang", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Dan juga dikenal sebagai {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Lokasi:", 11 | "location-value": "Kazan, Russia", 12 | "description-value": "Berambisi, 17 tahun mempelajari sendiri software engineer dengan penuh gairah akan teknologi, web desain dan development. Sudah mempelajari sejak 12 tahun. ahli dalam developing databases, membuat user interfaces dan menerapkan fitur baru dari saran pengguna. Siap mempelajari dan berkolaborasi dalam lingkungan dan komposisi yang berubah dengan cepat, beradaptasi dengan semua tahapan dalam pengembangan web tingkat lanjut." 13 | } -------------------------------------------------------------------------------- /public/locale/de/about.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Über mich", 3 | "full-name": { 4 | "first": "Kamil", 5 | "last": "Sakhabutdinov", 6 | "middle": "Rasimovich" 7 | }, 8 | "nickname-label": "Auch bekannt als {{nicknameValue}}", 9 | "nickname-value": "Domin", 10 | "location-label": "Standort:", 11 | "location-value": "Kasan, Russland", 12 | "description-value": "Ehrgeiziger, selbst erlernter 17-jähriger Software Engineer mit einer großen Leidenschaft für Technologien, web Design und Entwicklung. Programmierte seit dem Alter von 12 Jahren. Kenntnisse in Datenbankentwicklung, Erstellung von Benutzeroberflächen und Implementierung von Funktionen aus Benutzerfeedback. Bereit zu lernen und kollabieren in schnell wechselnden Bedingungen. Geschickt in allen Stufen von erweitertem Web-Development." 13 | } -------------------------------------------------------------------------------- /src/components/pages/index/landing/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Text } from '@mantine/core'; 3 | import { Canvas } from '@react-three/fiber'; 4 | import { Keyboard } from '@component/pages/index/keyboard'; 5 | import { about } from '@component/pages/index/about'; 6 | import { useStyles } from './styles'; 7 | 8 | /** 9 | * A landing slide 10 | * @returns {ReactElement} Canvas with keyboard model along with username text on it 11 | */ 12 | export const Landing: FunctionComponent = (): ReactElement => { 13 | const { classes } = useStyles(); 14 | return ( 15 | <> 16 | 17 | 18 | 19 | {about.username} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/pages/index/about/config.tsx: -------------------------------------------------------------------------------- 1 | import Domin from '@public/about/domin.jpg'; 2 | 3 | /** An about section card information */ 4 | export const about: About = { 5 | name: ['Kamil', 'Sakhabutdinov', 'Rasimovich'], 6 | username: 'Domin', 7 | avatar: Domin, 8 | description: [ 9 | 'Ambitious, self-taught 17 years old software engineer with a', 10 | 'great passion for technology, web design and development. Has', 11 | 'been programming since the age of 12. Proficient in developing', 12 | 'databases, creating user interfaces and implementing new features', 13 | 'based on user feedback. Ready to learn and collaborate in rapidly', 14 | 'changing environments and compositions. Adept in all stages of', 15 | 'advanced web development.', 16 | ].join(' '), 17 | location: 'Kazan, Russia', 18 | timeZone: 'Europe/Moscow', 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/layout/footer/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Flex, Text } from '@mantine/core'; 3 | import { useStyles } from './styles'; 4 | import { footerText } from './config'; 5 | import { Trans, useTranslation } from 'next-i18next'; 6 | 7 | /** 8 | * Footer component used under the page to display copyright 9 | * @returns {ReactElement} an absolute positioned element 10 | */ 11 | export const Footer: FunctionComponent = (): ReactElement => { 12 | const { classes } = useStyles(); 13 | const { t } = useTranslation('footer'); 14 | 15 | return ( 16 | 17 | 18 | 19 | {footerText} 20 | 21 | 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/components/layout/translations/config.tsx: -------------------------------------------------------------------------------- 1 | import { USFlag, DEFlag, IDFlag, ITFlag, RUFlag, UAFlag, FRFlag, PLFlag } from 'mantine-flagpack'; 2 | 3 | /** Translations array, used in a select */ 4 | export const translations: Translation[] = [ 5 | { 6 | name: 'English', 7 | flag: USFlag, 8 | locale: 'en', 9 | }, 10 | // { 11 | // name: 'Français', 12 | // flag: FRFlag, 13 | // locale: 'fr' 14 | // }, 15 | { 16 | name: 'Deutsch', 17 | flag: DEFlag, 18 | locale: 'de', 19 | }, 20 | // { 21 | // name: 'Italiano', 22 | // flag: ITFlag, 23 | // locale: 'it' 24 | // }, 25 | { 26 | name: 'Polski', 27 | flag: PLFlag, 28 | locale: 'pl', 29 | }, 30 | { 31 | name: 'Русский', 32 | flag: RUFlag, 33 | locale: 'ru', 34 | }, 35 | { 36 | name: 'Українська', 37 | flag: UAFlag, 38 | locale: 'uk', 39 | }, 40 | { 41 | name: 'Bahasa', 42 | flag: IDFlag, 43 | locale: 'id', 44 | }, 45 | ]; 46 | -------------------------------------------------------------------------------- /src/types/components/pages/keyboard.d.ts: -------------------------------------------------------------------------------- 1 | /** gltfjsx generated type */ 2 | type GLTFResult = GLTF & { 3 | nodes: { 4 | Object_4: import('three').Mesh; 5 | Object_5: import('three').Mesh; 6 | Object_6: import('three').Mesh; 7 | Object_7: import('three').Mesh; 8 | Object_8: import('three').Mesh; 9 | Object_9: import('three').Mesh; 10 | Object_10: import('three').Mesh; 11 | }; 12 | materials: { 13 | NovelKeys: import('three').MeshStandardMaterial; 14 | Lime: import('three').MeshStandardMaterial; 15 | Grape: import('three').MeshStandardMaterial; 16 | Blueberry: import('three').MeshStandardMaterial; 17 | Lemon: import('three').MeshStandardMaterial; 18 | Strawberry: import('three').MeshStandardMaterial; 19 | /** 20 | * #E3E3E3 - taken for a background color 21 | * @see https://www.color-name.com/hex/e3e3e3 22 | */ 23 | Material: import('three').MeshStandardMaterial; 24 | }; 25 | }; 26 | 27 | /** glTF ext */ 28 | type GLTF = import('three-stdlib').GLTF; 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/components/layout/mesh/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | overlay: { 5 | position: 'absolute', 6 | top: 0, 7 | left: 0, 8 | width: '100vw', 9 | height: '100vh', 10 | }, 11 | 12 | mesh: { 13 | // "--gradient-color-1": theme.colors.yellow[0], 14 | // "--gradient-color-2": theme.colors.green[0], 15 | // "--gradient-color-3": theme.colors.blue[0], 16 | // "--gradient-color-4": theme.colors.red[0], 17 | 18 | '--gradient-color-1': theme.colors.gray[0], 19 | '--gradient-color-2': theme.colors.gray[2], 20 | '--gradient-color-3': theme.colors.gray[0], 21 | '--gradient-color-4': theme.colors.gray[1], 22 | 23 | [theme.fn.smallerThan('md')]: { 24 | display: 'none', 25 | }, 26 | }, 27 | 28 | override: { 29 | backgroundColor: theme.colors.gray[0], 30 | 31 | [theme.fn.largerThan('md')]: { 32 | display: 'none', 33 | }, 34 | }, 35 | })); 36 | -------------------------------------------------------------------------------- /src/components/pages/index/skills/styles.tsx: -------------------------------------------------------------------------------- 1 | import { MantineTheme, createStyles, rem } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | title: { 5 | fontWeight: 700, 6 | }, 7 | 8 | item: { 9 | textAlign: 'center', 10 | overflow: 'hidden', 11 | height: 90, 12 | backgroundColor: 'white', 13 | transition: 'transform 500ms ease', 14 | cursor: 'pointer', 15 | 16 | '&:hover': { 17 | transform: 'scale(.97)', 18 | }, 19 | }, 20 | 21 | overlay: { 22 | position: 'absolute', 23 | top: 0, 24 | left: 0, 25 | right: 0, 26 | bottom: 0, 27 | }, 28 | 29 | href: { 30 | outline: 'none', 31 | 32 | '&:hover': { 33 | textDecoration: 'underline', 34 | }, 35 | 36 | '&:focus': { 37 | outline: 'none', 38 | border: 'none', 39 | }, 40 | }, 41 | 42 | sectionCard: { 43 | maxHeight: rem(340), 44 | 45 | [theme.fn.smallerThan('sm')]: { 46 | maxHeight: '40vh', 47 | }, 48 | }, 49 | })); 50 | -------------------------------------------------------------------------------- /src/components/ui/time/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Text } from '@mantine/core'; 3 | import { about } from '@component/pages/index/about'; 4 | 5 | /** 6 | * A locale time of the user based off configuration from about component 7 | * @returns {ReactElement} A text with time & offset 8 | */ 9 | export const Time: FunctionComponent = (): ReactElement => { 10 | // 2 digit hour & minute locale time 11 | const localeTime = new Date().toLocaleTimeString([], { 12 | hour: '2-digit', 13 | minute: '2-digit', 14 | hour12: false, 15 | timeZone: about.timeZone, 16 | }); 17 | // Find local timezone offset 18 | const offset = new Intl.DateTimeFormat([], { 19 | timeZoneName: 'short', 20 | timeZone: about.timeZone, 21 | }) 22 | .formatToParts() 23 | .find((i) => i.type === 'timeZoneName')?.value; 24 | return ( 25 | 26 | {localeTime}{' '} 27 | 28 | ({offset}) 29 | 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/components/layout/drawer/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, PropsWithChildren, ReactElement } from 'react'; 2 | import { useStyles } from './styles'; 3 | import { Drawer as MantineDrawer, DrawerProps } from '@mantine/core'; 4 | 5 | /** 6 | * A custom drawer component based off mantine's Drawer 7 | * @see https://mantine.dev/core/modal/ 8 | * @returns {ReactElement} A mobile drawer 9 | */ 10 | export const Drawer: FunctionComponent> = ({ 11 | children, 12 | ...props 13 | }): ReactElement => { 14 | const { classes } = useStyles(); 15 | 16 | return ( 17 | 32 | {children} 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/ui/modal/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, PropsWithChildren, ReactElement } from 'react'; 2 | import { Modal as MantineModal, ModalProps } from '@mantine/core'; 3 | import { useStyles } from './styles'; 4 | 5 | /** 6 | * A custom modal component based off mantine's Modal 7 | * @see https://mantine.dev/core/modal/ 8 | * @returns {ReactElement} A modal 9 | */ 10 | export const Modal: FunctionComponent> = ({ 11 | children, 12 | ...props 13 | }): ReactElement => { 14 | const { classes } = useStyles(); 15 | 16 | return ( 17 | 32 | {children} 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Domin-MND 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/layout/mesh/component.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, PropsWithChildren, ReactElement, useEffect } from 'react'; 2 | import { useStyles } from './styles'; 3 | import { Box } from '@mantine/core'; 4 | import { Gradient } from './module'; 5 | 6 | /** 7 | * A mesh background taken from module.js 8 | * @returns {ReactElement} a canvas or a box based off media query 9 | */ 10 | export const Mesh: FunctionComponent = ({ children }): ReactElement => { 11 | const { classes, cx } = useStyles(); 12 | 13 | // On component mount 14 | useEffect(() => { 15 | const gradient = new Gradient(); 16 | // Basically @ts-ignore but in a better way 17 | // since the module has no defined initGradient method 18 | (gradient as any).initGradient('.' + classes.mesh); 19 | }, [classes.mesh]); 20 | 21 | // We do not use cx to not splice classes into 1 22 | // since the gradient is initialized by the selector 23 | return ( 24 | <> 25 | 26 | 27 | {children} 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /src/types/components/pages/works.d.ts: -------------------------------------------------------------------------------- 1 | /** A project stack - languages, frameworks & technologies used */ 2 | interface StackBadge { 3 | /** Name of the stack, will be transformed to uppercase */ 4 | name: string; 5 | /** Color of the stack, can be a mantine color or hex */ 6 | color: import('@mantine/core').MantineColor | HEX; 7 | } 8 | 9 | /** A project object that is being mapped and generated card from */ 10 | interface Project { 11 | /** Title of the project displayed as the white upper text */ 12 | title: string; 13 | /** Description of the project displayed as the dimmed color text */ 14 | description: string; 15 | /** Banner image of the project, can be a nextjs module import */ 16 | banner: import('next/image').StaticImageData; 17 | /** Stack of the project */ 18 | stack: StackBadge[]; 19 | /** URL of the project, redirects in the new tab on card click */ 20 | url?: string; 21 | } 22 | 23 | /** Properties for Works component */ 24 | interface WorksProps { 25 | /** An array of projects to be mapped, optional - by default uses config */ 26 | projects?: Project[]; 27 | /** A filter for works to show, takes stack name as an argument, optional */ 28 | filter?: string; 29 | } 30 | -------------------------------------------------------------------------------- /src/types/components/layout/header.d.ts: -------------------------------------------------------------------------------- 1 | /** An object defining a tab */ 2 | interface Tab { 3 | /** Label displayed as the name of the tab */ 4 | label: React.ReactNode | ((name: string) => React.ReactNode); 5 | /** href used to navigate to the tab, has to be defined starting with hash */ 6 | href: string; 7 | /** A custom index of the slide to scroll to */ 8 | index?: number | number[]; 9 | } 10 | 11 | /** Hash URL of the tab */ 12 | type Hash = `#${string}`; 13 | 14 | /** Header's zustand state store */ 15 | interface HeaderStore { 16 | /** Hash of the active tab */ 17 | tab: Hash; 18 | /** Set the hash of the active tab */ 19 | setTab: (tab: Hash) => void; 20 | /** 21 | * Replaces the hash in the url with the href provided 22 | * @param {string} href - A url with a hash 23 | * @returns {void} 24 | */ 25 | pushHash: (hash: string) => void; 26 | } 27 | 28 | /** A translation object with a flag function component & its name */ 29 | interface Translation { 30 | /** Language translated name */ 31 | name: string; 32 | /** Flag component, has a temporary type */ 33 | flag: (props: import('mantine-flagpack').FlagProps) => JSX.Element; 34 | /** A 2 character length string locale code */ 35 | locale: string; 36 | } 37 | -------------------------------------------------------------------------------- /src/components/layout/help/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { useStyles } from './styles'; 3 | import { translateLink } from './config'; 4 | import { Text } from '@mantine/core'; 5 | import { Trans, useTranslation } from 'next-i18next'; 6 | 7 | /** 8 | * Help translate the portfolio text 9 | * @returns {ReactElement} a text with a subtle link inside 10 | */ 11 | export const Help: FunctionComponent = (): ReactElement => { 12 | const { classes } = useStyles(); 13 | const { t } = useTranslation('header'); 14 | 15 | return ( 16 | 17 | 32 | ), 33 | }} 34 | > 35 | Help translate the{' '} 36 | 37 | portfolio 38 | 39 | ... 40 | 41 | 42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/components/layout/navbar/styles.ts: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | tabsList: { 5 | borderLeft: 'none', 6 | width: '100%', 7 | }, 8 | 9 | tab: { 10 | fontWeight: 400, 11 | letterSpacing: 0.4, 12 | height: 38, 13 | 14 | '&:hover, &:active': { 15 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 16 | }, 17 | 18 | justifyContent: 'center', 19 | 20 | '&[data-active]': { 21 | color: 'unset', 22 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 23 | 24 | ':before': { 25 | width: 0, 26 | }, 27 | 28 | ':hover': { 29 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 30 | }, 31 | }, 32 | }, 33 | 34 | translation: { 35 | backgroundColor: 'transparent', 36 | height: 38, 37 | width: '100%', 38 | marginBottom: 6, 39 | 40 | ':hover': { 41 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37) + ' !important', 42 | }, 43 | 44 | ':active': { 45 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37) + ' !important', 46 | }, 47 | }, 48 | 49 | translationOpened: { 50 | // Set the background color if the dropdown is opened 51 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 52 | }, 53 | })); 54 | -------------------------------------------------------------------------------- /src/components/ui/card/component.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, PropsWithChildren, useRef } from 'react'; 2 | import { Card, ScrollArea, Title, CardProps as MantineCardProps, rem, Box } from '@mantine/core'; 3 | import { useStyles } from './styles'; 4 | 5 | /** 6 | * Section component used under the landing page to display content 7 | * @param {string} props.title Title of the section 8 | * @param {ReactNode} props.children Content of the section 9 | * @returns {FunctionComponent>} 60vh scrollable card 10 | */ 11 | export const SectionCard: FunctionComponent> = ({ 12 | children, 13 | title, 14 | className, 15 | ...props // Mantine card props 16 | }) => { 17 | const { classes, cx } = useStyles(); 18 | 19 | return ( 20 | 21 | {title && ( 22 | 23 | {title} 24 | 25 | )} 26 | {/* */} 33 | 34 | 35 | {children} 36 | 37 | 38 | {/* */} 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/components/ui/card/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme, rem } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | card: { 5 | maxWidth: theme.breakpoints.xs, 6 | width: '100%', 7 | // margin: 124, 8 | marginLeft: 'auto', 9 | marginRight: 'auto', 10 | overflow: 'unset', 11 | 12 | [theme.fn.smallerThan('xs')]: { 13 | marginLeft: theme.spacing.xs, 14 | marginRight: theme.spacing.xs, 15 | }, 16 | }, 17 | 18 | scrollArea: { 19 | borderRadius: theme.radius.xs, 20 | overflowX: 'hidden', 21 | }, 22 | 23 | innerScrollArea: { 24 | maxHeight: rem(440), 25 | overflowY: 'scroll', 26 | 27 | [theme.fn.smallerThan('sm')]: { 28 | maxHeight: '60vh', 29 | }, 30 | 31 | // Hide scrollbar for webkit 32 | '::-webkit-scrollbar': { 33 | display: 'none', 34 | }, 35 | 36 | // Hide scrollbar for firefox 37 | scrollbarWidth: 'none', 38 | }, 39 | 40 | title: { 41 | position: 'absolute', 42 | textTransform: 'uppercase', 43 | textAlign: 'center', 44 | 45 | [theme.fn.smallerThan('sm')]: { 46 | top: 0, 47 | left: '50%', 48 | transform: 'translate(-50%, -100%)', 49 | fontSize: `calc(${theme.fontSizes.xl} * 1.5)`, 50 | }, 51 | 52 | [theme.fn.largerThan('sm')]: { 53 | right: 0, 54 | transform: 'translate(100%)', 55 | writingMode: 'vertical-rl', 56 | 57 | letterSpacing: 2, 58 | }, 59 | }, 60 | })); 61 | -------------------------------------------------------------------------------- /src/components/pages/index/works/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, getStylesRef, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => { 4 | const image = getStylesRef('image'); 5 | 6 | return { 7 | card: { 8 | position: 'relative', 9 | height: 280, 10 | backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0], 11 | 12 | // Scale on hover 13 | [`&:hover .${image}`]: { 14 | transform: 'scale(1.03)', 15 | }, 16 | }, 17 | 18 | image: { 19 | ref: image, 20 | position: 'absolute', 21 | top: 0, 22 | left: 0, 23 | right: 0, 24 | bottom: 0, 25 | backgroundSize: 'cover', 26 | backgroundPosition: 'center', 27 | transition: 'transform 500ms ease', 28 | }, 29 | 30 | overlay: { 31 | position: 'absolute', 32 | top: '20%', 33 | left: 0, 34 | right: 0, 35 | bottom: 0, 36 | backgroundImage: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .90) 90%)', 37 | }, 38 | 39 | content: { 40 | height: '100%', 41 | position: 'relative', 42 | display: 'flex', 43 | flexDirection: 'column', 44 | justifyContent: 'flex-end', 45 | zIndex: 1, 46 | }, 47 | 48 | title: { 49 | color: theme.white, 50 | marginBottom: 5, 51 | }, 52 | 53 | description: { 54 | color: theme.colors.gray[4], 55 | textShadow: '0 1px 10px rgba(0, 0, 0, .5)', 56 | }, 57 | }; 58 | }); 59 | -------------------------------------------------------------------------------- /src/components/pages/index/contact/config.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | IconBrandGithub, 3 | IconBrandGitlab, 4 | IconBrandMastodon, 5 | IconBrandNpm, 6 | IconBrandReddit, 7 | IconBrandTelegram, 8 | IconBrandTwitter, 9 | IconMail, 10 | } from '@tabler/icons'; 11 | 12 | /** Social medias / Email / Phone (no phone number because yes) */ 13 | export const socials: Social[] = [ 14 | { 15 | name: 'GitHub', 16 | href: 'https://github.com/Domin-MND', 17 | username: 'Domin-MND', 18 | icon: IconBrandGithub, 19 | }, 20 | { 21 | name: 'GitLab', 22 | href: 'https://gitlab.com/Domin-MND', 23 | username: 'Domin-MND', 24 | icon: IconBrandGitlab, 25 | }, 26 | { 27 | name: 'Reddit', 28 | href: 'https://www.reddit.com/user/Domin-MC', 29 | username: 'Domin-MC', 30 | icon: IconBrandReddit, 31 | }, 32 | { 33 | name: 'Twitter', 34 | href: 'https://twitter.com/Dominiff', 35 | username: 'Dominiff', 36 | icon: IconBrandTwitter, 37 | }, 38 | { 39 | name: 'Telegram', 40 | href: 'https://t.me/Dominnya', 41 | username: 'Dominnya', 42 | icon: IconBrandTelegram, 43 | }, 44 | { 45 | name: 'Mastodon', 46 | href: 'https://mastodon.social/@dominmnd', 47 | username: 'dominmnd', 48 | icon: IconBrandMastodon, 49 | }, 50 | { 51 | name: 'NPM', 52 | href: 'https://www.npmjs.com/~domin-mnd', 53 | username: 'domin-mnd', 54 | icon: IconBrandNpm, 55 | }, 56 | { 57 | name: 'Email', 58 | href: 'mailto:domin@is-a.tech', 59 | username: 'Domin', 60 | icon: IconMail, 61 | }, 62 | ]; 63 | -------------------------------------------------------------------------------- /src/components/ui/carousel/state.ts: -------------------------------------------------------------------------------- 1 | import { tabs } from '@component/layout/header'; 2 | import { Embla } from '@mantine/carousel'; 3 | import { create } from 'zustand'; 4 | 5 | export const useStore = create((set, get) => ({ 6 | api: null, 7 | setApi: (api: Embla) => set({ api }), 8 | scrollViaURL: (url: string) => { 9 | /** Embla API */ 10 | const emblaApi = get().api; 11 | 12 | // Does not include slash 13 | const hash: RegExpMatchArray | '#' = url.match(/#([a-z0-9]+)/gi) ?? '#'; 14 | 15 | // Find the index of the tab with the same href as the hash 16 | // Else if no index is found, find the index of the first tab with the same href as the hash 17 | const index: number | number[] = 18 | tabs.find((tab) => tab.href === hash[0])?.index ?? 19 | tabs.findIndex((tab) => tab.href === hash[0]); 20 | 21 | // Turn the index into an array if it's not one 22 | const value: number[] = Array.isArray(index) ? index : [index]; 23 | if (value.length === 1) return emblaApi?.scrollTo(value[0]); 24 | 25 | const currentSlide: number = emblaApi?.selectedScrollSnap() ?? 0; 26 | // Find the index of the current slide in the array 27 | const slideIndex: number = value.indexOf(currentSlide); 28 | // If the current slide is not in the array, get the last one 29 | const foundSlide: number = slideIndex === -1 ? value.length - 1 : slideIndex; 30 | // Scroll the array so that the current slide is the first one 31 | // Then get the next slide 32 | const toggledValue: number = value.slice(foundSlide).concat(value.slice(0, foundSlide))[1]; 33 | 34 | emblaApi?.scrollTo(toggledValue); 35 | }, 36 | })); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "next dev", 4 | "build": "next build", 5 | "analyze": "ANALYZE=true next build", 6 | "start": "next start", 7 | "typecheck": "tsc --noEmit", 8 | "export": "next build && next export", 9 | "lint": "next lint", 10 | "prettier:check": "prettier --check \"**/*.{ts,tsx}\"", 11 | "prettier:write": "prettier --write \"**/*.{ts,tsx}\"", 12 | "component": "ikota component" 13 | }, 14 | "dependencies": { 15 | "@emotion/react": "^11.10.4", 16 | "@emotion/server": "^11.10.0", 17 | "@ikota/mantine": "^0.9.9", 18 | "@mantine/carousel": "^6.0.4", 19 | "@mantine/core": "^6.0.4", 20 | "@mantine/eslint-config": "^3.6.14", 21 | "@mantine/hooks": "^6.0.4", 22 | "@mantine/next": "^6.0.4", 23 | "@next/bundle-analyzer": "13.2.4", 24 | "@react-three/drei": "^9.53.3", 25 | "@react-three/fiber": "^8.10.0", 26 | "@tabler/icons": "^1.107.0", 27 | "cookies-next": "^2.1.1", 28 | "devicons-react": "^1.2.5", 29 | "embla-carousel-react": "^8.0.0-rc03", 30 | "i18next": "^22.4.9", 31 | "ikota": "^1.0.1", 32 | "mantine-flagpack": "^1.0.1", 33 | "next": "^13.2.4", 34 | "next-i18next": "^13.2.2", 35 | "react": "^18.2.0", 36 | "react-dom": "^18.2.0", 37 | "react-i18next": "^12.2.0", 38 | "sharp": "^0.32.0", 39 | "three": "^0.148.0", 40 | "zustand": "^4.3.8" 41 | }, 42 | "devDependencies": { 43 | "@next/eslint-plugin-next": "^13.2.4", 44 | "@types/node": "^18.11.4", 45 | "@types/react": "18.0.21", 46 | "@types/three": "^0.148.0", 47 | "eslint-config-next": "13.4.2", 48 | "prettier": "^2.8.7", 49 | "typescript": "5.0.3" 50 | }, 51 | "packageManager": "pnpm@8.5.0" 52 | } 53 | -------------------------------------------------------------------------------- /src/components/ui/menu/component.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, ReactElement, useEffect, useState } from 'react'; 2 | import { cardLabel, description, defaultButton, outlineButton } from './config'; 3 | import { Menu as MantineMenu } from '@mantine/core'; 4 | import { useStore } from './state'; 5 | 6 | export const Menu: FunctionComponent = (): ReactElement => { 7 | const [position, setPosition] = useState<{ x: number; y: number }>({ 8 | x: 0, 9 | y: 0, 10 | }); 11 | // const { classes } = useStyles({ x, y }); 12 | const state = useStore(); 13 | 14 | useEffect(() => { 15 | /** 16 | * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button#value Mouse values} 17 | */ 18 | document.addEventListener( 19 | 'mouseup', 20 | (event) => { 21 | if (event.button !== 2) return state.close(); 22 | // Show the menu 23 | state.open(); 24 | 25 | setPosition({ 26 | x: event.clientX, 27 | y: event.clientY, 28 | }); 29 | }, 30 | false 31 | ); 32 | 33 | // Do not open context menu at all 34 | document.addEventListener('contextmenu', (event) => event.preventDefault()); 35 | }); 36 | 37 | return ( 38 | 47 | 48 | Optimization 49 | 3D Model 50 | Mesh Background 51 | Blur 52 | 53 | 54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/components/pages/index/loading/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Overlay, rem } from '@mantine/core'; 3 | import { useProgress } from '@react-three/drei'; 4 | import { useStyles } from './styles'; 5 | 6 | /** 7 | * A loading overlay based off if three.js is loaded 8 | * @returns {ReactElement} A loading overlay 9 | */ 10 | export const Loading: FunctionComponent = (): ReactElement => { 11 | const { loaded } = useProgress(); 12 | const { classes } = useStyles(); 13 | 14 | // Translate into component 15 | return ( 16 | <> 17 | {!loaded && ( 18 | 19 | 27 | 28 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | )} 48 | 49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /src/components/layout/header/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, MantineTheme } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | header: { 5 | position: 'absolute', 6 | zIndex: 1, 7 | maxWidth: '100%', 8 | width: '100%', 9 | backdropFilter: 'blur(10px)', 10 | 11 | [theme.fn.smallerThan('xs')]: { 12 | backdropFilter: 'unset', 13 | backgroundColor: theme.fn.rgba(theme.colors.gray[0], 0.87), 14 | }, 15 | }, 16 | 17 | burger: { 18 | [theme.fn.largerThan('xs')]: { 19 | display: 'none', 20 | }, 21 | }, 22 | 23 | drawerTitle: { 24 | display: 'none', 25 | }, 26 | 27 | mobile: { 28 | [theme.fn.smallerThan('xs')]: { 29 | display: 'none', 30 | }, 31 | }, 32 | 33 | tab: { 34 | fontWeight: 400, 35 | letterSpacing: 0.4, 36 | height: 38, 37 | 38 | '&:hover, &:active': { 39 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 40 | }, 41 | 42 | '&[data-active]': { 43 | color: 'unset', 44 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 45 | 46 | ':hover': { 47 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 48 | }, 49 | }, 50 | }, 51 | 52 | translation: { 53 | backgroundColor: 'transparent', 54 | height: 38, 55 | 56 | ':hover': { 57 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37) + ' !important', 58 | }, 59 | 60 | ':active': { 61 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37) + ' !important', 62 | }, 63 | 64 | [theme.fn.smallerThan('xs')]: { 65 | display: 'none', 66 | }, 67 | }, 68 | 69 | translationOpened: { 70 | // Set the background color if the dropdown is opened 71 | backgroundColor: theme.fn.rgba(theme.colors.gray[3], 0.37), 72 | }, 73 | })); 74 | -------------------------------------------------------------------------------- /src/components/layout/translations/component.tsx: -------------------------------------------------------------------------------- 1 | import { useStyles } from './styles'; 2 | import { FunctionComponent, ReactElement, useState } from 'react'; 3 | import { Button, Menu, MenuProps } from '@mantine/core'; 4 | import { translations as oTranslations } from './config'; 5 | import { NextRouter, useRouter } from 'next/router'; 6 | import { Help } from '@component/layout/help'; 7 | 8 | /** 9 | * Translation Menu used in drawer & header with translation list 10 | * @see https://mantine.dev/core/menu/ 11 | * @returns {ReactElement} A responsive menu with the button opening it 12 | */ 13 | export const Translations: FunctionComponent = ({ 14 | classNames, 15 | ...props 16 | }): ReactElement => { 17 | const { classes } = useStyles(); 18 | const router: NextRouter = useRouter(); 19 | 20 | const [translation, setTranslation] = useState( 21 | oTranslations.find((t) => t.locale === router.locale) ?? oTranslations[0] 22 | ); 23 | 24 | const translations = oTranslations.map((translation) => ( 25 | } 28 | onClick={() => { 29 | setTranslation(translation); 30 | // Refresh the page when locale set 31 | // Don't use Link provided by next/link 32 | // It will cause carousel to re-render 33 | window.location.pathname = translation.locale; 34 | }} 35 | > 36 | {translation.name} 37 | 38 | )); 39 | 40 | return ( 41 |

50 | 51 | 54 | 55 | 56 | 57 | {translations} 58 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /public/locale/en/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Skills", 3 | "examples-label": "Examples:", 4 | "knowledge-label": "My knowledge of {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "I started my programming journey with Java for mobile development and I still love it to this day." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "After some time I switched to web development and all started with JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "A preprocessor scripting language that is interpreted or compiled into CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "A utility-first CSS framework for rapidly building custom designs." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "A must have for most of my projects. Used in this portfolio." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "A JavaScript library for building user interfaces." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "A predictable state container for JavaScript apps." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "A React framework for production. Used in this portfolio." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "A JavaScript framework for building user interfaces. In my opinion it is the amazing framework for beginners." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "A progressive Node.js framework for building efficient, reliable and scalable server-side applications." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Not using python anywhere but at college/to help my friends with their projects." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "I'm learning Rust and I'm loving it so far." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/fr/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Skills", 3 | "examples-label": "Examples:", 4 | "knowledge-label": "My knowledge of {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "I started my programming journey with Java for mobile development and I still love it to this day." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "After some time I switched to web development and all started with JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "A preprocessor scripting language that is interpreted or compiled into CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "A utility-first CSS framework for rapidly building custom designs." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "A must have for most of my projects. Used in this portfolio." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "A JavaScript library for building user interfaces." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "A predictable state container for JavaScript apps." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "A React framework for production. Used in this portfolio." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "A JavaScript framework for building user interfaces. In my opinion it is the amazing framework for beginners." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "A progressive Node.js framework for building efficient, reliable and scalable server-side applications." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Not using python anywhere but at college/to help my friends with their projects." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "I'm learning Rust and I'm loving it so far." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type { GetStaticProps } from 'next'; 2 | import { ReactNode } from 'react'; 3 | import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; 4 | import { useTranslation } from 'next-i18next'; 5 | import { VerticalCarousel } from '@component/ui/carousel'; 6 | import { Landing } from '@component/pages/index/landing'; 7 | import { Works } from '@component/pages/index/works'; 8 | import { SectionCard } from '@component/ui/card'; 9 | import { Skills } from '@component/pages/index/skills'; 10 | import { Contact } from '@component/pages/index/contact'; 11 | import { About } from '@component/pages/index/about'; 12 | import { Loading } from '@component/pages/index/loading'; 13 | import { Menu } from '@component/ui/menu'; 14 | 15 | // An index page (/) 16 | export default function Index(): ReactNode { 17 | // Getting section names from the translation files 18 | const { t } = useTranslation(['about', 'works', 'skills', 'contact']); 19 | // A shorthand function for getting title 20 | const title = (ns: string): string => t('section-name', { ns }); 21 | 22 | return ( 23 | <> 24 | 25 | {/* */} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | // Translating the page from the server side 46 | // or getServerSideProps: GetServerSideProps = async ({ locale }) 47 | export const getStaticProps: GetStaticProps = async ({ locale }) => ({ 48 | props: { 49 | ...(await serverSideTranslations(locale ?? 'en', [ 50 | 'about', 51 | 'common', 52 | 'contact', 53 | 'footer', 54 | 'header', 55 | 'skills', 56 | 'works', 57 | ])), 58 | }, 59 | }); 60 | -------------------------------------------------------------------------------- /public/locale/pl/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Umiejętności", 3 | "examples-label": "Przykłady:", 4 | "knowledge-label": "Moja wiedza na temat {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "Moją podróż z programowaniem zacząłem z Javą, żeby robić aplikacje mobilne i do teraz to kocham." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Po jakimś czasie zacząłem tworzyć strony internetowe używając JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "Język skryptowy preprocesora, który jest interpretowany lub kompilowany w CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "\"Utility-first\" struktura CSS do szybkiego tworzenia niestandardowych projektów." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "Najważniejszy element większości moich projektów. Użyty w tym portfolio." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "Biblioteka JavaScript do tworzenia interfejsów użytkownika." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "Kontener przewidywalnego stanu dla aplikacji JavaScript." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Struktura react do produkcji. Użyta w tym prortfolio." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "Struktura JavaScript do tworzenia interfejsów użytkownika. W mojej opinii niesamowita dla początkujących." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Progresywna struktura node.js do tworzenia efektywnych i niezawodnych aplikacji po stronie serwera." 45 | }, 46 | "Python": { 47 | "name": "Pyton", 48 | "description": "Nie używam Pythona nigdzie, ale na uczelni, żeby pomóc moim przyjaciołom z projektami." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Uczę się języka rust i narazie go uwielbiam." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/ru/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Навыки", 3 | "examples-label": "Примеры:", 4 | "knowledge-label": "Мой уровень знаний {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "Я начал свой путь программиста с этого языка для мобильной разработки, и я все еще люблю его по сей день." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Через некоторое время я перешёл в веб-разработку, и все началось с JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "Препроцессорный скриптовый язык, который интерпретируется или компилируется в CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "\"Utility-first\" CSS-фреймворк для быстрого создания UI." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "Маст хэв для большинства моих проектов. Используется в этом портфолио." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "Библиотека JavaScript для создания пользовательских интерфейсов." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "Инструмент для управления состоянием данных в приложениях JavaScript." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Фреймворк React для продакшна. Используется в этом портфолио." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "Фреймворк JavaScript для создания Ui. На мой взгляд, это прекрасный фреймворк для начинающих веб-разработчиков." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Передовой Node.js фреймворк для создания эффективных, надежных и масштабируемых серверных приложений." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Не использую Python нигде, кроме как в колледже/для помощи моим друзьям с их проектами." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Я изучаю Rust, и пока он мне нравится." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/uk/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Навички", 3 | "examples-label": "Приклади:", 4 | "knowledge-label": "Мій рівень знань {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "Я почав свою подорож з цієї мови програмування для мобільної розробки та люблю її по сьогоднішній день." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Після деякого часу я перейшов на веброзробку і все почалося з JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "Препроцесорна мова скриптів, яка інтерпретується або компілюється в CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "CSS-фреймворк для швидкого створення користувацьких дизайнів." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "\"Маст хев\" для більшості моїх проєктів. Використовується в цьому портфоліо." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "Бібліотека JavaScript для створення користувацьких інтерфейсів." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "Інструмент для управління станами даних в програмах JavaScript." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Фреймворк React для продакшна. Використовується в цьому портфоліо." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "JavaScript фреймворк для створення користувацьких інтерфейсів. На мою думку, це прекрасний фреймворк для початківців." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Прогресивний Node.js фреймворк для будівництва ефективних, надійних і масштабованих серверних програм." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Не користуюсь Python ніде, тільки в коледжі, або щоб допомагати друзям зі своїми проєктами." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Я вивчаю Rust і поки що обожнюю його." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/it/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Conoscenze", 3 | "examples-label": "Esempi:", 4 | "knowledge-label": "La mia conoscenza di {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "I started my programming journey with Java for mobile development and I still love it to this day." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Dopo un po' di tempo mi sono spostato verso lo sviluppo web e ho iniziato con JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "A preprocessor scripting language that is interpreted or compiled into CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "A utility-first CSS framework for rapidly building custom designs." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "Un must per la maggior parte dei miei progetti. Utilizzato per questo portofolio." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "A JavaScript library for building user interfaces." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "A predictable state container for JavaScript apps." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Un framework per React per ambienti di produzione. Lo utilizzo anche per questo portofolio." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "A JavaScript framework for building user interfaces. In my opinion it is the amazing framework for beginners." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Un framework progressivo per Node.js, perfetto per creare applicazioni nel lato server efficienti, affidabili e scalabili." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Non utilizzo Python da nessuna parte tranne che a scuola per aiutare i miei amici con i loro progetti." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Sto imparando Rust e per ora lo sto amando." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/de/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Fähigkeiten", 3 | "examples-label": "Beispiele:", 4 | "knowledge-label": "Meine Wissen in {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "Ich habe mein Abenteuer mit programmieren mit Java für Mobile-App Entwicklung angefangen und ich liebe diese Sprache bis heute immer noch." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Nach einiger Zeit wechselte ich zu Web-Development und alles hat mit JavaScript angefangen." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "Eine Präprozessor Skript-Sprache das zu CSS interpretiert oder kompiliert wird." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "Ein \"Utility-first\" CSS Framework um schnell benutzerdefinierte Designs zu erstellen." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "Ein Must-have für meine meisten Projekte. Wird in diesem Portfolio benutzt." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "Eine JavaScript Bibliothek zum erstellen von Benutzeroberflächen." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "Ein vorhersagbarer Zustandscontainer für JavaScript-Apps." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Ein React Framework für Produktion. Wird in diesem Portfolio benutzt." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "Ein Javascript Framework um Benutzeroberflächen zu erstellen. Meiner Meinung nach ist es ein großartiges Framework für Anfänger." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Ein fortschrittliches Node.js-Framework zum Erstellen effizienter, zuverlässiger und skalierbarer serverseitiger Anwendungen." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Benutze python nirgendwo, außer im college/um meinen Freunden mir ihren Projekten zu helfen." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Ich lerne Rust und bisher mag ich es." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /public/locale/id/skills.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Keahlian", 3 | "examples-label": "Contohnya:", 4 | "knowledge-label": "Pengetahuan ku akan {{skillName}}:", 5 | "skills": { 6 | "Java": { 7 | "name": "Java", 8 | "description": "Saya memulai perjalan programming saya dengan menggunakan Java untuk mobile development dan saya masih menyukainya hingga hari ini." 9 | }, 10 | "JavaScript": { 11 | "name": "JavaScript", 12 | "description": "Setelah beberapa waktu aku mengganti jadi web development dan semua berawal dengan JavaScript." 13 | }, 14 | "SASS": { 15 | "name": "SASS", 16 | "description": "Bahasa skrip preprosesor yang ditafsirkan atau dikompilasi ke dalam CSS." 17 | }, 18 | "Tailwind CSS": { 19 | "name": "Tailwind CSS", 20 | "description": "Kerangka kerja CSS yang mengutamakan utilitas untuk membuat desain khusus dengan cepat." 21 | }, 22 | "TypeScript": { 23 | "name": "TypeScript", 24 | "description": "Harus dimiliki untuk sebagian besar proyek saya. Digunakan dalam portofolio ini." 25 | }, 26 | "React": { 27 | "name": "React", 28 | "description": "JavaScript library untuk membuat antarmuka pengguna." 29 | }, 30 | "Redux": { 31 | "name": "Redux", 32 | "description": "Penampung status yang dapat diprediksi untuk aplikasi JavaScript." 33 | }, 34 | "Next.js": { 35 | "name": "Next.js", 36 | "description": "Framework React untuk produksi, digunakan di dalam portofolio ini." 37 | }, 38 | "Vue.js": { 39 | "name": "Vue.js", 40 | "description": "JavaScript framework untuk membangun user interface. Pendapat saya ini adalah framework paling bagus untuk para pemula." 41 | }, 42 | "NestJS": { 43 | "name": "NestJS", 44 | "description": "Framework Node.js progresif untuk membangun aplikasi sisi server yang efisien, andal, dan dapat diskalakan." 45 | }, 46 | "Python": { 47 | "name": "Python", 48 | "description": "Tidak menggunakan python dimanapun kecuali di perguruan tinggi/untuk membantu kawan kawanku dengan proyek mereka." 49 | }, 50 | "Rust": { 51 | "name": "Rust", 52 | "description": "Aku mempelajari Rust dan aku menyukainya hingga sekarang." 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/components/pages/index/about/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Avatar, Flex, Space, Text, Title } from '@mantine/core'; 3 | import { about } from './config'; 4 | import { useStyles } from './styles'; 5 | import { Trans, useTranslation } from 'next-i18next'; 6 | import { IconMapPin, IconClockHour4 } from '@tabler/icons'; 7 | import { Time } from '@component/ui/time'; 8 | 9 | /** 10 | * An about section with information about the person 11 | * @returns {ReactElement} A flex card with information about the person 12 | */ 13 | export const About: FunctionComponent = (): ReactElement => { 14 | const { classes } = useStyles(); 15 | const { t } = useTranslation('about'); 16 | 17 | return ( 18 | 19 | 20 | 21 |
22 | 23 | <Trans i18nKey="full-name.first" t={t}> 24 | {about.name[0]} 25 | </Trans>{' '} 26 | <Trans i18nKey="full-name.last" t={t}> 27 | {about.name[1]} 28 | </Trans> 29 | 30 | {about.username && ( 31 | 32 | {t('nickname-label', { 33 | nicknameValue: t('nickname-value'), 34 | })} 35 | 36 | )} 37 | 38 | 39 | 40 | 41 | 42 | {about.location} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 |
54 |
55 | 56 | 57 | {about.description} 58 | 59 | 60 |
61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /src/components/pages/index/contact/styles.tsx: -------------------------------------------------------------------------------- 1 | import { createStyles, getStylesRef, MantineTheme, rem } from '@mantine/core'; 2 | 3 | export const useStyles = createStyles((theme: MantineTheme) => ({ 4 | wrapper: { 5 | height: rem(440), 6 | boxSizing: 'border-box', 7 | borderRadius: theme.radius.xs, 8 | padding: `calc(${theme.spacing.xl} * 2.5)`, 9 | 10 | // An offset for contents inside the card 11 | position: 'relative', 12 | overflow: 'hidden', 13 | 14 | [theme.fn.smallerThan('sm')]: { 15 | height: '60vh', 16 | padding: `calc(${theme.spacing.xl} * 1.5)`, 17 | }, 18 | }, 19 | 20 | content: { 21 | height: '100%', 22 | position: 'relative', 23 | display: 'flex', 24 | flexDirection: 'column', 25 | justifyContent: 'flex-end', 26 | alignItems: 'center', 27 | zIndex: 1, 28 | }, 29 | 30 | image: { 31 | ref: getStylesRef('image'), 32 | position: 'absolute', 33 | top: 0, 34 | left: 0, 35 | right: 0, 36 | bottom: 0, 37 | backgroundSize: 'cover', 38 | backgroundPosition: 'center', 39 | transition: 'transform 500ms ease', 40 | }, 41 | 42 | title: { 43 | color: theme.white, 44 | // lineHeight: 1, 45 | fontWeight: 500, 46 | textAlign: 'center', 47 | 48 | [theme.fn.smallerThan('xs')]: { 49 | fontSize: `calc(${theme.fontSizes.xl} * 1.3)`, 50 | }, 51 | }, 52 | 53 | description: { 54 | color: theme.colors[theme.primaryColor][0], 55 | maxWidth: 300, 56 | 57 | [theme.fn.smallerThan('sm')]: { 58 | maxWidth: '100%', 59 | }, 60 | }, 61 | 62 | overlay: { 63 | position: 'absolute', 64 | top: '20%', 65 | left: 0, 66 | right: 0, 67 | bottom: 0, 68 | backgroundImage: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .90) 90%)', 69 | }, 70 | 71 | social: { 72 | color: theme.white, 73 | 74 | '&:hover': { 75 | color: theme.colors[theme.primaryColor][1], 76 | }, 77 | }, 78 | 79 | input: { 80 | backgroundColor: theme.white, 81 | borderColor: theme.colors.gray[4], 82 | color: theme.black, 83 | 84 | '&::placeholder': { 85 | color: theme.colors.gray[5], 86 | }, 87 | }, 88 | 89 | inputLabel: { 90 | color: theme.black, 91 | }, 92 | 93 | control: { 94 | backgroundColor: theme.colors[theme.primaryColor][6], 95 | }, 96 | })); 97 | -------------------------------------------------------------------------------- /src/components/layout/navbar/component.tsx: -------------------------------------------------------------------------------- 1 | import { useStyles } from './styles'; 2 | import { FunctionComponent, ReactElement, useState } from 'react'; 3 | import { tabs } from '@component/layout/header'; 4 | import { Tabs } from '@mantine/core'; 5 | import { Translations } from '@component/layout/translations'; 6 | import { useStore as useNavbarStore } from './state'; 7 | import { useStore as useHeaderStore } from '@component/layout/header'; 8 | import { Drawer } from '@component/layout/drawer'; 9 | import { Trans, useTranslation } from 'next-i18next'; 10 | 11 | export const Navbar: FunctionComponent = (): ReactElement => { 12 | const { classes, cx } = useStyles(); 13 | const [translationOpened, setTranslationOpened] = useState(false); 14 | const navbarState = useNavbarStore(); 15 | const headerState = useHeaderStore(); 16 | 17 | // Header translation 18 | const { t } = useTranslation('header'); 19 | 20 | return ( 21 | 22 | 28 | { 36 | navbarState.close(); 37 | headerState.pushHash(value ?? '#'); 38 | }} 39 | classNames={{ 40 | tabsList: classes.tabsList, 41 | tab: classes.tab, 42 | }} 43 | > 44 | 45 | {tabs.map((tab) => ( 46 | 47 | {typeof tab.label === 'function' ? ( 48 | tab.label( 49 | t('sections.' + ['landing', 'works', 'skills', 'contact'][tabs.indexOf(tab)]) 50 | ) 51 | ) : ( 52 | 58 | {tab.label} 59 | 60 | )} 61 | 62 | ))} 63 | 64 | 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/pages/index/contact/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { ActionIcon, SimpleGrid, Title, Tooltip } from '@mantine/core'; 3 | import { useStyles } from './styles'; 4 | import { socials } from './config'; 5 | import Kazan from '@public/contacts/kazan.jpg'; 6 | 7 | import { about } from '@component/pages/index/about'; 8 | import { Trans, useTranslation } from 'next-i18next'; 9 | 10 | /** 11 | * Contact card with a banner image and social media links along with overlay & some information 12 | * @returns {ReactElement} Contact card with a banner image and social media links 13 | */ 14 | export const Contact: FunctionComponent = (): ReactElement => { 15 | const { classes } = useStyles(); 16 | const { t } = useTranslation('about'); 17 | 18 | return ( 19 |
20 |
26 | 27 |
31 |
36 | 41 | {socials.map((social: Social) => ( 42 | 49 | 58 | 59 | 60 | 61 | ))} 62 | 63 | 64 | <Trans i18nKey="full-name.first" t={t}> 65 | {about.name[0]} 66 | </Trans>{' '} 67 | <Trans i18nKey="full-name.last" t={t}> 68 | {about.name[1]} 69 | </Trans> 70 | 71 |
72 |
73 | ); 74 | }; 75 | -------------------------------------------------------------------------------- /public/locale/fr/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Works", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portfolio", 6 | "description": "My personal portfolio website constisting of my projects, skill stack and contact information. This is what you visit right now.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Hybrid CMS extending framework power by its extensible codebase. Provides a completely new view on CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Todo tracker", 29 | "description": "A minimalistic todo app built with Vue.js and Tailwind CSS. Uses firebase for authentication and database.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Profile Card", 38 | "description": "A simple profile card made similar to discord's profile card. Is considered my first project in web development.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "A placeholder tool for generating non-existing speakable words. Great tool if you want to look at your product from a different angle.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "KNRTU-KAI Discord Bot", 55 | "description": "A Discord bot for KNRTU-KAI university. It provides a lot of useful features for students and teachers.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "A small modern API wrapper of the KNRTU-KAI website written for Node.js. It is used in the KNRTU-KAI Discord Bot.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "An effecient color tool for customizing and picking colors for projects without any hesitation.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /public/locale/it/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Progetti", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portfolio", 6 | "description": "My personal portfolio website constisting of my projects, skill stack and contact information. This is what you visit right now.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Hybrid CMS extending framework power by its extensible codebase. Provides a completely new view on CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Todo tracker", 29 | "description": "Un todo tracker minimalista creato con Vue.js e Tailwind CSS. Utilizza Firebase per l'autenticazione e il database.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Card profilo", 38 | "description": "Una card semplice simile al profilo di un utente su Discord. Viene considerato il mio primo progetto nello sviluppo web.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "A placeholder tool for generating non-existing speakable words. Great tool if you want to look at your product from a different angle.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "Il bot KNRTU-KAI per Discord", 55 | "description": "Un bot su Discord per l'università KNRTU-KAI. Offre moltissime funzioni utili per studenti e insegnanti.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "Un'API piccola e moderna scritta in Node.js per il sito di KNRTU-KAI. Viene utilizzata per il bot KNRTU-KAI su Discord.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "An effecient color tool for customizing and picking colors for projects without any hesitation.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /public/locale/uk/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Проєкти", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Портфоліо", 6 | "description": "Мій особистий сайт портфоліо складається з моїх проєктів, навичок та контактної інформації. Це те, що ви відвідуєте прямо зараз.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Гібридна CMS, розширююча можливості фреймворку внаслідок своєї розширюваної кодової бази. Надає абсолютно новий погляд на CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Трекер списку завдань", 29 | "description": "Мінімалістична програма побудована з Vue.js і Tailwind CSS. Використовує firebase для автентифікації та бази даних.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Картка профілю", 38 | "description": "Картка профілю зроблена такою ж як і картка профілю Discord. Це вважається моїм першим проєктом у веброзробці.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "Інструмент-заповнювач для створення неіснуючих вимовних слів. Чудовий інструмент, якщо ви хочете поглянути на свій продукт під іншим кутом.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "Discord бот КНДТУ-КАІ", 55 | "description": "Discord бот для університету КНДТУ-КАІ. Він надає багато корисних функцій для учнів та вчителів.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "Невеликий сучасний API враппер веб-сайту КНДТУ-КАІ написаний для Node.js. Він використовується в Discord боті КНДТУ-КАІ.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "Дієвий колірний інструмент для налаштування та вибору кольорів для проєктів без будь-яких коливань.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /public/locale/id/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Karya", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portofolio", 6 | "description": "Situs web portofolio pribadi saya yang terdiri dari proyek, kumpulan keterampilan, dan informasi kontak saya. Inilah yang Anda kunjungi sekarang.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Hybrid CMS memperluas kekuatan kerangka kerja dengan basis kode yang dapat diperluas. Memberikan tampilan yang benar-benar baru di CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Pelacak Todo", 29 | "description": "Aplikasi todo minimalis yang dibuat dengan Vue.js dan Tailwind CSS. Menggunakan firebase untuk autentikasi dan database.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Kartu Profil", 38 | "description": "Kartu profil sederhana yang dibuat mirip dengan kartu profil discord. proyek pertama saya dalam web development.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "Alat placeholder untuk menghasilkan kata-kata yang belum pernah terucap. Alat ini berguna jika Anda ingin melihat produk Anda dari sudut yang berbeda.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "KNRTU-KAI Discord Bot", 55 | "description": "Discord bot untuk KNRTU-KAI university. menyediakan banyak fitur yang berguna bagi pelajar dan juga guru.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "API modern kecil dari situs web KNRTU-KAI ditulis untuk Node.js. Ini digunakan dalam Discord Bot KNRTU-KAI.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "Alat warna yang efisien untuk menyesuaikan dan memilih warna untuk proyek tanpa ragu-ragu.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /public/locale/pl/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Projekty", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portfolio", 6 | "description": "Moje własne portfolio zawierające wszystkie moje projekty, umiejętności i informacje kontaktowe. To jest ta strona, którą w tym momencie widzisz.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Hybrydowy CMS rozszerzający moc struktury przez jego rozszerzalną bazę kodów. Wprowadza całkowicie nową perspektywę na CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Todo tracker", 29 | "description": "Minimalistyczna aplikacja z listą rzeczy do zrobienia, stworzona z użyciem Vue.js i Tailwind CSS. Używa firebase do uwierzetelniania i bazy danych.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Karta profilu", 38 | "description": "Prosta karta profilu zrobiona podobnie do karty profilu na discordzie. Jest uważany za mój pierwszy projekt tworzenia stron.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "Narzędzie zastępcze do tworzenia nieistniejących, ale wymawialnych słów. Fajne narzędzie, jeśli chcesz popatrzeć na swój projekt z innej perspektywy.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "KNRTU-KAI Discord Bot", 55 | "description": "Bot discord dla uniwersytetu KNRTU-KAI. Zawiera dużo użytecznych funkcji dla studentów i nauczycieli.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "Mały modernistyczny API wrapper strony KNRTU-KAI napisany dla Node.js. Jest użyty w bocie KNRTU-KAI.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "Wydajne narzędzie kolorystyczne do dostosowywania i wybierania kolorów do projektów.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /public/locale/ru/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Проекты", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Портфолио", 6 | "description": "Моё личное веб-портфолио, состоящее из моих проектов, набора навыков и контактной информации. Это то, что вы посещаете прямо сейчас.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Гибридная CMS, расширяющая возможности фреймворка за счет своей расширяемой кодовой базы. Предоставляет совершенно новый взгляд на CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Трекер списка задач", 29 | "description": "Минималистичный трекер списка дел, созданный с Vue.js и Tailwind CSS. Использует firebase для аутентификации и базы данных.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Карточка Профиля", 38 | "description": "Простая карточка профиля, сделанная аналогично карточке профиля в discord. Считается моим первым проектом в области веб-разработки.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "Инструмент-заполнитель для создания несуществующих произносимых слов. Отличный вариант, если вы хотите взглянуть на свой продукт под другим углом.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "Дискорд Бот КНИТУ-КАИ", 55 | "description": "Дискорд бот для КНИТУ-КАИ. Предоставляет множество полезных функций для студентов и преподавателей.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "Небольшой современный АПИ враппер веб-сайта КНИТУ-КАИ, написанный для Node.js. Используется в дискорд боте КНИТУ-КАИ.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "Действенный цветовой инструмент для настройки и выбора цветов для проектов без каких-либо колебаний.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/components/layout/header/component.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionComponent, ReactElement, useState } from 'react'; 2 | import { Burger, Button, Container, Group, Menu, Tabs } from '@mantine/core'; 3 | import { useRouter, NextRouter } from 'next/router'; 4 | import { useStyles } from './styles'; 5 | import { tabs } from './config'; 6 | import { useStore as useHeaderStore } from './state'; 7 | import { Trans, useTranslation } from 'next-i18next'; 8 | import { Translations } from '@component/layout/translations'; 9 | import { useStore as useNavbarStore } from '@component/layout/navbar'; 10 | 11 | /** 12 | * Header component used on the top of the page to display tabs 13 | * @returns {ReactElement} not fixed container with tabs & drawer 14 | */ 15 | export const Header: FunctionComponent = (): ReactElement => { 16 | // Whether translations dropdown is opened or not 17 | const [translationOpened, setTranslationOpened] = useState(false); 18 | const { classes, cx } = useStyles(); 19 | 20 | const router: NextRouter = useRouter(); 21 | 22 | // Header translation 23 | const { t } = useTranslation('header'); 24 | 25 | // Navbar disclosure state 26 | const navbarState = useNavbarStore(); 27 | 28 | // Header tab state 29 | const headerState = useHeaderStore(); 30 | 31 | return ( 32 | 33 | 34 | 40 | 46 | headerState.pushHash(value ?? '#')} 51 | classNames={{ 52 | root: classes.mobile, 53 | tab: classes.tab, 54 | }} 55 | > 56 | 57 | {tabs.map((tab) => ( 58 | 59 | {typeof tab.label === 'function' ? ( 60 | tab.label( 61 | t('sections.' + ['landing', 'works', 'skills', 'contact'][tabs.indexOf(tab)]) 62 | ) 63 | ) : ( 64 | 70 | {tab.label} 71 | 72 | )} 73 | 74 | ))} 75 | 76 | 77 | 78 | 79 | ); 80 | }; 81 | -------------------------------------------------------------------------------- /public/locale/de/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Werke", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portfolio", 6 | "description": "Meine persönliche Portfolio-Website die aus meinen Projekten, Skill-Stack und Kontaktinfos besteht. Das ist was du jetzt anschaust.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Ein Hybrid-CMS, das die Leistungsfähigkeit des Frameworks durch seine erweiterbare Codebasis erweitert. Bietet eine völlig neue Sicht auf CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Todo-Tracker", 29 | "description": "Eine minimalistische Todo-App mit Vue.js und Tailwind CSS erstellt. Benutzt Firebase für Authentifikation und für die Datenbank.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Profilkarte", 38 | "description": "Eine simple Profilkarte die Discord Profilkarten ähnelt. Es wird als mein erstes Projekt im Web-Development gezählt.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "Ein Platzhalterwerkzeug zum Generieren nicht-existierende aussprechbarer Wörter. Großartiges Werkzeug, wenn Sie Ihr Produkt aus einem anderen Blickwinkel betrachten möchten.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "KNRTU-KAI Discord-Bot", 55 | "description": "Ein Discord-Bot für die KNRTU-KAI Universität. Der Bot hat viele nützliche Funktionen für Studenten und Lehrer.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "Ein kleiner moderner, in Node.js geschriebener API-Wrapper für die KNRTU-KAI Website. Es wird im KNRTU-KAI Discord-Bot benutzt.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "Ein effizientes Werkzueg um Farben fur Projekte ohne zögerung zu wählen und anpassen.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/components/pages/index/keyboard/component.tsx: -------------------------------------------------------------------------------- 1 | import { OrbitControls, useGLTF } from '@react-three/drei'; 2 | import { FunctionComponent, ReactElement, useRef } from 'react'; 3 | import { useFrame } from '@react-three/fiber'; 4 | 5 | /** 6 | * A blank keyboard model without any positioning or rotation that is generated by gltfjsx 7 | * @author Joshua Sleepy 8 | * @see {@link https://sketchfab.com/3d-models/lowpoly-65-mechanical-keyboard-0cdd429eb08549ac954352169de5c8f8 Sketchfab original} 9 | * @license CC-BY-4.0 10 | * @param props Element props provided straight to the group 11 | * @returns {ReactElement} Group of meshes 12 | */ 13 | export const Model: FunctionComponent = ( 14 | props: JSX.IntrinsicElements['group'] 15 | ): ReactElement => { 16 | const { nodes, materials } = useGLTF('/landing/keyboard.glb') as unknown as GLTFResult; 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | /** 32 | * A keyboard model with orbit controls and auto rotation 33 | * @see {@link https://docs.pmnd.rs/react-three-fiber/api/canvas Canvas API} 34 | * @returns {ReactElement} Orbit controls and keyboard model that have to be wrapped in canvas 35 | */ 36 | export const Keyboard: FunctionComponent = (): ReactElement => { 37 | // Model mesh ref 38 | const meshRef: any = useRef(); 39 | 40 | // Orbit controls ref 41 | const orbitRef: any = useRef(); 42 | 43 | // Make a keyboard rotate left & right depending on the angle of the camera 44 | useFrame(() => { 45 | const azimuthalAngle = orbitRef.current.getAzimuthalAngle(); 46 | if (azimuthalAngle > 0.5) { 47 | orbitRef.current.autoRotateSpeed = 1; 48 | } else if (azimuthalAngle < -0.5) { 49 | orbitRef.current.autoRotateSpeed = -1; 50 | } 51 | }); 52 | 53 | return ( 54 | <> 55 | 58 | 61 | 62 | 63 | 64 | 73 | 74 | ); 75 | }; 76 | 77 | // Preload the model 78 | useGLTF.preload('/landing/keyboard.glb'); 79 | -------------------------------------------------------------------------------- /src/components/pages/index/works/component.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionComponent, ReactElement } from 'react'; 2 | import { Badge, Card, Group, SimpleGrid, Text } from '@mantine/core'; 3 | import { projects as projectStack } from './config'; 4 | import { useStyles } from './styles'; 5 | import { Trans, useTranslation } from 'next-i18next'; 6 | 7 | /** 8 | * A grid of works cards taken from config 9 | * @param {Project[]} projects An array of projects to be mapped, optional 10 | * @returns {ReactElement} A grid of works cards 11 | */ 12 | export const Works: FunctionComponent = ({ 13 | projects = projectStack, 14 | filter, 15 | }): ReactElement => { 16 | const { classes } = useStyles(); 17 | const { t } = useTranslation('works'); 18 | 19 | // Filter works by their stack if filter is provided 20 | const filteredProjects = filter 21 | ? projects.filter((project) => project.stack.some((stack) => stack.name === filter)) 22 | : projects; 23 | 24 | return ( 25 | 26 | {filteredProjects.map((project) => ( 27 | 37 |
43 |
47 |
52 |
53 | 54 | 55 | {project.title} 56 | 57 | 58 | 59 | 60 | 61 | {project.description} 62 | 63 | 64 | 65 | {project.stack.map((stack) => ( 66 | 75 | 76 | {stack.name} 77 | 78 | 79 | ))} 80 | 81 |
82 |
83 | 84 | ))} 85 | 86 | ); 87 | }; 88 | -------------------------------------------------------------------------------- /public/locale/en/works.json: -------------------------------------------------------------------------------- 1 | { 2 | "section-name": "Works", 3 | "works": { 4 | "Portfolio": { 5 | "title": "Portfolio", 6 | "description": "My personal portfolio website consisting of my projects, skill stack and contact information. This is what you visit right now.", 7 | "stack": { 8 | "Next.js": "Next.js", 9 | "React": "React", 10 | "TypeScript": "TypeScript", 11 | "Mantine": "Mantine", 12 | "Three.js": "Three.js" 13 | } 14 | }, 15 | "Splendid": { 16 | "title": "Splendid", 17 | "description": "Hybrid CMS extending framework power by its extensible codebase. Provides a completely new view on CMS.", 18 | "stack": { 19 | "NestJS": "NestJS", 20 | "Next.js": "Next.js", 21 | "React": "React", 22 | "TypeScript": "TypeScript", 23 | "Prisma": "Prisma", 24 | "Mantine": "Mantine" 25 | } 26 | }, 27 | "Todo tracker": { 28 | "title": "Todo tracker", 29 | "description": "A minimalistic todo app built with Vue.js and Tailwind CSS. Uses firebase for authentication and database.", 30 | "stack": { 31 | "Vue.js": "Vue.js", 32 | "Tailwind CSS": "Tailwind CSS", 33 | "Firebase": "Firebase" 34 | } 35 | }, 36 | "Profile Card": { 37 | "title": "Profile Card", 38 | "description": "A simple profile card made similar to discord's profile card. Is considered my first project in web development.", 39 | "stack": { 40 | "HTML": "HTML", 41 | "SASS": "SASS", 42 | "TypeScript": "TypeScript" 43 | } 44 | }, 45 | "Slova": { 46 | "title": "Slova", 47 | "description": "A placeholder tool for generating non-existing speakable words. Great tool if you want to look at your product from a different angle.", 48 | "stack": { 49 | "TypeScript": "TypeScript", 50 | "Jest": "Jest" 51 | } 52 | }, 53 | "KNRTU-KAI Discord Bot": { 54 | "title": "KNRTU-KAI Discord Bot", 55 | "description": "A Discord bot for KNRTU-KAI university. It provides a lot of useful features for students and teachers.", 56 | "stack": { 57 | "Sapphire": "Sapphire", 58 | "TypeScript": "TypeScript", 59 | "Prisma": "Prisma" 60 | } 61 | }, 62 | "Kai.js": { 63 | "title": "Kai.js", 64 | "description": "A small modern API wrapper of the KNRTU-KAI website written for Node.js. It is used in the KNRTU-KAI Discord Bot.", 65 | "stack": { 66 | "JavaScript": "JavaScript" 67 | } 68 | }, 69 | "Cvet": { 70 | "title": "Cvet", 71 | "description": "An effecient color tool for customizing and picking colors for projects without any hesitation.", 72 | "stack": { 73 | "TypeScript": "TypeScript", 74 | "Jest": "Jest", 75 | "Oclif": "Oclif" 76 | } 77 | }, 78 | "Ikota": { 79 | "title": "Ikota", 80 | "description": "A CLI boilerplate automation tool offering various utilities to help working with React web applications.", 81 | "stack": { 82 | "TypeScript": "TypeScript", 83 | "Oclif": "Oclif", 84 | "TSDX": "TSDX" 85 | } 86 | }, 87 | "Krasota": { 88 | "title": "Krasota", 89 | "description": "Responsive, strongly typed, open source light React UI library. Amazing solution for extending your UI.", 90 | "stack": { 91 | "TypeScript": "TypeScript", 92 | "React": "React" 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app'; 2 | import Head from 'next/head'; 3 | import { Inter } from 'next/font/google'; 4 | import { MantineProvider, AppShell } from '@mantine/core'; 5 | import { Header } from '@component/layout/header'; 6 | import { Footer } from '@component/layout/footer'; 7 | import { Navbar } from '@component/layout/navbar'; 8 | import { appWithTranslation, useTranslation } from 'next-i18next'; 9 | import { Mesh } from '@component/layout/mesh'; 10 | 11 | // Inter font from Google Fonts, with latin subset 12 | const inter = Inter({ 13 | subsets: ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext'], 14 | display: 'swap', 15 | }); 16 | 17 | // An app wrapper with MantineProvider 18 | const App = ({ Component, ...pageProps }: AppProps) => { 19 | const { t } = useTranslation('works'); 20 | 21 | return ( 22 | <> 23 | 24 | {t('works.Portfolio.title') || ''} 25 | 26 | 27 | 28 | 29 | 30 | 31 | {/* Making the card bigger */} 32 | 33 | 34 | {/* Favicons */} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ({ 68 | // Override default global styles without usage of css imports 69 | '::selection': { 70 | backgroundColor: theme.colors.dark[8], 71 | color: theme.white, 72 | }, 73 | }), 74 | }} 75 | > 76 | 77 | } footer={