diff --git a/.env b/.env index 5340f29..068fb25 100644 --- a/.env +++ b/.env @@ -2,16 +2,14 @@ # For others consider to use .env.local # Disable google analytics when running app in dev mode -NEXT_PUBLIC_DISABLE_GA_IN_DEV_MODE=true - -NEXT_PUBLIC_INSTAGRAM_ADDRESS="https://www.instagram.com/salsavivayerevan/" -NEXT_PUBLIC_FACEBOOK_ADDRESS="https://www.facebook.com/SalsaVivaYerevan/" -NEXT_PUBLIC_WHATSAPP_ADDRESS="https://wa.me/37443108588" -NEXT_PUBLIC_TELEGRAM_ADDRESS="https://t.me/SV_Yerevan" -NEXT_PUBLIC_CONTACT_EMAIL="sv.yerevan@gmail.com" -NEXT_PUBLIC_CONTACT_PHONE="+374 431 085 88" - -NEXT_PUBLIC_LOCATION_GOOGLE_MAPS_LINK="https://shorturl.at/gsuK5" -NEXT_PUBLIC_LOCATION_ADDRESS_TEXT=" 41, 4 Abovyan St, Yerevan 0009" - -BASE_PATH="/salsaviva" +DISABLE_GA_IN_DEV_MODE=true + +# Socials +INSTAGRAM_ADDRESS=https://www.instagram.com/salsavivayerevan/ +FACEBOOK_ADDRESS=https://www.facebook.com/SalsaVivaYerevan/ +WHATSAPP_ADDRESS=https://wa.me/37443108588 +TELEGRAM_ADDRESS=https://t.me/SV_Yerevan +CONTACT_EMAIL=sv.yerevan@gmail.com +CONTACT_PHONE=+374 431 085 88 +LOCATION_GOOGLE_MAPS_LINK=https://shorturl.at/gsuK5 +LOCATION_ADDRESS_TEXT=41, 4 Abovyan St, Yerevan 0009 diff --git a/.env.sample b/.env.sample index 84021a4..f8e8b8c 100644 --- a/.env.sample +++ b/.env.sample @@ -2,7 +2,15 @@ # Copy contents of this file to .env.local # Replace values with your own -NEXT_PUBLIC_GA_TRACKING_ID="" -NEXT_PUBLIC_HOTJAR_ID="" -NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION="" -NEXT_PUBLIC_FORMSPREE_ID="" +# Google analytics tracking id +GA_TRACKING_ID="" + +# Hotjar id and version +HOTJAR_ID="" +HOTJAR_SNIPPET_VERSION="" + +# Fromspree id to send form data +FORMSPREE_ID="" + +# Base path to load assets, for instance used in production on github pages +ASSETS_BASE_PATH="" diff --git a/.eslintrc.json b/.eslintrc.json index a0552bd..1f4c04a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,7 +57,8 @@ "newlines-between": "never", "warnOnUnassignedImports": true } - ] + ], + "no-duplicate-imports": 1 }, "overrides": [ { @@ -79,5 +80,6 @@ "project": "./tsconfig.json" } } - } + }, + "env": {} } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d697d2..1eba9c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,26 +5,25 @@ on: workflow_dispatch: workflow_call: inputs: - public_path: + assets_path: type: string - description: 'Inject public path' + description: 'Path to load static assets' required: false default: '' - branch: - type: string - description: 'Branch to build' - required: true - default: 'master' jobs: build: name: Build runs-on: ubuntu-latest + env: + GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }} + FORMSPREE_ID: ${{ secrets.FORMSPREE_ID }} + HOTJAR_ID: ${{ secrets.HOTJAR_ID }} + HOTJAR_SNIPPET_VERSION: ${{ secrets.HOTJAR_SNIPPET_VERSION }} + ASSETS_BASE_PATH: /salsaviv steps: - name: Checkout uses: actions/checkout@v3 - with: - ref: ${{ github.event.inputs.branch }} - name: Setup Nodejs uses: actions/setup-node@v3 @@ -36,18 +35,6 @@ jobs: npm pkg delete scripts.prepare npm ci - - name: Make envfile - uses: SpicyPizza/create-envfile@v2.0 - with: - envkey_NEXT_PUBLIC_GA_TRACKING_ID: "${{ secrets.NEXT_PUBLIC_GA_TRACKING_ID }}" - envkey_NEXT_PUBLIC_FORMSPREE_ID: "${{ secrets.NEXT_PUBLIC_FORMSPREE_ID }}" - envkey_NEXT_PUBLIC_HOTJAR_ID: ${{ secrets.NEXT_PUBLIC_HOTJAR_ID }} - envkey_NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION: ${{ secrets.NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION }} - envkey_PUBLIC_PATH: ${{ github.event.inputs.public_path }} - directory: '.' - file_name: .env.local - fail_on_empty: false - - name: Lint run: | npm run lint @@ -59,11 +46,12 @@ jobs: - name: Check seo run: npm run check-seo - - name: e2e - uses: cypress-io/github-action@v5 - with: - start: npm run start - record: false + # - name: e2e + # uses: cypress-io/github-action@v5 + # with: + # build: npm run build + # start: npm start + # record: false - name: Upload build results uses: actions/upload-artifact@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a1c6e9c..ba42eaf 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,15 +12,14 @@ permissions: id-token: write concurrency: - group: "pages" + group: 'pages' cancel-in-progress: false jobs: build: uses: ./.github/workflows/build.yml with: - branch: ${{ github.event.head_commit.message }} - public_path: https://${{ github.repository_owner }}.github.io/${{ github.repository_name }} + assets_path: /salsaviva secrets: inherit deploy: @@ -40,14 +39,13 @@ jobs: name: build path: ./out - name: Deploy - uses: JamesIves/github-pages-deploy-action@3.6.2 + uses: JamesIves/github-pages-deploy-action@v4 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages - FOLDER: out - CLEAN: true - TARGET_FOLDER: . - GIT_CONFIG_NAME: ${{ github.actor }} - GIT_CONFIG_EMAIL: ${{ github.actor }}@users.noreply.github.com - COMMIT_MESSAGE: Deploy from commit ${{ github.sha }} - + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: out + clean: true + target-folder: . + git-config-name: ${{ github.actor }} + git-config-email: ${{ github.actor }}@users.noreply.github.com + commit-message: Deploy from commit ${{ github.sha }} diff --git a/.husky/pre-commit b/.husky/pre-commit index d24fdfc..cf0c46b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npx lint-staged +npx --no-install lint-staged diff --git a/.vscode/settings.json b/.vscode/settings.json index 765390e..b94b31f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,16 @@ "editor.formatOnType": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" - } + }, + "json.schemas": [ + { + "fileMatch": ["eslintrc.json"], + "url": "https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/eslintrc.json" + }, + { + "fileMatch": ["prettierrc"], + "url": "https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/prettierrc-2.8.8.json" + } + ], + "json.schemaDownload.enable": true } diff --git a/app/about/page.tsx b/app/about/page.tsx index b6e5e4f..7f48caa 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -86,6 +86,7 @@ export default function About() { className={CATLinkCn} path="/contact" customTransition={{delay: 0.5}} + rel="noopener noreferrer" /> ); diff --git a/app/contact/page.tsx b/app/contact/page.tsx index d37bc5d..50157a7 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -5,6 +5,7 @@ import TransitionDuration from '@/lib/framerMotion/TransitionDuration'; import getTextSlideIntoViewVarinats from '@/lib/framerMotion/variants/getTextSlideIntoViewVarinats'; import FormWrapper from '@/components/pages/Contact/FormWrapper/FormWrapper'; import metadataBase from '../metadata'; +import {env} from '../env.mjs'; export const metadata: Metadata = { title: 'Contact', @@ -16,7 +17,7 @@ export const metadata: Metadata = { }; const titleVariants = getTextSlideIntoViewVarinats('right'); - +const {FORMSPREE_ID} = env; const containerCn = clsx('flex', 'flex-col', 'min-h-screen', 'w-full'); const titleCn = clsx('text-8xl', 'mt-24', 'ml-4', 'text-center'); @@ -27,13 +28,14 @@ export default function Contact() { return (
CONTACT US - +
); } diff --git a/app/env.mjs b/app/env.mjs index be97ead..d53d70b 100644 --- a/app/env.mjs +++ b/app/env.mjs @@ -1,36 +1,44 @@ import {createEnv} from '@t3-oss/env-nextjs'; import {z} from 'zod'; +/** + * Checks if a string is not empty. + */ const notEmptyString = z.string().min(1); -const number = z - .string() - .refine(v => !Number.isNaN(Number(v))) +/** + * Checks if a value is a number. + */ +const number = notEmptyString + .refine(v => { + return !isNaN(Number(v)) || !isNaN(Number(v.replace(/"'/g, ''))); + }) .transform(Number); +/** + * Checks if a value is a boolean. + */ const boolean = z .string() .refine(v => v === 'true' || v === 'false' || v === '') .transform(value => value === 'true'); -const server = {}; - -const client = { - NEXT_PUBLIC_GA_TRACKING_ID: notEmptyString, - NEXT_PUBLIC_FORMSPREE_ID: notEmptyString, - NEXT_PUBLIC_DISABLE_GA_IN_DEV_MODE: boolean, - NEXT_PUBLIC_INSTAGRAM_ADDRESS: notEmptyString.url(), - NEXT_PUBLIC_FACEBOOK_ADDRESS: notEmptyString.url(), - NEXT_PUBLIC_WHATSAPP_ADDRESS: notEmptyString.url(), - NEXT_PUBLIC_TELEGRAM_ADDRESS: notEmptyString.url(), - NEXT_PUBLIC_CONTACT_EMAIL: notEmptyString.email(), - NEXT_PUBLIC_CONTACT_PHONE: notEmptyString, - NEXT_PUBLIC_LOCATION_GOOGLE_MAPS_LINK: notEmptyString.url(), - NEXT_PUBLIC_LOCATION_ADDRESS_TEXT: notEmptyString, - NEXT_PUBLIC_GA_TRACKING_ID: notEmptyString, - NEXT_PUBLIC_HOTJAR_ID: notEmptyString, - NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION: number, - NEXT_PUBLIC_FORMSPREE_ID: notEmptyString, +const server = { + INSTAGRAM_ADDRESS: notEmptyString.url(), + FACEBOOK_ADDRESS: notEmptyString.url(), + WHATSAPP_ADDRESS: notEmptyString.url(), + TELEGRAM_ADDRESS: notEmptyString.url(), + DISABLE_GA_IN_DEV_MODE: boolean, + GA_TRACKING_ID: notEmptyString, + HOTJAR_ID: notEmptyString, + HOTJAR_SNIPPET_VERSION: number, + FORMSPREE_ID: notEmptyString, + CONTACT_EMAIL: notEmptyString.email(), + CONTACT_PHONE: notEmptyString, + LOCATION_GOOGLE_MAPS_LINK: notEmptyString.url(), + LOCATION_ADDRESS_TEXT: notEmptyString, }; +const client = {}; + const shared = { NODE_ENV: z.enum(['development', 'production']), }; @@ -39,20 +47,20 @@ export const env = createEnv({ server, client, shared, - experimental__runtimeEnv: { + runtimeEnv: { NODE_ENV: process.env.NODE_ENV, - NEXT_PUBLIC_GA_TRACKING_ID: process.env.NEXT_PUBLIC_GA_TRACKING_ID, - NEXT_PUBLIC_FORMSPREE_ID: process.env.NEXT_PUBLIC_FORMSPREE_ID, - NEXT_PUBLIC_DISABLE_GA_IN_DEV_MODE: process.env.NEXT_PUBLIC_DISABLE_GA_IN_DEV_MODE, - NEXT_PUBLIC_INSTAGRAM_ADDRESS: process.env.NEXT_PUBLIC_INSTAGRAM_ADDRESS, - NEXT_PUBLIC_FACEBOOK_ADDRESS: process.env.NEXT_PUBLIC_FACEBOOK_ADDRESS, - NEXT_PUBLIC_WHATSAPP_ADDRESS: process.env.NEXT_PUBLIC_WHATSAPP_ADDRESS, - NEXT_PUBLIC_TELEGRAM_ADDRESS: process.env.NEXT_PUBLIC_TELEGRAM_ADDRESS, - NEXT_PUBLIC_CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL, - NEXT_PUBLIC_CONTACT_PHONE: process.env.NEXT_PUBLIC_CONTACT_PHONE, - NEXT_PUBLIC_LOCATION_GOOGLE_MAPS_LINK: process.env.NEXT_PUBLIC_LOCATION_GOOGLE_MAPS_LINK, - NEXT_PUBLIC_LOCATION_ADDRESS_TEXT: process.env.NEXT_PUBLIC_LOCATION_ADDRESS_TEXT, - NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION: process.env.NEXT_PUBLIC_HOTJAR_SNIPPET_VERSION, - NEXT_PUBLIC_HOTJAR_ID: process.env.NEXT_PUBLIC_HOTJAR_ID, + INSTAGRAM_ADDRESS: process.env.INSTAGRAM_ADDRESS, + FACEBOOK_ADDRESS: process.env.FACEBOOK_ADDRESS, + WHATSAPP_ADDRESS: process.env.WHATSAPP_ADDRESS, + TELEGRAM_ADDRESS: process.env.TELEGRAM_ADDRESS, + DISABLE_GA_IN_DEV_MODE: process.env.DISABLE_GA_IN_DEV_MODE, + GA_TRACKING_ID: process.env.GA_TRACKING_ID, + HOTJAR_ID: process.env.HOTJAR_ID, + HOTJAR_SNIPPET_VERSION: process.env.HOTJAR_SNIPPET_VERSION, + FORMSPREE_ID: process.env.FORMSPREE_ID, + CONTACT_EMAIL: process.env.CONTACT_EMAIL, + CONTACT_PHONE: process.env.CONTACT_PHONE, + LOCATION_GOOGLE_MAPS_LINK: process.env.LOCATION_GOOGLE_MAPS_LINK, + LOCATION_ADDRESS_TEXT: process.env.LOCATION_ADDRESS_TEXT, }, }); diff --git a/app/error.tsx b/app/error.tsx index c68d3e9..01ae891 100644 --- a/app/error.tsx +++ b/app/error.tsx @@ -1,29 +1,19 @@ 'use client'; -import {useEffect} from 'react'; +import clsx from 'clsx'; +import Button from '@/components/shared/Button/Button'; + +const errContainerCn = clsx('pt-64', 'flex', 'flex-col', 'gap-8'); /** * @param {{error: Error; reset: () => void}} props Props. * @returns React component. */ -export default function Error({error, reset}: {error: Error; reset: () => void}) { - useEffect(() => { - // Log the error to an error reporting service - // eslint-disable-next-line no-console - console.error(error); - }, [error]); - +export default function Error({reset}: {error: Error; reset: () => void}) { return ( -
+

Something went wrong!

- +
); } diff --git a/app/gallery/page.tsx b/app/gallery/page.tsx index 402bba3..dcff9b4 100644 --- a/app/gallery/page.tsx +++ b/app/gallery/page.tsx @@ -5,6 +5,7 @@ import AppearInViewport from '@/components/shared/AppearInViewport/AppearInViewp import SocialIcons from '@/components/shared/SocialIcons/SocialIcons'; import TransitionDuration from '@/lib/framerMotion/TransitionDuration'; import metadataBase from '../metadata'; +import getSocialicons from '../socialIcons'; import './styles.css'; export const metadata: Metadata = { @@ -68,7 +69,7 @@ export default function GalleryPage() { > Want more? Follow us on social media! - +
); diff --git a/app/layout.tsx b/app/layout.tsx index 952ce6f..12867c2 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,12 +1,19 @@ import clsx from 'clsx'; import '@/lib/fontawesome/configure'; import Footer from '@/components/shared/Footer/Footer'; -import WithGtag from '@/lib/gtag/WithGtag'; +import WithGtagScript from '@/lib/gtag/WithGtagScript'; import CustomCursor from '@/lib/customCursor/CustomCursor'; import WebVitals from '@/components/shared/WebVitals/WebVitals'; +import WithHotjarScript from '@/lib/hotjar/WithHotjarScript'; +import {LocationChangeTracker} from '@/lib/gtag/LocationChangeTracker'; +import TopMenu from '@/components/shared/TopMenu/TopMenu'; +import ScrollToTopButton from '@/components/shared/ScrollToTop/ScrollToTop'; +import MotoinProvider from '@/lib/framerMotion/MotionProvider'; import meta from './metadata'; import viewportData from './viewport'; import {kumbhSans, robotoMono} from './fonts'; +import {env} from './env.mjs'; +import {menuItems, socialLinks} from './topMenuConfig'; import './styles.css'; export const metadata = meta; @@ -33,14 +40,33 @@ const mainCn = clsx('flex', 'flex-col', 'items-center', 'justify-start', 'grow') * @returns Global layout. */ export default function RootLayout({children}: {children: React.ReactNode}) { + const {GA_TRACKING_ID, DISABLE_GA_IN_DEV_MODE} = env; return ( - + + - -
{children}
+
+ + + + {children} + +