diff --git a/packages/lib-user/src/components/GroupStats/GroupStats.js b/packages/lib-user/src/components/GroupStats/GroupStats.js index 828a3060b7..2e6d711860 100644 --- a/packages/lib-user/src/components/GroupStats/GroupStats.js +++ b/packages/lib-user/src/components/GroupStats/GroupStats.js @@ -1,5 +1,13 @@ +import { Grid } from 'grommet' import { arrayOf, func, number, shape, string } from 'prop-types' +import { + ContentBox, + Layout, + MainContent, + ProjectCard +} from '@components/shared' + import DeleteGroup from './DeleteGroup' import EditGroup from './EditGroup' @@ -10,6 +18,7 @@ const DEFAULT_GROUP = { const DEFAULT_HANDLER = () => true const DEFAULT_STATS = { active_users: 0, + data: [], project_contributions: [ { count: 0, @@ -18,54 +27,140 @@ const DEFAULT_STATS = { } ], time_spent: 0, + top_contributors: [ + { + count: 0, + session_time: 0, + user_id: 0 + } + ], total_count: 0 } function GroupStats({ + allProjectsStats = DEFAULT_STATS, group = DEFAULT_GROUP, - groupStats = DEFAULT_STATS, + handleDateRangeSelect = DEFAULT_HANDLER, handleGroupDelete = DEFAULT_HANDLER, - handleGroupUpdate = DEFAULT_HANDLER + handleGroupUpdate = DEFAULT_HANDLER, + handleProjectSelect = DEFAULT_HANDLER, + projectStats = DEFAULT_STATS, + projects = [], + selectedDateRange = 'Last7Days', + selectedProject = 'AllProjects' }) { + // set stats based on selected project or all projects + const stats = selectedProject === 'AllProjects' ? allProjectsStats : projectStats + + // set top projects based on selected date range and all project stats + let topProjects = [] + const topProjectContributions = allProjectsStats.project_contributions + .sort((a, b) => b.count - a.count) + + topProjects = topProjectContributions + .map(projectContribution => { + const projectData = projects?.find(project => project.id === projectContribution.project_id.toString()) + return projectData + }) + .filter(project => project) + .slice(0, 6) + return ( -
-

Hi group with ID {group?.id}! 🙌

-

Your group display_name is {group?.display_name}.

-

Members:
{group?.links?.users?.toString()}

-

Here are your group stats:

-
{JSON.stringify(groupStats, null, 2)}
-
+ + + + + Top contributors go here. + + + + {topProjects.map(topProject => { + return ( + + ) + })} + + + -

-
-
+ ) } +const statsShape = shape({ + active_users: number, + data: arrayOf(shape({ + count: number, + period: string, + session_time: number + })), + project_contributions: arrayOf(shape({ + count: number, + project_id: number, + session_time: number + })), + time_spent: number, + top_contributors: arrayOf(shape({ + count: number, + session_time: number, + user_id: number + })), + total_count: number +}) + GroupStats.propTypes = { + allProjectsStats: statsShape, group: shape({ display_name: string, id: string }), - groupStats: shape({ - active_users: number, - project_contributions: arrayOf(shape({ - count: number, - project_id: number, - session_time: number - })), - time_spent: number, - total_count: number - }), + handleDateRangeSelect: func, handleGroupDelete: func, - handleGroupUpdate: func + handleGroupUpdate: func, + handleProjectSelect: func, + projectStats: statsShape, + projects: arrayOf(shape({ + id: string, + display_name: string + })), + selectedDateRange: string, + selectedProject: string } export default GroupStats diff --git a/packages/lib-user/src/components/GroupStats/GroupStatsContainer.js b/packages/lib-user/src/components/GroupStats/GroupStatsContainer.js index 768a2511c9..352eb4bb47 100644 --- a/packages/lib-user/src/components/GroupStats/GroupStatsContainer.js +++ b/packages/lib-user/src/components/GroupStats/GroupStatsContainer.js @@ -1,11 +1,11 @@ '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 GroupStats local development. - import { object, string } from 'prop-types' +import { useState } from 'react' import { usePanoptesAuthUser, + usePanoptesProjects, usePanoptesUserGroup, useStats } from '@hooks' @@ -13,6 +13,7 @@ import { import { deletePanoptesUserGroup, getBearerToken, + getDateInterval, updatePanoptesUserGroup } from '@utils' @@ -24,10 +25,15 @@ function GroupStatsContainer({ authClient, groupId }) { + const [selectedProject, setSelectedProject] = useState('AllProjects') + const [selectedDateRange, setSelectedDateRange] = useState('Last7Days') + + // fetch authenticated user const { data: authUser } = usePanoptesAuthUser(authClient) + // fetch user_group const { data, error: groupError, @@ -37,17 +43,57 @@ function GroupStatsContainer({ authUserId: authUser?.id, groupId }) + const group = data?.body?.user_groups?.[0] + + // fetch all projects stats, used by projects select and top projects regardless of selected project + const allProjectsStatsQuery = getDateInterval(selectedDateRange) + allProjectsStatsQuery.top_contributors = 10 const { - data: groupStats, - error: groupStatsError, - isLoading: groupStatsLoading + data: allProjectsStats, + error: statsError, + isLoading: statsLoading } = useStats({ authClient, authUserId: authUser?.id, endpoint: STATS_ENDPOINT, - sourceId: groupId + sourceId: group?.id, + query: allProjectsStatsQuery }) + + // fetch individual project stats + const projectStatsQuery = getDateInterval(selectedDateRange) + projectStatsQuery.project_id = parseInt(selectedProject) + projectStatsQuery.top_contributors = 10 + + const { + data: projectStats, + error: projectStatsError, + isLoading: projectStatsLoading + } = useStats({ + authClient, + authUserId: authUser?.id, + endpoint: STATS_ENDPOINT, + sourceId: group?.id, + query: projectStatsQuery + }) + + // fetch projects + const projectIDs = allProjectsStats?.project_contributions?.map(project => project.project_id) + + const { + data: projects, + error: projectsError, + isLoading: projectsLoading + } = usePanoptesProjects(projectIDs) + + function handleProjectSelect (project) { + setSelectedProject(project.value) + } + + function handleDateRangeSelect (dateRange) { + setSelectedDateRange(dateRange.value) + } async function getRequestHeaders() { const authorization = await getBearerToken(authClient) @@ -80,14 +126,18 @@ function GroupStatsContainer({ } } - const group = data?.body?.user_groups?.[0] - return ( ) } diff --git a/packages/lib-user/src/components/UserStats/UserStats.js b/packages/lib-user/src/components/UserStats/UserStats.js index fa9c6e6838..b034f7df50 100644 --- a/packages/lib-user/src/components/UserStats/UserStats.js +++ b/packages/lib-user/src/components/UserStats/UserStats.js @@ -1,10 +1,10 @@ import { arrayOf, func, number, shape, string } from 'prop-types' import { - Layout + Layout, + MainContent, } from '@components/shared' -import MainContent from './components/MainContent' import TopProjects from './components/TopProjects' const DEFAULT_HANDLER = () => true diff --git a/packages/lib-user/src/components/UserStats/components/MainContent/index.js b/packages/lib-user/src/components/UserStats/components/MainContent/index.js deleted file mode 100644 index e37d264f99..0000000000 --- a/packages/lib-user/src/components/UserStats/components/MainContent/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './MainContent' diff --git a/packages/lib-user/src/components/shared/Layout/Layout.js b/packages/lib-user/src/components/shared/Layout/Layout.js index 2631d0aa5e..de874466b6 100644 --- a/packages/lib-user/src/components/shared/Layout/Layout.js +++ b/packages/lib-user/src/components/shared/Layout/Layout.js @@ -128,7 +128,7 @@ function Layout ({ children }) { dark: 'dark-3', light: 'neutral-6' }} - gap='32px' + gap='30px' > {children} diff --git a/packages/lib-user/src/components/UserStats/components/MainContent/MainContent.js b/packages/lib-user/src/components/shared/MainContent/MainContent.js similarity index 100% rename from packages/lib-user/src/components/UserStats/components/MainContent/MainContent.js rename to packages/lib-user/src/components/shared/MainContent/MainContent.js diff --git a/packages/lib-user/src/components/UserStats/components/MainContent/MainContent.spec.js b/packages/lib-user/src/components/shared/MainContent/MainContent.spec.js similarity index 93% rename from packages/lib-user/src/components/UserStats/components/MainContent/MainContent.spec.js rename to packages/lib-user/src/components/shared/MainContent/MainContent.spec.js index 1dcab43758..4bba0dd08f 100644 --- a/packages/lib-user/src/components/UserStats/components/MainContent/MainContent.spec.js +++ b/packages/lib-user/src/components/shared/MainContent/MainContent.spec.js @@ -1,8 +1,8 @@ import { composeStory } from '@storybook/react' import { render, screen } from '@testing-library/react' -import { USER } from '../../../../../test/mocks/panoptes' -import { STATS } from '../../../../../test/mocks/stats.mock.js' +import { USER } from '../../../../test/mocks/panoptes' +import { STATS } from '../../../../test/mocks/stats.mock.js' import Meta, { Default } from './MainContent.stories.js' diff --git a/packages/lib-user/src/components/UserStats/components/MainContent/MainContent.stories.js b/packages/lib-user/src/components/shared/MainContent/MainContent.stories.js similarity index 78% rename from packages/lib-user/src/components/UserStats/components/MainContent/MainContent.stories.js rename to packages/lib-user/src/components/shared/MainContent/MainContent.stories.js index e67d399f2b..ae71d9c2dd 100644 --- a/packages/lib-user/src/components/UserStats/components/MainContent/MainContent.stories.js +++ b/packages/lib-user/src/components/shared/MainContent/MainContent.stories.js @@ -1,12 +1,12 @@ import { Box } from 'grommet' -import { PROJECTS, USER } from '../../../../../test/mocks/panoptes' -import { STATS } from '../../../../../test/mocks/stats.mock.js' +import { PROJECTS, USER } from '../../../../test/mocks/panoptes' +import { STATS } from '../../../../test/mocks/stats.mock' import MainContent from './MainContent' export default { - title: 'Components/UserStats/MainContent', + title: 'Components/shared/MainContent', component: MainContent, decorators: [ComponentDecorator] } diff --git a/packages/lib-user/src/components/shared/MainContent/index.js b/packages/lib-user/src/components/shared/MainContent/index.js new file mode 100644 index 0000000000..cc6f70e46b --- /dev/null +++ b/packages/lib-user/src/components/shared/MainContent/index.js @@ -0,0 +1 @@ +export { default } from './MainContent.js' diff --git a/packages/lib-user/src/components/shared/index.js b/packages/lib-user/src/components/shared/index.js index dd2aa5b858..f1b39a7693 100644 --- a/packages/lib-user/src/components/shared/index.js +++ b/packages/lib-user/src/components/shared/index.js @@ -1,6 +1,7 @@ export { default as BarChart } from './BarChart' export { default as ContentBox } from './ContentBox' export { default as Layout } from './Layout' +export { default as MainContent } from './MainContent' export { default as ProfileHeader } from './ProfileHeader' export { default as ProjectCard } from './ProjectCard' export { default as Select } from './Select' diff --git a/packages/lib-user/src/utils/getDateInterval.js b/packages/lib-user/src/utils/getDateInterval.js index 5dcc4cdfc4..8f24c2024d 100644 --- a/packages/lib-user/src/utils/getDateInterval.js +++ b/packages/lib-user/src/utils/getDateInterval.js @@ -12,7 +12,7 @@ function getPeriodFromDateDifference(difference) { } } -function getDateInterval(dateRange) { +export function getDateInterval(dateRange) { const endDate = new Date() const end_date = endDate.toISOString().substring(0, 10) @@ -81,5 +81,3 @@ function getDateInterval(dateRange) { } } } - -export { getDateInterval }