diff --git a/frontend/__tests__/components/AnimatedModal.spec.tsx b/frontend/__tests__/components/AnimatedModal.spec.tsx index 4ca8e8870eea..c0ecde36f85c 100644 --- a/frontend/__tests__/components/AnimatedModal.spec.tsx +++ b/frontend/__tests__/components/AnimatedModal.spec.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render } from "@solidjs/testing-library"; -import { AnimatedModal } from "../../src/ts/components/AnimatedModal"; +import { AnimatedModal } from "../../src/ts/components/common/AnimatedModal"; describe("AnimatedModal", () => { beforeEach(() => { diff --git a/frontend/__tests__/components/AsyncContent.spec.tsx b/frontend/__tests__/components/AsyncContent.spec.tsx index 1868b8c7c1ca..613dd48d2851 100644 --- a/frontend/__tests__/components/AsyncContent.spec.tsx +++ b/frontend/__tests__/components/AsyncContent.spec.tsx @@ -1,7 +1,7 @@ import { describe, it, expect } from "vitest"; import { render, screen, waitFor } from "@solidjs/testing-library"; import { createResource, Resource } from "solid-js"; -import AsyncContent from "../../src/ts/components/AsyncContent"; +import AsyncContent from "../../src/ts/components/common/AsyncContent"; describe("AsyncContent", () => { function renderWithResource( diff --git a/frontend/__tests__/components/Button.spec.tsx b/frontend/__tests__/components/Button.spec.tsx index 5f68eb9145d2..5475867747c1 100644 --- a/frontend/__tests__/components/Button.spec.tsx +++ b/frontend/__tests__/components/Button.spec.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi, afterEach } from "vitest"; import { render, cleanup } from "@solidjs/testing-library"; -import { Button } from "../../src/ts/components/Button"; +import { Button } from "../../src/ts/components/common/Button"; describe("Button component", () => { afterEach(() => { diff --git a/frontend/__tests__/components/ScrollToTop.spec.tsx b/frontend/__tests__/components/ScrollToTop.spec.tsx index 87bf82aabe5a..6c4d608ee971 100644 --- a/frontend/__tests__/components/ScrollToTop.spec.tsx +++ b/frontend/__tests__/components/ScrollToTop.spec.tsx @@ -1,7 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render } from "@solidjs/testing-library"; import { userEvent } from "@testing-library/user-event"; -import { ScrollToTop } from "../../src/ts/components/ScrollToTop"; +import { ScrollToTop } from "../../src/ts/components/layout/footer/ScrollToTop"; import * as CoreSignals from "../../src/ts/signals/core"; describe("ScrollToTop", () => { diff --git a/frontend/src/html/pages/about.html b/frontend/src/html/pages/about.html deleted file mode 100644 index 2e06ef79d9f8..000000000000 --- a/frontend/src/html/pages/about.html +++ /dev/null @@ -1,322 +0,0 @@ - diff --git a/frontend/src/index.html b/frontend/src/index.html index 70da78146f8e..75440c591c7a 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -37,7 +37,12 @@
- + diff --git a/frontend/src/styles/about.scss b/frontend/src/styles/about.scss deleted file mode 100644 index 40870fdc58b2..000000000000 --- a/frontend/src/styles/about.scss +++ /dev/null @@ -1,115 +0,0 @@ -.pageAbout { - display: flex; - flex-direction: column; - gap: 2rem; - - h2 { - font-size: 1rem; - margin: 0; - font-weight: unset; - } - - .created { - text-align: center; - color: var(--sub-color); - } - - .section { - display: grid; - - .bigtitle { - font-size: 2rem; - color: var(--sub-color); - margin: 0 0 1rem 0; - display: inline-flex; - align-items: baseline; - margin-bottom: 0.25em; - i { - margin-right: 0.5em; - } - } - - .title { - font-size: 1rem; - color: var(--sub-color); - margin: 0; - font-weight: 300; - display: inline-flex; - align-items: baseline; - margin-bottom: 0.25em; - i { - margin-right: 0.5em; - } - } - - .contactButtons, - .supportButtons { - margin-top: 1rem; - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 1rem; - button, - .button { - font-size: 1.5rem; - padding: 2rem 0; - } - } - - .supportButtons { - grid-template-columns: 1fr; - } - - .supporters, - .contributors { - display: grid; - // grid-template-columns: 1fr 1fr 1fr 1fr; - grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); - gap: 0.25rem; - color: var(--text-color); - } - - p { - margin: 0; - padding: 0; - color: var(--text-color); - &.small { - font-size: 0.75em; - color: var(--sub-color); - text-align: right; - } - } - } - - .triplegroup { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 1rem; - justify-items: center; - margin-top: 1rem; - } - .group { - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - .label { - color: var(--sub-color); - } - .val { - font-size: 3rem; - line-height: 3.5rem; - } - .valSmall { - font-size: 1.5rem; - line-height: 1.5rem; - } - } - - .chart canvas { - width: 100% !important; - } - .chart { - margin-top: 1rem; - position: relative; - } -} diff --git a/frontend/src/styles/ads.scss b/frontend/src/styles/ads.scss index 4ad1fea09c57..ea56a5cc7063 100644 --- a/frontend/src/styles/ads.scss +++ b/frontend/src/styles/ads.scss @@ -9,7 +9,6 @@ } .pageSettings .ad, -.pageAbout .ad, .pageAccount .ad { margin: 0 auto; } diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss index 9b0dc5e3bbaf..8166a5af1427 100644 --- a/frontend/src/styles/index.scss +++ b/frontend/src/styles/index.scss @@ -1,5 +1,5 @@ -@import "buttons", "fonts", "404", "ads", "about", "account", "animations", - "banners", "caret", "commandline", "core", "inputs", "keymap", "login", - "monkey", "nav", "notifications", "popups", "profile", "scroll", "settings", +@import "buttons", "fonts", "404", "ads", "account", "animations", "banners", + "caret", "commandline", "core", "inputs", "keymap", "login", "monkey", "nav", + "notifications", "popups", "profile", "scroll", "settings", "account-settings", "leaderboards", "test", "loading", "friends", "media-queries"; diff --git a/frontend/src/styles/media-queries-blue.scss b/frontend/src/styles/media-queries-blue.scss index acd40ab0bf65..8723a0c5360b 100644 --- a/frontend/src/styles/media-queries-blue.scss +++ b/frontend/src/styles/media-queries-blue.scss @@ -75,11 +75,6 @@ } } } - .pageAbout { - .triplegroup { - grid-template-columns: 1fr; - } - } .pageAccount { .triplegroup.stats { .title { diff --git a/frontend/src/styles/media-queries-green.scss b/frontend/src/styles/media-queries-green.scss index 78fca23abc3c..fce0689a95a7 100644 --- a/frontend/src/styles/media-queries-green.scss +++ b/frontend/src/styles/media-queries-green.scss @@ -62,13 +62,6 @@ font-size: 7rem; } } - .pageAbout { - .section { - .contactButtons { - grid-template-columns: 1fr 1fr; - } - } - } .pageSettings { .section { grid-template-columns: 1fr 1fr; diff --git a/frontend/src/styles/media-queries-purple.scss b/frontend/src/styles/media-queries-purple.scss index 0e5fe0534e8a..b4dd2809bd57 100644 --- a/frontend/src/styles/media-queries-purple.scss +++ b/frontend/src/styles/media-queries-purple.scss @@ -113,13 +113,6 @@ } } } - .pageAbout { - .section { - .contactButtons { - grid-template-columns: 1fr; - } - } - } .pageAccount { .accountVerificatinNotice { button { diff --git a/frontend/src/ts/components/AsyncContent.tsx b/frontend/src/ts/components/AsyncContent.tsx deleted file mode 100644 index f1b0b0801501..000000000000 --- a/frontend/src/ts/components/AsyncContent.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { ErrorBoundary, JSXElement, Resource, Suspense } from "solid-js"; -import { createErrorMessage } from "../utils/misc"; - -export default function AsyncContent(props: { - resource: Resource; - errorMessage?: string; - children: (data: T) => JSXElement; -}): JSXElement { - return ( - ( -
- {createErrorMessage(err, props.errorMessage ?? "An error occurred")} -
- )} - > - - - - } - > - {props.children(props.resource() as T)} - -
- ); -} diff --git a/frontend/src/ts/components/AnimatedModal.tsx b/frontend/src/ts/components/common/AnimatedModal.tsx similarity index 97% rename from frontend/src/ts/components/AnimatedModal.tsx rename to frontend/src/ts/components/common/AnimatedModal.tsx index d1fcee375618..b053ae1cf75f 100644 --- a/frontend/src/ts/components/AnimatedModal.tsx +++ b/frontend/src/ts/components/common/AnimatedModal.tsx @@ -1,12 +1,12 @@ import { JSXElement, createEffect, onCleanup, ParentProps } from "solid-js"; -import { applyReducedMotion } from "../utils/misc"; -import { useRefWithUtils } from "../hooks/useRefWithUtils"; +import { applyReducedMotion } from "../../utils/misc"; +import { useRefWithUtils } from "../../hooks/useRefWithUtils"; import { hideModal as storeHideModal, ModalId, isModalOpen, isModalChained, -} from "../stores/modals"; +} from "../../stores/modals"; type AnimationParams = { opacity?: number | [number, number]; diff --git a/frontend/src/ts/components/common/AsyncContent.tsx b/frontend/src/ts/components/common/AsyncContent.tsx new file mode 100644 index 000000000000..cf2d57bb1500 --- /dev/null +++ b/frontend/src/ts/components/common/AsyncContent.tsx @@ -0,0 +1,80 @@ +import { ErrorBoundary, JSXElement, Resource, Show, Suspense } from "solid-js"; +import { createErrorMessage } from "../../utils/misc"; +import * as Notifications from "../../elements/notifications"; +import { Conditional } from "./Conditional"; + +export default function AsyncContent( + props: { + resource: Resource; + errorMessage?: string; + } & ( + | { + alwaysShowContent?: never; + children: (data: T) => JSXElement; + } + | { + alwaysShowContent: true; + showLoader?: true; + children: (data: T | undefined) => JSXElement; + } + ), +): JSXElement { + const value = () => { + try { + return props.resource(); + } catch (err) { + const message = createErrorMessage( + err, + props.errorMessage ?? "An error occurred", + ); + console.error("AsyncContent error:", message); + Notifications.add(message, -1); + return undefined; + } + }; + const handleError = (err: unknown): string => { + console.error(err); + return createErrorMessage(err, props.errorMessage ?? "An error occurred"); + }; + + return ( + { + const p = props as { + showLoader?: true; + children: (data: T | undefined) => JSXElement; + }; + return ( + <> + +
+ +
+
+ {p.children(value())} + + ); + })()} + else={ +
{handleError(err)}
} + > + + + + } + > + + {props.children(props.resource() as T)} + + +
+ } + /> + ); +} diff --git a/frontend/src/ts/components/Button.tsx b/frontend/src/ts/components/common/Button.tsx similarity index 89% rename from frontend/src/ts/components/Button.tsx rename to frontend/src/ts/components/common/Button.tsx index 89596d0ea393..8c7be2f8b468 100644 --- a/frontend/src/ts/components/Button.tsx +++ b/frontend/src/ts/components/common/Button.tsx @@ -13,6 +13,7 @@ type BaseProps = { type ButtonProps = BaseProps & { onClick: () => void; href?: never; + sameTarget?: true; }; type AnchorProps = BaseProps & { @@ -53,8 +54,8 @@ export function Button(props: ButtonProps | AnchorProps): JSXElement { {content} diff --git a/frontend/src/ts/components/common/ChartJs.tsx b/frontend/src/ts/components/common/ChartJs.tsx new file mode 100644 index 000000000000..cdb9108020ed --- /dev/null +++ b/frontend/src/ts/components/common/ChartJs.tsx @@ -0,0 +1,67 @@ +import { onMount, onCleanup, createEffect, JSXElement } from "solid-js"; +import { + Chart, + ChartType, + ChartData, + ChartOptions, + DefaultDataPoint, +} from "chart.js"; +import { useRefWithUtils } from "../../hooks/useRefWithUtils"; +import { ChartWithUpdateColors } from "../../controllers/chart-controller"; +import { getThemeColors } from "../../signals/theme"; + +type ChartJSProps< + T extends ChartType = ChartType, + TData = DefaultDataPoint, +> = { + type: T; + data: ChartData; + options?: ChartOptions; + onChartInit?: (chart: Chart) => void; +}; + +export function ChartJs>( + props: ChartJSProps, +): JSXElement { + // Refs are assigned by SolidJS via the ref attribute + const [canvasRef, canvasEl] = useRefWithUtils(); + + let chart: ChartWithUpdateColors | undefined; + + onMount(() => { + //oxlint-disable-next-line no-non-null-assertion + chart = new ChartWithUpdateColors(canvasEl()!.native, { + type: props.type, + data: props.data, + options: props.options, + }); + + props.onChartInit?.(chart); + }); + + createEffect(() => { + if (!chart) return; + + chart.config.type = props.type; + chart.data = props.data; + if (props.options) { + chart.options = props.options; + } + chart.update(); + void chart.updateColors(); + }); + + createEffect(() => { + //react on theme changes + const colors = getThemeColors(); + if (!chart) return; + + void chart.updateColors(colors); + }); + + onCleanup(() => { + chart?.destroy(); + }); + + return ; +} diff --git a/frontend/src/ts/components/Conditional.tsx b/frontend/src/ts/components/common/Conditional.tsx similarity index 100% rename from frontend/src/ts/components/Conditional.tsx rename to frontend/src/ts/components/common/Conditional.tsx diff --git a/frontend/src/ts/components/Footer.scss b/frontend/src/ts/components/layout/footer/Footer.scss similarity index 98% rename from frontend/src/ts/components/Footer.scss rename to frontend/src/ts/components/layout/footer/Footer.scss index ba9cb9b98b3b..789db2135dd8 100644 --- a/frontend/src/ts/components/Footer.scss +++ b/frontend/src/ts/components/layout/footer/Footer.scss @@ -1,4 +1,4 @@ -@use "../../styles/media.scss" as *; +@use "../../../../styles/media.scss" as *; footer { position: relative; diff --git a/frontend/src/ts/components/Footer.tsx b/frontend/src/ts/components/layout/footer/Footer.tsx similarity index 96% rename from frontend/src/ts/components/Footer.tsx rename to frontend/src/ts/components/layout/footer/Footer.tsx index 233017eadfb6..62de12629b26 100644 --- a/frontend/src/ts/components/Footer.tsx +++ b/frontend/src/ts/components/layout/footer/Footer.tsx @@ -1,7 +1,7 @@ import { JSXElement } from "solid-js"; import { VersionButton } from "./VersionButton"; -import { Button } from "./Button"; -import { showModal } from "../stores/modals"; +import { Button } from "../../common/Button"; +import { showModal } from "../../../stores/modals"; import "./Footer.scss"; import { ThemeIndicator } from "./ThemeIndicator"; import { ScrollToTop } from "./ScrollToTop"; diff --git a/frontend/src/ts/components/ScrollToTop.scss b/frontend/src/ts/components/layout/footer/ScrollToTop.scss similarity index 100% rename from frontend/src/ts/components/ScrollToTop.scss rename to frontend/src/ts/components/layout/footer/ScrollToTop.scss diff --git a/frontend/src/ts/components/ScrollToTop.tsx b/frontend/src/ts/components/layout/footer/ScrollToTop.tsx similarity index 94% rename from frontend/src/ts/components/ScrollToTop.tsx rename to frontend/src/ts/components/layout/footer/ScrollToTop.tsx index ca1987366315..f3ce98dd9ab1 100644 --- a/frontend/src/ts/components/ScrollToTop.tsx +++ b/frontend/src/ts/components/layout/footer/ScrollToTop.tsx @@ -1,5 +1,5 @@ import { JSXElement, createSignal, onMount, onCleanup } from "solid-js"; -import { getActivePage } from "../signals/core"; +import { getActivePage } from "../../../signals/core"; import "./ScrollToTop.scss"; export function ScrollToTop(): JSXElement { diff --git a/frontend/src/ts/components/ThemeIndicator.scss b/frontend/src/ts/components/layout/footer/ThemeIndicator.scss similarity index 100% rename from frontend/src/ts/components/ThemeIndicator.scss rename to frontend/src/ts/components/layout/footer/ThemeIndicator.scss diff --git a/frontend/src/ts/components/ThemeIndicator.tsx b/frontend/src/ts/components/layout/footer/ThemeIndicator.tsx similarity index 78% rename from frontend/src/ts/components/ThemeIndicator.tsx rename to frontend/src/ts/components/layout/footer/ThemeIndicator.tsx index 4ad1e918d5f0..e6a1fbe18960 100644 --- a/frontend/src/ts/components/ThemeIndicator.tsx +++ b/frontend/src/ts/components/layout/footer/ThemeIndicator.tsx @@ -1,10 +1,10 @@ import { JSXElement, Show } from "solid-js"; -import { getThemeIndicator } from "../signals/core"; -import Config, { setConfig } from "../config"; -import { isAuthenticated } from "../firebase"; -import * as DB from "../db"; -import * as Notifications from "../elements/notifications"; -import * as Commandline from "../commandline/commandline"; +import { getThemeIndicator } from "../../../signals/core"; +import Config, { setConfig } from "../../../config"; +import { isAuthenticated } from "../../../firebase"; +import * as DB from "../../../db"; +import * as Notifications from "../../../elements/notifications"; +import * as Commandline from "../../../commandline/commandline"; import "./ThemeIndicator.scss"; export function ThemeIndicator(): JSXElement { diff --git a/frontend/src/ts/components/VersionButton.scss b/frontend/src/ts/components/layout/footer/VersionButton.scss similarity index 100% rename from frontend/src/ts/components/VersionButton.scss rename to frontend/src/ts/components/layout/footer/VersionButton.scss diff --git a/frontend/src/ts/components/VersionButton.tsx b/frontend/src/ts/components/layout/footer/VersionButton.tsx similarity index 85% rename from frontend/src/ts/components/VersionButton.tsx rename to frontend/src/ts/components/layout/footer/VersionButton.tsx index cb29bfd72745..617320ced5d7 100644 --- a/frontend/src/ts/components/VersionButton.tsx +++ b/frontend/src/ts/components/layout/footer/VersionButton.tsx @@ -1,10 +1,10 @@ import { JSXElement, Show, createSignal } from "solid-js"; -import { isDevEnvironment } from "../utils/misc"; +import { isDevEnvironment } from "../../../utils/misc"; import { envConfig } from "virtual:env-config"; import { COMPATIBILITY_CHECK } from "@monkeytype/contracts"; -import { lastSeenServerCompatibility } from "../ape/adapters/ts-rest-adapter"; -import { getVersion } from "../signals/core"; -import { showModal } from "../stores/modals"; +import { lastSeenServerCompatibility } from "../../../ape/adapters/ts-rest-adapter"; +import { getVersion } from "../../../signals/core"; +import { showModal } from "../../../stores/modals"; import "./VersionButton.scss"; export function VersionButton(): JSXElement { diff --git a/frontend/src/ts/components/ContactModal.scss b/frontend/src/ts/components/modals/ContactModal.scss similarity index 93% rename from frontend/src/ts/components/ContactModal.scss rename to frontend/src/ts/components/modals/ContactModal.scss index ca50648a7f9a..c059ee5fb74e 100644 --- a/frontend/src/ts/components/ContactModal.scss +++ b/frontend/src/ts/components/modals/ContactModal.scss @@ -1,4 +1,4 @@ -@use "../../styles/media.scss" as *; +@use "../../../styles/media.scss" as *; #ContactModal { .modal { diff --git a/frontend/src/ts/components/ContactModal.tsx b/frontend/src/ts/components/modals/ContactModal.tsx similarity index 95% rename from frontend/src/ts/components/ContactModal.tsx rename to frontend/src/ts/components/modals/ContactModal.tsx index 2800ba24808c..0e597eba738b 100644 --- a/frontend/src/ts/components/ContactModal.tsx +++ b/frontend/src/ts/components/modals/ContactModal.tsx @@ -1,7 +1,7 @@ import { JSXElement } from "solid-js"; -import { AnimatedModal } from "./AnimatedModal"; +import { AnimatedModal } from "../common/AnimatedModal"; import "./ContactModal.scss"; -import { Button } from "./Button"; +import { Button } from "../common/Button"; export function ContactModal(): JSXElement { return ( diff --git a/frontend/src/ts/components/Modals.tsx b/frontend/src/ts/components/modals/Modals.tsx similarity index 100% rename from frontend/src/ts/components/Modals.tsx rename to frontend/src/ts/components/modals/Modals.tsx diff --git a/frontend/src/ts/components/SupportModal.scss b/frontend/src/ts/components/modals/SupportModal.scss similarity index 95% rename from frontend/src/ts/components/SupportModal.scss rename to frontend/src/ts/components/modals/SupportModal.scss index 65fac07b939f..df5d1d28b1ff 100644 --- a/frontend/src/ts/components/SupportModal.scss +++ b/frontend/src/ts/components/modals/SupportModal.scss @@ -1,4 +1,4 @@ -@use "../../styles/media.scss" as *; +@use "../../../styles/media.scss" as *; #SupportModal { .modal { diff --git a/frontend/src/ts/components/SupportModal.tsx b/frontend/src/ts/components/modals/SupportModal.tsx similarity index 86% rename from frontend/src/ts/components/SupportModal.tsx rename to frontend/src/ts/components/modals/SupportModal.tsx index f4942b462c6e..b926ca003fd7 100644 --- a/frontend/src/ts/components/SupportModal.tsx +++ b/frontend/src/ts/components/modals/SupportModal.tsx @@ -1,9 +1,9 @@ import { JSXElement } from "solid-js"; -import { AnimatedModal } from "./AnimatedModal"; +import { AnimatedModal } from "../common/AnimatedModal"; import "./SupportModal.scss"; -import { showModal } from "../stores/modals"; -import { setCommandlineSubgroup } from "../signals/core"; -import { Button } from "./Button"; +import { showModal } from "../../stores/modals"; +import { setCommandlineSubgroup } from "../../signals/core"; +import { Button } from "../common/Button"; export function SupportModal(): JSXElement { return ( diff --git a/frontend/src/ts/components/VersionHistoryModal.scss b/frontend/src/ts/components/modals/VersionHistoryModal.scss similarity index 100% rename from frontend/src/ts/components/VersionHistoryModal.scss rename to frontend/src/ts/components/modals/VersionHistoryModal.scss diff --git a/frontend/src/ts/components/VersionHistoryModal.tsx b/frontend/src/ts/components/modals/VersionHistoryModal.tsx similarity index 90% rename from frontend/src/ts/components/VersionHistoryModal.tsx rename to frontend/src/ts/components/modals/VersionHistoryModal.tsx index d4a7eeab1137..b7626617c586 100644 --- a/frontend/src/ts/components/VersionHistoryModal.tsx +++ b/frontend/src/ts/components/modals/VersionHistoryModal.tsx @@ -1,10 +1,10 @@ import { JSXElement, createResource, For } from "solid-js"; import { format } from "date-fns/format"; -import { getReleasesFromGitHub } from "../utils/json-data"; -import { AnimatedModal } from "./AnimatedModal"; +import { getReleasesFromGitHub } from "../../utils/json-data"; +import { AnimatedModal } from "../common/AnimatedModal"; import "./VersionHistoryModal.scss"; -import AsyncContent from "./AsyncContent"; -import { isModalOpen } from "../stores/modals"; +import AsyncContent from "../common/AsyncContent"; +import { isModalOpen } from "../../stores/modals"; export function VersionHistoryModal(): JSXElement { const isOpen = (): boolean => isModalOpen("VersionHistory"); diff --git a/frontend/src/ts/components/mount.tsx b/frontend/src/ts/components/mount.tsx index fceb52743005..83adca3ef98a 100644 --- a/frontend/src/ts/components/mount.tsx +++ b/frontend/src/ts/components/mount.tsx @@ -2,12 +2,14 @@ import { render } from "solid-js/web"; import { qsa } from "../utils/dom"; import { JSXElement } from "solid-js"; -import { Footer } from "./Footer"; -import { Modals } from "./Modals"; +import { Footer } from "./layout/footer/Footer"; +import { Modals } from "./modals/Modals"; +import { AboutPage } from "./pages/AboutPage"; const components: Record JSXElement> = { Footer: () =>