Skip to content

Commit f2a2fa6

Browse files
committed
feat: add MoreButton, add modal for reporting abuse, add handler, responsive design,
1 parent e792064 commit f2a2fa6

File tree

13 files changed

+238
-51
lines changed

13 files changed

+238
-51
lines changed

packages/apps/human-app/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"@emotion/styled": "^11.11.0",
2121
"@faker-js/faker": "^9.7.0",
2222
"@fontsource/inter": "^5.0.17",
23+
"@fontsource/roboto": "^5.2.6",
2324
"@hcaptcha/react-hcaptcha": "^0.3.6",
2425
"@hookform/resolvers": "^5.0.1",
2526
"@human-protocol/sdk": "workspace:*",

packages/apps/human-app/frontend/src/main.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import '@fontsource/inter/400.css';
1212
import '@fontsource/inter/500.css';
1313
import '@fontsource/inter/600.css';
1414
import '@fontsource/inter/800.css';
15+
import '@fontsource/roboto';
16+
import '@fontsource/roboto/400.css';
17+
import '@fontsource/roboto/500.css';
1518
import { WalletConnectProvider } from '@/shared/contexts/wallet-connect';
1619
import { Web3AuthProvider } from '@/modules/auth-web3/context/web3-auth-context';
1720
import { JWTExpirationCheck } from '@/shared/contexts/jwt-expiration-check';

packages/apps/human-app/frontend/src/modules/worker/jobs/components/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './evm-address';
22
export * from './jobs-tab-panel';
33
export * from './my-jobs-table-actions';
44
export * from './escrow-address-search-form';
5-
export * from './reject-button';
65
export * from './reward-amount';
76
export * from './sorting';
7+
export * from './more-button';
8+
export * from './report-abuse-modal';
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/* eslint-disable camelcase -- ...*/
2+
import { useState } from 'react';
3+
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
4+
import { Button, MenuList, ListItemButton, Popover } from '@mui/material';
5+
import { useParams } from 'react-router-dom';
6+
import { useModal } from '@/shared/contexts/modal-context';
7+
import { useIsMobile } from '@/shared/hooks/use-is-mobile';
8+
import { useResignJobMutation } from '../my-jobs/hooks';
9+
import { type MyJob } from '../schemas';
10+
import { ReportAbuseModal } from './report-abuse-modal';
11+
12+
interface MoreButtonProps {
13+
job: MyJob;
14+
isDisabled: boolean;
15+
}
16+
17+
export function MoreButton({ job, isDisabled }: Readonly<MoreButtonProps>) {
18+
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
19+
const { address: oracleAddress } = useParams<{ address: string }>();
20+
const { mutate: rejectTaskMutation } = useResignJobMutation();
21+
const { openModal, closeModal } = useModal();
22+
const isMobile = useIsMobile();
23+
24+
const isOpen = Boolean(anchorEl);
25+
26+
const handleCancelTask = () => {
27+
setAnchorEl(null);
28+
rejectTaskMutation({
29+
oracle_address: oracleAddress ?? '',
30+
assignment_id: job.assignment_id,
31+
});
32+
};
33+
34+
const handleOpenReportAbuseModal = () => {
35+
setAnchorEl(null);
36+
openModal({
37+
content: (
38+
<ReportAbuseModal
39+
close={closeModal}
40+
escrowAddress={job.escrow_address}
41+
chainId={job.chain_id}
42+
/>
43+
),
44+
showCloseButton: false,
45+
});
46+
};
47+
48+
return (
49+
<>
50+
<Button
51+
disabled={isDisabled}
52+
sx={{
53+
minWidth: 'unset',
54+
width: { xs: '48px', md: '30px' },
55+
height: { xs: '48px', md: '30px' },
56+
p: 1,
57+
border: isMobile ? '1px solid #858EC6' : 'none',
58+
borderRadius: '4px',
59+
}}
60+
onClick={(e) => {
61+
!isDisabled && setAnchorEl(e.currentTarget);
62+
}}
63+
>
64+
<MoreHorizIcon sx={{ color: '#858EC6' }} />
65+
</Button>
66+
<Popover
67+
open={isOpen}
68+
anchorEl={anchorEl}
69+
onClose={() => {
70+
setAnchorEl(null);
71+
}}
72+
anchorOrigin={{
73+
vertical: isMobile ? 'top' : 'bottom',
74+
horizontal: 'right',
75+
}}
76+
transformOrigin={{
77+
vertical: isMobile ? 'bottom' : 'top',
78+
horizontal: 'right',
79+
}}
80+
slotProps={{
81+
paper: {
82+
sx: {
83+
mt: isMobile ? 0 : 1,
84+
},
85+
},
86+
}}
87+
>
88+
<MenuList>
89+
<ListItemButton onClick={handleCancelTask}>Cancel</ListItemButton>
90+
<ListItemButton onClick={handleOpenReportAbuseModal}>
91+
Report Abuse
92+
</ListItemButton>
93+
</MenuList>
94+
</Popover>
95+
</>
96+
);
97+
}
Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
/* eslint-disable camelcase -- ...*/
2-
import { Link, useParams } from 'react-router-dom';
1+
import { Link } from 'react-router-dom';
32
import { useTranslation } from 'react-i18next';
43
import { TableButton } from '@/shared/components/ui/table-button';
5-
import { useResignJobMutation } from '../my-jobs/hooks';
64
import { MyJobStatus } from '../types';
75
import { type MyJob } from '../schemas';
8-
import { RejectButton } from './reject-button';
6+
import { MoreButton } from './more-button';
97

108
interface MyJobsTableRejectActionProps {
119
job: MyJob;
@@ -15,10 +13,7 @@ export function MyJobsTableActions({
1513
job,
1614
}: Readonly<MyJobsTableRejectActionProps>) {
1715
const { t } = useTranslation();
18-
const { mutate: rejectTaskMutation, isPending: isRejectPending } =
19-
useResignJobMutation();
20-
const { address: oracleAddress } = useParams<{ address: string }>();
21-
const buttonDisabled = job.status !== MyJobStatus.ACTIVE || isRejectPending;
16+
const isDisabled = job.status !== MyJobStatus.ACTIVE;
2217

2318
if (!job.url) {
2419
return null;
@@ -28,24 +23,19 @@ export function MyJobsTableActions({
2823
<>
2924
<TableButton
3025
component={Link}
31-
disabled={buttonDisabled}
26+
disabled={isDisabled}
3227
fullWidth
3328
target="_blank"
3429
to={job.url}
35-
sx={{ maxWidth: { xs: 'unset', sm: '160px' } }}
30+
sx={{
31+
height: { xs: '48px', md: '30px' },
32+
maxWidth: { xs: 'unset', sm: '160px' },
33+
flex: { xs: 1, md: 'unset' },
34+
}}
3635
>
3736
{t('worker.jobs.solve')}
3837
</TableButton>
39-
<RejectButton
40-
disabled={buttonDisabled}
41-
loading={isRejectPending}
42-
onClick={() => {
43-
rejectTaskMutation({
44-
oracle_address: oracleAddress ?? '',
45-
assignment_id: job.assignment_id,
46-
});
47-
}}
48-
/>
38+
<MoreButton job={job} isDisabled={isDisabled} />
4939
</>
5040
);
5141
}

packages/apps/human-app/frontend/src/modules/worker/jobs/components/reject-button.tsx

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* eslint-disable camelcase */
2+
import { useState } from 'react';
3+
import { Box, Button, Stack, Typography, TextField } from '@mui/material';
4+
import { useIsMobile } from '@/shared/hooks/use-is-mobile';
5+
import { reportAbuse } from '../services/jobs.service';
6+
7+
interface ReportAbuseModalProps {
8+
escrowAddress: string;
9+
chainId: number;
10+
close: () => void;
11+
}
12+
13+
export function ReportAbuseModal({
14+
escrowAddress,
15+
chainId,
16+
close,
17+
}: Readonly<ReportAbuseModalProps>) {
18+
const [reason, setReason] = useState('');
19+
const isMobile = useIsMobile();
20+
21+
const handleReportAbuse = () => {
22+
close();
23+
void reportAbuse({
24+
escrow_address: escrowAddress,
25+
chain_id: chainId,
26+
reason: reason.trim(),
27+
});
28+
};
29+
30+
return (
31+
<Stack
32+
px={{ xs: 1, md: 5 }}
33+
pt={{ xs: 0, md: 3.5 }}
34+
pb={{ xs: 1.5, md: 5.5 }}
35+
>
36+
<Typography variant="h4" mb={2}>
37+
Report Abuse
38+
</Typography>
39+
<Typography variant={isMobile ? 'body2' : 'body1'}>
40+
Notice something inappropriate or incorrect? Let us know if this task
41+
contains harmful, offensive, or misleading content. Your report will be
42+
reviewed confidentially.
43+
</Typography>
44+
<TextField
45+
fullWidth
46+
multiline
47+
rows={3}
48+
label="Reason"
49+
value={reason}
50+
sx={{
51+
my: { xs: 4, md: 5 },
52+
}}
53+
onChange={(e) => {
54+
setReason(e.target.value);
55+
}}
56+
/>
57+
<Box display="flex" gap={2}>
58+
<Button fullWidth onClick={close} variant="outlined">
59+
Cancel
60+
</Button>
61+
<Button
62+
fullWidth
63+
onClick={handleReportAbuse}
64+
variant="contained"
65+
disabled={!reason.trim()}
66+
>
67+
{isMobile ? 'Report' : 'Report Abuse'}
68+
</Button>
69+
</Box>
70+
</Stack>
71+
);
72+
}

packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/components/desktop/columns.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,14 @@ export const getColumnsDefinition = ({
135135
size: 100,
136136
enableSorting: true,
137137
Cell: (props) => (
138-
<Grid sx={{ display: 'flex', justifyContent: 'flex-end', gap: '1rem' }}>
138+
<Grid
139+
sx={{
140+
display: 'flex',
141+
justifyContent: 'flex-end',
142+
alignItems: 'center',
143+
gap: 2,
144+
}}
145+
>
139146
<MyJobsTableActions job={props.row.original} />
140147
</Grid>
141148
),

packages/apps/human-app/frontend/src/modules/worker/jobs/my-jobs/components/mobile/my-jobs-list-mobile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export function MyJobsListMobile() {
181181
sx={{
182182
display: 'flex',
183183
justifyContent: 'flex-end',
184-
gap: '1rem',
184+
gap: 1,
185185
width: '100%',
186186
}}
187187
>

packages/apps/human-app/frontend/src/modules/worker/jobs/services/jobs.service.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type RefreshJobsBody,
1111
type RejectTaskBody,
1212
type AvailableJobsSuccessResponse,
13+
type ReportAbuseBody,
1314
} from '../types';
1415

1516
const apiPaths = {
@@ -19,6 +20,7 @@ const apiPaths = {
1920
resignJob: '/assignment/resign-job',
2021
refreshJobs: '/assignment/refresh',
2122
uiConfig: '/ui-config',
23+
reportAbuse: '/abuse/report',
2224
};
2325

2426
async function fetchAvailableJobs(args: JobsBody) {
@@ -102,4 +104,24 @@ async function refreshJobs(data: RefreshJobsBody) {
102104
}
103105
}
104106

105-
export { fetchAvailableJobs, fetchMyJobs, assignJob, resignJob, refreshJobs };
107+
async function reportAbuse(data: ReportAbuseBody) {
108+
try {
109+
await authorizedHumanAppApiClient.post(apiPaths.reportAbuse, {
110+
body: { ...data },
111+
});
112+
} catch (error: unknown) {
113+
if (error instanceof ApiClientError) {
114+
throw error;
115+
}
116+
throw new Error('Failed to report abuse');
117+
}
118+
}
119+
120+
export {
121+
fetchAvailableJobs,
122+
fetchMyJobs,
123+
assignJob,
124+
resignJob,
125+
refreshJobs,
126+
reportAbuse,
127+
};

0 commit comments

Comments
 (0)