From ee5d760bf104bf7e82a1e6e73be8b1085f284f0c Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Wed, 27 Dec 2023 13:35:55 -0500 Subject: [PATCH 01/13] chore(results): initialize page view - Added dropdowns to select season, race, and driver - Added tabs for viewing race results, drivers and constructors standings - Initial race results view --- next.config.js | 7 ++- package.json | 2 + pnpm-lock.yaml | 31 ++++++++-- src/app/components/Dropdown.tsx | 26 +++++++++ src/app/layout.tsx | 7 ++- src/app/page.tsx | 2 +- src/app/results/RaceResults.tsx | 100 ++++++++++++++++++++++++++++++++ src/app/results/layout.tsx | 51 ++++++++++++++++ src/app/results/page.tsx | 61 +++++++++++++++++++ src/app/results/utils.tsx | 28 +++++++++ src/atoms/seasons.tsx | 33 +++++++++++ 11 files changed, 341 insertions(+), 7 deletions(-) create mode 100644 src/app/components/Dropdown.tsx create mode 100644 src/app/results/RaceResults.tsx create mode 100644 src/app/results/layout.tsx create mode 100644 src/app/results/page.tsx create mode 100644 src/app/results/utils.tsx create mode 100644 src/atoms/seasons.tsx diff --git a/next.config.js b/next.config.js index 658404a..d538b03 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,9 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + // cache optimized images for 60 seconds + minimumCacheTTL: 60, + }, +}; module.exports = nextConfig; diff --git a/package.json b/package.json index 6b70c96..5a85e53 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "typecheck": "tsc --pretty --noEmit --incremental false" }, "dependencies": { + "clsx": "^2.0.0", + "jotai": "^2.6.0", "next": "latest", "react": "latest", "react-dom": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48caa96..893afd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,12 @@ settings: excludeLinksFromLockfile: false dependencies: + clsx: + specifier: ^2.0.0 + version: 2.0.0 + jotai: + specifier: ^2.6.0 + version: 2.6.0(@types/react@18.2.45)(react@18.2.0) next: specifier: latest version: 14.0.4(react-dom@18.2.0)(react@18.2.0) @@ -711,7 +717,6 @@ packages: /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: true /@types/react-dom@18.2.18: resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} @@ -725,11 +730,9 @@ packages: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 csstype: 3.1.3 - dev: true /@types/scheduler@0.16.8: resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} - dev: true /@types/semver@7.5.6: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} @@ -1428,6 +1431,11 @@ packages: engines: {node: '>=0.8'} dev: true + /clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -1694,7 +1702,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dev: true /culori@3.3.0: resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} @@ -3457,6 +3464,22 @@ packages: hasBin: true dev: true + /jotai@2.6.0(@types/react@18.2.45)(react@18.2.0): + resolution: {integrity: sha512-Vt6hsc04Km4j03l+Ax+Sc+FVft5cRJhqgxt6GTz6GM2eM3DyX3CdBdzcG0z2FrlZToL1/0OAkqDghIyARWnSuQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.45 + react: 18.2.0 + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} diff --git a/src/app/components/Dropdown.tsx b/src/app/components/Dropdown.tsx new file mode 100644 index 0000000..a38f86a --- /dev/null +++ b/src/app/components/Dropdown.tsx @@ -0,0 +1,26 @@ +// 'use client'; +import { BsFillCaretDownFill } from 'react-icons/bs'; + +interface IDropdown { + value: string; + items: string[]; + action: () => void; +} + +export const Dropdown = ({ value, items, action }: IDropdown) => ( +
+
+ {value} +
+ +
+); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c1033c9..11c1323 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -17,7 +17,12 @@ export default function RootLayout({ }) { return ( - {children} + +
+ Slick Telemetry +
+ {children} + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 0d7c36c..d036e08 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,7 +4,7 @@ export default function Home() {
-

Hello there

+

Hello

We are Slick Telemetry, like-minded individuals and fans of Formula 1. We are currently building an analysis platform for F1 diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx new file mode 100644 index 0000000..f1b134e --- /dev/null +++ b/src/app/results/RaceResults.tsx @@ -0,0 +1,100 @@ +import Image from 'next/image'; + +const TopThreeDummy = [ + { + name: 'Driver 1', + team: 'Team 1', + time: '1:23.456', + }, + { + name: 'Driver 2', + team: 'Team 3', + time: '1:24.456', + }, + { + name: 'Driver 3', + team: 'Team 2', + time: '1:25.456', + }, +]; + +const TopThree = () => ( +

+ {TopThreeDummy.map((driver) => ( +
+
+

{driver.name}

+

{driver.team}

+
+

{driver.time}

+
+ ))} +
+); + +const ResultCard = () => ( +
+
+
+ Shoes +
+
+

Name of Grand Prix

+
+
+ +
+

+ Location +
+ Time - Event Local & Client Local +

+ + + +
+ +
+
+
+); + +const WinterTesting = () => ( +
+
+ Shoes +
+
+
+

Winter Testing

+

Sakhir, Bahrain

+
+ + Testing Results + +
+
+); + +export const RaceResults = () => ( + <> + +
+ {/* 10 Placeholder Cards */} + {Array.from(Array(10).keys()).map((item) => ( + + ))} +
+ +); diff --git a/src/app/results/layout.tsx b/src/app/results/layout.tsx new file mode 100644 index 0000000..d8823e6 --- /dev/null +++ b/src/app/results/layout.tsx @@ -0,0 +1,51 @@ +'use client'; + +import { f1Seasons } from './utils'; +import { Dropdown } from '../components/Dropdown'; + +// Default Next Layout +export default function ResultsLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + + {children} + + ); +} + +const ResultsNav = () => { + const seasons = f1Seasons(); + const dummyRaces = [ + 'All Races', + 'Bahrain', + 'Mexico', + 'Monaco', + 'Imola', + 'Spain', + ]; + const dummyDrivers = [ + 'All Drivers', + 'Drive 1', + 'Drive 2', + 'Drive 3', + 'Drive 4', + 'Drive 5', + ]; + + return ( +
+

Results:

+ {}} /> + {}} /> + {}} + /> +
+ ); +}; diff --git a/src/app/results/page.tsx b/src/app/results/page.tsx new file mode 100644 index 0000000..7a561a8 --- /dev/null +++ b/src/app/results/page.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { clsx } from 'clsx'; +import { atom, useAtom } from 'jotai'; + +import { RaceResults } from './RaceResults'; + +const tabHeaders = [ + 'Race Results', + 'Drivers Championship', + 'Constructors Championship', +]; +const tabs = [ + , +
+ Tab 2 +
, +
+ Tab 3 +
, +]; +const tabView = atom(0); +export default function Page() { + // Testing Jotai & Atoms + const [tabIndex, setTabIndex] = useAtom(tabView); + + // Active tab has matching tabIndex + const TabButtons = tabHeaders.map((header, i) => ( + setTabIndex(i)} + > + {header} + + )); + + // Hide containers not matching tabIndex + const TabContainers = tabs.map((tab, i) => ( +
+ {tab} +
+ )); + + return ( +
+
+
+ {TabButtons} +
+ {TabContainers} +
+
+ ); +} diff --git a/src/app/results/utils.tsx b/src/app/results/utils.tsx new file mode 100644 index 0000000..faf70ea --- /dev/null +++ b/src/app/results/utils.tsx @@ -0,0 +1,28 @@ +/** + * @description + * Get all possible seasons/years with results + * @return {*} {string[]} + */ +export const f1Seasons = (): string[] => { + // Discuss : Bump to fetch call to get data + // ! Alter to be dynamic + const testingDate = new Date('02/22/2024'); + + const currDate = new Date(); + let currYear = currDate.getFullYear(); + + // Compare curr date to testing date (Feb 22 2024) + // If same year as testing and before testing date + // Get previous year + if ( + testingDate.getFullYear() === currYear && + currDate.getTime() < testingDate.getTime() + ) { + currYear -= 1; + } + + // Fill array with values between range + return Array.from({ length: currYear - 1950 + 1 }, (_v, index) => + (currYear - index).toString(), + ); +}; diff --git a/src/atoms/seasons.tsx b/src/atoms/seasons.tsx new file mode 100644 index 0000000..a4a0670 --- /dev/null +++ b/src/atoms/seasons.tsx @@ -0,0 +1,33 @@ +// import { atom, useAtom } from 'jotai' + +// // season +// // export const seasonAtom = atom('2023') + +// const f1Years = (): string[] => { +// // TODO : Bump to fetch call to get data + +// // ! Alter to be dynamic +// const testingDate = new Date("02/22/2024") + +// const currDate = new Date(); +// let currYear = currDate.getFullYear(); + +// // Compare to testing date (Feb 22 2024) +// // If it's the same year and before testing date +// if (testingDate.getFullYear() === currYear && currDate.getTime() < testingDate.getTime()) { +// currYear -= 1; +// } + +// const years = []; +// for (var i = currYear; i >= 1950; i--) { +// years.push(i.toString()); +// } +// return years; +// } + +// const createSeasonAtoms = () => { +// const seasons = atom(f1Years()) +// const currSeason = atom((get) => get(seasons)[0]) +// const setSeason = atom(null, (get, set, update) => set(season, (c) => c + 1)) +// return [valueAtom, setAtom] +// } From c61e8f72d6f6bf49d900a38f661119be8c8475c5 Mon Sep 17 00:00:00 2001 From: Pratik Borole Date: Fri, 29 Dec 2023 01:41:12 +0530 Subject: [PATCH 02/13] chore: updated dependencies --- .github/workflows/lint.yml | 5 +- .tool-versions | 1 + package.json | 4 +- pnpm-lock.yaml | 266 +++++++++++++++++++++++-------------- 4 files changed, 171 insertions(+), 105 deletions(-) create mode 100644 .tool-versions diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d2e1a79..12350af 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,5 @@ name: Code Check + on: push: branches: ['main'] @@ -11,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - name: ⬇️ Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: ⎔ Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 21 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..6b76adb --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 21.5.0 diff --git a/package.json b/package.json index 5a85e53..9db0880 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "typecheck": "tsc --pretty --noEmit --incremental false" }, "dependencies": { - "clsx": "^2.0.0", - "jotai": "^2.6.0", + "clsx": "latest", + "jotai": "latest", "next": "latest", "react": "latest", "react-dom": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 893afd3..5c83cd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: clsx: - specifier: ^2.0.0 + specifier: latest version: 2.0.0 jotai: - specifier: ^2.6.0 - version: 2.6.0(@types/react@18.2.45)(react@18.2.0) + specifier: latest + version: 2.6.1(@types/react@18.2.46)(react@18.2.0) next: specifier: latest version: 14.0.4(react-dom@18.2.0)(react@18.2.0) @@ -39,22 +39,22 @@ devDependencies: version: 20.10.5 '@types/react': specifier: latest - version: 18.2.45 + version: 18.2.46 '@types/react-dom': specifier: latest version: 18.2.18 '@typescript-eslint/eslint-plugin': specifier: latest - version: 6.15.0(@typescript-eslint/parser@6.15.0)(eslint@8.56.0)(typescript@5.3.3) + version: 6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': specifier: latest - version: 6.15.0(eslint@8.56.0)(typescript@5.3.3) + version: 6.16.0(eslint@8.56.0)(typescript@5.3.3) autoprefixer: specifier: latest version: 10.4.16(postcss@8.4.32) daisyui: specifier: latest - version: 4.4.20(postcss@8.4.32) + version: 4.4.24(postcss@8.4.32) eslint: specifier: latest version: 8.56.0 @@ -69,7 +69,7 @@ devDependencies: version: 10.0.0(eslint@8.56.0) eslint-plugin-unused-imports: specifier: latest - version: 3.0.0(@typescript-eslint/eslint-plugin@6.15.0)(eslint@8.56.0) + version: 3.0.0(@typescript-eslint/eslint-plugin@6.16.0)(eslint@8.56.0) husky: specifier: latest version: 8.0.3 @@ -87,7 +87,7 @@ devDependencies: version: 3.1.1 prettier-plugin-tailwindcss: specifier: latest - version: 0.5.9(prettier@3.1.1) + version: 0.5.10(prettier@3.1.1) release-it: specifier: latest version: 17.0.1(typescript@5.3.3) @@ -373,6 +373,18 @@ packages: resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} dev: true + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -619,6 +631,13 @@ packages: '@octokit/openapi-types': 19.1.0 dev: true + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + /@pnpm/config.env-replace@1.1.0: resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -721,11 +740,11 @@ packages: /@types/react-dom@18.2.18: resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} dependencies: - '@types/react': 18.2.45 + '@types/react': 18.2.46 dev: true - /@types/react@18.2.45: - resolution: {integrity: sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==} + /@types/react@18.2.46: + resolution: {integrity: sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 @@ -738,8 +757,8 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true - /@typescript-eslint/eslint-plugin@6.15.0(@typescript-eslint/parser@6.15.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==} + /@typescript-eslint/eslint-plugin@6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -750,11 +769,11 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.15.0 - '@typescript-eslint/type-utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.15.0 + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/type-utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 debug: 4.3.4 eslint: 8.56.0 graphemer: 1.4.0 @@ -767,8 +786,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==} + /@typescript-eslint/parser@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -777,10 +796,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.15.0 - '@typescript-eslint/types': 6.15.0 - '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.15.0 + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 debug: 4.3.4 eslint: 8.56.0 typescript: 5.3.3 @@ -788,16 +807,16 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager@6.15.0: - resolution: {integrity: sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==} + /@typescript-eslint/scope-manager@6.16.0: + resolution: {integrity: sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.15.0 - '@typescript-eslint/visitor-keys': 6.15.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 dev: true - /@typescript-eslint/type-utils@6.15.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==} + /@typescript-eslint/type-utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -806,8 +825,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) debug: 4.3.4 eslint: 8.56.0 ts-api-utils: 1.0.3(typescript@5.3.3) @@ -816,13 +835,13 @@ packages: - supports-color dev: true - /@typescript-eslint/types@6.15.0: - resolution: {integrity: sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==} + /@typescript-eslint/types@6.16.0: + resolution: {integrity: sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.15.0(typescript@5.3.3): - resolution: {integrity: sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==} + /@typescript-eslint/typescript-estree@6.16.0(typescript@5.3.3): + resolution: {integrity: sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -830,11 +849,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.15.0 - '@typescript-eslint/visitor-keys': 6.15.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 + minimatch: 9.0.3 semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.3.3) typescript: 5.3.3 @@ -842,8 +862,8 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.15.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==} + /@typescript-eslint/utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -851,9 +871,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.15.0 - '@typescript-eslint/types': 6.15.0 - '@typescript-eslint/typescript-estree': 6.15.0(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) eslint: 8.56.0 semver: 7.5.4 transitivePeerDependencies: @@ -861,11 +881,11 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys@6.15.0: - resolution: {integrity: sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==} + /@typescript-eslint/visitor-keys@6.16.0: + resolution: {integrity: sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.15.0 + '@typescript-eslint/types': 6.16.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1129,7 +1149,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.22.2 - caniuse-lite: 1.0.30001570 + caniuse-lite: 1.0.30001572 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -1224,6 +1244,12 @@ packages: concat-map: 0.0.1 dev: true + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -1236,8 +1262,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001570 - electron-to-chromium: 1.4.615 + caniuse-lite: 1.0.30001572 + electron-to-chromium: 1.4.616 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true @@ -1328,8 +1354,8 @@ packages: engines: {node: '>=14.16'} dev: true - /caniuse-lite@1.0.30001570: - resolution: {integrity: sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==} + /caniuse-lite@1.0.30001572: + resolution: {integrity: sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==} /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1708,8 +1734,8 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /daisyui@4.4.20(postcss@8.4.32): - resolution: {integrity: sha512-AR2fuFVVLHVTdbkV+XWAqjtymEoxXksrsEMkdzPQo2wANtWjSXuODUzePNade64gJ0Y2CdQtiQkaZI7fWcp13g==} + /daisyui@4.4.24(postcss@8.4.32): + resolution: {integrity: sha512-u/B3484J08V7N0rIYymnC+SyxOjlYQL+2vyhHWV+/KC+VaUcbEF2Z3H06eCPgdTiZ0J+ml44aH7wBhIymPFQ+g==} engines: {node: '>=16.9.0'} dependencies: css-selector-tokenizer: 0.8.0 @@ -1913,8 +1939,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.615: - resolution: {integrity: sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng==} + /electron-to-chromium@1.4.616: + resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} dev: true /emoji-regex@10.3.0: @@ -2097,11 +2123,11 @@ packages: dependencies: '@next/eslint-plugin-next': 14.0.4 '@rushstack/eslint-patch': 1.6.1 - '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) eslint-plugin-react: 7.33.2(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) @@ -2130,7 +2156,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -2140,8 +2166,8 @@ packages: debug: 4.3.4 enhanced-resolve: 5.15.0 eslint: 8.56.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 is-core-module: 2.13.1 @@ -2153,7 +2179,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -2174,16 +2200,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) debug: 3.2.7 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -2193,7 +2219,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.15.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -2202,7 +2228,7 @@ packages: doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -2285,7 +2311,7 @@ packages: eslint: 8.56.0 dev: true - /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.15.0)(eslint@8.56.0): + /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.16.0)(eslint@8.56.0): resolution: {integrity: sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2295,7 +2321,7 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.15.0(@typescript-eslint/parser@6.15.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/eslint-plugin': 6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 eslint-rule-composer: 0.3.0 dev: true @@ -2432,7 +2458,7 @@ packages: human-signals: 4.3.1 is-stream: 3.0.0 merge-stream: 2.0.0 - npm-run-path: 5.1.0 + npm-run-path: 5.2.0 onetime: 6.0.0 signal-exit: 3.0.7 strip-final-newline: 3.0.0 @@ -2447,7 +2473,7 @@ packages: human-signals: 5.0.0 is-stream: 3.0.0 merge-stream: 2.0.0 - npm-run-path: 5.1.0 + npm-run-path: 5.2.0 onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 @@ -2568,6 +2594,14 @@ packages: is-callable: 1.2.7 dev: true + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + /form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -2748,15 +2782,16 @@ packages: /glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - /glob@7.1.6: - resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 dev: true /glob@7.1.7: @@ -3459,13 +3494,22 @@ packages: set-function-name: 2.0.1 dev: true + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true dev: true - /jotai@2.6.0(@types/react@18.2.45)(react@18.2.0): - resolution: {integrity: sha512-Vt6hsc04Km4j03l+Ax+Sc+FVft5cRJhqgxt6GTz6GM2eM3DyX3CdBdzcG0z2FrlZToL1/0OAkqDghIyARWnSuQ==} + /jotai@2.6.1(@types/react@18.2.46)(react@18.2.0): + resolution: {integrity: sha512-GLQtAnA9iEKRMXnyCjf1azIxfQi5JausX2EI5qSlb59j4i73ZEyV/EXPDEAQj4uQNZYEefi3degv/Pw3+L/Dtg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=17.0.0' @@ -3476,7 +3520,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.45 + '@types/react': 18.2.46 react: 18.2.0 dev: false @@ -3875,6 +3919,13 @@ packages: brace-expansion: 1.1.11 dev: true + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -3888,6 +3939,11 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -3966,7 +4022,7 @@ packages: '@next/env': 14.0.4 '@swc/helpers': 0.5.2 busboy: 1.6.0 - caniuse-lite: 1.0.30001570 + caniuse-lite: 1.0.30001572 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.2.0 @@ -4056,8 +4112,8 @@ packages: path-key: 3.1.1 dev: true - /npm-run-path@5.1.0: - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + /npm-run-path@5.2.0: + resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: path-key: 4.0.0 @@ -4376,6 +4432,14 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.1.0 + minipass: 7.0.4 + dev: true + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4456,11 +4520,11 @@ packages: postcss: ^8.2.14 dependencies: postcss: 8.4.32 - postcss-selector-parser: 6.0.13 + postcss-selector-parser: 6.0.14 dev: true - /postcss-selector-parser@6.0.13: - resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + /postcss-selector-parser@6.0.14: + resolution: {integrity: sha512-65xXYsT40i9GyWzlHQ5ShZoK7JZdySeOozi/tz2EezDo6c04q6+ckYMeoY7idaie1qp2dT5KoYQ2yky6JuoHnA==} engines: {node: '>=4'} dependencies: cssesc: 3.0.0 @@ -4493,8 +4557,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.5.9(prettier@3.1.1): - resolution: {integrity: sha512-9x3t1s2Cjbut2QiP+O0mDqV3gLXTe2CgRlQDgucopVkUdw26sQi53p/q4qvGxMLBDfk/dcTV57Aa/zYwz9l8Ew==} + /prettier-plugin-tailwindcss@0.5.10(prettier@3.1.1): + resolution: {integrity: sha512-9UGSejqFxGG6brYjFfTYlJ8zs4L/lvZg1AngFfaC5Fs1otSskASv5IWKmjPu5MlABQUtTKtMArKyYr/hWpXSUg==} engines: {node: '>=14.21.3'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -4674,7 +4738,7 @@ packages: dependencies: find-up: 6.3.0 read-pkg: 8.1.0 - type-fest: 4.8.3 + type-fest: 4.9.0 dev: true /read-pkg-up@7.0.1: @@ -4703,7 +4767,7 @@ packages: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.0 parse-json: 7.1.1 - type-fest: 4.8.3 + type-fest: 4.9.0 dev: true /readable-stream@3.6.2: @@ -5306,14 +5370,14 @@ packages: client-only: 0.0.1 react: 18.2.0 - /sucrase@3.34.0: - resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} - engines: {node: '>=8'} + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} hasBin: true dependencies: '@jridgewell/gen-mapping': 0.3.3 commander: 4.1.1 - glob: 7.1.6 + glob: 10.3.10 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.6 @@ -5363,9 +5427,9 @@ packages: postcss-js: 4.0.1(postcss@8.4.32) postcss-load-config: 4.0.2(postcss@8.4.32) postcss-nested: 6.0.1(postcss@8.4.32) - postcss-selector-parser: 6.0.13 + postcss-selector-parser: 6.0.14 resolve: 1.22.8 - sucrase: 3.34.0 + sucrase: 3.35.0 transitivePeerDependencies: - ts-node dev: true @@ -5503,8 +5567,8 @@ packages: engines: {node: '>=14.16'} dev: true - /type-fest@4.8.3: - resolution: {integrity: sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==} + /type-fest@4.9.0: + resolution: {integrity: sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==} engines: {node: '>=16'} dev: true From b0d4be345d3ef4967f90574a4259cb474e67fc05 Mon Sep 17 00:00:00 2001 From: Pratik Borole Date: Fri, 29 Dec 2023 01:43:22 +0530 Subject: [PATCH 03/13] ci: updated workflows to include `dev` branch --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/lint.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index aff0e66..9017151 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,9 +13,9 @@ name: 'CodeQL' on: push: - branches: ['main'] + branches: ['main', 'dev'] pull_request: - branches: ['main'] + branches: ['main', 'dev'] schedule: - cron: '26 3 * * 6' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 12350af..dac695a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,9 @@ name: Code Check on: push: - branches: ['main'] + branches: ['main', 'dev'] pull_request: - branches: ['main'] + branches: ['main', 'dev'] jobs: lint: From 43e519cde54d3d7437968c8a7408fca597a6323c Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 29 Dec 2023 20:04:22 -0500 Subject: [PATCH 04/13] chore(results): placeholder tables for Drivers and Constructors standings Introduce faker lib for fake placeholder within new Table component. Additionally, appended some additional styling --- package.json | 1 + pnpm-lock.yaml | 8 +++++ src/app/components/Table.tsx | 64 +++++++++++++++++++++++++++++++++ src/app/layout.tsx | 4 ++- src/app/results/RaceResults.tsx | 24 +++++++++---- src/app/results/layout.tsx | 2 +- src/app/results/page.tsx | 45 +++++++++++++++++++---- 7 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 src/app/components/Table.tsx diff --git a/package.json b/package.json index 9db0880..d3f4c91 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "devDependencies": { "@commitlint/cli": "latest", "@commitlint/config-conventional": "latest", + "@faker-js/faker": "^8.3.1", "@release-it/conventional-changelog": "latest", "@types/node": "latest", "@types/react": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c83cd9..2b25e6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,6 +31,9 @@ devDependencies: '@commitlint/config-conventional': specifier: latest version: 18.4.3 + '@faker-js/faker': + specifier: ^8.3.1 + version: 8.3.1 '@release-it/conventional-changelog': specifier: latest version: 8.0.1(release-it@17.0.1) @@ -344,6 +347,11 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@faker-js/faker@8.3.1: + resolution: {integrity: sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + dev: true + /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} diff --git a/src/app/components/Table.tsx b/src/app/components/Table.tsx new file mode 100644 index 0000000..ebaedd9 --- /dev/null +++ b/src/app/components/Table.tsx @@ -0,0 +1,64 @@ +'use client'; + +import { Fragment } from 'react'; + +interface ITable { + title?: string; + headings: string[]; + data: { [key: string]: React.ReactNode }[]; +} + +export const Table = ({ title, headings, data }: ITable) => { + if (data.length <= 0 && headings.length <= 0) return; + + const Title = title &&

{title}

; + + return ( + <> + {Title} +
+ + {/* head */} + + + {headings.map((header) => ( + + ))} + + + + {/* body */} + + {data.length > 0 && + data.map((row, i) => ( + + + {headings.map( + (key) => + row && ( + + ), + )} + + + + ))} + +
+ {header.replace('_', ' ')} + {/* Placeholder for button */}
+ {row[key]} + + +
+
+ + ); +}; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 11c1323..2b897e0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -19,7 +19,9 @@ export default function RootLayout({ {children} diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx index f1b134e..c16d41b 100644 --- a/src/app/results/RaceResults.tsx +++ b/src/app/results/RaceResults.tsx @@ -23,7 +23,7 @@ const TopThree = () => ( {TopThreeDummy.map((driver) => (

{driver.name}

@@ -37,11 +37,16 @@ const TopThree = () => ( const ResultCard = () => (
-
+
+ 'https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg' + } + src='/shoe.jpg' alt='Shoes' />
@@ -67,15 +72,20 @@ const ResultCard = () => ( ); const WinterTesting = () => ( -
+
+ 'https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg' + } + src='/shoe.jpg' alt='Shoes' />
-
+

Winter Testing

Sakhir, Bahrain

@@ -90,7 +100,7 @@ const WinterTesting = () => ( export const RaceResults = () => ( <> -
+
{/* 10 Placeholder Cards */} {Array.from(Array(10).keys()).map((item) => ( diff --git a/src/app/results/layout.tsx b/src/app/results/layout.tsx index d8823e6..96dba2d 100644 --- a/src/app/results/layout.tsx +++ b/src/app/results/layout.tsx @@ -37,7 +37,7 @@ const ResultsNav = () => { ]; return ( -
+

Results:

{}} /> {}} /> diff --git a/src/app/results/page.tsx b/src/app/results/page.tsx index 7a561a8..d9f7bbf 100644 --- a/src/app/results/page.tsx +++ b/src/app/results/page.tsx @@ -1,22 +1,55 @@ 'use client'; +import { faker } from '@faker-js/faker'; import { clsx } from 'clsx'; import { atom, useAtom } from 'jotai'; import { RaceResults } from './RaceResults'; +import { Table } from '../components/Table'; -const tabHeaders = [ - 'Race Results', - 'Drivers Championship', - 'Constructors Championship', +const tabHeaders = ['Races', 'Drivers', 'Constructors']; + +const Table1Headings = [ + faker.database.column(), + faker.database.column(), + faker.database.column(), + faker.database.column(), +]; +const Table2Headings = [ + faker.database.column(), + faker.database.column(), + faker.database.column(), + faker.database.column(), ]; + const tabs = [ ,
- Tab 2 + ({ ...obj, [value]: faker.lorem.word() }), + {}, + ), + ), + ]} + /> ,
- Tab 3 +
({ ...obj, [value]: faker.lorem.word() }), + {}, + ), + ), + ]} + /> + , , ]; const tabView = atom(0); From f6a0bd1f912ddd9a79ccfc21eaf1a883407a9fea Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 29 Dec 2023 20:13:40 -0500 Subject: [PATCH 05/13] fix: update faker package to latest --- package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d3f4c91..0595fa4 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@commitlint/cli": "latest", "@commitlint/config-conventional": "latest", - "@faker-js/faker": "^8.3.1", + "@faker-js/faker": "latest", "@release-it/conventional-changelog": "latest", "@types/node": "latest", "@types/react": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b25e6e..f1075f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ devDependencies: specifier: latest version: 18.4.3 '@faker-js/faker': - specifier: ^8.3.1 + specifier: latest version: 8.3.1 '@release-it/conventional-changelog': specifier: latest From 09ae54e4dce92cd0bcce1012de1194d8bb5b3278 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Sun, 31 Dec 2023 11:17:43 -0500 Subject: [PATCH 06/13] refactor(results): shape placeholder data to match mockup --- src/app/results/page.tsx | 88 ++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/src/app/results/page.tsx b/src/app/results/page.tsx index d9f7bbf..6185d5b 100644 --- a/src/app/results/page.tsx +++ b/src/app/results/page.tsx @@ -9,45 +9,79 @@ import { Table } from '../components/Table'; const tabHeaders = ['Races', 'Drivers', 'Constructors']; -const Table1Headings = [ - faker.database.column(), - faker.database.column(), - faker.database.column(), - faker.database.column(), +const DriverHeadings = [ + 'Position', + 'Driver', + 'Constructor', + 'Points', + // Race Starts + // Race Finishes + // Podiums ]; -const Table2Headings = [ - faker.database.column(), - faker.database.column(), - faker.database.column(), - faker.database.column(), +const ConstuctorHeadings = [ + 'Position', + 'Constructor', + 'Points', + 'Drivers', + // Best Result + // DNFs ]; +const formatDriver = (key: string, i: number) => { + switch (key) { + case 'Position': + return i + 1; + case 'Driver': + case 'Constructor': + return faker.lorem.word(); + case 'Points': + return faker.number.int(26); + } +}; +const formatConstructor = (key: string, i: number) => { + switch (key) { + case 'Position': + return i + 1; + case 'Constructor': + return faker.lorem.word(); + case 'Points': + return faker.number.int(51); + case 'Drivers': + return ( + <> + {faker.lorem.word()} - {faker.number.int(26)}, {faker.lorem.word()} -{' '} + {faker.number.int(26)} + + ); + } +}; + const tabs = [ , +
({ ...obj, [value]: faker.lorem.word() }), - {}, - ), + headings={DriverHeadings} + data={Array.from({ length: 20 }, (_v, index) => + DriverHeadings.reduce( + (obj, value) => ({ ...obj, [value]: formatDriver(value, index) }), + {}, ), - ]} + )} /> ,
({ ...obj, [value]: faker.lorem.word() }), - {}, - ), + headings={ConstuctorHeadings} + data={Array.from({ length: 20 }, (_v, index) => + ConstuctorHeadings.reduce( + (obj, value) => ({ + ...obj, + [value]: formatConstructor(value, index), + }), + {}, ), - ]} + )} /> , , @@ -83,7 +117,7 @@ export default function Page() { return (
-
+
{TabButtons}
From fed607d11a286644b3ab137a748e0742d6ab3fa2 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Sun, 31 Dec 2023 11:22:55 -0500 Subject: [PATCH 07/13] ci: updated workflows to include staging branch --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/lint.yml | 4 ++-- README.md | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9017151..158da56 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,9 +13,9 @@ name: 'CodeQL' on: push: - branches: ['main', 'dev'] + branches: ['main', 'staging', 'dev'] pull_request: - branches: ['main', 'dev'] + branches: ['main', 'staging', 'dev'] schedule: - cron: '26 3 * * 6' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dac695a..a0a6b9c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,9 @@ name: Code Check on: push: - branches: ['main', 'dev'] + branches: ['main', 'staging', 'dev'] pull_request: - branches: ['main', 'dev'] + branches: ['main', 'staging', 'dev'] jobs: lint: diff --git a/README.md b/README.md index 15c9061..ecf6f98 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ This project is using [conventional commits](https://www.conventionalcommits.org - **Branches**: - `main` is the production mainline. + - `staging` is the staging line. - `dev` is the development line. - **PR merge strategy on Github** From 36bf4d463535985731355b63b495025d9fa1ae43 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Thu, 4 Jan 2024 22:13:38 -0500 Subject: [PATCH 08/13] fix: using next template file structure added function to fetch server data or dummy data --- package.json | 1 + pnpm-lock.yaml | 11 +++ src/app/layout.tsx | 33 +++++-- src/app/lib/placerholder-results.tsx | 71 ++++++++++++++ src/app/lib/utils.tsx | 69 ++++++++++++++ src/app/results/RaceResults.tsx | 65 ++++++++++--- src/app/results/layout.tsx | 42 ++++---- src/app/results/page.tsx | 121 +++--------------------- src/app/results/utils.tsx | 28 ------ src/app/{components => ui}/Dropdown.tsx | 0 src/app/ui/Nav.tsx | 67 +++++++++++++ src/app/{components => ui}/Table.tsx | 0 src/app/ui/Tabs.tsx | 47 +++++++++ tailwind.config.ts | 8 ++ 14 files changed, 387 insertions(+), 176 deletions(-) create mode 100644 src/app/lib/placerholder-results.tsx create mode 100644 src/app/lib/utils.tsx delete mode 100644 src/app/results/utils.tsx rename src/app/{components => ui}/Dropdown.tsx (100%) create mode 100644 src/app/ui/Nav.tsx rename src/app/{components => ui}/Table.tsx (100%) create mode 100644 src/app/ui/Tabs.tsx diff --git a/package.json b/package.json index 0595fa4..52e8d51 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "dependencies": { "clsx": "latest", "jotai": "latest", + "jotai-effect": "^0.2.3", "next": "latest", "react": "latest", "react-dom": "latest", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1075f5..2fca6aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: jotai: specifier: latest version: 2.6.1(@types/react@18.2.46)(react@18.2.0) + jotai-effect: + specifier: ^0.2.3 + version: 0.2.3(jotai@2.6.1) next: specifier: latest version: 14.0.4(react-dom@18.2.0)(react@18.2.0) @@ -3516,6 +3519,14 @@ packages: hasBin: true dev: true + /jotai-effect@0.2.3(jotai@2.6.1): + resolution: {integrity: sha512-Ecs40W4Y+SI1EqbvPdoLDvbyic5eMyD5ifJIQrrr+hWz1GEjmijVWyKw6TzmYzO2+++CkYPF+fYsWzsWsR0AAQ==} + peerDependencies: + jotai: '>=2.4.3' + dependencies: + jotai: 2.6.1(@types/react@18.2.46)(react@18.2.0) + dev: false + /jotai@2.6.1(@types/react@18.2.46)(react@18.2.0): resolution: {integrity: sha512-GLQtAnA9iEKRMXnyCjf1azIxfQi5JausX2EI5qSlb59j4i73ZEyV/EXPDEAQj4uQNZYEefi3degv/Pw3+L/Dtg==} engines: {node: '>=12.20.0'} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2b897e0..7cffb11 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,8 +1,11 @@ +import clsx from 'clsx'; import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import './globals.css'; +import { Nav } from './ui/Nav'; + const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { @@ -10,19 +13,35 @@ export const metadata: Metadata = { description: 'Formula 1 Data Analysis', }; -export default function RootLayout({ +const checkServer = async () => { + // Check if server exists + // Cannot use jotai on server component, aka RootLayout + const data = fetch('http://0.0.0.0:80', { cache: 'no-store' }).then( + (res) => { + if (!res.ok) { + return null; + } + return true; + }, + () => { + return null; + }, + ); + + return data; +}; + +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + const server = await checkServer(); + return ( - -
- -
+ +
- DriverHeadings.reduce( - (obj, value) => ({ ...obj, [value]: formatDriver(value, index) }), - {}, - ), - )} - /> +
,
-
- ConstuctorHeadings.reduce( - (obj, value) => ({ - ...obj, - [value]: formatConstructor(value, index), - }), - {}, - ), - )} - /> - , +
, , ]; -const tabView = atom(0); -export default function Page() { - // Testing Jotai & Atoms - const [tabIndex, setTabIndex] = useAtom(tabView); - - // Active tab has matching tabIndex - const TabButtons = tabHeaders.map((header, i) => ( - setTabIndex(i)} - > - {header} - - )); - - // Hide containers not matching tabIndex - const TabContainers = tabs.map((tab, i) => ( -
- {tab} -
- )); +export default function Page() { return (
-
-
- {TabButtons} -
- {TabContainers} -
+
); } diff --git a/src/app/results/utils.tsx b/src/app/results/utils.tsx deleted file mode 100644 index faf70ea..0000000 --- a/src/app/results/utils.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @description - * Get all possible seasons/years with results - * @return {*} {string[]} - */ -export const f1Seasons = (): string[] => { - // Discuss : Bump to fetch call to get data - // ! Alter to be dynamic - const testingDate = new Date('02/22/2024'); - - const currDate = new Date(); - let currYear = currDate.getFullYear(); - - // Compare curr date to testing date (Feb 22 2024) - // If same year as testing and before testing date - // Get previous year - if ( - testingDate.getFullYear() === currYear && - currDate.getTime() < testingDate.getTime() - ) { - currYear -= 1; - } - - // Fill array with values between range - return Array.from({ length: currYear - 1950 + 1 }, (_v, index) => - (currYear - index).toString(), - ); -}; diff --git a/src/app/components/Dropdown.tsx b/src/app/ui/Dropdown.tsx similarity index 100% rename from src/app/components/Dropdown.tsx rename to src/app/ui/Dropdown.tsx diff --git a/src/app/ui/Nav.tsx b/src/app/ui/Nav.tsx new file mode 100644 index 0000000..ee4756f --- /dev/null +++ b/src/app/ui/Nav.tsx @@ -0,0 +1,67 @@ +export const Nav = () => ( +
+
+ +
+ +
+
+
+
+ + + +
+ +
+
+
+

+ 53 days until Winter Testing +

+
+
+
+
+); diff --git a/src/app/components/Table.tsx b/src/app/ui/Table.tsx similarity index 100% rename from src/app/components/Table.tsx rename to src/app/ui/Table.tsx diff --git a/src/app/ui/Tabs.tsx b/src/app/ui/Tabs.tsx new file mode 100644 index 0000000..0463850 --- /dev/null +++ b/src/app/ui/Tabs.tsx @@ -0,0 +1,47 @@ +import clsx from 'clsx'; +import { atom, useAtom } from 'jotai'; + +const tabView = atom(0); + +interface ITabs { + headers: string[]; + containers: React.ReactNode[]; +} + +export const Tabs = ({ headers, containers }: ITabs) => { + // Testing Jotai & Atoms + const [tabIndex, setTabIndex] = useAtom(tabView); + + // Active tab has matching tabIndex + const TabButtons = headers.map((header, i) => ( + setTabIndex(i)} + > + {header} + + )); + + // Hide containers not matching tabIndex + const TabContainers = containers.map((tab, i) => ( +
+ {tab} +
+ )); + + return ( +
+
+ {TabButtons} +
+ {TabContainers} +
+ ); +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index 3dd0ac8..5cdef42 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -16,7 +16,15 @@ const config: Config = { 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, + keyframes: { + shimmer: { + '100%': { + transform: 'translateX(100%)', + }, + }, + }, }, + plugins: [require('daisyui')], }; export default config; From a22a8b54cd13a79f33ba0d77c05991422a4a0fe2 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 8 Jan 2024 10:24:13 -0500 Subject: [PATCH 09/13] chore: compose atoms to handle results filters --- src/atoms/results.tsx | 89 +++++++++++++++++++++++++++++++++++++++++++ src/atoms/seasons.tsx | 33 ---------------- 2 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 src/atoms/results.tsx delete mode 100644 src/atoms/seasons.tsx diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx new file mode 100644 index 0000000..2288ffe --- /dev/null +++ b/src/atoms/results.tsx @@ -0,0 +1,89 @@ +import { atom } from 'jotai'; +import { atomEffect } from 'jotai-effect'; + +import { fetchAPI, ISchedule } from '../app/lib/utils'; + +export const raceAtom = atom('All Races'); +export const racesAtom = atom([]); +export const seasonAtom = atom('2023'); +export const seasonsAtom = atom([]); +export const driverAtom = atom('All Drivers'); +export const driversAtom = atom([]); +export const sessionAtom = atom('Race'); +export const sessionsAtom = atom([]); +export const telemetryDisableAtom = atom(true); +export const resultUrlAtom = atom('/results'); + +export const racesDropdownAtom = atom((get) => + get(racesAtom).map((race) => race.EventName), +); + +export const fetchSeasons = atomEffect((get, set) => { + fetchAPI('seasons').then((data) => set(seasonsAtom, data)); +}); + +export const fetchRaces = atomEffect((get, set) => { + fetchAPI('schedule/' + get(seasonAtom)).then((data) => set(racesAtom, data)); +}); + +export const fetchDriver = atomEffect((get, set) => { + fetchAPI('drivers').then((data) => set(driversAtom, data)); +}); +export const fetchSessions = atomEffect((get, set) => { + fetchAPI('sessions').then((data) => set(sessionsAtom, data)); +}); + +export const handleSeasonChangeAtom = atom(null, (get, set, update: string) => { + set(seasonAtom, update); + set(raceAtom, 'All Races'); + set(driverAtom, 'All Drivers'); + set(sessionAtom, 'Race'); + set(resultUrlAtom, '/results/' + update); + + // Todo: Update RacesAtom +}); + +export const handleRaceChangeAtom = atom(null, (get, set, update: string) => { + set(raceAtom, update); + set(driverAtom, 'All Drivers'); + set(resultUrlAtom, '/results/' + get(seasonAtom) + '/' + update); + + // Todo: Update DriversAtom +}); + +export const handleDriverChangeAtom = atom(null, (get, set, update: string) => { + set(driverAtom, update); + set(sessionAtom, 'Race'); + set( + resultUrlAtom, + '/results/' + get(seasonAtom) + '/' + get(raceAtom) + '/' + update, + ); + + // Todo: Update SessionsAtom +}); + +export const handleSessionChangeAtom = atom( + null, + (get, set, update: string) => { + set(sessionAtom, update); + set( + resultUrlAtom, + '/results/' + + get(seasonAtom) + + '/' + + get(raceAtom) + + '/' + + get(driverAtom) + + '/' + + update, + ); + }, +); + +export const toggleTelemetryDisableAtom = atomEffect((get, set) => { + // Telemetry is disabled if no race and driver are selected + set( + telemetryDisableAtom, + get(raceAtom) === 'All Races' || get(driverAtom) === 'All Drivers', + ); +}); diff --git a/src/atoms/seasons.tsx b/src/atoms/seasons.tsx deleted file mode 100644 index a4a0670..0000000 --- a/src/atoms/seasons.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// import { atom, useAtom } from 'jotai' - -// // season -// // export const seasonAtom = atom('2023') - -// const f1Years = (): string[] => { -// // TODO : Bump to fetch call to get data - -// // ! Alter to be dynamic -// const testingDate = new Date("02/22/2024") - -// const currDate = new Date(); -// let currYear = currDate.getFullYear(); - -// // Compare to testing date (Feb 22 2024) -// // If it's the same year and before testing date -// if (testingDate.getFullYear() === currYear && currDate.getTime() < testingDate.getTime()) { -// currYear -= 1; -// } - -// const years = []; -// for (var i = currYear; i >= 1950; i--) { -// years.push(i.toString()); -// } -// return years; -// } - -// const createSeasonAtoms = () => { -// const seasons = atom(f1Years()) -// const currSeason = atom((get) => get(seasons)[0]) -// const setSeason = atom(null, (get, set, update) => set(season, (c) => c + 1)) -// return [valueAtom, setAtom] -// } From 68afa99b770df4ba8d0088c6505fd5b4c82a9cd1 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 8 Jan 2024 10:26:04 -0500 Subject: [PATCH 10/13] style: modify bg and remove shimmer keyframe from tailwind --- src/app/globals.css | 2 +- tailwind.config.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index fd81e88..6056993 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -19,7 +19,7 @@ body { color: rgb(var(--foreground-rgb)); background: linear-gradient( - to bottom, + to top, transparent, rgb(var(--background-end-rgb)) ) diff --git a/tailwind.config.ts b/tailwind.config.ts index 5cdef42..bd500b1 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -16,13 +16,6 @@ const config: Config = { 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, }, - keyframes: { - shimmer: { - '100%': { - transform: 'translateX(100%)', - }, - }, - }, }, plugins: [require('daisyui')], From 5cc02101c46fafe9bf2bf408c0143de911be40f8 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 8 Jan 2024 10:32:59 -0500 Subject: [PATCH 11/13] chore: build out landing page --- src/app/MainFilters.tsx | 117 +++++++++++++++++++++++++++++++++++++ src/app/lib/utils.tsx | 124 +++++++++++++++++++++++++++++++--------- src/app/page.tsx | 43 +++++++++----- 3 files changed, 244 insertions(+), 40 deletions(-) create mode 100644 src/app/MainFilters.tsx diff --git a/src/app/MainFilters.tsx b/src/app/MainFilters.tsx new file mode 100644 index 0000000..e8fde82 --- /dev/null +++ b/src/app/MainFilters.tsx @@ -0,0 +1,117 @@ +'use client'; + +import { useAtom } from 'jotai/react'; +import Link from 'next/link'; +import React from 'react'; + +import { Dropdown } from './ui/Dropdown'; +import { + driverAtom, + driversAtom, + fetchDriver, + fetchRaces, + fetchSeasons, + fetchSessions, + handleDriverChangeAtom, + handleRaceChangeAtom, + handleSeasonChangeAtom, + handleSessionChangeAtom, + raceAtom, + racesDropdownAtom, + resultUrlAtom, + seasonAtom, + seasonsAtom, + sessionAtom, + sessionsAtom, + telemetryDisableAtom, + toggleTelemetryDisableAtom, +} from '../atoms/results'; + +const SeasonDropdown = () => { + const [season] = useAtom(seasonAtom); + const [, handleSeasonChange] = useAtom(handleSeasonChangeAtom); + const [seasons] = useAtom(seasonsAtom); + + useAtom(fetchSeasons); + return ( + handleSeasonChange(val)} + /> + ); +}; + +const RaceDropdown = () => { + const [race] = useAtom(raceAtom); + const [, handleRaceChange] = useAtom(handleRaceChangeAtom); + const [races] = useAtom(racesDropdownAtom); + + useAtom(fetchRaces); + return ( + handleRaceChange(val)} + /> + ); +}; + +const DriverDropdown = () => { + const [driverName] = useAtom(driverAtom); + const [, handleDriverChange] = useAtom(handleDriverChangeAtom); + const [driverList] = useAtom(driversAtom); + useAtom(fetchDriver); + return ( + handleDriverChange(val)} + /> + ); +}; +const SessionDropdown = () => { + const [sessionName] = useAtom(sessionAtom); + const [, handleSessionChange] = useAtom(handleSessionChangeAtom); + const [sessionList] = useAtom(sessionsAtom); + useAtom(fetchSessions); + return ( + handleSessionChange(val)} + /> + ); +}; +export const MainFilters = () => { + useAtom(toggleTelemetryDisableAtom); + const [telemetryDisable] = useAtom(telemetryDisableAtom); + const [resultsUrl] = useAtom(resultUrlAtom); + + useAtom(fetchSeasons); + + return ( +
+ {/* */} +
+ + +
+ +
+ + in + +
+ +
+ + +
+
+ ); +}; diff --git a/src/app/lib/utils.tsx b/src/app/lib/utils.tsx index ca12649..1330a4a 100644 --- a/src/app/lib/utils.tsx +++ b/src/app/lib/utils.tsx @@ -1,3 +1,5 @@ +import { faker } from '@faker-js/faker'; + /** * @description * Get all possible seasons/years with results @@ -6,20 +8,20 @@ export const f1Seasons = (): string[] => { // Discuss : Bump to fetch call to get data // ! Alter to be dynamic - const testingDate = new Date('02/22/2024'); + // const testingDate = new Date('02/22/2024'); const currDate = new Date(); - let currYear = currDate.getFullYear(); + const currYear = currDate.getFullYear(); // Compare curr date to testing date (Feb 22 2024) // If same year as testing and before testing date // Get previous year - if ( - testingDate.getFullYear() === currYear && - currDate.getTime() < testingDate.getTime() - ) { - currYear -= 1; - } + // if ( + // testingDate.getFullYear() === currYear && + // currDate.getTime() < testingDate.getTime() + // ) { + // currYear -= 1; + // } // Fill array with values between range return Array.from({ length: currYear - 1950 + 1 }, (_v, index) => @@ -27,9 +29,66 @@ export const f1Seasons = (): string[] => { ); }; -const dataConfig: { [key: string]: string[] } = { +export interface ISchedule { + RoundNumber: number; + Country: string; + Location: string; + OfficialEventName: string; + EventDate: string; + EventName: string; + EventFormat: string; + Session1: string; + Session1Date: string; + Session1DateUtc: string; + Session2: string; + Session2Date: string; + Session2DateUtc: string; + Session3: string; + Session3Date: string; + Session3DateUtc: string; + Session4: string; + Session4Date: string; + Session4DateUtc: string; + Session5: string; + Session5Date: string; + Session5DateUtc: string; + F1ApiSupport: boolean; +} + +interface IDataConfigs { + seasons: string[]; + schedule: ISchedule[]; + drivers: string[]; + sessions: string[]; +} + +const dataConfig: IDataConfigs = { seasons: f1Seasons(), - races: ['All Races', 'Bahrain', 'Mexico', 'Monaco', 'Imola', 'Spain'], + schedule: Array.from(Array(3).keys()).map(() => ({ + RoundNumber: 0, + Country: faker.location.country(), + Location: faker.location.city(), + OfficialEventName: faker.word.words(5), + EventDate: faker.date.future({ years: 1 }).toString(), + EventName: faker.word.words(3), + EventFormat: 'string', + Session1: 'string', + Session1Date: faker.date.future({ years: 1 }).toString(), + Session1DateUtc: faker.date.future({ years: 1 }).toString(), + Session2: 'string', + Session2Date: faker.date.future({ years: 1 }).toString(), + Session2DateUtc: faker.date.future({ years: 1 }).toString(), + Session3: 'string', + Session3Date: faker.date.future({ years: 1 }).toString(), + Session3DateUtc: faker.date.future({ years: 1 }).toString(), + Session4: 'string', + Session4Date: faker.date.future({ years: 1 }).toString(), + Session4DateUtc: faker.date.future({ years: 1 }).toString(), + Session5: 'string', + Session5Date: faker.date.future({ years: 1 }).toString(), + Session5DateUtc: faker.date.future({ years: 1 }).toString(), + F1ApiSupport: true, + })), drivers: [ 'All Drivers', 'Drive 1', @@ -38,32 +97,43 @@ const dataConfig: { [key: string]: string[] } = { 'Drive 4', 'Drive 5', ], + sessions: ['Practice 1', 'Practice 2', 'Practice 3', 'Qualifying', 'Race'], }; -export const fetchAPI = (endpoint: string) => { +export const fetchAPI = async (endpoint: string) => { const server = document.body.classList.contains('server'); - const dummy: string[] | false = dataConfig[endpoint] || false; + const dummy: string[] | ISchedule[] | false = + dataConfig[ + endpoint.split('/')[0] as 'seasons' | 'schedule' | 'drivers' | 'sessions' + ] || false; if (!server) { return dummy; } else { // Fetch from server // TODO : update to axios - const data = fetch(`http://0.0.0.0:80/${endpoint}`).then( - (res) => { - if (!res.ok) { - // console.error(`Backend responded with ${res.status} error`); - return null; - } - return res.json(); - }, - () => { - // console.error('Could not reach backend', error); - return null; - }, - ); - // if error use dummy data + const data = await fetch(`http://0.0.0.0:80/${endpoint}`) + .then( + (res) => { + if (!res.ok) { + return dummy; + } + + return res.json(); + }, + () => { + // console.log('server error'); + + return dummy; + }, + ) + .then((data) => data) + .catch(() => { + // console.log('catch'); + return dummy; + }); - return data || dataConfig; + // console.log('data', data); + return data; } }; diff --git a/src/app/page.tsx b/src/app/page.tsx index d036e08..4d0a4ef 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,19 +1,36 @@ +import { MainFilters } from './MainFilters'; + export default function Home() { return (
-
-
-
-

Hello

-

- We are Slick Telemetry, like-minded individuals and fans of - Formula 1. We are currently building an analysis platform for F1 - Data Analysis! -

- -
-
-
+ + + {/* Suspense */} +
); } + +const Hero = () => ( +
+
+
+

Slick Telemetry

+ +

+ We are Slick Telemetry, like-minded individuals and fans of Formula 1. + We are currently building an analysis platform for F1 Data Analysis! +

+
+
+
+); + +const NextRace = () => { + return ( +
+

Winter Testing

+ Bahrain Feb 21, 2024 +
+ ); +}; From 39f317c8a9abe2f8b436daedc6fc12373e27526e Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 8 Jan 2024 10:37:20 -0500 Subject: [PATCH 12/13] chore: include main filters and available server data for results ui --- src/app/results/RaceResults.tsx | 205 ++++++++++++------------------ src/app/results/SeasonResults.tsx | 31 +++++ src/app/results/[season]/page.tsx | 13 ++ src/app/results/layout.tsx | 40 +----- src/app/results/page.tsx | 30 +---- 5 files changed, 132 insertions(+), 187 deletions(-) create mode 100644 src/app/results/SeasonResults.tsx create mode 100644 src/app/results/[season]/page.tsx diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx index 4b3423b..990afda 100644 --- a/src/app/results/RaceResults.tsx +++ b/src/app/results/RaceResults.tsx @@ -1,44 +1,61 @@ +import { useAtom } from 'jotai'; import Image from 'next/image'; +import { useMemo } from 'react'; -const TopThreeDummy = [ - { - name: 'Driver 1', - team: 'Team 1', - time: '1:23.456', - }, - { - name: 'Driver 2', - team: 'Team 3', - time: '1:24.456', - }, - { - name: 'Driver 3', - team: 'Team 2', - time: '1:25.456', - }, -]; +import { racesAtom } from '@/atoms/results'; -const TopThree = () => ( -
- {TopThreeDummy.map((driver) => ( -
-
-

{driver.name}

-

{driver.team}

+import { ISchedule } from '../lib/utils'; + +const ResultCard = ({ data }: { data: ISchedule }) => { + const eventDate = new Date(data.EventDate); + const eventPassed = new Date() > eventDate; + + return ( +
+
+
+ + 'https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg' + } + src='/shoe.jpg' + alt='Shoes' + /> +
+
+

+ {data.OfficialEventName.slice(0, -5)} +

-

{driver.time}

- ))} -
-); -const ResultCard = () => ( -
-
-
+
+

+ {data.Location}, {data.Country} +
+ {eventDate.toDateString()} +

+ + {eventPassed && ( +
+ +
+ )} +
+
+ ); +}; + +const WinterTesting = ({ data }: { data: ISchedule }) => { + const eventDate = new Date(data.EventDate); + const eventPassed = new Date() > eventDate; + + return ( +
+
( alt='Shoes' />
-
-

Name of Grand Prix

-
-
- -
-

- Location -
- Time - Event Local & Client Local -

- - - -
- -
-
-
-); - -const WinterTesting = () => ( -
-
- - 'https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg' - } - src='/shoe.jpg' - alt='Shoes' - /> -
-
-
-

Winter Testing

-

Sakhir, Bahrain

+
+
+

{data.OfficialEventName}

+

+ {data.Location}, {data.Country} +

+

{eventDate.toDateString()}

+
+ {eventPassed && ( + + Testing Results + + )}
- - Testing Results -
-
-); + ); +}; -// Loading animation -const shimmer = - 'before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent'; +export const RaceSchedule = () => { + const [races] = useAtom(racesAtom); -//
-//
-// -// 'https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg' -// } -// src='/shoe.jpg' -// alt='Shoes' -// /> -//
-//
-//
-//

Winter Testing

-//

Sakhir, Bahrain

-//
-// -// Testing Results -// -//
-//
-const WinterTestingSkeleton = () => ( -
-
-
-
-
-
-
-
-
-
-); + const winterTesting = useMemo( + () => races.find((race) => race.EventFormat === 'testing'), + [races], + ); + const mainEvents = useMemo( + () => races.filter((race) => race.EventFormat !== 'testing'), + [races], + ); -export const RaceResults = () => { return (
- - -
+ {/* If seasonAom === current/upcomming season, then add button to bring user to next event */} + {winterTesting && } +
{/* 10 Placeholder Cards */} - {Array.from(Array(10).keys()).map((item) => ( - + {mainEvents.map((race) => ( + ))}
diff --git a/src/app/results/SeasonResults.tsx b/src/app/results/SeasonResults.tsx new file mode 100644 index 0000000..c7291d2 --- /dev/null +++ b/src/app/results/SeasonResults.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { RaceSchedule } from './RaceResults'; +import { + constructorsData, + ConstuctorHeadings, + driverData, + DriverHeadings, +} from '../lib/placerholder-results'; +import { Table } from '../ui/Table'; +import { Tabs } from '../ui/Tabs'; + +const tabHeaders = ['Races', 'Drivers', 'Constructors']; +const tabs = [ + , + +
+
+ , +
+
, + , +]; + +export default function ResultsPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/results/[season]/page.tsx b/src/app/results/[season]/page.tsx new file mode 100644 index 0000000..623ac15 --- /dev/null +++ b/src/app/results/[season]/page.tsx @@ -0,0 +1,13 @@ +'use client'; + +import { useAtom } from 'jotai'; + +import ResultsPage from '../SeasonResults'; +import { handleSeasonChangeAtom } from '../../../atoms/results'; + +export default function Page({ params }: { params: { slug: string } }) { + const [, handleSeasonChange] = useAtom(handleSeasonChangeAtom); + handleSeasonChange(params.slug); + + return ; +} diff --git a/src/app/results/layout.tsx b/src/app/results/layout.tsx index b905a02..5070ae7 100644 --- a/src/app/results/layout.tsx +++ b/src/app/results/layout.tsx @@ -1,7 +1,6 @@ 'use client'; -import { f1Seasons } from '../lib/utils'; -import { Dropdown } from '../ui/Dropdown'; +import { MainFilters } from '../MainFilters'; // Default Next Layout export default function ResultsLayout({ @@ -11,41 +10,10 @@ export default function ResultsLayout({ }) { return ( <> - +
+ +
{children} ); } - -const seasons = f1Seasons(); -const dummyRaces = [ - 'All Races', - 'Bahrain', - 'Mexico', - 'Monaco', - 'Imola', - 'Spain', -]; -const dummyDrivers = [ - 'All Drivers', - 'Drive 1', - 'Drive 2', - 'Drive 3', - 'Drive 4', - 'Drive 5', -]; - -const ResultsNav = () => { - return ( -
-

Results:

- {}} /> - {}} /> - {}} - /> -
- ); -}; diff --git a/src/app/results/page.tsx b/src/app/results/page.tsx index ef5f2c4..daf84ed 100644 --- a/src/app/results/page.tsx +++ b/src/app/results/page.tsx @@ -1,31 +1,5 @@ -'use client'; - -import { RaceResults } from './RaceResults'; -import { - constructorsData, - ConstuctorHeadings, - driverData, - DriverHeadings, -} from '../lib/placerholder-results'; -import { Table } from '../ui/Table'; -import { Tabs } from '../ui/Tabs'; - -const tabHeaders = ['Races', 'Drivers', 'Constructors']; -const tabs = [ - , - -
-
- , -
-
, - , -]; +import ResultsPage from './SeasonResults'; export default function Page() { - return ( -
- -
- ); + return ; } From db7c249cac3aaf604d954b9c1be49b6415f6f238 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Mon, 8 Jan 2024 10:39:02 -0500 Subject: [PATCH 13/13] chore: further build out resuable components --- src/app/ui/Dropdown.tsx | 48 ++++++++++++------- src/app/ui/Nav.tsx | 100 ++++++++++++++++++++-------------------- src/app/ui/Tabs.tsx | 10 ++-- 3 files changed, 88 insertions(+), 70 deletions(-) diff --git a/src/app/ui/Dropdown.tsx b/src/app/ui/Dropdown.tsx index a38f86a..4ad91c7 100644 --- a/src/app/ui/Dropdown.tsx +++ b/src/app/ui/Dropdown.tsx @@ -1,26 +1,40 @@ // 'use client'; +import React from 'react'; import { BsFillCaretDownFill } from 'react-icons/bs'; interface IDropdown { value: string; items: string[]; - action: () => void; + action: (item: string) => void; } -export const Dropdown = ({ value, items, action }: IDropdown) => ( -
-
- {value} +export const Dropdown = ({ value, items, action }: IDropdown) => { + const handleClick = (item: string) => { + action(item); + + const activeEl = document.activeElement as HTMLElement; + activeEl && activeEl.blur(); + }; + + return ( +
+
+ {value} +
+
-
    - {items.map((item) => ( -
  • - {item} -
  • - ))} -
-
-); + ); +}; diff --git a/src/app/ui/Nav.tsx b/src/app/ui/Nav.tsx index ee4756f..851325c 100644 --- a/src/app/ui/Nav.tsx +++ b/src/app/ui/Nav.tsx @@ -1,16 +1,54 @@ +import Link from 'next/link'; + export const Nav = () => ( -
-
- -
-
    +
    +
    + + Slick Telemetry + +
    +
    + +
    +
    +
    +
    + + + +
    +
    -
    -
    -
    - - - -
    - -
    -
    -
    -

    - 53 days until Winter Testing -

    -
    +
    +
    +

    + 53 days until Winter Testing +

    diff --git a/src/app/ui/Tabs.tsx b/src/app/ui/Tabs.tsx index 0463850..fa06c91 100644 --- a/src/app/ui/Tabs.tsx +++ b/src/app/ui/Tabs.tsx @@ -37,11 +37,13 @@ export const Tabs = ({ headers, containers }: ITabs) => { )); return ( -
    -
    - {TabButtons} -
    +
    {TabContainers} +
    +
    + {TabButtons} +
    +
    ); };