import React from 'react' import Button from 'components/Button' import { Title } from 'components/Title' import Content from 'components/Content' import BaseLink from 'components/BaseLink' import { v2 } from 'api' import * as models from 'core/store/models' import { Grid, Card, CardContent, Table, TableBody, TableCell, TableHead, TableRow, Typography, TextField, } from '@material-ui/core' import Link from 'components/Link' import { useErrorHandler } from 'hooks/useErrorHandler' import { useLoadingPlaceholder } from 'hooks/useLoadingPlaceholder' import { createStyles, withStyles, WithStyles, Theme, } from '@material-ui/core/styles' import { DirectRequestRow } from './DirectRequestRow' import { OcrJobRow } from './OcrJobRow' import SearchIcon from '@material-ui/icons/Search' enum JobSpecTypes { jobSpec = 'specs', ocrJobSpec = 'specDBs', } interface Job { attributes: T id: string type: string } export type DirectRequest = Job export type OffChainReporting = Job export type CombinedJobs = DirectRequest | OffChainReporting function isDirectRequest(job: CombinedJobs): job is DirectRequest { return job.type === JobSpecTypes.jobSpec } function isOffChainReporting(job: CombinedJobs): job is OffChainReporting { return job.type === JobSpecTypes.ocrJobSpec } function getCreatedAt(job: CombinedJobs) { if (isDirectRequest(job)) { return job.attributes.createdAt } else if (isOffChainReporting(job)) { return job.attributes.offChainReportingOracleSpec.createdAt } else { return new Date().toString() } } const PAGE_SIZE = 1000 // We intentionally set this to a very high number to avoid pagination async function getJobs() { return Promise.all([ v2.specs.getJobSpecs(1, PAGE_SIZE), v2.ocrSpecs.getJobSpecs(), ]).then(([jobs, ocrJobs]) => { const combinedJobs = [...jobs.data, ...ocrJobs.data] const jobsByDate = combinedJobs.sort((a, b) => { const jobA = new Date(getCreatedAt(a)).getTime() const jobB = new Date(getCreatedAt(b)).getTime() return jobA > jobB ? -1 : 1 }) return jobsByDate }) } const searchIncludes = (searchParam: string) => { const lowerCaseSearchParam = searchParam.toLowerCase() return (stringToSearch: string) => { return stringToSearch.toLowerCase().includes(lowerCaseSearchParam) } } export const simpleJobFilter = (search: string) => (job: CombinedJobs) => { const test = searchIncludes(search) if (isDirectRequest(job)) { return ( test(job.id) || test(job.attributes.name) || job.attributes.initiators.some((initiator) => test(initiator.type)) || test('direct request') ) } if (isOffChainReporting(job)) { return ( test(job.id) || ([ 'contractAddress', 'keyBundleID', 'p2pPeerID', 'transmitterAddress', ] as Array< keyof models.OcrJobSpec['offChainReportingOracleSpec'] >).some((property) => test(String(job.attributes.offChainReportingOracleSpec[property])), ) || test('off-chain reporting') ) } return false } const styles = (theme: Theme) => createStyles({ card: { padding: theme.spacing.unit, marginBottom: theme.spacing.unit * 3, }, search: { marginBottom: theme.spacing.unit, }, }) export const JobsIndex = ({ classes, }: { classes: WithStyles['classes'] }) => { const [search, setSearch] = React.useState('') const [jobs, setJobs] = React.useState() const { error, ErrorComponent, setError } = useErrorHandler() const { LoadingPlaceholder } = useLoadingPlaceholder(!error && !jobs) React.useEffect(() => { document.title = 'Jobs' }, []) const jobFilter = React.useMemo(() => simpleJobFilter(search), [search]) React.useEffect(() => { getJobs().then(setJobs).catch(setError) }, [setError]) return ( Jobs {!error && jobs && ( setSearch(event.target.value)} /> ID Created Type Initiator {jobs && !jobs.length && ( You haven’t created any jobs yet. Create a new job{' '} here )} {jobs && jobs.filter(jobFilter).map((job: CombinedJobs) => { if (isDirectRequest(job)) { return } else if (isOffChainReporting(job)) { return } else { return Unknown Job Type } })}
)}
) } export default withStyles(styles)(JobsIndex)