Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib-user: Setup react-i18next for Contributors components #6553

Merged
merged 7 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/lib-user/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
"@zooniverse/async-states": "~0.0.1",
"@zooniverse/panoptes-js": "~0.5.0",
"dayjs": "~1.11.11",
"i18next": "~24.0.2",
"panoptes-client": "~5.6.0",
"react-i18next": "~14.1.3",
"swr": "~2.2.4",
"uuid": "~11.0.3"
},
Expand Down
24 changes: 13 additions & 11 deletions packages/lib-user/src/components/Contributors/Contributors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Loader, SpacedText } from '@zooniverse/react-components'
import { Box, Layer } from 'grommet'
import { arrayOf, bool, shape, string } from 'prop-types'
import { useState } from 'react'
import { useTranslation } from '../../translations/i18n.js'

import { fetchPanoptesUsers } from '../../utils'

Expand Down Expand Up @@ -30,10 +31,11 @@ function Contributors({
group,
membership
}) {
const { t } = useTranslation()
const [exportLoading, setExportLoading] = useState(false)
const [page, setPage] = useState(1)

const showContributors = adminMode
const showContributors = adminMode
|| membership?.roles.includes('group_admin')
|| (membership?.roles.includes('group_member') && group?.stats_visibility === 'private_show_agg_and_ind')
|| (membership?.roles.includes('group_member') && group?.stats_visibility === 'public_agg_show_ind_if_member')
Expand All @@ -43,7 +45,7 @@ function Contributors({
const statsQuery = {
individual_stats_breakdown: true,
}

const {
data: stats,
error: statsError,
Expand All @@ -68,7 +70,7 @@ function Contributors({
error: usersError,
isLoading: usersLoading
} = usePanoptesUsers(usersQuery)

// fetch projects
const arrayOfProjectContributionArrays = stats?.group_member_stats_breakdown?.map(member => member.project_contributions)
const flattenedProjectContributionArray = arrayOfProjectContributionArrays?.flat()
Expand Down Expand Up @@ -96,7 +98,7 @@ function Contributors({
})
}

const loadingExportMessage = 'Generating stats export...'
const loadingExportMessage = t('Contributors.generating')

async function handleGenerateExport() {
setExportLoading(true)
Expand All @@ -114,7 +116,7 @@ function Contributors({
stats,
users: allUsers
})

// Create an anchor element and trigger download
const link = document.createElement('a')
link.href = dataExportUrl
Expand Down Expand Up @@ -159,24 +161,24 @@ function Contributors({
primaryHeaderItem={
<HeaderLink
href={`/groups/${group.id}`}
label='back'
label={t('common.back')}
primaryItem={true}
/>
}
>
<ContentBox
linkLabel='Export all stats'
linkLabel={t('Contributors.exportLink')}
linkProps={{
as: 'button',
disabled: disableStatsExport,
onClick: handleGenerateExport
}}
title='Full Group Stats'
title={t('Contributors.title')}
>
{!showContributors ? (
<Box align='center' justify='center' fill pad='medium'>
<SpacedText uppercase={false}>
You do not have permission to view this group&apos;s contributors.
{t('Contributors.noPermission')}
</SpacedText>
</Box>
) : loading ? (
Expand All @@ -186,7 +188,7 @@ function Contributors({
) : error ? (
<Box align='center' justify='center' fill pad='medium'>
<SpacedText uppercase={false}>
There was an error.
{t('Contributors.error')}
</SpacedText>
<SpacedText uppercase={false}>
{error?.message}
Expand All @@ -200,7 +202,7 @@ function Contributors({
) : (
<Box align='center' justify='center' fill pad='medium'>
<SpacedText uppercase={false}>
There are no contributors to this group.
{t('Contributors.none')}
</SpacedText>
</Box>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Box } from 'grommet'
import { arrayOf, number, shape, string } from 'prop-types'
import { useTranslation } from '../../../../translations/i18n.js'

import { convertStatsSecondsToHours } from '@utils'

Expand All @@ -10,6 +11,8 @@ function ContributorsList({
contributors = [],
projects = []
}) {
const { t } = useTranslation()

let privateProjectIndex = 1
contributors.sort((a, b) => b.count - a.count)

Expand All @@ -24,11 +27,11 @@ function ContributorsList({
return (
<Box
key={contributor.id}
a11yTitle={`${contributor.display_name} member stats`}
a11yTitle={t('Contributors.ContributorsList.a11y', { name: contributor.display_name })}
as='li'
background={index % 2 === 0 ?
background={index % 2 === 0 ?
{ dark: 'dark-3', light: 'neutral-6' }
:
:
{ dark: 'dark-1', light: 'light-1' }
}
border={{ color: 'light-5', size: '0.5px' }}
Expand Down Expand Up @@ -56,7 +59,7 @@ function ContributorsList({
>
{contributor.project_contributions.map(statsProject => {
const project = projects.find(project => project.id === statsProject.project_id.toString())
const projectDisplayName = project?.display_name || `Private Project ${privateProjectIndex++}`
const projectDisplayName = project?.display_name || t('Contributors.ContributorsList.privateProject', { index: privateProjectIndex })
const projectHoursSpent = convertStatsSecondsToHours(statsProject.session_time)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SpacedText } from '@zooniverse/react-components'
import { Box } from 'grommet'
import { number, string } from 'prop-types'
import styled from 'styled-components'
import { useTranslation } from '../../../../translations/i18n.js'

const StyledBox = styled(Box)`
box-shadow: 8px 0px 6px -6px rgba(0, 0, 0, 0.25);
Expand All @@ -16,6 +17,7 @@ function MemberStats({
hours = 0,
login = ''
}) {
const { t } = useTranslation()
return (
<StyledBox
align='center'
Expand All @@ -31,7 +33,7 @@ function MemberStats({
gap='xsmall'
>
<Avatar
alt={`${login} avatar`}
alt={t('common.avatarAlt', { login })}
src={avatar || 'https://static.zooniverse.org/fem-assets/simple-avatar.jpg'}
/>
<Box
Expand Down Expand Up @@ -60,7 +62,7 @@ function MemberStats({
color={{ dark: 'neutral-6', light: 'neutral-7' }}
uppercase={false}
>
Classifications
{t('common.classifications')}
</SpacedText>
<SpacedText
color={{ light: 'neutral-1', dark: 'accent-1' }}
Expand All @@ -77,7 +79,7 @@ function MemberStats({
color={{ dark: 'neutral-6', light: 'neutral-7' }}
uppercase={false}
>
Hours
{t('common.hours')}
</SpacedText>
<SpacedText
color={{ light: 'neutral-1', dark: 'accent-1' }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { SpacedText } from '@zooniverse/react-components'
import { Box } from 'grommet'
import { string, number } from 'prop-types'
import { useTranslation } from '../../../../translations/i18n.js'

function ProjectStats({
classifications = 0,
hours = 0,
projectDisplayName = ''
}) {
const { t } = useTranslation()
return (
<Box
a11yTitle={`${projectDisplayName} member stats`}
a11yTitle={t('Contributors.ProjectStats.a11y', { project: projectDisplayName })}
align='center'
as='li'
border={{ color: 'light-5', side: 'vertical', size: '0.5px' }}
Expand All @@ -30,13 +32,13 @@ function ProjectStats({
textAlign='center'
uppercase={false}
>
{`${classifications.toLocaleString()} Classifications`}
{classifications.toLocaleString()} {t('common.classifications')}
</SpacedText>
<SpacedText
textAlign='center'
uppercase={false}
>
{`${hours.toLocaleString()} Hours`}
{hours.toLocaleString()} {t('common.hours')}
</SpacedText>
</Box>
)
Expand Down
23 changes: 13 additions & 10 deletions packages/lib-user/src/components/GroupStats/GroupStats.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Grid, ResponsiveContext } from 'grommet'
import { arrayOf, bool, func, shape, string } from 'prop-types'
import { useContext, useState } from 'react'
import useSWRMutation from 'swr/mutation'
import { useTranslation } from '../../translations/i18n.js'

import {
usePanoptesProjects,
Expand Down Expand Up @@ -40,10 +41,11 @@ function GroupStats({
setSelectedDateRange = DEFAULT_HANDLER,
setSelectedProject = DEFAULT_HANDLER
}) {
const { t } = useTranslation()
const [groupModalActive, setGroupModalActive] = useState(false)

const size = useContext(ResponsiveContext)

// define user_group membership key
const membershipKey = {
authUserId: authUser?.id,
Expand All @@ -55,7 +57,7 @@ function GroupStats({
// define user_group membership delete mutation
const { trigger: deleteMembership } = useSWRMutation(membershipKey, deletePanoptesMembership)

const showTopContributors = adminMode
const showTopContributors = adminMode
|| membership?.roles.includes('group_admin')
|| (membership?.roles.includes('group_member') && group?.stats_visibility === 'private_show_agg_and_ind')
|| (membership?.roles.includes('group_member') && group?.stats_visibility === 'public_agg_show_ind_if_member')
Expand All @@ -66,7 +68,7 @@ function GroupStats({
if (showTopContributors) {
allProjectsStatsQuery.top_contributors = 10
}

const {
data: allProjectsStats,
error: statsError,
Expand All @@ -77,14 +79,14 @@ function GroupStats({
sourceId: paramsValidationMessage ? null : group?.id,
query: allProjectsStatsQuery
})

// fetch individual project stats
const projectStatsQuery = getDateInterval(selectedDateRange)
projectStatsQuery.project_id = parseInt(selectedProject)
if (showTopContributors) {
projectStatsQuery.top_contributors = 10
}

const {
data: projectStats,
error: projectStatsError,
Expand All @@ -109,7 +111,7 @@ function GroupStats({
error: topContributorsError,
isLoading: topContributorsLoading
} = usePanoptesUsers(usersQuery)

// fetch projects
const projectIds = allProjectsStats?.project_contributions?.map(project => project.project_id)
const projectsQuery = {
Expand All @@ -135,13 +137,13 @@ function GroupStats({
async function handleGroupMembershipLeave ({
membershipId
}) {
const userConfirmed = window.confirm('Are you sure you want to leave this group?')
const userConfirmed = window.confirm(t('GroupStats.leaveQuestion'))
if (!userConfirmed) return

await deleteMembership({ membershipId }, {
revalidate: true
})

window.location.href = '/'
}

Expand All @@ -152,7 +154,8 @@ function GroupStats({
group,
handleGroupMembershipLeave,
handleGroupModalActive,
membership
membership,
t
})

const error = statsError || projectStatsError || projectsError
Expand All @@ -163,7 +166,7 @@ function GroupStats({
<GroupModal
active={groupModalActive}
handleClose={handleGroupModalActive}
title='manage group'
title={t('GroupStats.manage')}
titleColor='black'
>
<GroupUpdateFormContainer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { bool, func, node, shape, string } from 'prop-types'
import useSWRMutation from 'swr/mutation'
import { useTranslation } from '../../../../translations/i18n.js'

import {
deletePanoptesUserGroup,
Expand All @@ -18,6 +19,7 @@ function GroupUpdateFormContainer({
handleGroupModalActive = DEFAULT_HANDLER,
login
}) {
const { t } = useTranslation()
const { trigger: updateGroup } = useSWRMutation({
adminMode,
authUserId,
Expand All @@ -28,7 +30,7 @@ function GroupUpdateFormContainer({
try {
const deleteResponse = await deletePanoptesUserGroup({ groupId: group?.id })
if (!deleteResponse.ok) {
await alert(`Something went wrong. Please try again.\nError: ${deleteResponse?.statusText}`)
await alert(`${t('GroupStats.GroupUpdateFormContainer.error')} \n ${deleteResponse?.statusText}`)
return console.error(deleteResponse)
} else {
window.location.href = `/users/${login}/groups`
Expand All @@ -37,15 +39,15 @@ function GroupUpdateFormContainer({
console.error(error)
}
}

async function onSubmit(event) {
const { display_name, stats_visibility } = event.value
const data = {
display_name,
private: stats_visibility.startsWith('private'),
stats_visibility
}

try {
updateGroup({ groupId: group.id, data }, {
optimisticData: { ...group, ...data },
Expand Down
Loading
Loading