From 5400f6ffd441c9e6e1d1cf27361556b2aebd1b0c Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Wed, 29 Jul 2026 17:13:38 +0300 Subject: [PATCH 1/8] feat: new list of oracles and oracle card --- .../assets/icons-dark-mode/content-copy.svg | 3 - .../src/assets/icons/content-copy.svg | 3 - .../frontend/src/assets/icons/triangle.svg | 3 + .../worker/jobs-discovery/components/index.ts | 2 +- .../components/oracle-job-card.tsx | 266 ++++++++++++++++++ .../components/oracles-list.tsx | 54 ++++ .../components/oracles-table.tsx | 69 ----- .../jobs-discovery/jobs-discovery.page.tsx | 91 +++++- .../worker/profile/views/profile.page.tsx | 62 +--- .../worker/services/oracles.service.ts | 48 +++- .../drawer-menu-items-worker.tsx | 14 - .../components/drawer-menu-items/index.ts | 1 - .../frontend/src/router/components/index.ts | 1 - .../layout/protected/desktop-aside-bar.tsx | 76 ++++- .../frontend/src/router/router-paths.ts | 1 + .../ui/copy-to-clipboard-button.tsx | 60 ++++ .../src/shared/components/ui/icons.tsx | 124 +++++++- .../src/shared/components/ui/no-records.tsx | 7 +- .../ui/page-card/page-card-error.tsx | 36 +-- .../shared/components/ui/page-card/styles.ts | 2 +- .../human-app/frontend/src/shared/consts.ts | 2 +- .../frontend/src/shared/i18n/en.json | 15 +- 22 files changed, 737 insertions(+), 203 deletions(-) delete mode 100644 packages/apps/human-app/frontend/src/assets/icons-dark-mode/content-copy.svg delete mode 100644 packages/apps/human-app/frontend/src/assets/icons/content-copy.svg create mode 100644 packages/apps/human-app/frontend/src/assets/icons/triangle.svg create mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx create mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx delete mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-table.tsx delete mode 100644 packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx delete mode 100644 packages/apps/human-app/frontend/src/router/components/drawer-menu-items/index.ts create mode 100644 packages/apps/human-app/frontend/src/shared/components/ui/copy-to-clipboard-button.tsx diff --git a/packages/apps/human-app/frontend/src/assets/icons-dark-mode/content-copy.svg b/packages/apps/human-app/frontend/src/assets/icons-dark-mode/content-copy.svg deleted file mode 100644 index c4c4f89b16..0000000000 --- a/packages/apps/human-app/frontend/src/assets/icons-dark-mode/content-copy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/apps/human-app/frontend/src/assets/icons/content-copy.svg b/packages/apps/human-app/frontend/src/assets/icons/content-copy.svg deleted file mode 100644 index 4423c76961..0000000000 --- a/packages/apps/human-app/frontend/src/assets/icons/content-copy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/apps/human-app/frontend/src/assets/icons/triangle.svg b/packages/apps/human-app/frontend/src/assets/icons/triangle.svg new file mode 100644 index 0000000000..917cd60674 --- /dev/null +++ b/packages/apps/human-app/frontend/src/assets/icons/triangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/index.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/index.ts index 9bc965d5af..e8eb2de22d 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/index.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/index.ts @@ -1,4 +1,4 @@ export * from './oracles-table-job-types-select'; -export * from './oracles-table'; +export * from './oracles-list'; export * from './oracles-table-item-mobile'; export * from './oracles-table-desktop'; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx new file mode 100644 index 0000000000..576502b41b --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx @@ -0,0 +1,266 @@ +import { useState } from 'react'; +import { + Box, + Card, + CardContent, + Collapse, + Stack, + Tooltip, + Typography, +} from '@mui/material'; +import { t } from 'i18next'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + +import { Oracle } from '../../services/oracles.service'; +import { + MenuIcon, + OracleAddressIcon, + OracleRewardIcon, +} from '@/shared/components/ui/icons'; +import { useColorMode } from '@/shared/contexts/color-mode'; +import { shortenEscrowAddress } from '@/shared/helpers/evm'; +import { CopyToClipboardButton } from '@/shared/components/ui/copy-to-clipboard-button'; +import { useIsMobile } from '@/shared/hooks/use-is-mobile'; +import { Button } from '@/shared/components/ui/button'; +import { JOB_TYPES } from '@/shared/consts'; +import { JobType } from '@/modules/smart-contracts/EthKVStore/config'; +import { Chip } from '@/shared/components/ui/chip'; + +function JobTypesTooltipTitle({ jobTypes }: { jobTypes: string[] }) { + return ( + + {jobTypes.map((jobType) => { + const element = JOB_TYPES.find((j) => j === jobType) as JobType; + const label = t(`jobTypeLabels.${element}`); + return ( + + {label} + + ); + })} + + ); +} + +export function OracleJobCard({ oracle }: { oracle: Oracle }) { + const [isTaskTypesOpen, setIsTaskTypesOpen] = useState(false); + + const { colorPalette } = useColorMode(); + const isMobile = useIsMobile(); + + return ( + + + + {oracle.name} + + + + + + {t('worker.oraclesList.address')}: + + + {shortenEscrowAddress(oracle.address, 4, 4)} + + + + + + + {t('worker.oraclesList.reward')}: + + + 1 HMT - 2 HMT + + + + + + {isMobile ? ( + + ) : ( + } + disableHoverListener={isMobile} + disableFocusListener={isMobile} + disableTouchListener={isMobile} + slotProps={{ + tooltip: { + sx: { + p: 1.5, + bgcolor: colorPalette.background.paper, + boxShadow: 'none', + borderRadius: '15px', + border: `1px solid ${colorPalette.border.main}`, + }, + }, + }} + > + + + {oracle.jobTypes.length}{' '} + {oracle.jobTypes.length > 1 + ? t('worker.oraclesList.taskTypes') + : t('worker.oraclesList.taskType')} + + + )} + + + + + {oracle.jobTypes.map((jobType) => { + const element = JOB_TYPES.find((j) => j === jobType) as JobType; + const label = t(`jobTypeLabels.${element}`); + return ( + + ); + })} + + + + + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx new file mode 100644 index 0000000000..264a4ddaf0 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx @@ -0,0 +1,54 @@ +import { Grid, Stack } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +import { Loader } from '@/shared/components/ui/loader'; +import { NoRecords } from '@/shared/components/ui/no-records'; +import { PageCardError } from '@/shared/components/ui/page-card'; +import { OracleJobCard } from './oracle-job-card'; +import { Oracle } from '../../services/oracles.service'; + +type OraclesListProps = { + data: Oracle[] | undefined; + isError: boolean; + isPending: boolean; +}; + +export function OraclesList({ data, isError, isPending }: OraclesListProps) { + const { t } = useTranslation(); + + if (isPending) { + return ( + + + + ); + } + + if (isError) { + return ( + + ); + } + + if (!data?.length) { + return ; + } + + return ( + + {data.map((oracle) => ( + + + + ))} + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-table.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-table.tsx deleted file mode 100644 index 9e19e8af0e..0000000000 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Stack, Grid } from '@mui/material'; -import { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '@/shared/hooks/use-is-mobile'; -import { Loader } from '@/shared/components/ui/loader'; -import { NoRecords } from '@/shared/components/ui/no-records'; -import { useGetOraclesNotifications } from '@/modules/worker/hooks/use-get-oracles-notifications'; -import { PageCardError } from '@/shared/components/ui/page-card'; -import { useGetOracles } from '../../hooks'; -import { OraclesTableItemMobile } from './oracles-table-item-mobile'; -import { OraclesTableDesktop } from './oracles-table-desktop'; - -export function OraclesTable() { - const isMobile = useIsMobile(); - const { t } = useTranslation(); - const { onError } = useGetOraclesNotifications(); - const { - data: oraclesData, - isError: isOraclesDataError, - isPending: isOraclesDataPending, - error: oraclesDataError, - } = useGetOracles(); - - useEffect(() => { - if (oraclesDataError) { - onError(oraclesDataError); - } - }, [oraclesDataError, onError]); - - if (isOraclesDataPending) { - return ( - - - - ); - } - - if (isOraclesDataError) { - return ( - - ); - } - - if (!oraclesData.length) { - return ; - } - - return isMobile ? ( - - {oraclesData.map((oracle) => ( - - ))} - - ) : ( - - ); -} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx index 6fcb56287a..b42e58b73a 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx @@ -1,25 +1,88 @@ -import { Paper, Stack } from '@mui/material'; +import { useEffect } from 'react'; +import { Box, Stack, Typography } from '@mui/material'; +import { t } from 'i18next'; + +import { OraclesList } from './components'; +import { useColorMode } from '@/shared/contexts/color-mode'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; -import { OraclesTableJobTypesSelect, OraclesTable } from './components'; +import { useGetOracles } from '../hooks'; +import { useGetOraclesNotifications } from '../hooks/use-get-oracles-notifications'; + +const bull = ( + + • + +); export function JobsDiscoveryPage() { + const { colorPalette } = useColorMode(); const isMobile = useIsMobile(); + const { onError } = useGetOraclesNotifications(); + const { + data: oraclesData, + isError, + isPending, + isSuccess, + error, + } = useGetOracles(); + + useEffect(() => { + if (error) { + onError(error); + } + }, [error, onError]); + + const oraclesCount = oraclesData?.length ?? 0; + return ( - - + - - - + > + + + + {t('worker.oraclesList.jobOracles')} + + {isSuccess && ( + <> + {bull} + + {oraclesCount}{' '} + {oraclesCount === 1 + ? t('worker.oraclesList.source') + : t('worker.oraclesList.sources')} + + + )} + + + ); } diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx index 5db121de0c..bd51c58944 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx @@ -1,5 +1,5 @@ -import { Box, IconButton, Stack, Tooltip, Typography } from '@mui/material'; -import { useEffect, useRef, useState, type MouseEvent } from 'react'; +import { useEffect } from 'react'; +import { Box, Stack, Typography } from '@mui/material'; import { t } from 'i18next'; import { Link, useNavigate } from 'react-router-dom'; @@ -11,22 +11,16 @@ import { } from '@/shared/hooks/use-notification'; import { useColorMode } from '@/shared/contexts/color-mode/use-color-mode'; import { ProfileData } from '../components/profile-data'; -import { - CheckmarkIcon, - CopyIcon, - LogoutIcon, -} from '@/shared/components/ui/icons'; +import { CheckmarkIcon, LogoutIcon } from '@/shared/components/ui/icons'; import { shortenEscrowAddress } from '@/shared/helpers/evm'; import { Button } from '@/shared/components/ui/button'; import { browserAuthProvider } from '@/shared/contexts/browser-auth-provider'; import { routerPaths } from '@/router/router-paths'; import { BackButton } from '@/shared/components/ui/page-card/back-button'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; +import { CopyToClipboardButton } from '@/shared/components/ui/copy-to-clipboard-button'; export function WorkerProfilePage() { - const [isCopied, setIsCopied] = useState(false); - const timeoutRef = useRef(null); - const { user } = useAuthenticatedUser(); const { colorPalette } = useColorMode(); const { isConnected, initializing, web3ProviderMutation } = @@ -39,22 +33,6 @@ export function WorkerProfilePage() { navigate(routerPaths.worker.jobsDiscovery); }; - const handleCopyClick = (e: MouseEvent) => { - if (isCopied) return; - - e.stopPropagation(); - navigator.clipboard.writeText(user.wallet_address ?? ''); - setIsCopied(true); - - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - - timeoutRef.current = setTimeout(() => { - setIsCopied(false); - }, 1500); - }; - const handleSignOut = () => { browserAuthProvider.signOut({ callback: () => { @@ -63,15 +41,6 @@ export function WorkerProfilePage() { }); }; - useEffect(() => { - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - timeoutRef.current = null; - } - }; - }, []); - useEffect(() => { if (initializing) return; @@ -209,6 +178,9 @@ export function WorkerProfilePage() { {shortenEscrowAddress(user.wallet_address ?? '', 9, 8)} - - - - - + diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts index 518006885e..79a1e17c99 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts @@ -38,6 +38,51 @@ const H_CAPTCHA_ORACLE: Oracle = { registrationNeeded: false, }; +const MOCKED_ORACLES: Oracle[] = [ + { + address: '0x1111111111111111111111111111111111111111', + chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + jobTypes: ['image_points'], + role: 'Recording Oracle', + url: 'https://example.com/oracle-1', + name: 'Mock Oracle 1', + registrationNeeded: false, + }, + { + address: '0x2222222222222222222222222222222222222222', + chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + jobTypes: ['image_points', 'image_boxes'], + role: 'Recording Oracle', + url: 'https://example.com/oracle-2', + name: 'Mock Oracle 2', + registrationNeeded: false, + }, + { + address: '0x3333333333333333333333333333333333333333', + chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + jobTypes: ['image_points', 'image_boxes'], + role: 'Recording Oracle', + url: 'https://example.com/oracle-3', + name: 'Mock Oracle 3', + registrationNeeded: false, + }, + { + address: '0x4444444444444444444444444444444444444444', + chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + jobTypes: [ + 'image_points', + 'image_boxes', + 'image_polygons', + 'social_media_engagement', + ], + role: 'Recording Oracle', + url: 'https://example.com/oracle-4', + name: 'Mock Oracle 4', + registrationNeeded: false, + }, + // repeat 4 more with unique addresses/names +]; + async function getOracles(selectedJobTypes: string[]) { try { const params = selectedJobTypes.length @@ -52,7 +97,8 @@ async function getOracles(selectedJobTypes: string[]) { selectedJobTypes.length === 0 || selectedJobTypes.some((t) => H_CAPTCHA_ORACLE.jobTypes.includes(t)) ) { - oracles.push(H_CAPTCHA_ORACLE); + //oracles.push(H_CAPTCHA_ORACLE); + oracles.push(...MOCKED_ORACLES); } if (env.VITE_FEATURE_FLAG_JOBS_DISCOVERY) { diff --git a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx b/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx deleted file mode 100644 index 1c28472e9b..0000000000 --- a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/drawer-menu-items-worker.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { t } from 'i18next'; -import type { UserData } from '@/modules/auth/context/auth-context'; -import { routerPaths } from '@/router/router-paths'; -import { KycStatus } from '@/modules/worker/profile/types'; - -export const workerDrawerTopMenuItems = (user: UserData | null) => { - return [ - { - label: t('components.DrawerNavigation.jobs'), - link: routerPaths.worker.jobsDiscovery, - disabled: !user?.wallet_address || user.kyc_status !== KycStatus.APPROVED, - }, - ]; -}; diff --git a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/index.ts b/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/index.ts deleted file mode 100644 index 8dd1acfaae..0000000000 --- a/packages/apps/human-app/frontend/src/router/components/drawer-menu-items/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './drawer-menu-items-worker'; diff --git a/packages/apps/human-app/frontend/src/router/components/index.ts b/packages/apps/human-app/frontend/src/router/components/index.ts index 0a514e542c..c78a32e4dc 100644 --- a/packages/apps/human-app/frontend/src/router/components/index.ts +++ b/packages/apps/human-app/frontend/src/router/components/index.ts @@ -1,3 +1,2 @@ -export * from './drawer-menu-items'; export * from './layout/protected'; export * from './layout/unprotected'; diff --git a/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx b/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx index 051f917544..3de8970006 100644 --- a/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx +++ b/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx @@ -1,17 +1,47 @@ +import { + Box, + IconButton, + List, + ListItem, + ListItemButton, + Stack, +} from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { t } from 'i18next'; +import { Link } from 'react-router-dom'; + import { ProfileData } from '@/modules/worker/profile/components/profile-data'; +import { routerPaths } from '@/router/router-paths'; import { Button } from '@/shared/components/ui/button'; import { ColorModeSwitch } from '@/shared/components/ui/dark-mode-switch'; -import { HelpIcon, HumanLogoNavbarIcon } from '@/shared/components/ui/icons'; +import { + HelpIcon, + HumanLogoNavbarIcon, + TriangleIcon, +} from '@/shared/components/ui/icons'; import { useColorMode } from '@/shared/contexts/color-mode/use-color-mode'; import { useHandleMainNavIconClick } from '@/shared/hooks/use-handle-main-nav-icon-click'; -import { Box, IconButton, Stack } from '@mui/material'; -import { useTranslation } from 'react-i18next'; + +const menuItems = [ + { + label: t('components.DrawerNavigation.availableJobs'), + href: routerPaths.worker.jobsDiscovery, + }, + { + label: t('components.DrawerNavigation.myJobs'), + href: routerPaths.worker.myJobs, + }, +]; export function DesktopAsideBar() { - const { colorPalette } = useColorMode(); + const { colorPalette, isDarkMode } = useColorMode(); const { t } = useTranslation(); const handleMainNavIconClick = useHandleMainNavIconClick(); + const activeLinkGradient = isDarkMode + ? 'linear-gradient(90deg, rgba(212, 207, 255, 0.10) 0%, rgba(37, 29, 71, 0.10) 90%)' + : 'linear-gradient(90deg, rgba(50, 10, 141, 0.10) 0%, rgba(255, 255, 255, 0.10) 90%)'; + return ( - + + + {menuItems.map((item) => { + const { href, label } = item; + const isActive = window.location.pathname === href; + return ( + + + {isActive && } + {label} + + + ); + })} + + ; +}; + +export function CopyToClipboardButton({ value, sx }: Props) { + const [isCopied, setIsCopied] = useState(false); + const timeoutRef = useRef(null); + + const handleCopyClick = (e: MouseEvent) => { + if (isCopied) return; + + e.stopPropagation(); + navigator.clipboard.writeText(value ?? ''); + setIsCopied(true); + + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + timeoutRef.current = setTimeout(() => { + setIsCopied(false); + }, 1500); + }; + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + }; + }, []); + + return ( + + + + + + ); +} diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx index f05cdb0c89..422e1f4a8a 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx @@ -22,8 +22,6 @@ import SunIconLight from '@/assets/icons/sun.svg'; import MoonIconDark from '@/assets/icons-dark-mode/moon.svg'; import MoonIconLight from '@/assets/icons/moon.svg'; import { useColorMode } from '@/shared/contexts/color-mode'; -import CopyIconLight from '@/assets/icons/content-copy.svg'; -import CopyIconDark from '@/assets/icons-dark-mode/content-copy.svg'; import EditIconLight from '@/assets/icons/edit-icon.svg'; import EditIconDark from '@/assets/icons-dark-mode/edit-icon.svg'; import DeleteIconLight from '@/assets/icons/delete-icon.svg'; @@ -33,6 +31,7 @@ import VeriffIconDark from '@/assets/icons-dark-mode/veriff.svg'; import HourglassIconLight from '@/assets/icons/hourglass.svg'; import HourglassIconDark from '@/assets/icons-dark-mode/hourglass.svg'; import LogoutIcon from '@/assets/icons/logout.svg'; +import TriangleIcon from '@/assets/icons/triangle.svg'; function HumanLogoIcon() { const { isDarkMode } = useColorMode(); @@ -78,10 +77,6 @@ function MoonIcon() { const { isDarkMode } = useColorMode(); return isDarkMode ? : ; } -function CopyIcon() { - const { isDarkMode } = useColorMode(); - return isDarkMode ? : ; -} function EditIcon() { const { isDarkMode } = useColorMode(); return isDarkMode ? : ; @@ -90,6 +85,15 @@ function DeleteIcon() { const { isDarkMode } = useColorMode(); return isDarkMode ? : ; } +function VeriffIcon() { + const { isDarkMode } = useColorMode(); + return isDarkMode ? : ; +} +function HourglassIcon() { + const { isDarkMode } = useColorMode(); + return isDarkMode ? : ; +} + function InboxIcon(props: SvgIconProps) { return ( @@ -142,14 +146,106 @@ function SuccessIcon(props: SvgIconProps) { ); } -function VeriffIcon() { - const { isDarkMode } = useColorMode(); - return isDarkMode ? : ; +function CopyIcon(props: SvgIconProps) { + return ( + + + + + + + + + + + + ); } -function HourglassIcon() { - const { isDarkMode } = useColorMode(); - return isDarkMode ? : ; +function OracleAddressIcon(props: SvgIconProps) { + return ( + + + + + ); +} + +function OracleRewardIcon(props: SvgIconProps) { + return ( + + + + + ); +} + +function MenuIcon(props: SvgIconProps) { + return ( + + + + + + ); } export { @@ -172,4 +268,8 @@ export { VeriffIcon, HourglassIcon, LogoutIcon, + TriangleIcon, + OracleAddressIcon, + OracleRewardIcon, + MenuIcon, }; diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/no-records.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/no-records.tsx index 190638947e..cf7b5fae01 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/no-records.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/no-records.tsx @@ -1,4 +1,5 @@ import { Grid } from '@mui/material'; +import { t } from 'i18next'; import { useColorMode } from '@/shared/contexts/color-mode'; export function NoRecords() { @@ -7,13 +8,13 @@ export function NoRecords() { return ( - No records to display + {t('components.noRecords')} ); } diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-error.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-error.tsx index 0c38cc4720..106aa870ae 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-error.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-error.tsx @@ -1,8 +1,7 @@ -import { Grid, Stack } from '@mui/material'; +import { Grid } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import { t } from 'i18next'; import { Button } from '@/shared/components/ui/button'; -import { routerPaths } from '@/router/router-paths'; import { Alert } from '@/shared/components/ui/alert'; import { useColorMode } from '@/shared/contexts/color-mode'; import { commonDarkPageCardStyles, commonPageCardStyles } from './styles'; @@ -24,31 +23,20 @@ export function PageCardError({ : commonStyleForTheme; return ( - + {errorMessage} - - - - + ); } diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts index e26f4b48a7..30fab1d441 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts +++ b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts @@ -8,7 +8,7 @@ export const commonPageCardStyles: SxProps = { justifyContent: 'center', alignItems: 'center', borderRadius: '20px', - minHeight: '70vh', + //minHeight: '70vh', maxWidth: '1600px', width: '100%', background: constColorPalette.white, diff --git a/packages/apps/human-app/frontend/src/shared/consts.ts b/packages/apps/human-app/frontend/src/shared/consts.ts index abfe8a21bc..7ba98cc59e 100644 --- a/packages/apps/human-app/frontend/src/shared/consts.ts +++ b/packages/apps/human-app/frontend/src/shared/consts.ts @@ -1,4 +1,4 @@ import { JobType } from '@/modules/smart-contracts/EthKVStore/config'; export const JOB_TYPES = Object.values(JobType); -export const MOBILE_BOTTOM_TRAY_HEIGHT = '110px'; +export const MOBILE_BOTTOM_TRAY_HEIGHT = '90px'; diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index f4b9c5984b..fd0e2e1e15 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -61,6 +61,8 @@ "jobs": "Tasks", "captchaLabeling": "hCaptcha labeling", "jobsDiscovery": "Jobs discovery", + "availableJobs": "Available Jobs", + "myJobs": "My Jobs", "profile": "Profile", "help": "Help", "logout": "Log Out" @@ -91,7 +93,8 @@ "reload": "Reload", "goHome": "Home Page" }, - "copyToClipboard": "Copied" + "copyToClipboard": "Copied", + "noRecords": "No records to display" }, "homepage": { "humanApp": "HUMAN App", @@ -243,6 +246,16 @@ "abandoned": "Abandoned" } }, + "oraclesList": { + "jobOracles": "Job Oracles", + "address": "Address", + "reward": "Reward", + "taskTypes": "Task types", + "taskType": "Task type", + "exploreTasks": "Explore Tasks", + "source": "source", + "sources": "sources" + }, "oraclesTable": { "annotationTool": "Annotation tool", "oracleAddress": "Oracle address", From 9d6fcc1597bbb7d23e25fc7ad485c57b3b105d3a Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Wed, 29 Jul 2026 20:31:46 +0300 Subject: [PATCH 2/8] feat: hcaptcha widget --- .../frontend/src/assets/icons/hcaptcha.svg | 46 +++++++++ .../hcaptcha-labeling.page.tsx | 10 +- .../components/hcaptha-widget.tsx | 98 +++++++++++++++++++ .../components/oracle-job-card.tsx | 4 +- .../worker/jobs-discovery/helpers/index.ts | 1 - .../helpers/is-hcaptcha-oracle.spec.ts | 39 -------- .../helpers/is-hcaptcha-oracle.ts | 4 - .../hooks/use-select-oracle-navigation.ts | 24 +---- .../jobs-discovery/jobs-discovery.page.tsx | 20 +++- .../src/shared/components/ui/icons.tsx | 5 + .../frontend/src/shared/i18n/en.json | 6 ++ 11 files changed, 180 insertions(+), 77 deletions(-) create mode 100644 packages/apps/human-app/frontend/src/assets/icons/hcaptcha.svg create mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx delete mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.spec.ts delete mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.ts diff --git a/packages/apps/human-app/frontend/src/assets/icons/hcaptcha.svg b/packages/apps/human-app/frontend/src/assets/icons/hcaptcha.svg new file mode 100644 index 0000000000..65989c188c --- /dev/null +++ b/packages/apps/human-app/frontend/src/assets/icons/hcaptcha.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx index 8ff36d9b51..0ee1281f7f 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx @@ -4,7 +4,6 @@ import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; import { useRef } from 'react'; import { env } from '@/shared/env'; -import { breakpoints } from '@/shared/styles/breakpoints'; import { Counter } from '@/shared/components/ui/counter'; import { getErrorMessageForError } from '@/shared/errors'; import { getTomorrowDate } from '@/shared/helpers/date'; @@ -118,11 +117,8 @@ export function HcaptchaLabelingPage() { alignItems: 'flex-start', justifyContent: 'center', height: '100%', - width: '376px', - gap: '52px', - [breakpoints.mobile]: { - width: 'unset', - }, + width: { xs: 'unset', md: '376px' }, + gap: 6, }} > @@ -138,7 +134,7 @@ export function HcaptchaLabelingPage() { ref={captchaRef} reportapi={env.VITE_H_CAPTCHA_LABELING_BASE_URL} sitekey={user.site_key ?? ''} - theme={isDarkMode ? 'dark' : 'light'} + theme="contrast" /> ) : ( diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx new file mode 100644 index 0000000000..bc2885a09d --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx @@ -0,0 +1,98 @@ +import { Box, Stack, Typography } from '@mui/material'; +import { Link } from 'react-router-dom'; +import { t } from 'i18next'; + +import { Button } from '@/shared/components/ui/button'; +import { HcaptchaIcon } from '@/shared/components/ui/icons'; +import { useColorMode } from '@/shared/contexts/color-mode'; +import { routerPaths } from '@/router/router-paths'; +import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user'; +import { useIsMobile } from '@/shared/hooks/use-is-mobile'; +import { useMemo } from 'react'; + +const getHCaptchaPagePath = (siteKey: string | null | undefined): string => + siteKey + ? routerPaths.worker.HcaptchaLabeling + : routerPaths.worker.enableLabeler; + +const renderDescription = (color: string, isMobile: boolean) => { + return ( + + {isMobile + ? t('worker.hcaptchaWidget.shortDescription') + : t('worker.hcaptchaWidget.description')} + + ); +}; + +export function HCaptchaWidget() { + const { colorPalette } = useColorMode(); + const isMobile = useIsMobile(); + const { user } = useAuthenticatedUser(); + + const hCaptchaPagePath = useMemo( + () => getHCaptchaPagePath(user.site_key), + [user.site_key] + ); + + console.log(user); + + return ( + + + + + + {t('worker.hcaptchaWidget.title')} + + {!isMobile && + renderDescription(colorPalette.text.auxiliary200, isMobile)} + + + + {isMobile && + renderDescription(colorPalette.text.auxiliary200, isMobile)} + + + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx index 576502b41b..cff8e77a91 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx @@ -79,7 +79,8 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { }} > diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/index.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/index.ts index 7db6869470..c7ea564552 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/index.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/index.ts @@ -1,2 +1 @@ -export * from './is-hcaptcha-oracle'; export * from './should-navigate-to-registration'; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.spec.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.spec.ts deleted file mode 100644 index 117e9daaf9..0000000000 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; -import { faker } from '@faker-js/faker'; -import { env } from '@/shared/env'; -import { isHCaptchaOracle } from './is-hcaptcha-oracle'; - -vi.mock('@/shared/env', () => ({ - env: { - VITE_H_CAPTCHA_ORACLE_ADDRESS: '0x1234567890abcdef1234567890abcdef12345678', - }, -})); - -describe('isHCaptchaOracle Helper', () => { - it('should return true when the address matches the hCaptcha oracle address', () => { - const result = isHCaptchaOracle( - '0x1234567890abcdef1234567890abcdef12345678' - ); - expect(result).toBe(true); - }); - - it('should return false when the address does not match the hCaptcha oracle address', () => { - const result = isHCaptchaOracle(faker.finance.ethereumAddress()); - expect(result).toBe(false); - }); - - it('should handle case sensitivity correctly', () => { - const mixedCaseAddress = env.VITE_H_CAPTCHA_ORACLE_ADDRESS.replace( - 'abcdef', - 'ABCDEF' - ); - - const result = isHCaptchaOracle(mixedCaseAddress); - expect(result).toBe(true); - }); - - it('should handle empty strings', () => { - const result = isHCaptchaOracle(''); - expect(result).toBe(false); - }); -}); diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.ts deleted file mode 100644 index 9b5cefc8a3..0000000000 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/is-hcaptcha-oracle.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { env } from '@/shared/env'; - -export const isHCaptchaOracle = (address: string): boolean => - address.toLowerCase() === env.VITE_H_CAPTCHA_ORACLE_ADDRESS.toLowerCase(); diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/hooks/use-select-oracle-navigation.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/hooks/use-select-oracle-navigation.ts index 1c81650c57..9879955be2 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/hooks/use-select-oracle-navigation.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/hooks/use-select-oracle-navigation.ts @@ -1,26 +1,15 @@ +import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; -import { useCallback, useMemo } from 'react'; -import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user'; + import { routerPaths } from '@/router/router-paths'; -import { shouldNavigateToRegistration, isHCaptchaOracle } from '../helpers'; +import { shouldNavigateToRegistration } from '../helpers'; import { type Oracle } from '../../services/oracles.service'; import { useGetRegistrationDataInOracles } from './use-get-registration-data-oracles'; -const getHCaptchaPagePath = (siteKey: string | null | undefined): string => - siteKey - ? routerPaths.worker.HcaptchaLabeling - : routerPaths.worker.enableLabeler; - export const useSelectOracleNavigation = () => { const navigate = useNavigate(); - const { user } = useAuthenticatedUser(); const { data } = useGetRegistrationDataInOracles(); - const hCaptchaPagePath = useMemo( - () => getHCaptchaPagePath(user.site_key), - [user.site_key] - ); - const selectOracle = useCallback( (oracle: Oracle) => { if (shouldNavigateToRegistration(oracle, data)) { @@ -30,16 +19,11 @@ export const useSelectOracleNavigation = () => { return; } - if (isHCaptchaOracle(oracle.address)) { - navigate(hCaptchaPagePath); - return; - } - navigate(`${routerPaths.worker.jobs}/${oracle.address}`, { state: { oracle }, }); }, - [data, navigate, hCaptchaPagePath] + [data, navigate] ); return { selectOracle }; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx index b42e58b73a..5dbac68120 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx @@ -7,6 +7,7 @@ import { useColorMode } from '@/shared/contexts/color-mode'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; import { useGetOracles } from '../hooks'; import { useGetOraclesNotifications } from '../hooks/use-get-oracles-notifications'; +import { HCaptchaWidget } from './components/hcaptha-widget'; const bull = ( @@ -41,19 +42,28 @@ export function JobsDiscoveryPage() { sx={{ justifyContent: 'center', alignItems: 'space-between', - py: 6, - px: 4, + pt: { xs: 2, md: 0 }, + pb: { xs: 2, md: 4 }, + px: { xs: 1, md: 4 }, + mx: { xs: 2, md: 0 }, + borderRadius: { xs: '10px', md: 0 }, + border: { + xs: `1px solid ${colorPalette.border.main}`, + md: 'none', + }, borderBottom: { - xs: 'none', + xs: `1px solid ${colorPalette.border.main}`, md: `1px solid ${colorPalette.border.main}`, }, }} - > + > + + : ; } +function HcaptchaIcon() { + return ; +} function InboxIcon(props: SvgIconProps) { return ( @@ -272,4 +276,5 @@ export { OracleAddressIcon, OracleRewardIcon, MenuIcon, + HcaptchaIcon, }; diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index fd0e2e1e15..54486da332 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -266,6 +266,12 @@ "gettingOracles": "There was an error while fetching oracles, please try again later." } }, + "hcaptchaWidget": { + "title": "hCaptcha task available!", + "description": "Complete image labeling challenges and earn rewards by helping train privacy-preserving hCaptcha models.", + "shortDescription": "Complete image labeling challenges and earn rewards.", + "startTask": "Start Task" + }, "registrationInExchangeOracle": { "requiredMessage": "This oracle requires a registration process. Click on the button below to see the registration tutorial:", "instructionsButton": "See registration tutorial", From 294846e421f786defb28001aa08b8d7ea8c331b4 Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Thu, 30 Jul 2026 15:20:29 +0300 Subject: [PATCH 3/8] feat: disabled state of hcaptcha widget --- .../hcaptcha-disabled-icon.svg | 9 + .../assets/icons/hcaptcha-disabled-icon.svg | 9 + .../components/hcaptha-widget.tsx | 274 +++++++++++++++--- .../jobs-discovery/jobs-discovery.page.tsx | 6 +- .../src/shared/components/ui/icons.tsx | 11 + .../frontend/src/shared/i18n/en.json | 6 +- 6 files changed, 263 insertions(+), 52 deletions(-) create mode 100644 packages/apps/human-app/frontend/src/assets/icons-dark-mode/hcaptcha-disabled-icon.svg create mode 100644 packages/apps/human-app/frontend/src/assets/icons/hcaptcha-disabled-icon.svg diff --git a/packages/apps/human-app/frontend/src/assets/icons-dark-mode/hcaptcha-disabled-icon.svg b/packages/apps/human-app/frontend/src/assets/icons-dark-mode/hcaptcha-disabled-icon.svg new file mode 100644 index 0000000000..c4d02582e3 --- /dev/null +++ b/packages/apps/human-app/frontend/src/assets/icons-dark-mode/hcaptcha-disabled-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/apps/human-app/frontend/src/assets/icons/hcaptcha-disabled-icon.svg b/packages/apps/human-app/frontend/src/assets/icons/hcaptcha-disabled-icon.svg new file mode 100644 index 0000000000..4307dd8758 --- /dev/null +++ b/packages/apps/human-app/frontend/src/assets/icons/hcaptcha-disabled-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx index bc2885a09d..14caf0eb31 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx @@ -1,14 +1,25 @@ +import { useMemo } from 'react'; import { Box, Stack, Typography } from '@mui/material'; -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { t } from 'i18next'; +import { env } from '@/shared/env'; import { Button } from '@/shared/components/ui/button'; -import { HcaptchaIcon } from '@/shared/components/ui/icons'; +import { + HcaptchaDisabledIcon, + HcaptchaIcon, +} from '@/shared/components/ui/icons'; import { useColorMode } from '@/shared/contexts/color-mode'; import { routerPaths } from '@/router/router-paths'; import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; -import { useMemo } from 'react'; +import { + useHCaptchaUserStats, + useDailyHmtSpent, +} from '../../hcaptcha-labeling/hooks'; +import { getTomorrowDate } from '@/shared/helpers/date'; +import { Counter } from '@/shared/components/ui/counter'; +import { Loader } from '@/shared/components/ui/loader'; const getHCaptchaPagePath = (siteKey: string | null | undefined): string => siteKey @@ -16,83 +27,250 @@ const getHCaptchaPagePath = (siteKey: string | null | undefined): string => : routerPaths.worker.enableLabeler; const renderDescription = (color: string, isMobile: boolean) => { + let description = ''; + + if (isMobile) { + description = t('worker.hcaptchaWidget.shortDescription'); + } else { + description = t('worker.hcaptchaWidget.description'); + } + return ( - {isMobile - ? t('worker.hcaptchaWidget.shortDescription') - : t('worker.hcaptchaWidget.description')} + {description} ); }; export function HCaptchaWidget() { const { colorPalette } = useColorMode(); - const isMobile = useIsMobile(); const { user } = useAuthenticatedUser(); + const isMobile = useIsMobile(); + const navigate = useNavigate(); + + const { data: hcaptchaUserStats, isPending: isHcaptchaUserStatsPending } = + useHCaptchaUserStats(); + + const { data: dailyHmtSpent, isPending: isDailyHmtSpentPending } = + useDailyHmtSpent(); const hCaptchaPagePath = useMemo( () => getHCaptchaPagePath(user.site_key), [user.site_key] ); - console.log(user); + if (isHcaptchaUserStatsPending || isDailyHmtSpentPending) { + return ( + + + + ); + } + + const isCaptchaLimitReached = + !!dailyHmtSpent && + !!hcaptchaUserStats && + hcaptchaUserStats.currentDateStats.solved >= + env.VITE_DAILY_SOLVED_CAPTCHA_LIMIT && + dailyHmtSpent.spend >= env.VITE_HMT_DAILY_SPENT_LIMIT; return ( - - - - + + + + + {t('worker.hcaptchaWidget.titleDisabled')} + + {!isMobile && ( + + {t('worker.hcaptchaWidget.descriptionDisabled')} + + )} + + + + + {isMobile + ? t('worker.hcaptchaWidget.nextIn') + : t('worker.hcaptchaWidget.nextAvailable')} + + + { + navigate(0); + }} + /> + + + + ) : ( + <> + + + + + {t('worker.hcaptchaWidget.title')} + + {!isMobile && + renderDescription(colorPalette.text.auxiliary200, isMobile)} + + + - {t('worker.hcaptchaWidget.title')} - - {!isMobile && - renderDescription(colorPalette.text.auxiliary200, isMobile)} - - - - {isMobile && - renderDescription(colorPalette.text.auxiliary200, isMobile)} - - + {isMobile && + renderDescription(colorPalette.text.auxiliary200, isMobile)} + + + + )} ); } diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx index 5dbac68120..1f58c03768 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx @@ -42,9 +42,9 @@ export function JobsDiscoveryPage() { sx={{ justifyContent: 'center', alignItems: 'space-between', - pt: { xs: 2, md: 0 }, - pb: { xs: 2, md: 4 }, - px: { xs: 1, md: 4 }, + pt: { xs: 0, md: 0 }, + pb: { xs: 0, md: 4 }, + px: { xs: 0, md: 4 }, mx: { xs: 2, md: 0 }, borderRadius: { xs: '10px', md: 0 }, border: { diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx index 4a72b01d43..04c5512bc8 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/icons.tsx @@ -33,6 +33,8 @@ import HourglassIconDark from '@/assets/icons-dark-mode/hourglass.svg'; import LogoutIcon from '@/assets/icons/logout.svg'; import TriangleIcon from '@/assets/icons/triangle.svg'; import HcaptchaIconAsset from '@/assets/icons/hcaptcha.svg'; +import HcaptchaDisabledIconLight from '@/assets/icons/hcaptcha-disabled-icon.svg'; +import HcaptchaDisabledIconDark from '@/assets/icons-dark-mode/hcaptcha-disabled-icon.svg'; function HumanLogoIcon() { const { isDarkMode } = useColorMode(); @@ -97,6 +99,14 @@ function HourglassIcon() { function HcaptchaIcon() { return ; } +function HcaptchaDisabledIcon() { + const { isDarkMode } = useColorMode(); + return isDarkMode ? ( + + ) : ( + + ); +} function InboxIcon(props: SvgIconProps) { return ( @@ -277,4 +287,5 @@ export { OracleRewardIcon, MenuIcon, HcaptchaIcon, + HcaptchaDisabledIcon, }; diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index 54486da332..de2bedde09 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -268,9 +268,13 @@ }, "hcaptchaWidget": { "title": "hCaptcha task available!", + "titleDisabled": "No hCaptcha task available", "description": "Complete image labeling challenges and earn rewards by helping train privacy-preserving hCaptcha models.", "shortDescription": "Complete image labeling challenges and earn rewards.", - "startTask": "Start Task" + "descriptionDisabled": "You have reached the daily limit for the hCaptcha tasks, Please come back later.", + "startTask": "Start Task", + "nextAvailable": "Next Available in", + "nextIn": "Next in" }, "registrationInExchangeOracle": { "requiredMessage": "This oracle requires a registration process. Click on the button below to see the registration tutorial:", From ac36ee5c1506f80014d1690757ce4b6fb434a89b Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Fri, 31 Jul 2026 10:49:50 +0300 Subject: [PATCH 4/8] feat: redesign a captcha relaated pages and the user stats dialog; minor improvements --- .../signup/operator/views/add-stake.page.tsx | 12 +- .../components/breadcrumbs.tsx | 42 +++ .../components/user-stats-accordion.tsx | 59 ++-- .../components/user-stats-details.tsx | 60 ++-- .../components/user-stats-drawer.tsx | 132 +++++---- .../hcaptcha-labeling/enable-labeler.page.tsx | 114 ++++--- .../hcaptcha-labeling.page.tsx | 278 ++++++++++++------ .../hooks/enable-hcaptcha-labeling.ts | 4 +- .../components/hcaptha-widget.tsx | 2 +- .../components/layout/protected/layout.tsx | 71 +---- .../components/layout/protected/navbar.tsx | 87 +----- .../human-app/frontend/src/router/router.tsx | 8 +- .../shared/components/expiration-modal.tsx | 6 +- .../ui/page-card/page-card-error.tsx | 19 +- .../ui/page-card/page-card-loader.tsx | 18 +- .../components/ui/page-card/page-card.tsx | 14 +- .../shared/components/ui/page-card/styles.ts | 26 +- .../frontend/src/shared/hooks/index.ts | 1 - .../hooks/use-is-hcaptcha-labeling-page.ts | 7 - .../frontend/src/shared/i18n/en.json | 10 +- .../src/shared/styles/color-palette.ts | 1 + .../src/shared/styles/dark-color-palette.ts | 9 +- .../frontend/src/shared/styles/dark-theme.ts | 7 +- 23 files changed, 508 insertions(+), 479 deletions(-) create mode 100644 packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/breadcrumbs.tsx delete mode 100644 packages/apps/human-app/frontend/src/shared/hooks/use-is-hcaptcha-labeling-page.ts diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx index 9f3bb5c5b0..cdf8fd8a55 100644 --- a/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx @@ -10,7 +10,6 @@ import { import { getErrorMessageForError } from '@/shared/errors'; import { Alert } from '@/shared/components/ui/alert'; import { useColorMode } from '@/shared/contexts/color-mode'; -import { onlyDarkModeColor } from '@/shared/styles/dark-color-palette'; import { useAddStakeMutationState, useGetStakedAmount, @@ -20,7 +19,7 @@ import { StakeForm, Buttons } from '../components/add-stake'; import { stakedAmountFormatter } from '../utils'; export function AddStakeOperatorPage() { - const { colorPalette, isDarkMode } = useColorMode(); + const { colorPalette } = useColorMode(); const [displayForm, setDisplayForm] = useState(false); const { data: stakedAmount, @@ -87,14 +86,7 @@ export function AddStakeOperatorPage() { {t('operator.addStake.label')} - + {stakedAmountFormatter(stakedAmount)} {displayForm ? ( diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/breadcrumbs.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/breadcrumbs.tsx new file mode 100644 index 0000000000..15ca2053e8 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/breadcrumbs.tsx @@ -0,0 +1,42 @@ +import { Box, Link, Typography } from '@mui/material'; +import { Link as RouterLink } from 'react-router-dom'; +import { t } from 'i18next'; + +import { routerPaths } from '@/router/router-paths'; +import { useColorMode } from '@/shared/contexts/color-mode/use-color-mode'; + +export function Breadcrumbs() { + const { colorPalette } = useColorMode(); + + return ( + + + {t('worker.jobs.availableJobs')} + + + {'>'} + + + {t('worker.jobs.hCaptcha')} + + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-accordion.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-accordion.tsx index 082157d655..f198e6d185 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-accordion.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-accordion.tsx @@ -1,11 +1,14 @@ -import Accordion from '@mui/material/Accordion'; -import AccordionSummary from '@mui/material/AccordionSummary'; -import AccordionDetails from '@mui/material/AccordionDetails'; -import Typography from '@mui/material/Typography'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import Grid from '@mui/material/Grid'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { t } from 'i18next'; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Grid, + Typography, +} from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + import { TopNotificationType, useNotification, @@ -16,10 +19,14 @@ import { useHCaptchaUserStats } from '../hooks'; import { UserStatsDetails } from './user-stats-details'; import { LoadingOverlay } from './user-stats-loading-overlay'; -const accordionWidth = { width: '284px' }; +const ACCORDION_WIDTH = '284px'; +const ACCORDION_HEIGHT = '48px'; export function UserStatsAccordion() { + const [isExpanded, setIsExpanded] = useState(false); const { colorPalette } = useColorMode(); + const { showNotification } = useNotification(); + const { data: hcaptchaUserStats, isPending: isHcaptchaUserStatsPending, @@ -28,28 +35,32 @@ export function UserStatsAccordion() { refetch: refetchUserStats, isRefetching: isHcaptchaUserStatsRefetching, } = useHCaptchaUserStats(); - const { showNotification } = useNotification(); useEffect(() => { if (isHcaptchaUserStatsError) { showNotification({ - type: TopNotificationType.WARNING, + type: TopNotificationType.ERROR, message: getErrorMessageForError(hcaptchaUserStatsError), }); } - // eslint-disable-next-line react-hooks/exhaustive-deps -- ... - }, [isHcaptchaUserStatsError, hcaptchaUserStatsError]); + }, [isHcaptchaUserStatsError, hcaptchaUserStatsError, showNotification]); return ( - + { + setIsExpanded(expanded); + }} sx={{ - ...accordionWidth, position: 'relative', overflow: 'hidden', + width: ACCORDION_WIDTH, + minHeight: ACCORDION_HEIGHT, + zIndex: 1, }} > - {isHcaptchaUserStatsRefetching && ( + {isExpanded && isHcaptchaUserStatsRefetching && ( } id="panel1-header" - sx={{ ...accordionWidth, height: '76px' }} + sx={{ + width: ACCORDION_WIDTH, + height: ACCORDION_HEIGHT, + py: 1.5, + '& > .MuiAccordionSummary-content': { + m: 0, + }, + '&.Mui-expanded': { minHeight: ACCORDION_HEIGHT }, + }} > {t('worker.hcaptchaLabelingStats.statistics')} {hcaptchaUserStats ? ( - + void refetchUserStats()} stats={hcaptchaUserStats} diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-details.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-details.tsx index 6196dfd4be..4bb9a0a58e 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-details.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-details.tsx @@ -1,33 +1,29 @@ import { Divider, IconButton, Stack, Typography } from '@mui/material'; import { t } from 'i18next'; + import { RefreshIcon } from '@/shared/components/ui/icons'; -import { onlyDarkModeColor } from '@/shared/styles/dark-color-palette'; import { useColorMode } from '@/shared/contexts/color-mode'; import { useIsMobile } from '@/shared/hooks'; import { type HCaptchaUserStatsSuccess } from '../types'; -export function UserStatsDetails({ - stats, - refetch, - isRefetching, -}: Readonly<{ +type Props = { stats: HCaptchaUserStatsSuccess; refetch: () => void; isRefetching: boolean; -}>) { - const { colorPalette, isDarkMode } = useColorMode(); - const isMobile = useIsMobile(); +}; - const statsColor = isDarkMode - ? onlyDarkModeColor.additionalTextColor - : colorPalette.primary.light; +export function UserStatsDetails({ stats, refetch, isRefetching }: Props) { + const { colorPalette } = useColorMode(); + const isMobile = useIsMobile(); return ( - - - - + {!isMobile && ( + + )} + + + {t('worker.hcaptchaLabelingStats.allTime')} {t('worker.hcaptchaLabelingStats.jobsServed')} - + {stats.served} @@ -46,7 +42,7 @@ export function UserStatsDetails({ {t('worker.hcaptchaLabelingStats.jobsComplete')} - + {stats.solved} @@ -54,29 +50,41 @@ export function UserStatsDetails({ {t('worker.hcaptchaLabelingStats.hmtEarned')} - + {stats.balance.total}{' '} - + {t('inputMasks.humanCurrencySuffix')} - + - + - + {t('worker.hcaptchaLabelingStats.lastHour')} {t('worker.hcaptchaLabelingStats.earnedLastHour')} - + {stats.balance.recent}{' '} - + {t('inputMasks.humanCurrencySuffix')} - + diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-drawer.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-drawer.tsx index db65ccd070..dcd304ab0e 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-drawer.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/components/user-stats-drawer.tsx @@ -1,80 +1,98 @@ -import { Box, CssBaseline, Drawer, Stack, Typography } from '@mui/material'; +import { Box, Drawer, IconButton, Stack, Typography } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; import { t } from 'i18next'; + import { Loader } from '@/shared/components/ui/loader'; import { Alert } from '@/shared/components/ui/alert'; import { getErrorMessageForError } from '@/shared/errors'; import { useHCaptchaUserStats } from '../hooks'; import { UserStatsDetails } from './user-stats-details'; import { LoadingOverlay } from './user-stats-loading-overlay'; +import { useColorMode } from '@/shared/contexts/color-mode'; -export interface UserStatsDrawerNavigationProps { +type Props = { isOpen: boolean; -} + onClose: () => void; +}; + +export function UserStatsDrawer({ isOpen, onClose }: Props) { + const { colorPalette } = useColorMode(); -export function UserStatsDrawer({ - isOpen, -}: Readonly) { const { data: hcaptchaUserStats, error: hcaptchaUserStatsError, - status: hcaptchaUserStatsStatus, + isSuccess, + isPending, refetch: hcaptchaUserStatsRefetch, isRefetching: isHcaptchaUserStatsRefetching, } = useHCaptchaUserStats(); return ( - - - theme.zIndex.drawer, + '& .MuiDrawer-paper': { width: '100%', - flexShrink: 0, - '& .MuiDrawer-paper': { - width: '100%', - paddingTop: 11, - }, - }} - > - - {isHcaptchaUserStatsRefetching && ( - + + {isHcaptchaUserStatsRefetching && ( + + )} + + + {t('worker.hcaptchaLabelingStats.hCapchaStatistics')} + + + + + + + {isSuccess && ( + void hcaptchaUserStatsRefetch()} + stats={hcaptchaUserStats} + isRefetching={isHcaptchaUserStatsRefetching} /> )} - - {hcaptchaUserStatsStatus === 'success' ? ( - <> - - {t('worker.hcaptchaLabelingStats.hCapchaStatistics')} - - void hcaptchaUserStatsRefetch()} - stats={hcaptchaUserStats} - isRefetching={isHcaptchaUserStatsRefetching} - /> - - ) : null} - {hcaptchaUserStatsStatus === 'error' ? ( - - {getErrorMessageForError(hcaptchaUserStatsError)} - - ) : null} - {hcaptchaUserStatsStatus === 'pending' ? ( - - ) : null} - - - - + {!!hcaptchaUserStatsError && ( + + {getErrorMessageForError(hcaptchaUserStatsError)} + + )} + {isPending && } + + + ); } diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx index 71b64fa101..d13f7f1a93 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx @@ -1,81 +1,101 @@ -import Grid from '@mui/material/Grid'; -import Paper from '@mui/material/Paper'; +import { useEffect } from 'react'; import { t } from 'i18next'; -import Typography from '@mui/material/Typography'; -import { Navigate } from 'react-router-dom'; +import { Box, Paper, Stack, Typography } from '@mui/material'; + import { Button } from '@/shared/components/ui/button'; -import { PageCardError } from '@/shared/components/ui/page-card'; import { getErrorMessageForError } from '@/shared/errors'; -import { breakpoints } from '@/shared/styles/breakpoints'; -import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user'; -import { routerPaths } from '@/router/router-paths'; +import { useColorMode } from '@/shared/contexts/color-mode/use-color-mode'; +import { + TopNotificationType, + useNotification, +} from '@/shared/hooks/use-notification'; import { useEnableHCaptchaLabelingMutation } from './hooks'; +import { Breadcrumbs } from './components/breadcrumbs'; export function EnableLabelerPage() { - const { user } = useAuthenticatedUser(); - const { mutate, error, isError, isPending } = + const { colorPalette } = useColorMode(); + const { showNotification } = useNotification(); + const { mutate, error, isError, isPending, reset } = useEnableHCaptchaLabelingMutation(); - if (!user.wallet_address) { - return ; - } - - if (isError) { - return ; - } + useEffect(() => { + if (isError) { + showNotification({ + message: getErrorMessageForError(error), + type: TopNotificationType.ERROR, + }); + reset(); + } + }, [error, isError, showNotification, reset]); return ( - - + + + - - - + {t('worker.enableHCaptchaLabeling.description')} - - - - - + + + + ); } diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx index 0ee1281f7f..d3d769476a 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx @@ -1,8 +1,9 @@ -import HCaptcha from '@hcaptcha/react-hcaptcha'; -import { Grid, Paper, Typography } from '@mui/material'; +import { useRef, useState } from 'react'; import { t } from 'i18next'; -import { useNavigate } from 'react-router-dom'; -import { useRef } from 'react'; +import { useNavigate, Link as RouterLink } from 'react-router-dom'; +import HCaptcha from '@hcaptcha/react-hcaptcha'; +import { Box, Divider, Paper, Stack, Typography } from '@mui/material'; + import { env } from '@/shared/env'; import { Counter } from '@/shared/components/ui/counter'; import { getErrorMessageForError } from '@/shared/errors'; @@ -10,7 +11,6 @@ import { getTomorrowDate } from '@/shared/helpers/date'; import { useAuthenticatedUser } from '@/modules/auth/hooks/use-authenticated-user'; import { useHCaptchaLabelingNotifications } from '@/modules/worker/hooks/use-hcaptcha-labeling-notifications'; import { useColorMode } from '@/shared/contexts/color-mode'; -import { onlyDarkModeColor } from '@/shared/styles/dark-color-palette'; import { PageCardLoader, PageCardError, @@ -20,21 +20,23 @@ import { useDailyHmtSpent, useSolveHCaptchaMutation, } from './hooks'; +import { Breadcrumbs } from './components/breadcrumbs'; +import { Button } from '@/shared/components/ui/button'; +import { routerPaths } from '@/router/router-paths'; +import { UserStatsAccordion } from './components/user-stats-accordion'; +import { useIsMobile } from '@/shared/hooks/use-is-mobile'; +import { UserStatsDrawer } from './components/user-stats-drawer'; export function HcaptchaLabelingPage() { - const { colorPalette, isDarkMode } = useColorMode(); + const [isStatsDrawerOpen, setIsStatsDrawerOpen] = useState(false); const captchaRef = useRef(null); + + const { colorPalette } = useColorMode(); const { user } = useAuthenticatedUser(); - const { onSuccess, onError } = useHCaptchaLabelingNotifications(); - const statsColor = isDarkMode - ? onlyDarkModeColor.additionalTextColor - : colorPalette.primary.light; + const navigate = useNavigate(); + const isMobile = useIsMobile(); - const resetCaptcha = () => { - if (captchaRef.current) { - captchaRef.current.resetCaptcha(); - } - }; + const { onSuccess, onError } = useHCaptchaLabelingNotifications(); const { mutate: solveHCaptchaMutation } = useSolveHCaptchaMutation({ onSuccess: () => { @@ -61,7 +63,8 @@ export function HcaptchaLabelingPage() { error: dailyHmtSpentError, } = useDailyHmtSpent(); - const navigate = useNavigate(); + const isError = isDailyHmtSpentError || isHcaptchaUserStatsError; + const isPending = isHcaptchaUserStatsPending || isDailyHmtSpentPending; const canSolveCaptcha = dailyHmtSpent && @@ -74,87 +77,190 @@ export function HcaptchaLabelingPage() { solveHCaptchaMutation({ token }); }; - if (isHcaptchaUserStatsPending || isDailyHmtSpentPending) { - return ; - } - - if (isHcaptchaUserStatsError || isDailyHmtSpentError) { - return ( - - ); - } + const resetCaptcha = () => { + if (captchaRef.current) { + captchaRef.current.resetCaptcha(); + } + }; return ( - - - + {isMobile ? ( + + ) : ( + + )} + + + - - - {t('worker.hcaptchaLabeling.description')} - - {canSolveCaptcha ? ( - - - - ) : ( - - - {t('worker.hcaptchaLabeling.noJobs')} - - - { - navigate(0); + {isPending && !isError && } + {isError && ( + + )} + {!isPending && !isError && ( + + {canSolveCaptcha ? ( + <> + + {t('worker.hcaptchaLabeling.description')} + + + + + + ) : ( + + + {t('worker.hcaptchaLabeling.noJobs')} + + - - - )} - - - - + > + + + {t('worker.hcaptchaLabeling.waitFor')} + + + { + navigate(0); + }} + /> + + + + + + {t('worker.hcaptchaLabeling.or')} + + + + + + + + )} + + )} + + + setIsStatsDrawerOpen(false)} + /> + ); } diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hooks/enable-hcaptcha-labeling.ts b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hooks/enable-hcaptcha-labeling.ts index fcc7c9ccdd..52140a2f4f 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hooks/enable-hcaptcha-labeling.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hooks/enable-hcaptcha-labeling.ts @@ -1,5 +1,6 @@ import { useMutation } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; + import { routerPaths } from '@/router/router-paths'; import { useAccessTokenRefresh } from '@/api/hooks/use-access-token-refresh'; import * as hCaptchaLabelingService from '../services/hcaptcha-labeling.service'; @@ -7,12 +8,11 @@ import * as hCaptchaLabelingService from '../services/hcaptcha-labeling.service' export function useEnableHCaptchaLabelingMutation() { const navigate = useNavigate(); const { refreshAccessTokenAsync } = useAccessTokenRefresh(); + const mutation = useMutation({ mutationFn: async () => { const result = await hCaptchaLabelingService.enableHCaptchaLabeling(); - await refreshAccessTokenAsync({ authType: 'web2' }); - return result; }, onSuccess: () => { diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx index 14caf0eb31..847f3ab916 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx @@ -72,7 +72,7 @@ export function HCaptchaWidget() { prop !== 'open', -})<{ - open?: boolean; -}>(({ theme, open }) => ({ - width: '100%', +const Main = styled('main')({ display: 'flex', flex: '1', - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - ...(open && { - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - }), -})); + width: '100%', +}); -export function ProtectedLayout({ - renderHCaptchaStatisticsDrawer, - renderGovernanceBanner, -}: { - renderHCaptchaStatisticsDrawer?: (isOpen: boolean) => ReactElement; - renderGovernanceBanner?: boolean; -}) { +export function ProtectedLayout() { const layoutElementRef = useRef(null); - const isHCaptchaLabelingPage = useIsHCaptchaLabelingPage(); + const isMobile = useIsMobile(); - const [drawerOpen, setDrawerOpen] = useState(!isMobile); - const [hcaptchaDrawerOpen, setHcaptchaDrawerOpen] = useState(false); const { colorPalette } = useColorMode(); const location = useLocation(); - const toggleUserStatsDrawer = isHCaptchaLabelingPage - ? () => { - setHcaptchaDrawerOpen((state) => !state); - } - : undefined; const isProfilePage = location.pathname === routerPaths.worker.profile; const isBottomTrayVisible = isMobile && !isProfilePage; - useEffect(() => { - if (isMobile) { - setHcaptchaDrawerOpen(false); - setDrawerOpen(false); - } else { - setHcaptchaDrawerOpen(false); - setDrawerOpen(true); - } - }, [isMobile]); - return ( - {isMobile && ( - - )} - + {isMobile && } {!isMobile && } - {isHCaptchaLabelingPage && renderHCaptchaStatisticsDrawer - ? renderHCaptchaStatisticsDrawer(hcaptchaDrawerOpen) - : null} -
+
- {renderGovernanceBanner && } + {isBottomTrayVisible && }
-
+
); diff --git a/packages/apps/human-app/frontend/src/router/components/layout/protected/navbar.tsx b/packages/apps/human-app/frontend/src/router/components/layout/protected/navbar.tsx index 1fde484268..fcd04711bf 100644 --- a/packages/apps/human-app/frontend/src/router/components/layout/protected/navbar.tsx +++ b/packages/apps/human-app/frontend/src/router/components/layout/protected/navbar.tsx @@ -1,65 +1,15 @@ import { Stack } from '@mui/material'; -// import MenuIcon from '@mui/icons-material/Menu'; -// import CloseIcon from '@mui/icons-material/Close'; import { t } from 'i18next'; + import { HumanLogoNavbarIcon } from '@/shared/components/ui/icons'; import { Button } from '@/shared/components/ui/button'; -//import { useIsHCaptchaLabelingPage } from '@/shared/hooks/use-is-hcaptcha-labeling-page'; import { useColorMode } from '@/shared/contexts/color-mode'; import { useHandleMainNavIconClick } from '@/shared/hooks/use-handle-main-nav-icon-click'; import { ColorModeSwitch } from '@/shared/components/ui/dark-mode-switch'; -interface NavbarProps { - open: boolean; - setOpen: (open: boolean) => void; - toggleUserStatsDrawer?: () => void; - userStatsDrawerOpen: boolean; -} - -export function Navbar({ - setOpen, - open, - // userStatsDrawerOpen, - // toggleUserStatsDrawer, -}: NavbarProps) { - const handleMainNavIconClick = useHandleMainNavIconClick(); +export function Navbar() { const { colorPalette } = useColorMode(); - //const isHCaptchaLabelingPage = useIsHCaptchaLabelingPage(); - - // let iconButton = null; - // if (open) { - // iconButton = ( - // { - // setOpen(false); - // }} - // > - // - // - // ); - // } else if (userStatsDrawerOpen) { - // iconButton = ( - // { - // if (toggleUserStatsDrawer) { - // toggleUserStatsDrawer(); - // } - // }} - // > - // - // - // ); - // } else { - // iconButton = ( - // { - // setOpen(true); - // }} - // > - // - // - // ); - // } + const handleMainNavIconClick = useHandleMainNavIconClick(); return ( - {/* - {isHCaptchaLabelingPage && toggleUserStatsDrawer ? ( - - ) : null} - {iconButton} - */} ); } diff --git a/packages/apps/human-app/frontend/src/router/router.tsx b/packages/apps/human-app/frontend/src/router/router.tsx index f938b1b921..9f6b069caa 100644 --- a/packages/apps/human-app/frontend/src/router/router.tsx +++ b/packages/apps/human-app/frontend/src/router/router.tsx @@ -8,7 +8,6 @@ import { import { RequireAuth } from '@/modules/auth/providers/require-auth'; import { RequireWalletConnect } from '@/shared/contexts/wallet-connect'; import { RequireWeb3Auth } from '@/modules/auth-web3/providers/require-web3-auth'; -import { UserStatsDrawer } from '@/modules/worker/hcaptcha-labeling'; import { routerPaths } from './router-paths'; import { ProtectedLayout, UnprotectedLayout } from './components'; @@ -38,12 +37,7 @@ export function Router() { - ( - - )} - renderGovernanceBanner - /> + } key={routerProps.path} diff --git a/packages/apps/human-app/frontend/src/shared/components/expiration-modal.tsx b/packages/apps/human-app/frontend/src/shared/components/expiration-modal.tsx index 302469ed2f..2ebb4e4e41 100644 --- a/packages/apps/human-app/frontend/src/shared/components/expiration-modal.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/expiration-modal.tsx @@ -4,7 +4,6 @@ import { useNavigate } from 'react-router-dom'; import { Button } from '@/shared/components/ui/button'; import { routerPaths } from '@/router/router-paths'; import { browserAuthProvider } from '@/shared/contexts/browser-auth-provider'; -import { breakpoints } from '@/shared/styles/breakpoints'; import { useModal } from '@/shared/contexts/modal-context'; import { useColorMode } from '../contexts/color-mode'; @@ -24,10 +23,7 @@ export function ExpirationModal() { sx={{ alignItems: 'center', justifyContent: 'center', - p: 15, - [breakpoints.mobile]: { - p: 2, - }, + p: { xs: 2, md: 15 }, }} > + {errorMessage} @@ -37,6 +32,6 @@ export function PageCardError({ > {t('components.pageCardError.reload')} - + ); } diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-loader.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-loader.tsx index 578fa382bc..ca6fbdb51e 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-loader.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card-loader.tsx @@ -1,30 +1,24 @@ -import { Grid } from '@mui/material'; +import { Stack } from '@mui/material'; import { Loader } from '@/shared/components/ui/loader'; -import { useColorMode } from '@/shared/contexts/color-mode'; import { useIsMobile } from '@/shared/hooks'; -import { commonDarkPageCardStyles, commonPageCardStyles } from './styles'; +import { commonStyles } from './styles'; import { type CommonProps } from './types'; export function PageCardLoader({ cardMaxWidth = '100%' }: CommonProps) { - const { isDarkMode } = useColorMode(); const isMobile = useIsMobile(); - const commonStyleForTheme = isDarkMode - ? commonDarkPageCardStyles - : commonPageCardStyles; - const sx = cardMaxWidth ? { - ...commonStyleForTheme, + ...commonStyles, maxWidth: cardMaxWidth, } - : commonStyleForTheme; + : commonStyles; return ( - + - + ); } diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card.tsx b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card.tsx index 8d6bd44b34..f398af4c63 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card.tsx +++ b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/page-card.tsx @@ -1,7 +1,7 @@ -import { Grid, Typography } from '@mui/material'; +import { Grid, Stack, Typography } from '@mui/material'; import { useNavigate } from 'react-router-dom'; -import { useColorMode } from '@/shared/contexts/color-mode'; -import { commonDarkPageCardStyles, commonPageCardStyles } from './styles'; + +import { commonStyles } from './styles'; import { BackButton } from './back-button'; type NavigationTarget = string | (() => void); @@ -26,7 +26,6 @@ export function PageCard({ childrenMaxWidth = '486px', showBackButton = true, }: PageCardProps) { - const { isDarkMode } = useColorMode(); const navigate = useNavigate(); const contentStyles = { @@ -51,10 +50,9 @@ export function PageCard({ }; return ( - @@ -124,6 +122,6 @@ export function PageCard({ - + ); } diff --git a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts index 30fab1d441..e084791850 100644 --- a/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts +++ b/packages/apps/human-app/frontend/src/shared/components/ui/page-card/styles.ts @@ -1,29 +1,7 @@ -import type { SxProps, Theme } from '@mui/material'; -import { breakpoints } from '@/shared/styles/breakpoints'; -import { colorPalette as constColorPalette } from '@/shared/styles/color-palette'; -import { darkColorPalette as constDarkColorPalette } from '@/shared/styles/dark-color-palette'; - -export const commonPageCardStyles: SxProps = { - flexDirection: 'column', +export const commonStyles = { justifyContent: 'center', alignItems: 'center', - borderRadius: '20px', - //minHeight: '70vh', maxWidth: '1600px', width: '100%', - background: constColorPalette.white, -}; - -export const commonDarkPageCardStyles: SxProps = { - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - borderRadius: '20px', - minHeight: '70vh', - maxWidth: '1600px', - width: '100%', - background: constDarkColorPalette.paper.main, - [breakpoints.mobile]: { - background: constDarkColorPalette.backgroundColor, - }, + background: 'transparent', }; diff --git a/packages/apps/human-app/frontend/src/shared/hooks/index.ts b/packages/apps/human-app/frontend/src/shared/hooks/index.ts index 6f64178930..7803ff3958 100644 --- a/packages/apps/human-app/frontend/src/shared/hooks/index.ts +++ b/packages/apps/human-app/frontend/src/shared/hooks/index.ts @@ -1,7 +1,6 @@ export * from './use-combine-pages'; export * from './use-count-down'; export * from './use-handle-main-nav-icon-click'; -export * from './use-is-hcaptcha-labeling-page'; export * from './use-notification'; export * from './use-is-mobile'; export * from './use-reset-mutation-errors'; diff --git a/packages/apps/human-app/frontend/src/shared/hooks/use-is-hcaptcha-labeling-page.ts b/packages/apps/human-app/frontend/src/shared/hooks/use-is-hcaptcha-labeling-page.ts deleted file mode 100644 index a5b0f95f61..0000000000 --- a/packages/apps/human-app/frontend/src/shared/hooks/use-is-hcaptcha-labeling-page.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useLocation } from 'react-router-dom'; -import { routerPaths } from '@/router/router-paths'; - -export function useIsHCaptchaLabelingPage() { - const location = useLocation(); - return location.pathname === routerPaths.worker.HcaptchaLabeling; -} diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index de2bedde09..037af6acfc 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -271,7 +271,7 @@ "titleDisabled": "No hCaptcha task available", "description": "Complete image labeling challenges and earn rewards by helping train privacy-preserving hCaptcha models.", "shortDescription": "Complete image labeling challenges and earn rewards.", - "descriptionDisabled": "You have reached the daily limit for the hCaptcha tasks, Please come back later.", + "descriptionDisabled": "You have reached the daily limit for the hCaptcha tasks, please come back later.", "startTask": "Start Task", "nextAvailable": "Next Available in", "nextIn": "Next in" @@ -283,9 +283,10 @@ "completeButton": "Complete" }, "jobs": { + "hCaptcha": "hCaptcha", "successFullyAssignedJob": "Successfully assigned a task!", "errorFetchingData": "There was an error while fetching data, please try again", - "availableJobs": "Available Tasks", + "availableJobs": "Available Jobs", "myJobs": "My Tasks", "jobsDiscovery": "Jobs Discovery", "jobDescription": "Job description", @@ -333,7 +334,10 @@ }, "hcaptchaLabeling": { "description": "HMT payouts are automatically computed by an oracle based on correctness and volume of tasks completed. Payout timing depends on total job size, and can take as little as an hour or as long as a week or so, depending on overall job status.", - "noJobs": "No jobs available at the moment. Please come back in:" + "noJobs": "You have reached the daily limit for the hCaptcha tasks, please come back later.", + "waitFor": "Wait for", + "browseAvailableJobs": "Browse Available Jobs", + "or": "Or" }, "enableHCaptchaLabeling": { "description": "CAPTCHA tasks involve the correct labeling of images in a group of images. Read the instructional prompt, and select the images that best correspond to that prompt.", diff --git a/packages/apps/human-app/frontend/src/shared/styles/color-palette.ts b/packages/apps/human-app/frontend/src/shared/styles/color-palette.ts index 96dbe6571e..7541c72350 100644 --- a/packages/apps/human-app/frontend/src/shared/styles/color-palette.ts +++ b/packages/apps/human-app/frontend/src/shared/styles/color-palette.ts @@ -5,6 +5,7 @@ export const colorPalette = { text: { primary: '#320A8D', secondary: '#B2AFC1', + light: '#6309ff', disabled: '#CBCFE6', disabledSecondary: '#8494C3', auxiliary100: '#000000', diff --git a/packages/apps/human-app/frontend/src/shared/styles/dark-color-palette.ts b/packages/apps/human-app/frontend/src/shared/styles/dark-color-palette.ts index 17a8511898..7332fdc271 100644 --- a/packages/apps/human-app/frontend/src/shared/styles/dark-color-palette.ts +++ b/packages/apps/human-app/frontend/src/shared/styles/dark-color-palette.ts @@ -7,6 +7,7 @@ export const darkColorPalette = { text: { primary: '#D4CFFF', secondary: '#6C6978', + light: '#9387ff', disabled: 'rgba(212, 207, 255, 0.5)', disabledSecondary: 'rgba(147, 135, 255, 1)', auxiliary100: '#ffffff', @@ -74,11 +75,3 @@ export const darkColorPalette = { // for 'warning', 'info' native colors from MUI were pointed as expected // 'info' native colors from MUI were pointed as expected } satisfies typeof colorPalette; - -// if Figma design was inconsistent for some reasons and there are extra colors for dark mode should be included in this object -export const onlyDarkModeColor = { - backArrowBg: 'rgba(246, 247, 254, 0.1)', - additionalTextColor: '#9387FF', - mainColorWithOpacity: '#CDC7FFCC', - listItemColor: '#CDC7FF29', -}; diff --git a/packages/apps/human-app/frontend/src/shared/styles/dark-theme.ts b/packages/apps/human-app/frontend/src/shared/styles/dark-theme.ts index 2d8780982b..6bb3fb5298 100644 --- a/packages/apps/human-app/frontend/src/shared/styles/dark-theme.ts +++ b/packages/apps/human-app/frontend/src/shared/styles/dark-theme.ts @@ -1,8 +1,5 @@ import type { ThemeOptions } from '@mui/material'; -import { - darkColorPalette, - onlyDarkModeColor, -} from '@/shared/styles/dark-color-palette'; +import { darkColorPalette } from '@/shared/styles/dark-color-palette'; import { typography } from '@/shared/styles/typography'; import { breakpoints } from '@/shared/styles/breakpoints'; @@ -153,7 +150,7 @@ export const darkTheme: ThemeOptions = { MuiInputBase: { styleOverrides: { root: { - borderColor: onlyDarkModeColor.mainColorWithOpacity, + borderColor: '#cdc7ffcc', '&:hover': { borderColor: 'white', }, From 6be9ec4cdc0ee13ddb4f2680a8435fa8385581a8 Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Fri, 31 Jul 2026 12:17:39 +0300 Subject: [PATCH 5/8] fix: address copilot comments --- .../components/hcaptha-widget.tsx | 6 +++--- .../jobs-discovery/components/oracles-list.tsx | 5 ++++- .../jobs-discovery/jobs-discovery.page.tsx | 8 +++----- .../modules/worker/services/oracles.service.ts | 2 -- .../layout/protected/desktop-aside-bar.tsx | 18 +++++++++--------- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx index 847f3ab916..240a77deda 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx @@ -72,7 +72,7 @@ export function HCaptchaWidget() { @@ -224,7 +224,7 @@ export function HCaptchaWidget() { diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx index 264a4ddaf0..2713b8b605 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracles-list.tsx @@ -45,7 +45,10 @@ export function OraclesList({ data, isError, isPending }: OraclesListProps) { return ( {data.map((oracle) => ( - + ))} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx index 1f58c03768..9fc7872b8d 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx @@ -2,12 +2,12 @@ import { useEffect } from 'react'; import { Box, Stack, Typography } from '@mui/material'; import { t } from 'i18next'; -import { OraclesList } from './components'; +import { OraclesList } from './components/oracles-list'; +import { HCaptchaWidget } from './components/hcaptha-widget'; import { useColorMode } from '@/shared/contexts/color-mode'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; -import { useGetOracles } from '../hooks'; +import { useGetOracles } from '../hooks/use-get-oracles'; import { useGetOraclesNotifications } from '../hooks/use-get-oracles-notifications'; -import { HCaptchaWidget } from './components/hcaptha-widget'; const bull = ( @@ -41,7 +41,6 @@ export function JobsDiscoveryPage() { H_CAPTCHA_ORACLE.jobTypes.includes(t)) ) { - //oracles.push(H_CAPTCHA_ORACLE); oracles.push(...MOCKED_ORACLES); } diff --git a/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx b/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx index 3de8970006..363945c19d 100644 --- a/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx +++ b/packages/apps/human-app/frontend/src/router/components/layout/protected/desktop-aside-bar.tsx @@ -7,8 +7,7 @@ import { Stack, } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { t } from 'i18next'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import { ProfileData } from '@/modules/worker/profile/components/profile-data'; import { routerPaths } from '@/router/router-paths'; @@ -24,18 +23,19 @@ import { useHandleMainNavIconClick } from '@/shared/hooks/use-handle-main-nav-ic const menuItems = [ { - label: t('components.DrawerNavigation.availableJobs'), + labelKey: 'components.DrawerNavigation.availableJobs', href: routerPaths.worker.jobsDiscovery, }, { - label: t('components.DrawerNavigation.myJobs'), + labelKey: 'components.DrawerNavigation.myJobs', href: routerPaths.worker.myJobs, }, -]; +] as const; export function DesktopAsideBar() { const { colorPalette, isDarkMode } = useColorMode(); const { t } = useTranslation(); + const { pathname } = useLocation(); const handleMainNavIconClick = useHandleMainNavIconClick(); const activeLinkGradient = isDarkMode @@ -75,11 +75,11 @@ export function DesktopAsideBar() { {menuItems.map((item) => { - const { href, label } = item; - const isActive = window.location.pathname === href; + const { href, labelKey } = item; + const isActive = pathname === href; return ( {isActive && } - {label} + {t(labelKey)} ); From f9ba0d0a8060e170bfe4efe7abf4468e3cf67d37 Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Fri, 31 Jul 2026 12:57:56 +0300 Subject: [PATCH 6/8] refactor: adapt card ui to the backend adjustments --- .../components/oracle-job-card.tsx | 47 ++++++++++----- .../should-navigate-to-registration.spec.ts | 4 ++ .../worker/services/oracles.service.ts | 58 ++++++++++++------- .../frontend/src/shared/i18n/en.json | 4 +- 4 files changed, 74 insertions(+), 39 deletions(-) diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx index cff8e77a91..3cb2cd4af5 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/oracle-job-card.tsx @@ -57,6 +57,10 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { const { colorPalette } = useColorMode(); const isMobile = useIsMobile(); + const isRewardAmountsEqual = + oracle.minRewardAmount === oracle.maxRewardAmount; + const hasTasks = oracle.nTasks > 0; + return ( - 1 HMT - 2 HMT + {isRewardAmountsEqual ? ( + <> + {oracle.minRewardAmount} {oracle.rewardToken} + + ) : ( + <> + {oracle.minRewardAmount} {oracle.rewardToken} -{' '} + {oracle.maxRewardAmount} {oracle.rewardToken} + + )} @@ -151,7 +164,7 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { py: { xs: 2, md: 1 }, px: { xs: 2, md: 0 }, mx: { xs: -2, md: 0 }, - gap: isTaskTypesOpen ? 1 : 0, + gap: isTaskTypesOpen && hasTasks ? 1 : 0, borderTop: { xs: `1px solid ${colorPalette.border.main}`, md: 'none', @@ -173,6 +186,7 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { startIcon={ setIsTaskTypesOpen(!isTaskTypesOpen)} + onClick={() => hasTasks && setIsTaskTypesOpen(!isTaskTypesOpen)} > - {oracle.jobTypes.length}{' '} - {oracle.jobTypes.length > 1 - ? t('worker.oraclesList.taskTypes') - : t('worker.oraclesList.taskType')} + {oracle.nTasks}{' '} + {oracle.nTasks === 1 + ? t('worker.oraclesList.task') + : t('worker.oraclesList.tasks')} ) : ( } - disableHoverListener={isMobile} - disableFocusListener={isMobile} - disableTouchListener={isMobile} + disableHoverListener={isMobile || !hasTasks} slotProps={{ tooltip: { sx: { @@ -219,14 +231,16 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { gap: 0.75, color: colorPalette.text.auxiliary200, fontWeight: 600, - cursor: { xs: 'default', md: 'pointer' }, + cursor: hasTasks + ? { xs: 'default', md: 'pointer' } + : 'default', }} > - {oracle.jobTypes.length}{' '} - {oracle.jobTypes.length > 1 - ? t('worker.oraclesList.taskTypes') - : t('worker.oraclesList.taskType')} + {oracle.nTasks}{' '} + {oracle.nTasks === 1 + ? t('worker.oraclesList.task') + : t('worker.oraclesList.tasks')} )} @@ -234,6 +248,7 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { variant="text" disableRipple sx={{ + display: hasTasks ? 'flex' : 'none', p: 0, gap: 0.5, bgcolor: 'transparent', @@ -246,7 +261,7 @@ export function OracleJobCard({ oracle }: { oracle: Oracle }) { /> - + {oracle.jobTypes.map((jobType) => { const element = JOB_TYPES.find((j) => j === jobType) as JobType; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/should-navigate-to-registration.spec.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/should-navigate-to-registration.spec.ts index 133385f694..a1eb43e1e1 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/should-navigate-to-registration.spec.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/helpers/should-navigate-to-registration.spec.ts @@ -12,6 +12,10 @@ describe('shouldNavigateToRegistration Helper', () => { url: faker.internet.url(), jobTypes: [faker.word.noun()], name: faker.company.name(), + nTasks: faker.number.int({ min: 1, max: 10 }), + minRewardAmount: faker.finance.amount(), + maxRewardAmount: faker.finance.amount(), + rewardToken: faker.finance.currencyCode(), }; const oracleWithNoRegistration: Oracle = { diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts index 258fddd74c..1423e2feaf 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { ApiClientError, authorizedHumanAppApiClient } from '@/api'; import { env } from '@/shared/env'; -import { MainnetChains, TestnetChains } from '@/modules/smart-contracts/chains'; +import { TestnetChains } from '@/modules/smart-contracts/chains'; const apiPaths = { oracles: '/oracles', @@ -13,6 +13,10 @@ const OracleSchema = z.object({ role: z.string(), name: z.string(), url: z.string(), + nTasks: z.number(), + minRewardAmount: z.string(), + maxRewardAmount: z.string(), + rewardToken: z.string(), jobTypes: z.array(z.string()), registrationNeeded: z.boolean().optional().nullable(), registrationInstructions: z.string().optional().nullable(), @@ -26,49 +30,60 @@ export type Oracle = OracleBase & { name: string; }; -const isTestnet = env.VITE_NETWORK === 'testnet'; - -const H_CAPTCHA_ORACLE: Oracle = { - address: env.VITE_H_CAPTCHA_ORACLE_ADDRESS, - chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, - jobTypes: env.VITE_H_CAPTCHA_ORACLE_TASK_TYPES, - role: env.VITE_H_CAPTCHA_ORACLE_ROLE, - url: env.VITE_H_CAPTCHA_ORACLE_ANNOTATION_TOOL, - name: 'hCaptcha', - registrationNeeded: false, -}; - +// const H_CAPTCHA_ORACLE: Oracle = { +// address: env.VITE_H_CAPTCHA_ORACLE_ADDRESS, +// chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, +// jobTypes: env.VITE_H_CAPTCHA_ORACLE_TASK_TYPES, +// role: env.VITE_H_CAPTCHA_ORACLE_ROLE, +// url: env.VITE_H_CAPTCHA_ORACLE_ANNOTATION_TOOL, +// name: 'hCaptcha', +// registrationNeeded: false, +// }; + +//TODO: Remove before merging const MOCKED_ORACLES: Oracle[] = [ { address: '0x1111111111111111111111111111111111111111', - chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + chainId: TestnetChains[0].chainId, jobTypes: ['image_points'], role: 'Recording Oracle', url: 'https://example.com/oracle-1', name: 'Mock Oracle 1', + nTasks: 1, + minRewardAmount: '0.0001', + maxRewardAmount: '0.0002', + rewardToken: 'HMT', registrationNeeded: false, }, { address: '0x2222222222222222222222222222222222222222', - chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + chainId: TestnetChains[0].chainId, jobTypes: ['image_points', 'image_boxes'], role: 'Recording Oracle', url: 'https://example.com/oracle-2', name: 'Mock Oracle 2', + nTasks: 2, + minRewardAmount: '0.1', + maxRewardAmount: '0.2', + rewardToken: 'HMT', registrationNeeded: false, }, { address: '0x3333333333333333333333333333333333333333', - chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + chainId: TestnetChains[0].chainId, jobTypes: ['image_points', 'image_boxes'], role: 'Recording Oracle', url: 'https://example.com/oracle-3', name: 'Mock Oracle 3', + nTasks: 3, + minRewardAmount: '0.1', + maxRewardAmount: '0.2', + rewardToken: 'HMT', registrationNeeded: false, }, { address: '0x4444444444444444444444444444444444444444', - chainId: isTestnet ? TestnetChains[0].chainId : MainnetChains[0].chainId, + chainId: TestnetChains[0].chainId, jobTypes: [ 'image_points', 'image_boxes', @@ -78,6 +93,10 @@ const MOCKED_ORACLES: Oracle[] = [ role: 'Recording Oracle', url: 'https://example.com/oracle-4', name: 'Mock Oracle 4', + nTasks: 8, + minRewardAmount: '1', + maxRewardAmount: '2', + rewardToken: 'HMT', registrationNeeded: false, }, ]; @@ -92,10 +111,7 @@ async function getOracles(selectedJobTypes: string[]) { let oracles: Oracle[] = []; - if ( - selectedJobTypes.length === 0 || - selectedJobTypes.some((t) => H_CAPTCHA_ORACLE.jobTypes.includes(t)) - ) { + if (selectedJobTypes.length === 0) { oracles.push(...MOCKED_ORACLES); } diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index 037af6acfc..3f625a82e1 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -250,8 +250,8 @@ "jobOracles": "Job Oracles", "address": "Address", "reward": "Reward", - "taskTypes": "Task types", - "taskType": "Task type", + "tasks": "Tasks", + "task": "Task", "exploreTasks": "Explore Tasks", "source": "source", "sources": "sources" From dd6ec66ac2de6b13fd0b3185c8c4dea6515b2f00 Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Fri, 31 Jul 2026 13:00:21 +0300 Subject: [PATCH 7/8] chore: change mock data --- .../frontend/src/modules/worker/services/oracles.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts index 1423e2feaf..944d4b927e 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/services/oracles.service.ts @@ -50,8 +50,8 @@ const MOCKED_ORACLES: Oracle[] = [ url: 'https://example.com/oracle-1', name: 'Mock Oracle 1', nTasks: 1, - minRewardAmount: '0.0001', - maxRewardAmount: '0.0002', + minRewardAmount: '0.001', + maxRewardAmount: '0.002', rewardToken: 'HMT', registrationNeeded: false, }, From 01d6ffcb8ba6529ba37048a5dce4c96f85bd7f02 Mon Sep 17 00:00:00 2001 From: KirillKirill Date: Mon, 3 Aug 2026 20:29:53 +0300 Subject: [PATCH 8/8] chore: minor UI and code improvements --- .../hcaptcha-labeling/enable-labeler.page.tsx | 3 +- .../hcaptcha-labeling.page.tsx | 3 +- .../components/hcaptha-widget.tsx | 11 ++----- .../jobs-discovery/jobs-discovery.page.tsx | 4 +-- .../available-jobs/available-jobs-view.tsx | 17 ----------- .../worker/jobs/components/evm-address.tsx | 17 ++++------- .../modules/worker/jobs/components/index.ts | 1 + .../jobs/components/my-jobs-table-actions.tsx | 9 +++--- .../worker/jobs/components/network-name.tsx | 22 ++++++++++++++ .../worker/jobs/components/reward-amount.tsx | 18 ++++++++---- .../jobs/hooks/use-jobs-notifications.tsx | 4 +-- .../src/modules/worker/jobs/jobs.page.tsx | 29 +++---------------- .../worker/profile/views/profile.page.tsx | 7 ++--- .../frontend/src/router/components/footer.tsx | 10 ++----- .../components/layout/protected/layout.tsx | 3 +- .../src/shared/components/ui/base-drawer.tsx | 5 +++- .../shared/components/ui/modal/base-modal.tsx | 7 +++-- .../components/ui/modal/global-modal.tsx | 1 + .../src/shared/components/ui/table-button.tsx | 2 +- .../frontend/src/shared/styles/theme.ts | 6 ++++ .../frontend/src/shared/styles/typography.ts | 9 ++---- 21 files changed, 81 insertions(+), 107 deletions(-) delete mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs/available-jobs/available-jobs-view.tsx create mode 100644 packages/apps/human-app/frontend/src/modules/worker/jobs/components/network-name.tsx diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx index d13f7f1a93..b8315d7aa4 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/enable-labeler.page.tsx @@ -36,8 +36,7 @@ export function EnableLabelerPage() { sx={{ display: 'flex', width: '100%', - pt: { xs: 2, md: 2 }, - pb: { xs: 2, md: 6 }, + py: { xs: 2, md: 6 }, px: { xs: 2, md: 4 }, borderBottom: { xs: 'none', diff --git a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx index d3d769476a..5429d3c0ff 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/hcaptcha-labeling/hcaptcha-labeling.page.tsx @@ -94,8 +94,7 @@ export function HcaptchaLabelingPage() { justifyContent: 'space-between', gap: 2, width: '100%', - pt: { xs: 2, md: 0 }, - pb: { xs: 2, md: 4 }, + py: { xs: 2, md: 6 }, px: { xs: 2, md: 4 }, borderBottom: { xs: 'none', diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx index 240a77deda..985b8a9576 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/components/hcaptha-widget.tsx @@ -41,7 +41,6 @@ const renderDescription = (color: string, isMobile: boolean) => { color, fontSize: { xs: 12, md: 16 }, fontWeight: 500, - letterSpacing: '0.12px', lineHeight: 'normal', }} > @@ -128,18 +127,15 @@ export function HCaptchaWidget() { sx={{ color: colorPalette.text.auxiliary200, fontWeight: 700, - letterSpacing: '0.12px', }} > {t('worker.hcaptchaWidget.titleDisabled')} {!isMobile && ( @@ -230,10 +226,7 @@ export function HCaptchaWidget() { > {t('worker.hcaptchaWidget.title')} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx index 9fc7872b8d..5280eb2f72 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs-discovery/jobs-discovery.page.tsx @@ -41,9 +41,7 @@ export function JobsDiscoveryPage() { - ) : ( - - ); -} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/evm-address.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/evm-address.tsx index 1370dd854a..6d2e704c0c 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/evm-address.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/evm-address.tsx @@ -1,7 +1,6 @@ -import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; +import { Tooltip, Typography } from '@mui/material'; + import { shortenEscrowAddress } from '@/shared/helpers/evm'; -import { breakpoints } from '@/shared/styles/breakpoints'; import { useIsMobile } from '@/shared/hooks/use-is-mobile'; import { useColorMode } from '@/shared/contexts/color-mode'; @@ -9,19 +8,13 @@ export function EvmAddress({ address }: { address: string }) { const { colorPalette } = useColorMode(); const isMobile = useIsMobile(); - const shortAddress = isMobile - ? shortenEscrowAddress(address, 4, 4) - : shortenEscrowAddress(address); + const shortAddress = shortenEscrowAddress(address, 4, 4); return ( {shortAddress} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/index.ts b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/index.ts index ca383549de..edc417100b 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/index.ts +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/index.ts @@ -6,3 +6,4 @@ export * from './reward-amount'; export * from './sorting'; export * from './more-button'; export * from './report-abuse-modal'; +export * from './network-name'; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/my-jobs-table-actions.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/my-jobs-table-actions.tsx index 886dc72875..dd48dec93a 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/my-jobs-table-actions.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/my-jobs-table-actions.tsx @@ -1,17 +1,16 @@ import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; + import { TableButton } from '@/shared/components/ui/table-button'; import { MyJobStatus } from '../types'; import { type MyJob } from '../schemas'; import { MoreButton } from './more-button'; -interface MyJobsTableRejectActionProps { +type Props = { job: MyJob; -} +}; -export function MyJobsTableActions({ - job, -}: Readonly) { +export function MyJobsTableActions({ job }: Props) { const { t } = useTranslation(); const isDisabled = job.status !== MyJobStatus.ACTIVE; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/network-name.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/network-name.tsx new file mode 100644 index 0000000000..5db2e7c854 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/network-name.tsx @@ -0,0 +1,22 @@ +import { Typography } from '@mui/material'; + +import { getNetworkName } from '@/modules/smart-contracts/get-network-name'; +import { useColorMode } from '@/shared/contexts/color-mode'; +import { useIsMobile } from '@/shared/hooks'; + +export function NetworkName({ chainId }: { chainId: number }) { + const { colorPalette } = useColorMode(); + const isMobile = useIsMobile(); + + return ( + + {getNetworkName(chainId)} + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/reward-amount.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/reward-amount.tsx index f4af594c6c..534b4bdcd9 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/components/reward-amount.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/components/reward-amount.tsx @@ -1,5 +1,6 @@ -import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; +import { Tooltip, Typography } from '@mui/material'; + +import { useIsMobile } from '@/shared/hooks/use-is-mobile'; export function RewardAmount({ reward_amount, @@ -10,30 +11,37 @@ export function RewardAmount({ reward_token?: string; color?: string; }) { + const isMobile = useIsMobile(); + const variant = isMobile ? 'body2' : 'body1'; + if (!(reward_amount !== undefined && reward_token)) { return ''; } + const parsedReward = Number(reward_amount); const isNumeric = Number.isFinite(parsedReward); + if (!isNumeric) { return ( - + {`${reward_amount} ${reward_token}`} ); } + const hasDecimals = parsedReward - Math.floor(parsedReward) !== 0; if (hasDecimals) { return ( - + {`${parsedReward.toFixed(2)} ${reward_token}`} ); } + return ( - + {`${reward_amount} ${reward_token}`} ); diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/hooks/use-jobs-notifications.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/hooks/use-jobs-notifications.tsx index 474a8294f8..97322d8d96 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/hooks/use-jobs-notifications.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/hooks/use-jobs-notifications.tsx @@ -10,7 +10,7 @@ export const useJobsNotifications = () => { const onJobAssignmentSuccess = () => { showNotification({ - message: t('worker.jobs.successFullyAssignedJob'), + message: t('worker.jobs.successfullyAssignedJob'), type: TopNotificationType.SUCCESS, durationMs: 5000, }); @@ -19,7 +19,7 @@ export const useJobsNotifications = () => { const onJobAssignmentError = (error: Error) => { showNotification({ message: getErrorMessageForError(error), - type: TopNotificationType.WARNING, + type: TopNotificationType.ERROR, durationMs: 5000, }); }; diff --git a/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx index a4e79bd62c..4d6724549f 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/jobs/jobs.page.tsx @@ -7,12 +7,9 @@ import { useIsMobile } from '@/shared/hooks/use-is-mobile'; import { useColorMode } from '@/shared/contexts/color-mode'; import { NoRecords } from '@/shared/components/ui/no-records'; import { PageCardLoader } from '@/shared/components/ui/page-card'; -import { useGetUiConfig } from '@/shared/hooks'; import { useGetOracles } from '../hooks'; import { useGetOraclesNotifications } from '../hooks/use-get-oracles-notifications'; import { TabPanel } from './components'; -import { AvailableJobsView } from './available-jobs'; -import { MyJobsView } from './my-jobs/my-jobs-view'; function generateTabA11yProps(index: number) { return { @@ -30,19 +27,13 @@ export function JobsPage() { error, } = useGetOracles(); - const { - data: uiConfigData, - isPending: isPendingUiConfig, - isError: isErrorUiConfig, - } = useGetUiConfig(); - const { address: oracle_address } = useParams<{ address: string }>(); const { t } = useTranslation(); const [activeTab, setActiveTab] = useState(0); const isMobile = useIsMobile(); - const isError = isErrorGetOracles || isErrorUiConfig; - const isPending = isPendingGetOracles || isPendingUiConfig; + const isError = isErrorGetOracles; + const isPending = isPendingGetOracles; const { onError } = useGetOraclesNotifications(); const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => { @@ -115,22 +106,10 @@ export function JobsPage() { - {isError ? ( - - ) : ( - - )} + {isError ? : null} - {isError ? ( - - ) : ( - - )} + {isError ? : null} diff --git a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx index bd51c58944..9d573f20e6 100644 --- a/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/worker/profile/views/profile.page.tsx @@ -67,7 +67,7 @@ export function WorkerProfilePage() { ]); return ( - + } diff --git a/packages/apps/human-app/frontend/src/router/components/footer.tsx b/packages/apps/human-app/frontend/src/router/components/footer.tsx index 8eb703de07..79ae8ef89e 100644 --- a/packages/apps/human-app/frontend/src/router/components/footer.tsx +++ b/packages/apps/human-app/frontend/src/router/components/footer.tsx @@ -6,13 +6,9 @@ import { useColorMode } from '@/shared/contexts/color-mode'; interface FooterProps { displayChatIcon?: boolean; - isProtected?: boolean; } -export function Footer({ - displayChatIcon = true, - isProtected = false, -}: FooterProps) { +export function Footer({ displayChatIcon = true }: FooterProps) { const { colorPalette, isDarkMode } = useColorMode(); const { t } = useTranslation(); @@ -24,8 +20,8 @@ export function Footer({ container sx={{ width: '100%', - px: { xs: 3, md: 0 }, - py: { xs: 2, md: isProtected ? 0 : 3 }, + px: { xs: 3, md: 2 }, + py: { xs: 2, md: 3 }, bgcolor: { xs: colorPalette.background.paper, md: 'transparent' }, borderTop: { xs: `1px solid ${colorPalette.border.main}`, diff --git a/packages/apps/human-app/frontend/src/router/components/layout/protected/layout.tsx b/packages/apps/human-app/frontend/src/router/components/layout/protected/layout.tsx index 2372b2f4bf..213a11e448 100644 --- a/packages/apps/human-app/frontend/src/router/components/layout/protected/layout.tsx +++ b/packages/apps/human-app/frontend/src/router/components/layout/protected/layout.tsx @@ -51,7 +51,6 @@ export function ProtectedLayout() { {isBottomTrayVisible && }
-