From 93ca8e3879500c307c8ec28beafe852941f4325d Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Fri, 16 Apr 2021 10:12:53 +0200 Subject: [PATCH] Job details (#528) * refactor * refactor job details * styling, cleanup, get algo name * output dataset & algo dt symbol * more styling and job metadata output * history background tweak * human numbers, edge case fixes --- src/@types/ComputeJobMetaData.d.ts | 13 +- src/components/atoms/Button.module.css | 1 + src/components/atoms/Lists.module.css | 4 - src/components/atoms/Loader.module.css | 2 + .../molecules/FormFields/AssetSelection.tsx | 2 +- .../pages/History/ComputeDetails.module.css | 3 - .../pages/History/ComputeDetails.tsx | 109 ---------------- .../History/ComputeJobs/Details.module.css | 72 +++++++++++ .../pages/History/ComputeJobs/Details.tsx | 119 ++++++++++++++++++ .../History/ComputeJobs/Results.module.css | 4 + .../pages/History/ComputeJobs/Results.tsx | 90 +++++++++++++ .../index.module.css} | 0 .../index.tsx} | 107 ++++++---------- src/components/pages/History/index.module.css | 6 +- src/components/pages/History/index.tsx | 6 +- 15 files changed, 332 insertions(+), 206 deletions(-) delete mode 100644 src/components/pages/History/ComputeDetails.module.css delete mode 100644 src/components/pages/History/ComputeDetails.tsx create mode 100644 src/components/pages/History/ComputeJobs/Details.module.css create mode 100644 src/components/pages/History/ComputeJobs/Details.tsx create mode 100644 src/components/pages/History/ComputeJobs/Results.module.css create mode 100644 src/components/pages/History/ComputeJobs/Results.tsx rename src/components/pages/History/{ComputeJobs.module.css => ComputeJobs/index.module.css} (100%) rename src/components/pages/History/{ComputeJobs.tsx => ComputeJobs/index.tsx} (64%) diff --git a/src/@types/ComputeJobMetaData.d.ts b/src/@types/ComputeJobMetaData.d.ts index 444378339..f3b583e9f 100644 --- a/src/@types/ComputeJobMetaData.d.ts +++ b/src/@types/ComputeJobMetaData.d.ts @@ -1,11 +1,6 @@ -export interface ComputeJobMetaData { - jobId: string - did: string - dateCreated: string - dateFinished: string +import { ComputeJob } from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute' + +export interface ComputeJobMetaData extends ComputeJob { assetName: string - status: number - statusText: string - algorithmLogUrl: string - resultsUrls: string[] + assetDtSymbol: string } diff --git a/src/components/atoms/Button.module.css b/src/components/atoms/Button.module.css index 5e300945f..c83453a4f 100644 --- a/src/components/atoms/Button.module.css +++ b/src/components/atoms/Button.module.css @@ -5,6 +5,7 @@ margin: 0; display: inline-block; width: fit-content; + min-width: 7rem; padding: calc(var(--spacer) / 3) var(--spacer); font-size: var(--font-size-base); font-family: var(--font-family-base); diff --git a/src/components/atoms/Lists.module.css b/src/components/atoms/Lists.module.css index 1ae912739..fca1818ac 100644 --- a/src/components/atoms/Lists.module.css +++ b/src/components/atoms/Lists.module.css @@ -5,10 +5,6 @@ list-style-position: inside; } -.item span { - color: var(--brand-grey-dark); -} - .ulItem { list-style-type: square; } diff --git a/src/components/atoms/Loader.module.css b/src/components/atoms/Loader.module.css index 3c6c27056..8519d8f1a 100644 --- a/src/components/atoms/Loader.module.css +++ b/src/components/atoms/Loader.module.css @@ -1,5 +1,7 @@ .loaderWrap { display: flex; + align-items: center; + justify-content: center; } .loader { diff --git a/src/components/molecules/FormFields/AssetSelection.tsx b/src/components/molecules/FormFields/AssetSelection.tsx index fc9536189..9f64647c3 100644 --- a/src/components/molecules/FormFields/AssetSelection.tsx +++ b/src/components/molecules/FormFields/AssetSelection.tsx @@ -4,9 +4,9 @@ import slugify from 'slugify' import classNames from 'classnames/bind' import PriceUnit from '../../atoms/Price/PriceUnit' import { ReactComponent as External } from '../../../images/external.svg' -import styles from './AssetSelection.module.css' import InputElement from '../../atoms/Input/InputElement' import Loader from '../../atoms/Loader' +import styles from './AssetSelection.module.css' const cx = classNames.bind(styles) diff --git a/src/components/pages/History/ComputeDetails.module.css b/src/components/pages/History/ComputeDetails.module.css deleted file mode 100644 index 7fbc3bc43..000000000 --- a/src/components/pages/History/ComputeDetails.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.title { - margin-bottom: calc(var(--spacer) / 4); -} diff --git a/src/components/pages/History/ComputeDetails.tsx b/src/components/pages/History/ComputeDetails.tsx deleted file mode 100644 index ea21fe79a..000000000 --- a/src/components/pages/History/ComputeDetails.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { Logger } from '@oceanprotocol/lib' -import React, { ReactElement, useEffect, useState } from 'react' -import Loader from '../../atoms/Loader' -import Modal from '../../atoms/Modal' -import { ComputeJobMetaData } from '../../../@types/ComputeJobMetaData' -import Time from '../../atoms/Time' -import shortid from 'shortid' -import styles from './ComputeDetails.module.css' -import { Status } from './ComputeJobs' -import { ListItem } from '../../atoms/Lists' -import { useOcean } from '../../../providers/Ocean' - -export default function ComputeDetailsModal({ - computeJob, - isOpen, - onToggleModal -}: { - computeJob: ComputeJobMetaData - isOpen: boolean - onToggleModal: () => void -}): ReactElement { - const { ocean, account } = useOcean() - const [isLoading, setIsLoading] = useState(false) - - const isFinished = computeJob.dateFinished !== null - - useEffect(() => { - async function getDetails() { - if (!account || !ocean || !computeJob || !isOpen || !isFinished) return - - try { - setIsLoading(true) - const job = await ocean.compute.status( - account, - computeJob.did, - undefined, - undefined, - computeJob.jobId - ) - if (job?.length > 0) { - computeJob.algorithmLogUrl = job[0].algorithmLogUrl - // hack because ComputeJob returns resultsUrl instead of resultsUrls, issue created already - computeJob.resultsUrls = - (job[0] as any).resultsUrl !== '' ? (job[0] as any).resultsUrl : [] - } - } catch (error) { - Logger.error(error.message) - } finally { - setIsLoading(false) - } - } - getDetails() - }, [ocean, account, isOpen, computeJob, isFinished]) - - return ( - -

{computeJob.assetName}

-

- Created

- {computeJob.statusText} - - {isFinished && - (isLoading ? ( - - ) : ( - <> -
    - - {computeJob.algorithmLogUrl ? ( - - View Log - - ) : ( - 'No logs found' - )} - - - {computeJob.resultsUrls?.map((url, i) => - url ? ( - - - View Result {i} - - - ) : ( - 'No results found.' - ) - )} -
- - ))} -
- ) -} diff --git a/src/components/pages/History/ComputeJobs/Details.module.css b/src/components/pages/History/ComputeJobs/Details.module.css new file mode 100644 index 000000000..785537c38 --- /dev/null +++ b/src/components/pages/History/ComputeJobs/Details.module.css @@ -0,0 +1,72 @@ +.main { + margin-bottom: var(--spacer); +} + +.main > div:first-child { + margin-bottom: calc(var(--spacer) / 2); +} + +.asset { + composes: box from '../../../atoms/Box.module.css'; + box-shadow: none; + padding: calc(var(--spacer) / 2); + margin-bottom: calc(var(--spacer) / 2); +} + +.asset + .asset { + margin-left: var(--spacer); + border-top-left-radius: 0; + border-bottom-left-radius: 0; + position: relative; + overflow: visible; +} + +.asset + .asset:before { + content: ''; + display: block; + position: absolute; + left: -1px; + top: -1.15rem; + bottom: 3px; + width: 1px; + background-color: var(--border-color); +} + +.asset p { + margin: 0; +} + +.asset code { + padding: 0; +} + +.assetTitle { + margin-bottom: 0; + font-size: var(--font-size-base); + color: var(--font-color-text); +} + +.assetLink { + display: inline-block; + margin-left: calc(var(--spacer) / 8); +} + +.assetLink svg { + margin: 0; + fill: var(--color-primary); + width: 0.6em; + height: 0.6em; +} + +.assetMeta, +.assetMeta code { + color: var(--color-secondary); + font-size: var(--font-size-small); +} + +.meta { + display: grid; + gap: var(--spacer); + grid-template-columns: 1fr 1fr; + margin-top: calc(var(--spacer) * 1.5); +} diff --git a/src/components/pages/History/ComputeJobs/Details.tsx b/src/components/pages/History/ComputeJobs/Details.tsx new file mode 100644 index 000000000..c1bede935 --- /dev/null +++ b/src/components/pages/History/ComputeJobs/Details.tsx @@ -0,0 +1,119 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import axios from 'axios' +import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData' +import Time from '../../../atoms/Time' +import Button from '../../../atoms/Button' +import Modal from '../../../atoms/Modal' +import MetaItem from '../../../organisms/AssetContent/MetaItem' +import { ReactComponent as External } from '../../../../images/external.svg' +import { retrieveDDO } from '../../../../utils/aquarius' +import { useOcean } from '../../../../providers/Ocean' +import Results from './Results' +import styles from './Details.module.css' + +function Asset({ + title, + symbol, + did +}: { + title: string + symbol: string + did: string +}) { + return ( +
+

+ {title}{' '} + + + +

+

+ {symbol} | {did} +

+
+ ) +} + +function DetailsAssets({ job }: { job: ComputeJobMetaData }) { + const { config } = useOcean() + const [algoName, setAlgoName] = useState() + const [algoDtSymbol, setAlgoDtSymbol] = useState() + + useEffect(() => { + async function getAlgoMetadata() { + const source = axios.CancelToken.source() + + const ddo = await retrieveDDO( + job.algoDID, + config.metadataCacheUri, + source.token + ) + setAlgoDtSymbol(ddo.dataTokenInfo.symbol) + + const { attributes } = ddo.findServiceByType('metadata') + setAlgoName(attributes?.main.name) + } + getAlgoMetadata() + }, [config?.metadataCacheUri, job.algoDID]) + + return ( + <> + + + + ) +} + +export default function Details({ + job +}: { + job: ComputeJobMetaData +}): ReactElement { + const [isDialogOpen, setIsDialogOpen] = useState(false) + + return ( + <> + + setIsDialogOpen(false)} + > + + + +
+ } + /> + {job.dateFinished && ( + } + /> + )} + {job.jobId}} /> + {job.resultsDid && ( + {job.resultsDid}} + /> + )} +
+
+ + ) +} diff --git a/src/components/pages/History/ComputeJobs/Results.module.css b/src/components/pages/History/ComputeJobs/Results.module.css new file mode 100644 index 000000000..1c3c5f3c6 --- /dev/null +++ b/src/components/pages/History/ComputeJobs/Results.module.css @@ -0,0 +1,4 @@ +.results { + composes: asset from './Details.module.css'; + border-bottom-left-radius: var(--border-radius) !important; +} diff --git a/src/components/pages/History/ComputeJobs/Results.tsx b/src/components/pages/History/ComputeJobs/Results.tsx new file mode 100644 index 000000000..7a93eef8f --- /dev/null +++ b/src/components/pages/History/ComputeJobs/Results.tsx @@ -0,0 +1,90 @@ +import { Logger } from '@oceanprotocol/lib' +import React, { ReactElement, useState } from 'react' +import Loader from '../../../atoms/Loader' +import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData' +import { ListItem } from '../../../atoms/Lists' +import Button from '../../../atoms/Button' +import { useOcean } from '../../../../providers/Ocean' +import styles from './Results.module.css' + +export default function Results({ + job +}: { + job: ComputeJobMetaData +}): ReactElement { + const { ocean, account } = useOcean() + const [isLoading, setIsLoading] = useState(false) + const [hasFetched, setHasFetched] = useState(false) + const isFinished = job.dateFinished !== null + + async function getResults() { + if (!account || !ocean || !job) return + + try { + setIsLoading(true) + const jobStatus = await ocean.compute.status( + account, + job.did, + undefined, + undefined, + job.jobId + ) + if (jobStatus?.length > 0) { + job.algorithmLogUrl = jobStatus[0].algorithmLogUrl + job.resultsUrl = jobStatus[0].resultsUrl + } + } catch (error) { + Logger.error(error.message) + } finally { + setIsLoading(false) + setHasFetched(true) + } + } + + return ( +
+ {hasFetched ? ( +
    + + {job.algorithmLogUrl ? ( + + View Log + + ) : ( + 'No logs found.' + )} + + + {job.resultsUrl && + Array.isArray(job.resultsUrl) && + job.resultsUrl.map((url, i) => + url ? ( + + + View Result {i + 1} + + + ) : ( + No results found. + ) + )} +
+ ) : ( + + )} +
+ ) +} diff --git a/src/components/pages/History/ComputeJobs.module.css b/src/components/pages/History/ComputeJobs/index.module.css similarity index 100% rename from src/components/pages/History/ComputeJobs.module.css rename to src/components/pages/History/ComputeJobs/index.module.css diff --git a/src/components/pages/History/ComputeJobs.tsx b/src/components/pages/History/ComputeJobs/index.tsx similarity index 64% rename from src/components/pages/History/ComputeJobs.tsx rename to src/components/pages/History/ComputeJobs/index.tsx index d41ee99f3..00862ce87 100644 --- a/src/components/pages/History/ComputeJobs.tsx +++ b/src/components/pages/History/ComputeJobs/index.tsx @@ -1,21 +1,20 @@ import React, { ReactElement, useEffect, useState } from 'react' -import Time from '../../atoms/Time' -import styles from './ComputeJobs.module.css' -import Button from '../../atoms/Button' -import ComputeDetails from './ComputeDetails' -import { ComputeJobMetaData } from '../../../@types/ComputeJobMetaData' -import { Link } from 'gatsby' -import { DDO, Logger, ServiceCommon, ServiceCompute } from '@oceanprotocol/lib' -import Dotdotdot from 'react-dotdotdot' -import Table from '../../atoms/Table' -import { useOcean } from '../../../providers/Ocean' -import { gql, useQuery } from '@apollo/client' -import { useWeb3 } from '../../../providers/Web3' -import { queryMetadata } from '../../../utils/aquarius' -import axios, { CancelToken } from 'axios' -import { ComputeOrders } from '../../../@types/apollo/ComputeOrders' import web3 from 'web3' -import AssetTitle from '../../molecules/AssetListTitle' +import Time from '../../../atoms/Time' +import { Link } from 'gatsby' +import { DDO, Logger, Service, ServiceCompute } from '@oceanprotocol/lib' +import { ComputeJobMetaData } from '../../../../@types/ComputeJobMetaData' +import Dotdotdot from 'react-dotdotdot' +import Table from '../../../atoms/Table' +import { useOcean } from '../../../../providers/Ocean' +import { gql, useQuery } from '@apollo/client' +import { useWeb3 } from '../../../../providers/Web3' +import { queryMetadata } from '../../../../utils/aquarius' +import axios, { CancelToken } from 'axios' +import { ComputeOrders } from '../../../../@types/apollo/ComputeOrders' +import Details from './Details' +import styles from './index.module.css' + const getComputeOrders = gql` query ComputeOrders($user: String!) { tokenOrders( @@ -33,22 +32,6 @@ const getComputeOrders = gql` } } ` -function DetailsButton({ row }: { row: ComputeJobMetaData }): ReactElement { - const [isDialogOpen, setIsDialogOpen] = useState(false) - - return ( - <> - - setIsDialogOpen(false)} - /> - - ) -} export function Status({ children }: { children: string }): ReactElement { return
{children}
@@ -57,36 +40,36 @@ export function Status({ children }: { children: string }): ReactElement { const columns = [ { name: 'Data Set', - selector: function getAssetRow(row: ComputeAsset) { + selector: function getAssetRow(row: ComputeJobMetaData) { return ( - {row.assetName} + {row.assetName} ) } }, { name: 'Created', - selector: function getTimeRow(row: ComputeAsset) { + selector: function getTimeRow(row: ComputeJobMetaData) { return