diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 82ae79a229..da5e000230 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -6,7 +6,6 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@hcaptcha/react-hcaptcha": "^1.8.1", - "@human-protocol/core": "*", "@human-protocol/sdk": "*", "@mui/lab": "^5.0.0-alpha.141", "@mui/material": "^5.11.7", @@ -34,11 +33,12 @@ "start": "vite --port 3005", "build": "vite build", "preview": "vite preview", - "start-prod": "serve -s build", + "start-prod": "serve -s dist", "test": "vitest run -u", "format:prettier": "prettier --write \"**/*.{ts,tsx,js,jsx}\"", "format:lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"", - "format": "npm run format:prettier && npm run format:lint" + "format": "npm run format:prettier && npm run format:lint", + "vercel-build": "yarn workspace @human-protocol/sdk build && yarn build" }, "browserslist": { "production": [ diff --git a/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx index 54825efc1d..a1ffed8cdb 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx @@ -30,7 +30,7 @@ export const ForgotPasswordForm = () => { await authService.forgotPassword(email); setIsSuccess(true); } catch (err) { - setAlertMsg(err?.message); + setAlertMsg(err?.response?.data?.message ?? err?.message); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Auth/ResendEmailVerificationForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/ResendEmailVerificationForm.jsx index 755baf19aa..13297ec976 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/ResendEmailVerificationForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/ResendEmailVerificationForm.jsx @@ -22,7 +22,7 @@ export const ResendEmailVerificationForm = ({ onFinish }) => { await authService.resendEmailVerification(email); onFinish(); } catch (err) { - setAlertMsg(err?.message); + setAlertMsg(err?.response?.data?.message ?? err?.message); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx index 66940f249c..f6d46ec925 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx @@ -7,6 +7,7 @@ import { Link } from 'react-router-dom'; import * as authService from '../../services/auth'; import { useAppDispatch } from '../../state'; import { fetchUserBalanceAsync, signIn } from '../../state/auth/reducer'; +import { fetchUserJobsAsync } from '../../state/jobs/reducer'; import { Password } from './Password'; import { LoginValidationSchema } from './schema'; @@ -34,8 +35,9 @@ export const SignInForm = ({ onError }) => { }); dispatch(signIn(data)); dispatch(fetchUserBalanceAsync()); + dispatch(fetchUserJobsAsync()); } catch (err) { - onError(err?.response?.data?.message); + onError(err?.response?.data?.message ?? err.message); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx index d70ffafa05..a51512b750 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx @@ -27,7 +27,7 @@ export const SignUpForm = ({ onFinish, onError }) => { setIsSuccess(true); } catch (err) { - onError(err?.message); + onError(err?.response?.data?.message ?? err.message); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx b/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx index c6deb9f0d6..1a72cbb4a9 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx @@ -29,7 +29,7 @@ export const VerifyEmailForm = () => { await authService.verifyEmail({ token }); setIsSuccess(true); } catch (err) { - setAlertMsg(err?.response?.data?.message); + setAlertMsg(err?.response?.data?.message ?? err.message); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Headers/AuthHeader.tsx b/packages/apps/job-launcher/client/src/components/Headers/AuthHeader.tsx index d7c08c4639..48a24b61c8 100644 --- a/packages/apps/job-launcher/client/src/components/Headers/AuthHeader.tsx +++ b/packages/apps/job-launcher/client/src/components/Headers/AuthHeader.tsx @@ -63,12 +63,16 @@ export const AuthHeader = () => { }; const handleLogOut = async () => { - if (refreshToken) { - setIsLoggingOut(true); - await authServices.signOut(refreshToken); - dispatch(signOut()); - setIsLoggingOut(false); + setIsLoggingOut(true); + try { + if (refreshToken) { + await authServices.signOut(refreshToken); + } + } catch (err) { + console.log(err); } + dispatch(signOut()); + setIsLoggingOut(false); }; let segements = pathname.split('/').filter((s) => s); diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx index c640f92c5e..04058f15ae 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/CreateJob.tsx @@ -31,7 +31,7 @@ export const CreateJob = () => { '0px 1px 5px 0px rgba(233, 235, 250, 0.20), 0px 2px 2px 0px rgba(233, 235, 250, 0.50), 0px 3px 1px -2px #E9EBFA', }} > - + Job Type { error={touched.type && Boolean(errors.type)} onBlur={handleBlur} > - - Binary Classification - Points Bounding Boxes - + + + setFieldValue('description', e.target.value) + } + onBlur={handleBlur} + placeholder="Description" + label="Description" + error={touched.description && Boolean(errors.description)} + helperText={errors.description} + multiline + rows={11} + /> + + + + + + value.map((option, index) => ( + + )) + } + renderInput={(params) => ( + + )} + onChange={(e, value) => setFieldValue('labels', value)} + onBlur={handleBlur} + placeholder="Labels" + /> + + setFieldValue('dataUrl', e.target.value)} onBlur={handleBlur} placeholder="Data URL" + label="Data URL" error={touched.dataUrl && Boolean(errors.dataUrl)} helperText={errors.dataUrl} /> - + { } onBlur={handleBlur} placeholder="Ground Truth URL" + label="Ground Truth URL" error={ touched.groundTruthUrl && Boolean(errors.groundTruthUrl) } helperText={errors.groundTruthUrl} /> + + setFieldValue('userGuide', e.target.value)} + onBlur={handleBlur} + placeholder="User Guide" + label="User Guide URL" + error={touched.userGuide && Boolean(errors.userGuide)} + helperText={errors.userGuide} + /> + { } onBlur={handleBlur} placeholder="Accuracy target %" + label="Accuracy target %" error={ touched.accuracyTarget && Boolean(errors.accuracyTarget) } @@ -181,7 +200,6 @@ export const CvatJobRequestForm = () => { /> - { > diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/FiatPayForm.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/FiatPayForm.tsx index 25f2d386f7..2ead83f7db 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/FiatPayForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/FiatPayForm.tsx @@ -155,6 +155,8 @@ export const FiatPayForm = ({ // create job const { jobType, chainId, fortuneRequest, cvatRequest } = jobRequest; + if (!chainId) return; + if (jobType === JobType.Fortune && fortuneRequest) { await jobService.createFortuneJob(chainId, fortuneRequest, fundAmount); } else if (jobType === JobType.CVAT && cvatRequest) { diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/FortuneJobRequestForm.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/FortuneJobRequestForm.tsx index 594d356b4f..d4884a4de8 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/FortuneJobRequestForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/FortuneJobRequestForm.tsx @@ -2,6 +2,7 @@ import { Box, Button, FormControl, Grid, TextField } from '@mui/material'; import { Formik } from 'formik'; import React from 'react'; import { useCreateJobPageUI } from '../../../providers/CreateJobPageUIProvider'; +import { FortuneJobRequestValidationSchema } from './schema'; export const FortuneJobRequestForm = () => { const { jobRequest, updateJobRequest, goToPrevStep, goToNextStep } = @@ -29,7 +30,7 @@ export const FortuneJobRequestForm = () => { {({ @@ -43,7 +44,7 @@ export const FortuneJobRequestForm = () => { setFieldValue, }) => (
- + { onChange={(e) => setFieldValue('title', e.target.value)} onBlur={handleBlur} placeholder="Title" + label="Title" error={touched.title && Boolean(errors.title)} helperText={errors.title} /> @@ -67,6 +69,7 @@ export const FortuneJobRequestForm = () => { } onBlur={handleBlur} placeholder="Fortunes Requested" + label="Fortunes Requested" type="number" inputProps={{ min: 0, step: 1 }} error={ @@ -87,6 +90,7 @@ export const FortuneJobRequestForm = () => { } onBlur={handleBlur} placeholder="Description" + label="Description" error={touched.description && Boolean(errors.description)} helperText={errors.description} /> @@ -102,7 +106,13 @@ export const FortuneJobRequestForm = () => { > diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/PayJob.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/PayJob.tsx index e0d5777454..dd3eabd77f 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/PayJob.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/PayJob.tsx @@ -28,7 +28,7 @@ export const PayJob = () => { ) { setErrorMessage(err.code); } else { - setErrorMessage(err.message); + setErrorMessage(err?.response?.data?.message ?? err?.message); } }; diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts new file mode 100644 index 0000000000..3d97f46091 --- /dev/null +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts @@ -0,0 +1,25 @@ +import * as Yup from 'yup'; + +export const CvatJobRequestValidationSchema = Yup.object().shape({ + title: Yup.string().required('Title is required'), + description: Yup.string().required('Description is required'), + dataUrl: Yup.string().required('Data URL is required').url('Invalid URL'), + groundTruthUrl: Yup.string() + .required('Ground Truth URL is required') + .url('Invalid URL'), + userGuide: Yup.string() + .required('User Guide URL is required') + .url('Invalid URL'), + accuracyTarget: Yup.number() + .required('Accuracy target is required') + .moreThan(0, 'Accuracy target must be greater than 0') + .max(100, 'Accuracy target must be less than or equal to 100'), +}); + +export const FortuneJobRequestValidationSchema = Yup.object().shape({ + title: Yup.string().required('Title is required'), + description: Yup.string().required('Description is required'), + fortunesRequested: Yup.number() + .required('FortunesRequested is required') + .moreThan(0, 'FortunesRequested must be greater than 0'), +}); diff --git a/packages/apps/job-launcher/client/src/components/Jobs/StatusToggleButtons.tsx b/packages/apps/job-launcher/client/src/components/Jobs/StatusToggleButtons.tsx index 7e7098b3ed..46d04f68e8 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/StatusToggleButtons.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/StatusToggleButtons.tsx @@ -1,26 +1,21 @@ import ToggleButton from '@mui/material/ToggleButton'; -import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; -import * as React from 'react'; +import ToggleButtonGroup, { + ToggleButtonGroupProps, +} from '@mui/material/ToggleButtonGroup'; +import React, { FC } from 'react'; +import { JobStatus } from '../../types'; const RANGE_BUTTONS = [ - { label: 'All', value: 'All' }, - { label: 'Launched', value: 'Launched' }, - { label: 'Pending', value: 'Pending' }, - { label: 'Completed', value: 'Completed' }, - { label: 'Cancelled', value: 'Cancelled' }, - { label: 'Failed', value: 'Failed' }, + { label: 'All', value: JobStatus.ALL }, + { label: 'Launched', value: JobStatus.LAUNCHED }, + { label: 'Pending', value: JobStatus.PENDING }, + { label: 'Cancelled', value: JobStatus.CANCELED }, + { label: 'Failed', value: JobStatus.FAILED }, ]; -export function StatusToggleButtons() { - const handleChange = ( - event: React.MouseEvent, - newValue: number - ) => { - console.log(newValue); - }; - +export const StatusToggleButtons: FC = (props) => { return ( - + {RANGE_BUTTONS.map(({ label, value }) => ( ); -} +}; diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Table.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Table.tsx index 09b787e5e3..3808a64362 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Table.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Table.tsx @@ -1,13 +1,20 @@ +import { ChainId } from '@human-protocol/sdk'; import { Box, Button, IconButton, Typography } from '@mui/material'; import { Link, useNavigate } from 'react-router-dom'; import { CopyLinkIcon } from '../../components/Icons/CopyLinkIcon'; import { Table } from '../../components/Table'; -import { useJobs } from '../../hooks/useJobs'; +import { useJobs } from '../../state/jobs/hooks'; import { JobStatus } from '../../types'; -export const JobTable = ({ status }: { status: JobStatus }) => { +export const JobTable = ({ + status, + chainId, +}: { + status: JobStatus; + chainId: ChainId; +}) => { + const { data, isLoading } = useJobs({ status, chainId }); const navigate = useNavigate(); - const { data, isLoading } = useJobs(status); return ( { const [amount, setAmount] = useState(); const [isSuccess, setIsSuccess] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); const { data: signer } = useSigner(); const { data: rate } = useTokenRate('hmt', 'usd'); @@ -66,8 +68,8 @@ export const CryptoTopUpForm = () => { dispatch(fetchUserBalanceAsync()); setIsSuccess(true); - } catch (err) { - console.error(err); + } catch (err: any) { + setErrorMessage(err?.response?.data?.message ?? err?.message); setIsSuccess(false); } @@ -190,6 +192,16 @@ export const CryptoTopUpForm = () => { + setErrorMessage(null)} + anchorOrigin={{ vertical: 'top', horizontal: 'center' }} + > + setErrorMessage(null)} severity="error"> + {errorMessage} + + ); }; diff --git a/packages/apps/job-launcher/client/src/components/TopUpAccount/FiatTopUpForm.tsx b/packages/apps/job-launcher/client/src/components/TopUpAccount/FiatTopUpForm.tsx index 69d3175b22..986fa1cf54 100644 --- a/packages/apps/job-launcher/client/src/components/TopUpAccount/FiatTopUpForm.tsx +++ b/packages/apps/job-launcher/client/src/components/TopUpAccount/FiatTopUpForm.tsx @@ -87,8 +87,8 @@ export const FiatTopUpForm = () => { dispatch(fetchUserBalanceAsync()); setIsSuccess(true); - } catch (err) { - setErrorMessage(err.message); + } catch (err: any) { + setErrorMessage(err?.response?.data?.message ?? err?.message); setIsSuccess(false); } setIsLoading(false); diff --git a/packages/apps/job-launcher/client/src/hooks/useJobs.ts b/packages/apps/job-launcher/client/src/hooks/useJobs.ts deleted file mode 100644 index 5115344e6d..0000000000 --- a/packages/apps/job-launcher/client/src/hooks/useJobs.ts +++ /dev/null @@ -1,9 +0,0 @@ -import useSWR from 'swr'; -import * as jobService from '../services/job'; -import { JobStatus } from '../types'; - -export const useJobs = (status: JobStatus) => { - return useSWR(`human-protocol-${status}-jobs`, () => - jobService.getJobListByStatus(status) - ); -}; diff --git a/packages/apps/job-launcher/client/src/main.tsx b/packages/apps/job-launcher/client/src/main.tsx index 390234fc54..b0b249debf 100644 --- a/packages/apps/job-launcher/client/src/main.tsx +++ b/packages/apps/job-launcher/client/src/main.tsx @@ -28,6 +28,7 @@ import { LOCAL_STORAGE_KEYS } from './constants'; import reportWebVitals from './reportWebVitals'; import { store } from './state'; import { fetchUserBalanceAsync, signIn } from './state/auth/reducer'; +import { fetchUserJobsAsync } from './state/jobs/reducer'; import theme from './theme'; // import { isJwtExpired } from './utils/jwt'; @@ -108,6 +109,7 @@ loadStripe(publishableKey).then((stripePromise) => { }) ); store.dispatch(fetchUserBalanceAsync()); + store.dispatch(fetchUserJobsAsync()); } root.render( diff --git a/packages/apps/job-launcher/client/src/pages/Dashboard/index.tsx b/packages/apps/job-launcher/client/src/pages/Dashboard/index.tsx index 7b7bdefbc7..effb96be94 100644 --- a/packages/apps/job-launcher/client/src/pages/Dashboard/index.tsx +++ b/packages/apps/job-launcher/client/src/pages/Dashboard/index.tsx @@ -1,5 +1,6 @@ +import { ChainId } from '@human-protocol/sdk'; import { Box, Grid, Typography } from '@mui/material'; -import React from 'react'; +import React, { useState } from 'react'; import { JobsGraph } from '../../components/Dashboard/JobsGraph'; import { LiquidityData } from '../../components/Dashboard/LiquidityData'; import { OracleReputation } from '../../components/Dashboard/OracleRepuptation'; @@ -7,10 +8,12 @@ import { WorkersPerformance } from '../../components/Dashboard/WorkersPerformanc import { StatusToggleButtons } from '../../components/Jobs/StatusToggleButtons'; import { JobTable } from '../../components/Jobs/Table'; import { NetworkSelect } from '../../components/NetworkSelect'; -import { SearchField } from '../../components/SearchField'; import { JobStatus } from '../../types'; export default function Dashboard() { + const [chainId, setChainId] = useState(ChainId.ALL); + const [status, setStatus] = useState(JobStatus.ALL); + return ( - - + setChainId(e.target.value as ChainId)} + /> @@ -58,10 +64,14 @@ export default function Dashboard() { }} > Jobs - + setStatus(value)} + /> - + ); } diff --git a/packages/apps/job-launcher/client/src/pages/Job/JobDetail/index.tsx b/packages/apps/job-launcher/client/src/pages/Job/JobDetail/index.tsx index 74b93b667c..4f5b831052 100644 --- a/packages/apps/job-launcher/client/src/pages/Job/JobDetail/index.tsx +++ b/packages/apps/job-launcher/client/src/pages/Job/JobDetail/index.tsx @@ -1,12 +1,9 @@ -import { Box, Card, Grid, IconButton, Stack, Typography } from '@mui/material'; +import { Box, Card, Grid, Stack, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import React from 'react'; import { useParams } from 'react-router-dom'; import { CardTextRow } from '../../../components/CardTextRow'; import { CopyAddressButton } from '../../../components/CopyAddressButton'; -import { CopyLinkIcon } from '../../../components/Icons/CopyLinkIcon'; -import { SearchField } from '../../../components/SearchField'; -import { Table } from '../../../components/Table'; import { useJobDetails } from '../../../hooks/useJobDetails'; const CardContainer = styled(Card)(({ theme }) => ({ @@ -35,9 +32,6 @@ export default function JobDetail() { Job details - - - @@ -162,6 +156,8 @@ export default function JobDetail() { + {/* + TODO: Add this back in when we have a way to fetch the job solution Job solutions @@ -203,7 +199,7 @@ export default function JobDetail() { ]} /> - + */} ); } diff --git a/packages/apps/job-launcher/client/src/pages/Job/JobList/index.tsx b/packages/apps/job-launcher/client/src/pages/Job/JobList/index.tsx index fbfe738cbd..54a2de09b7 100644 --- a/packages/apps/job-launcher/client/src/pages/Job/JobList/index.tsx +++ b/packages/apps/job-launcher/client/src/pages/Job/JobList/index.tsx @@ -1,21 +1,25 @@ +import { ChainId } from '@human-protocol/sdk'; import { Box, Typography } from '@mui/material'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Navigate, useParams } from 'react-router-dom'; import { JobTable } from '../../../components/Jobs/Table'; import { NetworkSelect } from '../../../components/NetworkSelect'; -import { SearchField } from '../../../components/SearchField'; import { JobStatus } from '../../../types'; const JOB_NAV_ITEMS = [ { status: JobStatus.LAUNCHED, label: 'launched' }, { status: JobStatus.PENDING, label: 'pending' }, - // { status: JobStatus.PAID, label: 'completed' }, { status: JobStatus.CANCELED, label: 'cancelled' }, { status: JobStatus.FAILED, label: 'failed' }, ]; export default function JobList() { const params = useParams(); + const [chainId, setChainId] = useState(ChainId.ALL); + + useEffect(() => { + setChainId(ChainId.ALL); + }, [params.status]); const item = JOB_NAV_ITEMS.find((x) => x.label === params.status); if (!item) { @@ -44,11 +48,14 @@ export default function JobList() { - - + setChainId(e.target.value as ChainId)} + /> - + ); } diff --git a/packages/apps/job-launcher/client/src/providers/CreateJobPageUIProvider.tsx b/packages/apps/job-launcher/client/src/providers/CreateJobPageUIProvider.tsx index 02f66f66f9..c3d76d6d58 100644 --- a/packages/apps/job-launcher/client/src/providers/CreateJobPageUIProvider.tsx +++ b/packages/apps/job-launcher/client/src/providers/CreateJobPageUIProvider.tsx @@ -1,5 +1,4 @@ import React, { createContext, useContext, useState } from 'react'; -import { SUPPORTED_CHAIN_IDS } from '../constants/chains'; import { CreateJobStep, JobRequest, JobType, PayMethod } from '../types'; export type CreateJobPageUIType = { @@ -21,7 +20,7 @@ const initialData: Omit< payMethod: PayMethod.Crypto, jobRequest: { jobType: JobType.Fortune, - chainId: SUPPORTED_CHAIN_IDS[0], + chainId: undefined, }, }; @@ -39,7 +38,6 @@ export const CreateJobPageUIProvider = ({ const [payMethod, setPayMethod] = useState(PayMethod.Crypto); const [jobRequest, setJobRequest] = useState({ jobType: JobType.Fortune, - chainId: SUPPORTED_CHAIN_IDS[0], }); const goToPrevStep = () => { @@ -60,7 +58,6 @@ export const CreateJobPageUIProvider = ({ setPayMethod(PayMethod.Crypto); setJobRequest({ jobType: JobType.Fortune, - chainId: SUPPORTED_CHAIN_IDS[0], }); }; diff --git a/packages/apps/job-launcher/client/src/services/job.ts b/packages/apps/job-launcher/client/src/services/job.ts index 06f04d5bcc..94d8d2565a 100644 --- a/packages/apps/job-launcher/client/src/services/job.ts +++ b/packages/apps/job-launcher/client/src/services/job.ts @@ -34,14 +34,14 @@ export const createCvatJob = async ( fundAmount: Number(amount), dataUrl: data.dataUrl, labels: data.labels, - minQuality: Number(data.accuracyTarget), + minQuality: Number(data.accuracyTarget) / 100, gtUrl: data.groundTruthUrl, type: data.type, }; await api.post('/job/cvat', body); }; -export const getJobListByStatus = async (status: JobStatus) => { +export const getJobList = async (status?: JobStatus) => { const { data } = await api.get(`/job/list`, { params: { status } }); return data; }; @@ -52,6 +52,6 @@ export const getJobResult = async (jobId: number) => { }; export const getJobDetails = async (jobId: number) => { - const { data } = await api.get(`/details/${jobId}`); + const { data } = await api.get(`/job/details/${jobId}`); return data; }; diff --git a/packages/apps/job-launcher/client/src/state/index.ts b/packages/apps/job-launcher/client/src/state/index.ts index 526600069a..ab5f61b3ba 100644 --- a/packages/apps/job-launcher/client/src/state/index.ts +++ b/packages/apps/job-launcher/client/src/state/index.ts @@ -3,9 +3,10 @@ import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux'; import auth from './auth/reducer'; import dashboard from './dashboard/reducer'; +import jobs from './jobs/reducer'; export const store = configureStore({ - reducer: { auth, dashboard }, + reducer: { auth, dashboard, jobs }, }); export type AppState = ReturnType; diff --git a/packages/apps/job-launcher/client/src/state/jobs/hooks.ts b/packages/apps/job-launcher/client/src/state/jobs/hooks.ts new file mode 100644 index 0000000000..a74bedbbfa --- /dev/null +++ b/packages/apps/job-launcher/client/src/state/jobs/hooks.ts @@ -0,0 +1,25 @@ +import { ChainId, NETWORKS } from '@human-protocol/sdk'; +import { useAppSelector } from '..'; +import { JobStatus } from '../../types'; + +export const useJobs = ({ + status, + chainId, +}: { + status: JobStatus; + chainId: ChainId; +}) => { + const { jobs, dataLoaded, loadingFailed } = useAppSelector( + (state) => state.jobs + ); + + return { + data: jobs.filter( + (job) => + (status === JobStatus.ALL || job.status === status) && + (chainId === ChainId.ALL || NETWORKS[chainId]?.title === job.network) + ), + isLoading: !dataLoaded && !loadingFailed, + isError: loadingFailed, + }; +}; diff --git a/packages/apps/job-launcher/client/src/state/jobs/reducer.ts b/packages/apps/job-launcher/client/src/state/jobs/reducer.ts new file mode 100644 index 0000000000..56d012b016 --- /dev/null +++ b/packages/apps/job-launcher/client/src/state/jobs/reducer.ts @@ -0,0 +1,30 @@ +import { createAsyncThunk, createReducer } from '@reduxjs/toolkit'; +import { AppState } from '..'; +import * as jobService from '../../services/job'; +import { JobsState, UserJob } from './types'; + +const initialState: JobsState = { + jobs: [], + dataLoaded: false, + loadingFailed: false, +}; + +export const fetchUserJobsAsync = createAsyncThunk< + UserJob[], + void, + { state: AppState } +>('auth/fetchUserJobsAsync', async () => { + const jobs = await jobService.getJobList(); + return jobs; +}); + +export default createReducer(initialState, (builder) => { + builder.addCase(fetchUserJobsAsync.fulfilled, (state, action) => { + state.jobs = action.payload; + state.dataLoaded = true; + }); + builder.addCase(fetchUserJobsAsync.rejected, (state, action) => { + state.loadingFailed = true; + state.dataLoaded = false; + }); +}); diff --git a/packages/apps/job-launcher/client/src/state/jobs/types.ts b/packages/apps/job-launcher/client/src/state/jobs/types.ts new file mode 100644 index 0000000000..196ff6b35e --- /dev/null +++ b/packages/apps/job-launcher/client/src/state/jobs/types.ts @@ -0,0 +1,15 @@ +import { JobStatus } from '../../types'; + +export type UserJob = { + address: string; + fundAmount: number; + jobId: number; + network: string; + status: JobStatus; +}; + +export type JobsState = { + jobs: UserJob[]; + dataLoaded: boolean; + loadingFailed: boolean; +}; diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index e9de695e9f..718195eb8e 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -69,7 +69,6 @@ export enum JobType { } export enum CvatJobType { - IMAGE_LABEL_BINARY = 'IMAGE_LABEL_BINARY', IMAGE_POINTS = 'IMAGE_POINTS', IMAGE_BOXES = 'IMAGE_BOXES', } @@ -86,23 +85,25 @@ export type CvatRequest = { description: string; dataUrl: string; groundTruthUrl: string; + userGuide: string; accuracyTarget: number; }; export type JobRequest = { jobType: JobType; - chainId: ChainId; + chainId?: ChainId; fortuneRequest?: FortuneRequest; cvatRequest?: CvatRequest; }; export enum JobStatus { - PENDING = 'PENDING', - PAID = 'PAID', + ALL = 'ALL', LAUNCHED = 'LAUNCHED', + PENDING = 'PENDING', + CANCELED = 'CANCELED', FAILED = 'FAILED', + PAID = 'PAID', TO_CANCEL = 'TO_CANCEL', - CANCELED = 'CANCELED', } export type JobDetailsResponse = { diff --git a/packages/apps/job-launcher/client/src/utils/api.ts b/packages/apps/job-launcher/client/src/utils/api.ts index b6ba2df953..19ff24628d 100644 --- a/packages/apps/job-launcher/client/src/utils/api.ts +++ b/packages/apps/job-launcher/client/src/utils/api.ts @@ -18,8 +18,7 @@ axiosInstance.interceptors.request.use( axiosInstance.interceptors.response.use( (response) => response, (error) => { - console.log(error.response.status); - if (error.response.status === 401) { + if (error?.response?.status === 401) { localStorage.removeItem('HUMAN_JOB_LAUNCHER_REFRESH_TOKEN'); localStorage.removeItem('HUMAN_JOB_LAUNCHER_ACCESS_TOKEN'); window.location.href = '/'; diff --git a/packages/apps/job-launcher/client/vite.config.ts b/packages/apps/job-launcher/client/vite.config.ts index 34e4111e25..51fb0ed37b 100644 --- a/packages/apps/job-launcher/client/vite.config.ts +++ b/packages/apps/job-launcher/client/vite.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ }, build: { commonjsOptions: { - include: [/human-protocol-sdk/, /node_modules/], + include: [/core/, /human-protocol-sdk/, /node_modules/], }, }, server: {