From a9b36dc6277193e0f32f2c1c82c47c100bddf58b Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Thu, 1 Feb 2024 15:46:41 -0600 Subject: [PATCH 01/12] Pass authClient to UserStats --- packages/app-root/src/app/users/[login]/stats/page.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/app-root/src/app/users/[login]/stats/page.js b/packages/app-root/src/app/users/[login]/stats/page.js index 16a6212f93..4a41ab2927 100644 --- a/packages/app-root/src/app/users/[login]/stats/page.js +++ b/packages/app-root/src/app/users/[login]/stats/page.js @@ -1,9 +1,12 @@ 'use client' - import { UserStats } from '@zooniverse/user' +import auth from 'panoptes-client/lib/auth' -export default function UserPage() { +export default function UserPage({ params }) { return ( - + ) } From f9005b0479fd5576b3ac79874c8b9a105cd58d20 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Thu, 1 Feb 2024 15:58:37 -0600 Subject: [PATCH 02/12] Init UserStats add ProfileHeader --- .../src/components/UserStats/UserStats.js | 52 +++++++++---------- .../shared/ContentBox/ContentBox.js | 2 +- .../src/components/shared/Layout/Layout.js | 1 + .../src/components/shared/Layout/index.js | 1 + 4 files changed, 29 insertions(+), 27 deletions(-) create mode 100644 packages/lib-user/src/components/shared/Layout/index.js diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index 5f251e4669..13d3596256 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -1,44 +1,44 @@ 'use client' -// This component is a work in progress. It is not intended to be imported as-is, but is currently being used for initial UserStats local development. +import { object, string } from 'prop-types' -import { node } from 'prop-types' +import { + usePanoptesUser, + useUserStats +} from '@hooks' import Layout from '../shared/Layout/Layout' +import ContentBox from '../shared/ContentBox/ContentBox' +import ProfileHeader from '../shared/ProfileHeader/ProfileHeader' function UserStats ({ - children + authClient, + login = '' }) { + + const { data: user, error, isLoading } = usePanoptesUser(authClient) + const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, userID: user?.id }) + return ( -
-
-

User profile header goes here.

-

Bar chart goes here.

-
-
-

Top projects goes here.

-
-
+ + +
) } UserStats.propTypes = { - children: node + // authClient: object, + login: string } export default UserStats diff --git a/packages/lib-user/src/components/shared/ContentBox/ContentBox.js b/packages/lib-user/src/components/shared/ContentBox/ContentBox.js index afbd105655..ad009bbd62 100644 --- a/packages/lib-user/src/components/shared/ContentBox/ContentBox.js +++ b/packages/lib-user/src/components/shared/ContentBox/ContentBox.js @@ -36,7 +36,7 @@ function ContentBox({ }} border={border} elevation={screenSize === 'small' ? 'none' : 'xsmall'} - margin={screenSize === 'small' ? 'none' : '30px'} + margin={screenSize === 'small' ? '30px' : 'none'} pad='30px' round={screenSize === 'small' ? 'none' : '8px'} {...rest} diff --git a/packages/lib-user/src/components/shared/Layout/Layout.js b/packages/lib-user/src/components/shared/Layout/Layout.js index 5cd374cc5e..2631d0aa5e 100644 --- a/packages/lib-user/src/components/shared/Layout/Layout.js +++ b/packages/lib-user/src/components/shared/Layout/Layout.js @@ -128,6 +128,7 @@ function Layout ({ children }) { dark: 'dark-3', light: 'neutral-6' }} + gap='32px' > {children} diff --git a/packages/lib-user/src/components/shared/Layout/index.js b/packages/lib-user/src/components/shared/Layout/index.js new file mode 100644 index 0000000000..768acd5680 --- /dev/null +++ b/packages/lib-user/src/components/shared/Layout/index.js @@ -0,0 +1 @@ +export { default as Layout } from './Layout.js' From 661e8e8adaa192a7b9a0e0b383df5004e0f56676 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Tue, 6 Feb 2024 13:45:41 -0600 Subject: [PATCH 03/12] Refactor lib-user auth and user request --- .../src/app/users/[login]/stats/page.js | 2 - packages/lib-user/dev/components/App/App.js | 3 +- packages/lib-user/package.json | 2 +- .../src/components/UserStats/UserStats.js | 11 +- .../lib-user/src/hooks/usePanoptesUser.js | 129 +++++++++++++----- packages/lib-user/webpack.dev.js | 1 + 6 files changed, 100 insertions(+), 48 deletions(-) diff --git a/packages/app-root/src/app/users/[login]/stats/page.js b/packages/app-root/src/app/users/[login]/stats/page.js index 4a41ab2927..69c1cf9b03 100644 --- a/packages/app-root/src/app/users/[login]/stats/page.js +++ b/packages/app-root/src/app/users/[login]/stats/page.js @@ -1,11 +1,9 @@ 'use client' import { UserStats } from '@zooniverse/user' -import auth from 'panoptes-client/lib/auth' export default function UserPage({ params }) { return ( ) diff --git a/packages/lib-user/dev/components/App/App.js b/packages/lib-user/dev/components/App/App.js index 1da9ecf08e..1ba1974321 100644 --- a/packages/lib-user/dev/components/App/App.js +++ b/packages/lib-user/dev/components/App/App.js @@ -14,7 +14,7 @@ function App ({ const [userAuth, setUserAuth] = useState(null) const [dark, setDarkTheme] = useState(false) - const { data: user, error, isLoading: userLoading } = usePanoptesUser(oauth) + const { data: user, error, isLoading: userLoading } = usePanoptesUser() useEffect(() => { async function initUserAuth () { @@ -107,7 +107,6 @@ function App ({ content = ( ) diff --git a/packages/lib-user/package.json b/packages/lib-user/package.json index 2e99cc10ac..413286d955 100644 --- a/packages/lib-user/package.json +++ b/packages/lib-user/package.json @@ -32,6 +32,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "panoptes-client": "~5.6.0", "swr": "~2.2.4" }, "peerDependencies": { @@ -72,7 +73,6 @@ "jsdom": "~23.0.0", "mocha": "~10.2.0", "nock": "~13.4.0", - "panoptes-client": "~5.6.0", "process": "~0.11.10", "prop-types": "^15.8.1", "sinon": "~17.0.0", diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index 13d3596256..5d32493536 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -1,6 +1,6 @@ 'use client' -import { object, string } from 'prop-types' +import { string } from 'prop-types' import { usePanoptesUser, @@ -12,12 +12,11 @@ import ContentBox from '../shared/ContentBox/ContentBox' import ProfileHeader from '../shared/ProfileHeader/ProfileHeader' function UserStats ({ - authClient, login = '' }) { - - const { data: user, error, isLoading } = usePanoptesUser(authClient) - const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, userID: user?.id }) + const { data: user, error, isLoading } = usePanoptesUser() + + const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ userID: user?.id }) return ( @@ -27,6 +26,7 @@ function UserStats ({ height='400px' > { + localStorage?.removeItem('panoptes-user') } - }, [authClient]) + }, [data]) - return { data: user, error, isLoading: loading } + return { data: storedUser, error, isLoading } } diff --git a/packages/lib-user/webpack.dev.js b/packages/lib-user/webpack.dev.js index 87e86f0d23..3d98a4281a 100644 --- a/packages/lib-user/webpack.dev.js +++ b/packages/lib-user/webpack.dev.js @@ -49,6 +49,7 @@ module.exports = { fs: false, // for markdown-it plugins path: require.resolve("path-browserify"), + process: false, url: false, } }, From fc0c22da452dafd8fe61df3530b804ad85e45409 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Wed, 7 Feb 2024 17:25:27 -0600 Subject: [PATCH 04/12] Refactor useUserStats --- .../src/app/users/[login]/stats/page.js | 2 + packages/lib-user/dev/components/App/App.js | 1 + .../src/components/UserStats/UserStats.js | 9 ++- .../shared/ProfileHeader/ProfileHeader.js | 3 +- .../lib-user/src/hooks/usePanoptesAuth.js | 11 ++- packages/lib-user/src/hooks/useUserStats.js | 74 ++++++++++--------- 6 files changed, 54 insertions(+), 46 deletions(-) diff --git a/packages/app-root/src/app/users/[login]/stats/page.js b/packages/app-root/src/app/users/[login]/stats/page.js index 69c1cf9b03..4a41ab2927 100644 --- a/packages/app-root/src/app/users/[login]/stats/page.js +++ b/packages/app-root/src/app/users/[login]/stats/page.js @@ -1,9 +1,11 @@ 'use client' import { UserStats } from '@zooniverse/user' +import auth from 'panoptes-client/lib/auth' export default function UserPage({ params }) { return ( ) diff --git a/packages/lib-user/dev/components/App/App.js b/packages/lib-user/dev/components/App/App.js index 1ba1974321..075c362b25 100644 --- a/packages/lib-user/dev/components/App/App.js +++ b/packages/lib-user/dev/components/App/App.js @@ -107,6 +107,7 @@ function App ({ content = ( ) diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index 5d32493536..787976d7dd 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -1,6 +1,6 @@ 'use client' -import { string } from 'prop-types' +import { object, string } from 'prop-types' import { usePanoptesUser, @@ -12,11 +12,12 @@ import ContentBox from '../shared/ContentBox/ContentBox' import ProfileHeader from '../shared/ProfileHeader/ProfileHeader' function UserStats ({ + authClient, login = '' }) { const { data: user, error, isLoading } = usePanoptesUser() - - const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ userID: user?.id }) + + const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, userID: user?.id }) return ( @@ -30,6 +31,7 @@ function UserStats ({ classifications={userStats?.total_count} displayName={user?.display_name} login={login} + projects={userStats?.project_contributions?.length} /> @@ -37,6 +39,7 @@ function UserStats ({ } UserStats.propTypes = { + authClient: object, login: string } diff --git a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js index cf4f7e44f6..6dd2790833 100644 --- a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js +++ b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js @@ -22,7 +22,6 @@ function TitledStat ({ > {title} @@ -32,7 +31,7 @@ function TitledStat ({ size='xlarge' weight='bold' > - {value} + {value.toLocaleString()} ) diff --git a/packages/lib-user/src/hooks/usePanoptesAuth.js b/packages/lib-user/src/hooks/usePanoptesAuth.js index 369c15d2f4..52ab0ba889 100644 --- a/packages/lib-user/src/hooks/usePanoptesAuth.js +++ b/packages/lib-user/src/hooks/usePanoptesAuth.js @@ -1,16 +1,15 @@ import { useEffect, useState } from 'react' -import { getBearerToken } from '@utils/index.js' +import { getBearerToken } from '@utils' export default function usePanoptesAuth({ authClient, userID }) { const [authorization, setAuthorization] = useState() + async function checkAuth() { + const token = await getBearerToken(authClient) + setAuthorization(token) + } useEffect(function onUserChange() { - async function checkAuth() { - const token = await getBearerToken(authClient) - setAuthorization(token) - } - checkAuth() }, [authClient, userID]) diff --git a/packages/lib-user/src/hooks/useUserStats.js b/packages/lib-user/src/hooks/useUserStats.js index babfa8d803..71bcbbc149 100644 --- a/packages/lib-user/src/hooks/useUserStats.js +++ b/packages/lib-user/src/hooks/useUserStats.js @@ -1,42 +1,46 @@ -import { useEffect, useState } from 'react' +import useSWR from 'swr' -import { getBearerToken } from '@utils/index.js' +import { usePanoptesAuth } from '@hooks' -// TODO: refactor with SWR +// TODO: refactor for stats hosts (staging, production, etc.) -export default function useUserStats({ authClient, userID }) { - const [error, setError] = useState(null) - const [userStats, setUserStats] = useState(null) - const [loading, setLoading] = useState(true) +const SWROptions = { + revalidateIfStale: true, + revalidateOnMount: true, + revalidateOnFocus: true, + revalidateOnReconnect: true, + refreshInterval: 0 +} + +const defaultEndpoint = '/classifications/users' +const defaultQuery = { + // end_date: null, + period: 'year', + project_contributions: true, + // project_id: null, + // start_date: null, + time_spent: true, + // workflow_id: null, +} - useEffect(function () { - async function fetchUserStats() { - setLoading(true) - setUserStats(null) - - try { - const authorization = await getBearerToken(authClient) - const headers = { authorization } - const response = await fetch(`https://eras-staging.zooniverse.org/classifications/users/${userID}?period=week`, { headers }) - const data = await response.json() - if (!ignore) { - setUserStats(data) - } - } catch (error) { - setError(error) - } - - setLoading(false) - } +async function fetchUserStats({ endpoint, query, userID, authorization }) { + const queryParams = new URLSearchParams(query).toString() + const headers = { authorization } + + try { + const response = await fetch(`https://eras-staging.zooniverse.org${endpoint}/${userID}?${queryParams}`, { headers }) + const data = await response.json() + return data + } catch (error) { + console.log(error) + return null + } +} - let ignore = false - if (authClient && userID) { - fetchUserStats(authClient, userID) - } - return () => { - ignore = true - } - }, [authClient, userID]) +export default function useUserStats({ authClient, endpoint = defaultEndpoint, query = defaultQuery, userID }) { + const authorization = usePanoptesAuth({ authClient, userID }) + + const key = authorization ? { endpoint, query, userID, authorization } : null - return { data: userStats, error, isLoading: loading } + return useSWR(key, fetchUserStats, SWROptions) } From ba8dbcb2be67a76ee817c673589efd279f540814 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Wed, 7 Feb 2024 23:07:03 -0600 Subject: [PATCH 05/12] Refactor hooks for auth and oauth --- packages/lib-user/dev/components/App/App.js | 2 +- .../src/components/UserStats/UserStats.js | 2 +- .../lib-user/src/hooks/usePanoptesUser.js | 107 ++++-------------- packages/lib-user/src/hooks/useUserStats.js | 2 +- 4 files changed, 23 insertions(+), 90 deletions(-) diff --git a/packages/lib-user/dev/components/App/App.js b/packages/lib-user/dev/components/App/App.js index 075c362b25..1da9ecf08e 100644 --- a/packages/lib-user/dev/components/App/App.js +++ b/packages/lib-user/dev/components/App/App.js @@ -14,7 +14,7 @@ function App ({ const [userAuth, setUserAuth] = useState(null) const [dark, setDarkTheme] = useState(false) - const { data: user, error, isLoading: userLoading } = usePanoptesUser() + const { data: user, error, isLoading: userLoading } = usePanoptesUser(oauth) useEffect(() => { async function initUserAuth () { diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index 787976d7dd..a3892fa93f 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -15,7 +15,7 @@ function UserStats ({ authClient, login = '' }) { - const { data: user, error, isLoading } = usePanoptesUser() + const { data: user, error, isLoading } = usePanoptesUser(authClient) const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, userID: user?.id }) diff --git a/packages/lib-user/src/hooks/usePanoptesUser.js b/packages/lib-user/src/hooks/usePanoptesUser.js index d555b741df..6a017c53d0 100644 --- a/packages/lib-user/src/hooks/usePanoptesUser.js +++ b/packages/lib-user/src/hooks/usePanoptesUser.js @@ -1,55 +1,7 @@ import auth from 'panoptes-client/lib/auth' import { useEffect, useState } from 'react' -import useSWR from 'swr' -/* - This is a variation of the usePanoptesUser hook from the lib-react-components package. - This hook uses the Panoptes API to get a user object, and stores it in localStorage. - This hook does not use the Panoptes JWT to get a user object, as the Panoptes JWT user object does not contain the avatar_src property. - The avatar_src is needed for various components in lib-user. - - *If the Panoptes JWT is refactored to include the avatar_src property, this hook can be replaced with the lib-react-components usePanoptesUser hook.* -*/ - -const isBrowser = typeof window !== 'undefined' - -const SWROptions = { - revalidateIfStale: true, - revalidateOnMount: true, - revalidateOnFocus: true, - revalidateOnReconnect: true, - refreshInterval: 0 -} - -if (isBrowser) { - auth.checkCurrent() -} - -const localStorage = isBrowser ? window.localStorage : null -const storedUserJSON = localStorage?.getItem('panoptes-user') -let storedUser = storedUserJSON && JSON.parse(storedUserJSON) -/* - Null users crash the ZooHeader component. - Set them to undefined for now. -*/ -if (storedUser === null) { - storedUser = undefined -} - -/* - A user that does not have an avatar has an avatar_src of null. - A user that has an avatar_src of undefined was set from the Panoptes JWT, and might have an avatar, so the user will be requested from the Panoptes API. -*/ -if (storedUser?.avatar_src === undefined) { - storedUser = undefined -} - -const defaultKey = { - user: storedUser, - endpoint: '/me' -} - -async function fetchPanoptesUser({ user: storedUser }) { +async function fetchPanoptesUser() { const panoptesUser = await auth.checkCurrent() if (panoptesUser) { // A lot of user properties are not needed in lib-user, so we only return the ones we need; edit as needed. @@ -57,52 +9,33 @@ async function fetchPanoptesUser({ user: storedUser }) { return { admin, avatar_src, display_name, id, login } } - return {} + return null } -export default function usePanoptesUser() { - const [key, setKey] = useState(defaultKey) +export default function usePanoptesUser(authClient) { + const [error, setError] = useState(null) + const [user, setUser] = useState(null) + const [loading, setLoading] = useState(true) - /* - `useSWR` here will always return the same stale user object. - See https://github.com/zooniverse/panoptes-javascript-client/issues/207 - */ - const { data, error, isLoading } = useSWR(key, fetchPanoptesUser, SWROptions) - if (data) { - storedUser = data - } - - useEffect(function subscribeToAuthChanges() { + useEffect(function () { async function checkUserSession() { - const user = await fetchPanoptesUser({ user: storedUser }) - if (user?.login) { - setKey({ - user, - endpoint: '/me' - }) - } else { - setKey({ - user: undefined, - endpoint: '/me' - }) + setLoading(true) + try { + const panoptesUser = await fetchPanoptesUser() + setUser(panoptesUser) + } catch (error) { + setError(error) } + setLoading(false) } - auth.listen('change', checkUserSession) - return function () { - auth.stopListening('change', checkUserSession) - } - }, []) - - useEffect(function persistUserInStorage() { - if (data) { - localStorage?.setItem('panoptes-user', JSON.stringify(data)) - } + checkUserSession() + authClient.listen('change', checkUserSession) - return () => { - localStorage?.removeItem('panoptes-user') + return function () { + authClient.stopListening('change', checkUserSession) } - }, [data]) + }, [authClient]) - return { data: storedUser, error, isLoading } + return { data: user, error, isLoading: loading } } diff --git a/packages/lib-user/src/hooks/useUserStats.js b/packages/lib-user/src/hooks/useUserStats.js index 71bcbbc149..77686211be 100644 --- a/packages/lib-user/src/hooks/useUserStats.js +++ b/packages/lib-user/src/hooks/useUserStats.js @@ -40,7 +40,7 @@ async function fetchUserStats({ endpoint, query, userID, authorization }) { export default function useUserStats({ authClient, endpoint = defaultEndpoint, query = defaultQuery, userID }) { const authorization = usePanoptesAuth({ authClient, userID }) - const key = authorization ? { endpoint, query, userID, authorization } : null + const key = (authorization && userID) ? { endpoint, query, userID, authorization } : null return useSWR(key, fetchUserStats, SWROptions) } From 9cb1c5d74c50734be55010f9f5559721d8d96ce6 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Wed, 7 Feb 2024 23:20:44 -0600 Subject: [PATCH 06/12] Add stats hosts --- packages/lib-user/package.json | 2 +- packages/lib-user/src/hooks/useUserStats.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/lib-user/package.json b/packages/lib-user/package.json index 413286d955..c472565877 100644 --- a/packages/lib-user/package.json +++ b/packages/lib-user/package.json @@ -32,12 +32,12 @@ "build-storybook": "storybook build" }, "dependencies": { + "@zooniverse/panoptes-js": "~0.4.0", "panoptes-client": "~5.6.0", "swr": "~2.2.4" }, "peerDependencies": { "@zooniverse/grommet-theme": "3.x.x", - "@zooniverse/panoptes-js": "~0.4.0", "@zooniverse/react-components": "~1.x.x", "grommet": "2.x.x", "react": "~18.2.0", diff --git a/packages/lib-user/src/hooks/useUserStats.js b/packages/lib-user/src/hooks/useUserStats.js index 77686211be..d6a6efb9ba 100644 --- a/packages/lib-user/src/hooks/useUserStats.js +++ b/packages/lib-user/src/hooks/useUserStats.js @@ -1,9 +1,8 @@ +import { env } from '@zooniverse/panoptes-js' import useSWR from 'swr' import { usePanoptesAuth } from '@hooks' -// TODO: refactor for stats hosts (staging, production, etc.) - const SWROptions = { revalidateIfStale: true, revalidateOnMount: true, @@ -12,6 +11,15 @@ const SWROptions = { refreshInterval: 0 } +function statsHost(env) { + switch (env) { + case 'production': + return 'https://eras.zooniverse.org' + default: + return 'https://eras-staging.zooniverse.org' + } +} + const defaultEndpoint = '/classifications/users' const defaultQuery = { // end_date: null, @@ -24,11 +32,12 @@ const defaultQuery = { } async function fetchUserStats({ endpoint, query, userID, authorization }) { + const stats = statsHost(env) const queryParams = new URLSearchParams(query).toString() const headers = { authorization } try { - const response = await fetch(`https://eras-staging.zooniverse.org${endpoint}/${userID}?${queryParams}`, { headers }) + const response = await fetch(`${stats}${endpoint}/${userID}?${queryParams}`, { headers }) const data = await response.json() return data } catch (error) { From bcf739431fcb101c12026245845928af9fcfd0fb Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Tue, 13 Feb 2024 10:38:50 -0600 Subject: [PATCH 07/12] Fix ProfileHeader test --- .../src/components/shared/ProfileHeader/ProfileHeader.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.spec.js b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.spec.js index 179f98f6ae..0ab5391182 100644 --- a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.spec.js +++ b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.spec.js @@ -56,7 +56,7 @@ describe('components > shared > ProfileHeader', function () { it('should show the group\'s classifications', function () { render() - const classifications = screen.getByText('1526') + const classifications = screen.getByText('1,526') expect(classifications).to.be.ok() }) From eae9fe2def99b25e128417772c8773476e68b194 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Fri, 16 Feb 2024 15:16:22 -0600 Subject: [PATCH 08/12] Refactor ProfileHeader titled stats --- .../shared/ProfileHeader/ProfileHeader.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js index 6dd2790833..837a40e581 100644 --- a/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js +++ b/packages/lib-user/src/components/shared/ProfileHeader/ProfileHeader.js @@ -16,6 +16,11 @@ function TitledStat ({ title = '', value = 0 }) { + let displayValue = value + if (isNaN(value)) { + displayValue = 0 + } + return ( - {value.toLocaleString()} + {Math.round(displayValue).toLocaleString()} ) @@ -44,12 +49,12 @@ TitledStat.propTypes = { function ProfileHeader ({ avatar = '', - classifications = 0, - contributors = 0, + classifications = undefined, + contributors = undefined, displayName = '', - hours = 0, + hours = undefined, login = '', - projects = 0, + projects = undefined, screenSize = 'medium' }) { return ( @@ -96,25 +101,25 @@ function ProfileHeader ({ direction='row' gap='small' > - {classifications ? + {classifications !== undefined ? : null} - {hours ? + {hours !== undefined ? : null} - {contributors ? + {contributors !== undefined ? : null} - {projects ? + {projects !== undefined ? Date: Fri, 16 Feb 2024 15:24:01 -0600 Subject: [PATCH 09/12] Move dateRanges --- .../src/components/shared/BarChart/BarChart.js | 5 ++++- .../components/shared/BarChart/BarChart.stories.js | 5 ++++- .../src/components/shared/Select/Select.spec.js | 5 ++++- .../src/components/shared/Select/Select.stories.js | 5 ++++- packages/lib-user/src/hooks/useUserStats.js | 11 +---------- packages/lib-user/src/index.js | 1 + .../shared/BarChart/helpers => utils}/dateRanges.js | 0 .../BarChart/helpers => utils}/dateRanges.spec.js | 0 packages/lib-user/src/utils/index.js | 1 + 9 files changed, 19 insertions(+), 14 deletions(-) rename packages/lib-user/src/{components/shared/BarChart/helpers => utils}/dateRanges.js (100%) rename packages/lib-user/src/{components/shared/BarChart/helpers => utils}/dateRanges.spec.js (100%) diff --git a/packages/lib-user/src/components/shared/BarChart/BarChart.js b/packages/lib-user/src/components/shared/BarChart/BarChart.js index 00cba54167..c04e6141f8 100644 --- a/packages/lib-user/src/components/shared/BarChart/BarChart.js +++ b/packages/lib-user/src/components/shared/BarChart/BarChart.js @@ -2,7 +2,10 @@ import { DataChart, Text } from 'grommet' import { arrayOf, number, shape, string } from 'prop-types' import withResponsiveContext from '@zooniverse/react-components/helpers/withResponsiveContext' -import dateRanges from './helpers/dateRanges' +import { + dateRanges +} from '@utils' + import getDateRangeLabel from './helpers/getDateRangeLabel' function BarChart ({ diff --git a/packages/lib-user/src/components/shared/BarChart/BarChart.stories.js b/packages/lib-user/src/components/shared/BarChart/BarChart.stories.js index d6cf4a7ab1..b475140837 100644 --- a/packages/lib-user/src/components/shared/BarChart/BarChart.stories.js +++ b/packages/lib-user/src/components/shared/BarChart/BarChart.stories.js @@ -1,6 +1,9 @@ import { Box } from 'grommet' -import dateRanges from './helpers/dateRanges.js' +import { + dateRanges +} from '@utils' + import { last7days, last30days, diff --git a/packages/lib-user/src/components/shared/Select/Select.spec.js b/packages/lib-user/src/components/shared/Select/Select.spec.js index 730da578f9..473c47f2ce 100644 --- a/packages/lib-user/src/components/shared/Select/Select.spec.js +++ b/packages/lib-user/src/components/shared/Select/Select.spec.js @@ -2,7 +2,10 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { composeStory } from '@storybook/react' -import dateRanges from '../BarChart/helpers/dateRanges' +import { + dateRanges +} from '@utils' + import Meta, { DateRanges } from './Select.stories' describe('components > shared > Select', function() { diff --git a/packages/lib-user/src/components/shared/Select/Select.stories.js b/packages/lib-user/src/components/shared/Select/Select.stories.js index e8af2542ad..b23dce43ad 100644 --- a/packages/lib-user/src/components/shared/Select/Select.stories.js +++ b/packages/lib-user/src/components/shared/Select/Select.stories.js @@ -1,7 +1,10 @@ import { Box } from 'grommet' + import Select from './Select.js' -import dateRanges from '../BarChart/helpers/dateRanges.js' +import { + dateRanges +} from '@utils' export default { title: 'Components/shared/Select', diff --git a/packages/lib-user/src/hooks/useUserStats.js b/packages/lib-user/src/hooks/useUserStats.js index d6a6efb9ba..6181b96e24 100644 --- a/packages/lib-user/src/hooks/useUserStats.js +++ b/packages/lib-user/src/hooks/useUserStats.js @@ -21,15 +21,6 @@ function statsHost(env) { } const defaultEndpoint = '/classifications/users' -const defaultQuery = { - // end_date: null, - period: 'year', - project_contributions: true, - // project_id: null, - // start_date: null, - time_spent: true, - // workflow_id: null, -} async function fetchUserStats({ endpoint, query, userID, authorization }) { const stats = statsHost(env) @@ -46,7 +37,7 @@ async function fetchUserStats({ endpoint, query, userID, authorization }) { } } -export default function useUserStats({ authClient, endpoint = defaultEndpoint, query = defaultQuery, userID }) { +export default function useUserStats({ authClient, endpoint = defaultEndpoint, query, userID }) { const authorization = usePanoptesAuth({ authClient, userID }) const key = (authorization && userID) ? { endpoint, query, userID, authorization } : null diff --git a/packages/lib-user/src/index.js b/packages/lib-user/src/index.js index 24f7c6a2e9..75a1891efb 100644 --- a/packages/lib-user/src/index.js +++ b/packages/lib-user/src/index.js @@ -13,6 +13,7 @@ export { default as useUserStats } from './hooks/useUserStats.js' // utils export { default as createPanoptesUserGroup } from './utils/createPanoptesUserGroup.js' +export { default as dateRanges } from './utils/dateRanges.js' export { default as deletePanoptesUserGroup } from './utils/deletePanoptesUserGroup.js' export { default as getBearerToken } from './utils/getBearerToken.js' export { default as updatePanoptesUserGroup} from './utils/updatePanoptesUserGroup.js' diff --git a/packages/lib-user/src/components/shared/BarChart/helpers/dateRanges.js b/packages/lib-user/src/utils/dateRanges.js similarity index 100% rename from packages/lib-user/src/components/shared/BarChart/helpers/dateRanges.js rename to packages/lib-user/src/utils/dateRanges.js diff --git a/packages/lib-user/src/components/shared/BarChart/helpers/dateRanges.spec.js b/packages/lib-user/src/utils/dateRanges.spec.js similarity index 100% rename from packages/lib-user/src/components/shared/BarChart/helpers/dateRanges.spec.js rename to packages/lib-user/src/utils/dateRanges.spec.js diff --git a/packages/lib-user/src/utils/index.js b/packages/lib-user/src/utils/index.js index b44ec40324..4ac053f88e 100644 --- a/packages/lib-user/src/utils/index.js +++ b/packages/lib-user/src/utils/index.js @@ -1,4 +1,5 @@ export { default as createPanoptesUserGroup } from './createPanoptesUserGroup.js' +export { default as dateRanges } from './dateRanges.js' export { default as deletePanoptesUserGroup } from './deletePanoptesUserGroup.js' export { default as getBearerToken } from './getBearerToken.js' export { default as updatePanoptesUserGroup } from './updatePanoptesUserGroup.js' \ No newline at end of file From 2b4cf192535a6da7a1e6cf116cf59672a6340cde Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Fri, 16 Feb 2024 15:27:59 -0600 Subject: [PATCH 10/12] Add statsQuery to UserStats --- packages/lib-user/src/components/UserStats/UserStats.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index a3892fa93f..1dd32644f1 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -17,7 +17,12 @@ function UserStats ({ }) { const { data: user, error, isLoading } = usePanoptesUser(authClient) - const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, userID: user?.id }) + const statsQuery = { + period: 'year', + project_contributions: true, + time_spent: true + } + const { data: userStats, error: statsError, isLoading: statsLoading } = useUserStats({ authClient, query: statsQuery, userID: user?.id }) return ( From 5ebea831e796a5affad2c887e77c909e133372e0 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Wed, 21 Feb 2024 16:34:59 -0600 Subject: [PATCH 11/12] Remove withResponsiveContext, replace with Grommet ResponsiveContext --- .../src/components/shared/BarChart/BarChart.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/lib-user/src/components/shared/BarChart/BarChart.js b/packages/lib-user/src/components/shared/BarChart/BarChart.js index c04e6141f8..be6daddb66 100644 --- a/packages/lib-user/src/components/shared/BarChart/BarChart.js +++ b/packages/lib-user/src/components/shared/BarChart/BarChart.js @@ -1,6 +1,6 @@ -import { DataChart, Text } from 'grommet' +import { DataChart, ResponsiveContext, Text } from 'grommet' import { arrayOf, number, shape, string } from 'prop-types' -import withResponsiveContext from '@zooniverse/react-components/helpers/withResponsiveContext' +import { useContext } from 'react' import { dateRanges @@ -11,9 +11,9 @@ import getDateRangeLabel from './helpers/getDateRangeLabel' function BarChart ({ data = [], dateRange = dateRanges.Last7Days, - screenSize = 'small', type = 'count' }) { + const size = useContext(ResponsiveContext) const dateRangeLabel = getDateRangeLabel(dateRange) const typeLabel = type === 'count' ? 'Classifications' : 'Time' const readableDateRange = dateRange @@ -27,10 +27,10 @@ function BarChart ({ property: type, type: 'bar' } - if (screenSize !== 'small' && data.length < 9) { + if (size !== 'small' && data.length < 9) { chartOptions.thickness = 'xlarge' } - if (screenSize === 'small') { + if (size === 'small') { if (data.length < 12) { chartOptions.thickness = 'small' } else if (data.length > 11 && data.length < 19) { @@ -107,8 +107,7 @@ BarChart.propTypes = { session_time: number })), dateRange: string, - screenSize: string, type: string } -export default withResponsiveContext(BarChart) +export default BarChart From 41a4e8724cf7181d4cdf00cbcca003a46b2e6fc7 Mon Sep 17 00:00:00 2001 From: Mark Bouslog Date: Wed, 21 Feb 2024 16:37:50 -0600 Subject: [PATCH 12/12] Refactor user login usage --- packages/app-root/src/app/users/[login]/stats/page.js | 3 +-- .../lib-user/src/components/UserStats/UserStats.js | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/app-root/src/app/users/[login]/stats/page.js b/packages/app-root/src/app/users/[login]/stats/page.js index 4a41ab2927..827a4c378d 100644 --- a/packages/app-root/src/app/users/[login]/stats/page.js +++ b/packages/app-root/src/app/users/[login]/stats/page.js @@ -2,11 +2,10 @@ import { UserStats } from '@zooniverse/user' import auth from 'panoptes-client/lib/auth' -export default function UserPage({ params }) { +export default function UserPage() { return ( ) } diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index 1dd32644f1..a233c7a997 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -1,6 +1,6 @@ 'use client' -import { object, string } from 'prop-types' +import { object } from 'prop-types' import { usePanoptesUser, @@ -12,8 +12,7 @@ import ContentBox from '../shared/ContentBox/ContentBox' import ProfileHeader from '../shared/ProfileHeader/ProfileHeader' function UserStats ({ - authClient, - login = '' + authClient }) { const { data: user, error, isLoading } = usePanoptesUser(authClient) @@ -35,7 +34,7 @@ function UserStats ({ avatar={user?.avatar_src} classifications={userStats?.total_count} displayName={user?.display_name} - login={login} + login={user?.login} projects={userStats?.project_contributions?.length} /> @@ -44,8 +43,7 @@ function UserStats ({ } UserStats.propTypes = { - authClient: object, - login: string + authClient: object } export default UserStats