From fc693e55489d81ae714049120a1106bce0e779e1 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 09:35:49 -0300 Subject: [PATCH 01/55] feat: replace GitHub link with user profile button in navbar Fetches the logged-in user from dashboard.marketdata.app/api/user/ and shows their Gravatar avatar + username linking to the dashboard. Shows a plain "Log in" link when unauthenticated. Avatar is hidden on mobile. --- docusaurus.config.js | 3 +-- package.json | 1 + src/css/custom.css | 21 +++++++++++++++ src/theme/NavbarItem/ComponentTypes.js | 7 +++++ src/theme/NavbarItem/UserProfile.js | 37 ++++++++++++++++++++++++++ yarn.lock | 24 +++++++++++++++++ 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/theme/NavbarItem/ComponentTypes.js create mode 100644 src/theme/NavbarItem/UserProfile.js diff --git a/docusaurus.config.js b/docusaurus.config.js index 481ae109..b57f77c7 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -222,8 +222,7 @@ const config = { position: "right", }, { - href: "https://github.com/MarketDataApp/documentation", - label: "GitHub", + type: "custom-UserProfile", position: "right", }, ], diff --git a/package.json b/package.json index a0d91e3f..fbbcdff9 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "dotenv": "^16.0.2", + "md5": "^2.3.0", "prism-react-renderer": "^2.3.1", "react": "^18.0.0", "react-dom": "^18.0.0" diff --git a/src/css/custom.css b/src/css/custom.css index c7da7a30..620ed331 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -202,6 +202,27 @@ background-color: #E65100; /* Beta: Orange */ } +/* Navbar user profile button */ +.navbar-user-profile { + display: flex; + align-items: center; + gap: 8px; + text-decoration: none; +} + +.navbar-user-avatar { + width: 28px; + height: 28px; + border-radius: 50%; + display: block; +} + +@media (max-width: 996px) { + .navbar-user-avatar { + display: none; + } +} + /* Clear floats so following content starts below (e.g. after float: right images) */ .clear-float { clear: both; diff --git a/src/theme/NavbarItem/ComponentTypes.js b/src/theme/NavbarItem/ComponentTypes.js new file mode 100644 index 00000000..30ef4de2 --- /dev/null +++ b/src/theme/NavbarItem/ComponentTypes.js @@ -0,0 +1,7 @@ +import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes'; +import UserProfile from '@site/src/theme/NavbarItem/UserProfile'; + +export default { + ...ComponentTypes, + 'custom-UserProfile': UserProfile, +}; diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js new file mode 100644 index 00000000..c75125b2 --- /dev/null +++ b/src/theme/NavbarItem/UserProfile.js @@ -0,0 +1,37 @@ +import React, { useState, useEffect } from 'react'; +import md5 from 'md5'; + +export default function UserProfile() { + const [user, setUser] = useState(null); + const [loaded, setLoaded] = useState(false); + + useEffect(() => { + fetch('https://dashboard.marketdata.app/api/user/', { credentials: 'include' }) + .then(r => r.json()) + .then(data => { + if (data.s === 'ok') setUser(data); + setLoaded(true); + }) + .catch(() => setLoaded(true)); + }, []); + + if (!loaded) return null; + + if (!user) { + return ( + + Log in + + ); + } + + const hash = md5(user.email.trim().toLowerCase()); + const avatarUrl = `https://www.gravatar.com/avatar/${hash}?s=32&d=mp`; + + return ( + + {user.login} + {user.login} + + ); +} diff --git a/yarn.lock b/yarn.lock index 75d75845..4c50f702 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2836,6 +2836,11 @@ character-reference-invalid@^2.0.0: resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz" integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + cheerio-select@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz" @@ -3166,6 +3171,11 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + crypto-random-string@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz" @@ -4755,6 +4765,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-ci@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz" @@ -5192,6 +5207,15 @@ math-intrinsics@^1.1.0: resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + mdast-util-directive@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz" From 3c0edada25e8d2e33a6cab866bd6ce08441b9ba1 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 09:54:08 -0300 Subject: [PATCH 02/55] fix: cache user API response to eliminate navbar CLS on page navigation --- src/theme/NavbarItem/UserProfile.js | 47 ++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index c75125b2..f023e8fb 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -1,18 +1,57 @@ import React, { useState, useEffect } from 'react'; import md5 from 'md5'; +// Module-level cache: undefined = not fetched, null = logged out, object = user +// Persists across SPA navigations within the same page session. +let moduleCache; + +function readSessionCache() { + try { + const raw = sessionStorage.getItem('mda_user'); + if (raw === null) return undefined; + return raw === 'null' ? null : JSON.parse(raw); + } catch { + return undefined; + } +} + +function writeSessionCache(user) { + try { + sessionStorage.setItem('mda_user', user === null ? 'null' : JSON.stringify(user)); + } catch {} +} + export default function UserProfile() { - const [user, setUser] = useState(null); - const [loaded, setLoaded] = useState(false); + const [user, setUser] = useState(moduleCache); + const [loaded, setLoaded] = useState(moduleCache !== undefined); useEffect(() => { + // Already have data from module cache — skip fetch + if (moduleCache !== undefined) return; + + // Check sessionStorage (covers hard refresh within the same tab session) + const cached = readSessionCache(); + if (cached !== undefined) { + moduleCache = cached; + setUser(cached); + setLoaded(true); + return; + } + fetch('https://dashboard.marketdata.app/api/user/', { credentials: 'include' }) .then(r => r.json()) .then(data => { - if (data.s === 'ok') setUser(data); + const result = data.s === 'ok' ? data : null; + moduleCache = result; + writeSessionCache(result); + setUser(result); setLoaded(true); }) - .catch(() => setLoaded(true)); + .catch(() => { + moduleCache = null; + writeSessionCache(null); + setLoaded(true); + }); }, []); if (!loaded) return null; From 5c32475359eeb36a0d7a99219bdca22c6e846477 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 09:57:55 -0300 Subject: [PATCH 03/55] fix: render user profile item correctly in mobile hamburger menu --- src/theme/NavbarItem/UserProfile.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index f023e8fb..627a4f40 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -21,7 +21,7 @@ function writeSessionCache(user) { } catch {} } -export default function UserProfile() { +export default function UserProfile({ mobile }) { const [user, setUser] = useState(moduleCache); const [loaded, setLoaded] = useState(moduleCache !== undefined); @@ -56,6 +56,18 @@ export default function UserProfile() { if (!loaded) return null; + const label = user ? user.login : 'Log in'; + + if (mobile) { + return ( +
  • + + {label} + +
  • + ); + } + if (!user) { return ( From 0f0420bc96fb5e5fa5508f85710e161429783a6b Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 10:01:48 -0300 Subject: [PATCH 04/55] fix: show 'Customer Dashboard' with external link icon in mobile menu --- src/theme/NavbarItem/UserProfile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index 627a4f40..625ee3dd 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import md5 from 'md5'; +import IconExternalLink from '@theme/Icon/ExternalLink'; // Module-level cache: undefined = not fetched, null = logged out, object = user // Persists across SPA navigations within the same page session. @@ -61,8 +62,8 @@ export default function UserProfile({ mobile }) { if (mobile) { return (
  • - - {label} + + Customer Dashboard
  • ); From 706b0d90fc19aea3288f47342f240bd04f18d7af Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 10:07:01 -0300 Subject: [PATCH 05/55] fix: show 'Log in' immediately instead of blank gap while API loads --- src/theme/NavbarItem/UserProfile.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index 625ee3dd..85cfdd61 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -23,8 +23,9 @@ function writeSessionCache(user) { } export default function UserProfile({ mobile }) { - const [user, setUser] = useState(moduleCache); - const [loaded, setLoaded] = useState(moduleCache !== undefined); + // Default to null (renders "Log in") so there's no blank gap before the API returns. + // If we already have cached data, initialize from it immediately. + const [user, setUser] = useState(moduleCache ?? null); useEffect(() => { // Already have data from module cache — skip fetch @@ -35,7 +36,6 @@ export default function UserProfile({ mobile }) { if (cached !== undefined) { moduleCache = cached; setUser(cached); - setLoaded(true); return; } @@ -46,19 +46,13 @@ export default function UserProfile({ mobile }) { moduleCache = result; writeSessionCache(result); setUser(result); - setLoaded(true); }) .catch(() => { moduleCache = null; writeSessionCache(null); - setLoaded(true); }); }, []); - if (!loaded) return null; - - const label = user ? user.login : 'Log in'; - if (mobile) { return (
  • From cbcdc837c606ddfc8c837d5fb9c019d62ff3af27 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 10:26:25 -0300 Subject: [PATCH 06/55] feat: use btn-hover-orange from @marketdataapp/ui for the Log in button --- docusaurus.config.js | 5 ++++- src/theme/NavbarItem/UserProfile.js | 2 +- yarn.lock | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index b57f77c7..deb810c4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -49,7 +49,10 @@ const config = { ({ blog: false, theme: { - customCss: require.resolve("./src/css/custom.css"), + customCss: [ + require.resolve("@marketdataapp/ui/css/components"), + require.resolve("./src/css/custom.css"), + ], }, sitemap: process.env.PROD == "true" diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index 85cfdd61..3dcb3d3d 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -65,7 +65,7 @@ export default function UserProfile({ mobile }) { if (!user) { return ( - + Log in ); diff --git a/yarn.lock b/yarn.lock index 4c50f702..da81a808 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1583,7 +1583,7 @@ "@marketdataapp/ui@github:MarketDataApp/ui": version "1.1.0" - resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/d83dfd362a12e3173f02eb2c31eb43f4a7379fed" + resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/176bf3f1853fbd0594073b32abe045b056428cbf" "@mdx-js/mdx@^3.0.0": version "3.0.0" From 9a3c61527901aca75a61f755c589e85438224f53 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 10:32:42 -0300 Subject: [PATCH 07/55] fix: mobile menu shows 'Log in' when logged out, 'Customer Dashboard' when logged in --- src/theme/NavbarItem/UserProfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index 3dcb3d3d..92c07338 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -57,7 +57,7 @@ export default function UserProfile({ mobile }) { return (
  • - Customer Dashboard + {user ? <>Customer Dashboard : 'Log in'}
  • ); From e01191ca705cad0b94465608b19ec7b117d203fa Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 10:35:07 -0300 Subject: [PATCH 08/55] feat: add Log out button on desktop for logged-in users --- src/theme/NavbarItem/UserProfile.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/theme/NavbarItem/UserProfile.js b/src/theme/NavbarItem/UserProfile.js index 92c07338..be73b043 100644 --- a/src/theme/NavbarItem/UserProfile.js +++ b/src/theme/NavbarItem/UserProfile.js @@ -75,9 +75,14 @@ export default function UserProfile({ mobile }) { const avatarUrl = `https://www.gravatar.com/avatar/${hash}?s=32&d=mp`; return ( - - {user.login} - {user.login} - + <> + + {user.login} + {user.login} + + + Log out + + ); } From 281e0b270af56335efa0fddad1a078525d1fc6a6 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 12:21:51 -0300 Subject: [PATCH 09/55] style: update comment for navbar user profile to reflect logged-in state --- src/css/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/custom.css b/src/css/custom.css index 620ed331..94e16d9e 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -202,7 +202,7 @@ background-color: #E65100; /* Beta: Orange */ } -/* Navbar user profile button */ +/* Navbar user profile (logged-in state) */ .navbar-user-profile { display: flex; align-items: center; From 6e3d99b73bbb259fae1ed98f93b44d09399804c1 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 12:22:59 -0300 Subject: [PATCH 10/55] chore: upgrade @marketdataapp/ui to 1.2.0 --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index da81a808..d1e6fdae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1582,8 +1582,8 @@ integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== "@marketdataapp/ui@github:MarketDataApp/ui": - version "1.1.0" - resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/176bf3f1853fbd0594073b32abe045b056428cbf" + version "1.2.0" + resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/deec041ddded7a58ccc3fd969a44504cbb68b1d4" "@mdx-js/mdx@^3.0.0": version "3.0.0" From 0d6857d85fe5e758b26e213798b6d55728d420a9 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 15:37:16 -0300 Subject: [PATCH 11/55] chore: upgrade @marketdataapp/ui to v2.0.0 (Tailwind v4) --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fbbcdff9..3de8ea1f 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@docusaurus/plugin-client-redirects": "3.0.1", "@docusaurus/plugin-content-docs": "3.0.1", "@docusaurus/preset-classic": "3.0.1", - "@marketdataapp/ui": "github:MarketDataApp/ui", + "@marketdataapp/ui": "github:MarketDataApp/ui#v2.0.0", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "dotenv": "^16.0.2", diff --git a/yarn.lock b/yarn.lock index d1e6fdae..e95afdce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,9 +1581,9 @@ resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@marketdataapp/ui@github:MarketDataApp/ui": - version "1.2.0" - resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/deec041ddded7a58ccc3fd969a44504cbb68b1d4" +"@marketdataapp/ui@github:MarketDataApp/ui#v2.0.0": + version "2.0.0" + resolved "https://codeload.github.com/MarketDataApp/ui/tar.gz/8d3c10261efdd3edaf72c615630d74d27ca19cd1" "@mdx-js/mdx@^3.0.0": version "3.0.0" From 0caef7087f2953e96f2bd2b5cc22f30de3d1dad8 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Tue, 3 Mar 2026 15:40:45 -0300 Subject: [PATCH 12/55] fix: skip Cloudflare Zaraz script in development /cdn-cgi/zaraz/i.js only exists on the live Cloudflare site. In dev, the Webpack dev server returns an HTML 404 for that path, which throws "Unexpected token '<'" when the browser tries to execute it as JS. --- src/theme/Root.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/theme/Root.js b/src/theme/Root.js index e23fdc5e..a71b0a6c 100644 --- a/src/theme/Root.js +++ b/src/theme/Root.js @@ -2,12 +2,16 @@ import React from 'react'; import Head from '@docusaurus/Head'; import Context7Widget from '@site/src/components/Context7Widget'; +const isProd = process.env.NODE_ENV === 'production'; + export default function Root({children}) { return ( <> - -