diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 66f8667c..4d86125f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,6 +1,9 @@ +import { getOurProjects } from '@uxcore/api/our-projects'; +import { GlobalContext as UXCoreGlobalContext } from '@uxcore/components/Context/GlobalContext'; +import UXCoreLayoutShell from '@uxcore/layouts/Layout'; import { useRouter } from 'next/router'; import { SessionProvider } from 'next-auth/react'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import useGlobals from '@hooks/useGlobals'; import useMobile from '@hooks/useMobile'; @@ -32,6 +35,10 @@ function AppContent({ Component, pageProps: { session, ...pageProps } }: TApp) { const loadingTimer = useRef(null); const [accountData, setAccountData] = useState(null); const [token, setToken] = useState(null); + const [uxcatUserInfo, setUxcatUserInfo] = useState(null); + const [selectedTitle, setSelectedTitle] = useState(''); + const [updatedUsername, setUpdatedUsername] = useState(''); + const [ourProjectsModalData, setOurProjectsModalData] = useState(null); const isIndexingOn = process.env.NEXT_PUBLIC_INDEXING === 'on'; const isProduction = process.env.NEXT_PUBLIC_ENV === 'prod'; @@ -226,6 +233,59 @@ function AppContent({ Component, pageProps: { session, ...pageProps } }: TApp) { }; }, []); + const isUxcoreRoute = + router.pathname.startsWith('/uxcore') || + router.pathname.startsWith('/uxcg') || + router.pathname.startsWith('/uxcat') || + router.pathname.startsWith('/uxcp') || + router.pathname.startsWith('/uxcore-api'); + + useEffect(() => { + document.body.classList.toggle('uxcorePage', isUxcoreRoute); + }, [isUxcoreRoute]); + + useEffect(() => { + if (!isUxcoreRoute) return; + let cancelled = false; + (async () => { + try { + const data = await getOurProjects(router.locale || 'en'); + if (!cancelled) setOurProjectsModalData(data || null); + } catch (err) { + console.warn('[our-projects] fetch failed:', err); + } + })(); + return () => { + cancelled = true; + }; + }, [isUxcoreRoute, router.locale]); + + const uxcoreContextValue = useMemo( + () => ({ + accountData, + setAccountData, + setToken, + uxcatUserInfo, + setUxcatUserInfo, + selectedTitle, + setSelectedTitle, + updatedUsername, + setUpdatedUsername, + ourProjectsModalData, + setOurProjectsModalData, + uxCoreData: null, + uxcgLocalizedData: null, + uxcgData: null, + }), + [ + accountData, + uxcatUserInfo, + selectedTitle, + updatedUsername, + ourProjectsModalData, + ], + ); + useEffect(() => { if (!accountData?.id || !accountData?.createdAt) return; @@ -297,9 +357,17 @@ function AppContent({ Component, pageProps: { session, ...pageProps } }: TApp) { preload="none" loop /> - - - + {isUxcoreRoute ? ( + + + + + + ) : ( + + + + )} ); diff --git a/src/pages/uxcg/[slug].tsx b/src/pages/uxcg/[slug].tsx index e804302c..99fa8285 100644 --- a/src/pages/uxcg/[slug].tsx +++ b/src/pages/uxcg/[slug].tsx @@ -1,40 +1,29 @@ -import { GetStaticPaths, GetStaticProps } from 'next'; -import { useRouter } from 'next/router'; -import { - FC, - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; - -import { QuestionType, TagType } from '@uxcore/local-types/data'; -import { TRouter } from '@uxcore/local-types/global'; - -import useMobile from '@uxcore/hooks/useMobile'; - -import { - copyToClipboard, - generateQuestionsSeo, - getAdjacentUXCGTitles, - mergeQuestionsLocalization, -} from '@uxcore/lib/helpers'; -import { getUXCGSlugPaths } from '@uxcore/lib/paths'; - +import { getStrapiBiases } from '@uxcore/api/biases'; import { getUXCGSeo } from '@uxcore/api/mainPageSeo'; import { getStrapiQuestions } from '@uxcore/api/questions'; import { getTags } from '@uxcore/api/tags'; - -import { GlobalContext } from '@uxcore/components/Context/GlobalContext'; import SeoGenerator from '@uxcore/components/SeoGenerator'; import UXCGModal from '@uxcore/components/UXCGModal'; import UXCGModalMobile from '@uxcore/components/UXCGModalMobile'; - +import useMobile from '@uxcore/hooks/useMobile'; import UXCGLayout from '@uxcore/layouts/UXCGLayout'; - import { getUXCGRedirects } from '@uxcore/lib/getUXCGRedirects'; +import { + copyToClipboard, + generateQuestionsSeo, + getAdjacentUXCGTitles, + mergeQuestionsLocalization, +} from '@uxcore/lib/helpers'; +import { getUXCGSlugPaths } from '@uxcore/lib/paths'; +import { + QuestionType, + StrapiBiasType, + TagType, +} from '@uxcore/local-types/data'; +import { TRouter } from '@uxcore/local-types/global'; +import { GetStaticPaths, GetStaticProps } from 'next'; +import { useRouter } from 'next/router'; +import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import styles from './UxcgId.module.scss'; @@ -51,6 +40,7 @@ interface UXCGIdProps { allQuestions: QuestionType; id?: number; languageSwitchSlugs?: Record; + biases: Record; } const Slug: FC = ({ @@ -65,6 +55,7 @@ const Slug: FC = ({ allQuestions, id, languageSwitchSlugs, + biases, }) => { const router = useRouter(); const { asPath, locale } = router as TRouter; @@ -83,7 +74,6 @@ const Slug: FC = ({ const [clickedQuestionId, setClickedQuestionId] = useState(null); const [answerId, setAnswerId] = useState(null); const [searchValue, setSearchValue] = useState(searchTerm as string); - const { uxCoreData } = useContext(GlobalContext); const [isCopyTooltipVisible, setIsCopyTooltipVisible] = useState(false); const tooltipTimer: { current: any } = useRef(); const { prev, next } = getAdjacentUXCGTitles(locale, allQuestions, id); @@ -192,7 +182,7 @@ const Slug: FC = ({ {isMobile ? ( = ({ setIsModalClosed={setIsModalClosed} questionId={questionId} answerId={answerId} - biases={uxCoreData && uxCoreData[locale]} + biases={biases?.[locale]} tags={tags} totalLength={questionsLength} onChangeQuestionId={handleSelectedQuestion} @@ -251,9 +241,13 @@ export const getStaticPaths: GetStaticPaths = async ({ locales }) => { // paths so the build doesn't abort; fallback: 'blocking' below makes // requests render on-demand at runtime where Strapi is reachable. try { - const newPaths = await getUXCGSlugPaths(locales); - return { paths: [...newPaths], fallback: 'blocking' }; } catch (err) { - console.warn('[getStaticPaths] build-time fetch failed, empty paths fallback:', err); + const newPaths = await getUXCGSlugPaths(locales); + return { paths: [...newPaths], fallback: 'blocking' }; + } catch (err) { + console.warn( + '[getStaticPaths] build-time fetch failed, empty paths fallback:', + err, + ); return { paths: [], fallback: 'blocking' }; } }; @@ -276,6 +270,7 @@ export const getStaticProps: GetStaticProps = async ({ params, locale }) => { const tags = getTags(); const questions = await getStrapiQuestions(); + const biases = await getStrapiBiases(); const sortedQuestions = mergeQuestionsLocalization( questions.en, @@ -330,6 +325,7 @@ export const getStaticProps: GetStaticProps = async ({ params, locale }) => { questions: sortedQuestions, allQuestions: questions, id: question?.attributes.number || null, + biases, }, revalidate: 5, }; diff --git a/src/pages/uxcore/[slug].tsx b/src/pages/uxcore/[slug].tsx index 5363e2f2..930730d8 100644 --- a/src/pages/uxcore/[slug].tsx +++ b/src/pages/uxcore/[slug].tsx @@ -1,27 +1,27 @@ -import { GetStaticPaths, GetStaticProps } from 'next'; -import { useRouter } from 'next/router'; -import { FC, useContext, useEffect, useMemo, useState } from 'react'; - -import type { QuestionType, StrapiBiasType, TagType } from '@uxcore/local-types/data'; -import { TRouter } from '@uxcore/local-types/global'; - -import useMobile from '@uxcore/hooks/useMobile'; -import useUXCoreGlobals from '@uxcore/hooks/useUXCoreGlobals'; - -import { getAdjacentBiasTitles, mergeBiasesLocalization } from '@uxcore/lib/helpers'; -import { getUXCoreTextPaths } from '@uxcore/lib/paths'; - import { getStrapiBiases } from '@uxcore/api/biases'; import { getTags } from '@uxcore/api/tags'; - import { GlobalContext } from '@uxcore/components/Context/GlobalContext'; import SeoGenerator from '@uxcore/components/SeoGenerator'; import UXCoreModal from '@uxcore/components/UXCoreModal'; import UXCoreModalMobile from '@uxcore/components/UXCoreModalMobile'; - +import useMobile from '@uxcore/hooks/useMobile'; +import useUXCoreGlobals from '@uxcore/hooks/useUXCoreGlobals'; import UXCoreLayout from '@uxcore/layouts/UXCoreLayout'; - import { getRedirectMap } from '@uxcore/lib/getUXCoreRedirects'; +import { + getAdjacentBiasTitles, + mergeBiasesLocalization, +} from '@uxcore/lib/helpers'; +import { getUXCoreTextPaths } from '@uxcore/lib/paths'; +import type { + QuestionType, + StrapiBiasType, + TagType, +} from '@uxcore/local-types/data'; +import { TRouter } from '@uxcore/local-types/global'; +import { GetStaticPaths, GetStaticProps } from 'next'; +import { useRouter } from 'next/router'; +import { FC, useContext, useEffect, useMemo, useState } from 'react'; import styles from './uxcoreId.module.scss'; @@ -30,6 +30,7 @@ interface UXCoreProps { currentModalData?: StrapiBiasType; currentActiveBias?: any; languageSwitchSlugs: Record; + biases: Record; } const UXCoreIds: FC = ({ @@ -37,11 +38,11 @@ const UXCoreIds: FC = ({ currentModalData, currentActiveBias, languageSwitchSlugs, + biases, }) => { - const { uxCoreData, uxcgLocalizedData } = useContext(GlobalContext); + const { uxcgLocalizedData } = useContext(GlobalContext); const [strapiQuestions, setStrapiQuestions] = useState([]); const [activeBiasNumber, setActiveBiasNumber] = useState(null); - const [biases, setBiases] = useState([]); const [isModalClosed, setIsModalClosed] = useState(true); const [{ toggleIsProductView }, { isProductView }] = useUXCoreGlobals(); const router = useRouter(); @@ -135,14 +136,6 @@ const UXCoreIds: FC = ({ router.prefetch('/uxcore'); }, []); - useEffect(() => { - if (uxCoreData) { - setBiases(uxCoreData); - } else { - setBiases([]); - } - }, [uxCoreData]); - useEffect(() => { if (uxcgLocalizedData) { setStrapiQuestions(uxcgLocalizedData[locale]); @@ -219,9 +212,13 @@ export const getStaticPaths: GetStaticPaths = async ({ locales }) => { // paths so the build doesn't abort; fallback: 'blocking' below makes // requests render on-demand at runtime where Strapi is reachable. try { - const newPaths = await getUXCoreTextPaths(locales); - return { paths: [...newPaths], fallback: 'blocking' }; } catch (err) { - console.warn('[getStaticPaths] build-time fetch failed, empty paths fallback:', err); + const newPaths = await getUXCoreTextPaths(locales); + return { paths: [...newPaths], fallback: 'blocking' }; + } catch (err) { + console.warn( + '[getStaticPaths] build-time fetch failed, empty paths fallback:', + err, + ); return { paths: [], fallback: 'blocking' }; } }; @@ -274,6 +271,7 @@ export const getStaticProps: GetStaticProps = async ({ params, locale }) => { currentModalData: currentActiveBias, languageSwitchSlugs, currentActiveBias: currentActiveBiasWithLocale.attributes, + biases: strapiBiases, }, revalidate: 5, }; diff --git a/src/styles/globals.scss b/src/styles/globals.scss index fd8d72bb..4e4a4b17 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -376,6 +376,95 @@ body { font-family: 'Source-Serif-Regular', sans-serif; } +// UX Core @font-face declarations (consolidated here from +// src/uxcore/styles/uxcore-fonts.scss per review). The scoped +// `body.uxcorePage *` rule below restores prod behaviour where every +// element on a UX Core route falls through to Lato when no component- +// level font-family is set — it must stay AFTER the global `* { font-family }` +// rule above so specificity wins. +@font-face { + font-family: 'Lato'; + src: url('/fonts/Lato/Lato-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Lato'; + src: url('/fonts/Lato/Lato-Semibold.woff2') format('woff2'); + font-weight: bold; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IBM Plex Mono'; + src: url('/fonts/biases/IBMPlexMono-Regular.ttf') format('truetype'); + font-weight: normal; + font-display: swap; +} + +@font-face { + font-family: 'Oswald'; + src: url('/fonts/biases/Oswald-Bold') format('truetype'); + font-weight: normal; + font-display: swap; +} + +@font-face { + font-family: 'RedHatDisplay'; + src: url('/fonts/biases/RedHatDisplay.ttf'); + font-display: swap; +} + +@font-face { + font-family: 'DelaGothicOne-Regular'; + src: url('/fonts/DelaGothicOne-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Manrope-ExtraLight.ttf'; + src: url('/fonts/Manrope-ExtraLight.ttf') format('truetype'); + font-weight: 200; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IBMPlexSans-Regular.ttf'; + src: url('/fonts/IBMPlexSans-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'NotoSansArmenian-Regular'; + src: url('/fonts/NotoSansArmenian/NotoSansArmenian-Regular.ttf') + format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Cormorant-Garamond-Medium'; + src: url('/fonts/Cormorant_Garamond/static/CormorantGaramond-Medium.ttf') + format('truetype'); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +body.uxcorePage, +body.uxcorePage * { + font-family: 'Lato', 'NotoSansArmenian-Regular', Arial, serif; +} + @media (max-width: 961px) { body { background-size: contain;