diff --git a/apps/rda/package.json b/apps/rda/package.json index 4c6ae472..dd77bbea 100644 --- a/apps/rda/package.json +++ b/apps/rda/package.json @@ -18,7 +18,9 @@ "@dans-framework/theme": "workspace:*", "@dans-framework/rdt-search-ui": "workspace:*", "@fontsource/roboto": "^5.0.7", + "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.3", + "html-react-parser": "^4.2.2", "i18next": "^23.4.1", "i18next-browser-languagedetector": "^7.1.0", "i18next-resources-to-backend": "^1.1.4", diff --git a/apps/rda/src/pages/record/index.tsx b/apps/rda/src/pages/record/index.tsx index 0b159c7d..acd1b076 100644 --- a/apps/rda/src/pages/record/index.tsx +++ b/apps/rda/src/pages/record/index.tsx @@ -2,8 +2,9 @@ import { Chip, Container } from "@mui/material"; import type { Result } from "@dans-framework/rdt-search-ui"; import React from "react"; import { useParams } from "react-router-dom"; - -import styles from "../search/index.module.css"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Grid from "@mui/material/Unstable_Grid2"; interface RdaRecord { card_url: string; @@ -47,36 +48,51 @@ export function RdaRecord() { return ( -
-

{record.title || empty}

-

{record.dc_description || ""}

- -
- {record.page_url && ( - - - - )} - {record.uri && ( - - - - )} - {record.pid_lod && ( - - - - )} -
- -
+ + + + {record.title || Untitled} + + {record.dc_description || ""} + + + +
+ {record.page_url && ( + + + + )} + {record.uri && ( + + + + )} + {record.pid_lod && ( + + + + )} +
+ +
+
); } const style = { display: "grid", - gridTemplateColumns: "200px 1fr", + gridTemplateColumns: "1fr 4fr", + marginBottom: "0.25rem", }; function Metadata({ name, value }: { name: string; value: string | string[] }) { @@ -85,23 +101,36 @@ function Metadata({ name, value }: { name: string; value: string | string[] }) { const _value = Array.isArray(value) ? value.join(" / ") : value; return ( -
  • -
    {name}
    -
    {_value}
    -
  • +
    + + {name} + + + {_value} + +
    ); } export function MetadataList({ record }: { record: RdaRecord | Result }) { return ( - + ); } @@ -109,11 +138,21 @@ function ShowJSON({ record }: { record: RdaRecord }) { const [active, setActive] = React.useState(false); return ( -
    - - {active &&
    {JSON.stringify(record, undefined, 3)}
    } -
    + + {active && ( +
    +          {JSON.stringify(record, undefined, 3)}
    +        
    + )} + ); } diff --git a/apps/rda/src/pages/search/index.module.css b/apps/rda/src/pages/search/index.module.css index 8885862d..2391027e 100644 --- a/apps/rda/src/pages/search/index.module.css +++ b/apps/rda/src/pages/search/index.module.css @@ -1,53 +1,3 @@ .wrapper { - margin: auto; - max-width: 1400px; - padding-top: 4rem; + padding-top: 2rem; } - -.result { - background: white; - box-shadow: 0 0px 20px #DDD; - margin: 0 0 2rem 0; - padding: 1rem; -} - -.result em { - background: yellow; -} - -.header { - display: grid; - grid-template-columns: 1fr fit-content(0); - grid-gap: 1rem; -} - -.header h3 { - margin-top: 0; -} - -.header div { - color: gray; - white-space: nowrap; -} - -.description { - margin-bottom: 1rem; -} - -.showjson pre { - background: rgba(0, 0, 255, .05); - border: 1px solid darkblue; - border-radius: .25rem; - overflow: hidden; - padding: 1rem; -} - -.showjson button, -.description button { - background: none; - border: none; - color: #007bff; - cursor: pointer; - font-size: 0.8rem; - padding: 0; -} \ No newline at end of file diff --git a/apps/rda/src/pages/search/index.tsx b/apps/rda/src/pages/search/index.tsx index 611c9a69..c1b2d7b4 100644 --- a/apps/rda/src/pages/search/index.tsx +++ b/apps/rda/src/pages/search/index.tsx @@ -12,10 +12,6 @@ import { useNavigate } from "react-router-dom"; import styles from "./index.module.css"; const config: Partial = { - style: { - background: "#F6F6F6", - buttonBackground: "#ececec", - }, fullTextFields: ["title^2", "dc_description"], fullTextHighlight: { fields: { @@ -42,7 +38,6 @@ export function RdaSearch({ url={`${ import.meta.env.VITE_ELASTICSEARCH_API_ENDPOINT }/dans-rda2/_search`} - style={config.style} > empty"; return ( -
    -
    -

    - {item.dc_date &&
    {new Date(item.dc_date).toDateString()}
    } -

    - {/*
    */} + <> + {parse(title)} + {item.dc_date && ( + + {new Date(item.dc_date).toDateString()} + + )} -
    + ); } function ReadMore({ item }: { item: ResultBodyProps["result"] }) { - const [active, setActive] = React.useState(false); - - const hasHighlight = item.highlight?.dc_description?.[0] != null; + const [active, setActive] = useState(false); // No description, return nothing - if (item.dc_description == null) return null; - - // Highlighted description, return it - if (hasHighlight) { - return ( -
    - ); - } + if (item.dc_description === null) return null; - const [visibleText, hiddenText] = item.dc_description.split(/\. (.*)/); + const [visibleText, hiddenText] = [ + item.dc_description.substring(0, 180), + item.dc_description.substring(180), + ]; // There is only one sentence, return it if (hiddenText == null || hiddenText.trim().length === 0) { - return
    {visibleText}
    ; + return {visibleText}; } return ( -
    - {visibleText}.{active && ` ${hiddenText}`} -   - -
    + <> + + {`${visibleText}${ + visibleText.length < item.dc_description.length && !active + ? "..." + : hiddenText + }`} + + + ); } diff --git a/packages/deposit/src/features/metadata/fields/AutocompleteAPIField.tsx b/packages/deposit/src/features/metadata/fields/AutocompleteAPIField.tsx index b7b059aa..2809edcf 100644 --- a/packages/deposit/src/features/metadata/fields/AutocompleteAPIField.tsx +++ b/packages/deposit/src/features/metadata/fields/AutocompleteAPIField.tsx @@ -699,7 +699,7 @@ const AutocompleteAPIField = ({ // only for freesolo, add input directly as option if ( field.allowFreeText && - !isLoading && + !isLoading && !isFetching && debouncedInputValue === inputValue ) { diff --git a/packages/rdt-search-ui b/packages/rdt-search-ui index 7ea361e0..dce9778b 160000 --- a/packages/rdt-search-ui +++ b/packages/rdt-search-ui @@ -1 +1 @@ -Subproject commit 7ea361e08424b23bc03c0db2472e36d8cf26a3ca +Subproject commit dce9778b2e63f276b8a8e639329e7a649076ecda diff --git a/packages/user-auth/src/languages/locales/en/user.json b/packages/user-auth/src/languages/locales/en/user.json index 0e92b58b..40853806 100644 --- a/packages/user-auth/src/languages/locales/en/user.json +++ b/packages/user-auth/src/languages/locales/en/user.json @@ -35,6 +35,10 @@ "submittedTo": "Submitted to", "submissionStatus": "Status", "processing": "Processing your submission", - "error": "Error with your submission", - "success": "Succesfully submitted" + "error": "Error with your submission, click to see details", + "success": "Succesfully submitted", + "moreInfo": "Error", + "errorExplanation": "This is the error we got back from the server", + "retryItem": "Correct the error and try again", + "copyItem": "Copy this dataset for a new submission" } \ No newline at end of file diff --git a/packages/user-auth/src/languages/locales/nl/user.json b/packages/user-auth/src/languages/locales/nl/user.json index a3993714..3c2859f7 100644 --- a/packages/user-auth/src/languages/locales/nl/user.json +++ b/packages/user-auth/src/languages/locales/nl/user.json @@ -35,6 +35,10 @@ "submittedTo": "Gesubmit naar", "submissionStatus": "Status", "processing": "Bezig met verwerken", - "error": "Fout bij het verwerken van je data", - "success": "Succesvol gesubmit" + "error": "Fout bij het verwerken van je data, click om details te zien", + "success": "Succesvol gesubmit", + "moreInfo": "Fout", + "errorExplanation": "Dit is de foutmelding die we van de server terugkregen", + "retryItem": "Verbeter de foutmelding en probeer opnieuw", + "copyItem": "Kopiƫer deze dataset voor een nieuwe submissie" } \ No newline at end of file diff --git a/packages/user-auth/src/types/index.ts b/packages/user-auth/src/types/index.ts index 334ceada..0f741372 100644 --- a/packages/user-auth/src/types/index.ts +++ b/packages/user-auth/src/types/index.ts @@ -29,17 +29,16 @@ export interface ValidateTarget { export type ReleaseVersion = "DRAFT" | "PUBLISH"; -type IngestStatus = - | "initial" - | "processing" - | "finish" - | "error" - | "rejected" - | "failed"; +type ActiveStatus = "initial" | "processing" | "submitted" | "finalizing"; +type ErrorStatus = "rejected" | "failed" | "error"; +type SuccessStatus = "finish" | "accepted" | "success"; +export type IngestStatus = ActiveStatus | ErrorStatus | SuccessStatus; +type IngestStatusKeys = "processing" | "error" | "success"; +export type DepositStatus = { [key in IngestStatusKeys]: IngestStatus[] }; export interface TargetOutput { "ingest-status": IngestStatus; - "target-output": string; + "target-output": any; "target-repo-name": string; "target-repo-display-name": string; "target-url": string; diff --git a/packages/user-auth/src/user/UserSubmissions.tsx b/packages/user-auth/src/user/UserSubmissions.tsx index 2eacdabd..e239b29d 100644 --- a/packages/user-auth/src/user/UserSubmissions.tsx +++ b/packages/user-auth/src/user/UserSubmissions.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, MouseEvent } from "react"; import { useNavigate } from "react-router-dom"; import Container from "@mui/material/Container"; import Typography from "@mui/material/Typography"; @@ -19,15 +19,18 @@ import moment from "moment"; import { useSiteTitle, setSiteTitle } from "@dans-framework/utils"; import { useFetchUserSubmissionsQuery } from "./userApi"; import { useAuth } from "react-oidc-context"; -import type { SubmissionResponse, TargetOutput } from "../types"; +import type { SubmissionResponse, TargetOutput, DepositStatus } from "../types"; import CircularProgress from "@mui/material/CircularProgress"; import LinearProgress from "@mui/material/LinearProgress"; import PendingIcon from "@mui/icons-material/Pending"; import ErrorIcon from "@mui/icons-material/Error"; +import ReplayIcon from "@mui/icons-material/Replay"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import Tooltip from "@mui/material/Tooltip"; +import Popover from "@mui/material/Popover"; +import Button from "@mui/material/Button"; -const depositStatus = { +const depositStatus: DepositStatus = { processing: ["initial", "processing", "submitted", "finalizing"], error: ["rejected", "failed", "error"], success: ["finish", "accepted", "success"], @@ -118,13 +121,13 @@ const SubmissionList = ({ // useMemo to make sure columns don't change const columns = useMemo( () => [ - ...(type === "draft" - ? [ - { - field: "viewLink", - headerName: "", - width: 30, - getActions: (params: any) => [ + { + field: "viewLink", + headerName: "", + width: 30, + getActions: (params: any) => { + return type === "draft" + ? [ } label={t("editItem")} @@ -132,11 +135,26 @@ const SubmissionList = ({ navigate(`/${depositSlug}?id=${params.row.id}`) } />, - ], - type: "actions", - }, - ] - : []), + ] + : // for submitted forms, either edit in case of error, or load with existing data for new submission + // params.value is true for an error, false for success + !params.row.processing && params.row.error + ? [ + + } + label={t("retryItem")} + onClick={() => + // todo: need to work on this, how are we going to reload submitted form data for resubmission + navigate(`/${depositSlug}?id=${params.row.id}`) + } + /> + , + ] + : []; + }, + type: "actions", + }, { field: "title", headerName: t("title"), @@ -160,46 +178,11 @@ const SubmissionList = ({ renderCell: (params: any) => ( {params.value.map((v: TargetOutput, i: number) => ( - - - {depositStatus.processing.indexOf( - v["ingest-status"], - ) !== -1 ? ( - - ) : depositStatus.error.indexOf(v["ingest-status"]) !== - -1 ? ( - - ) : depositStatus.success.indexOf( - v["ingest-status"], - ) !== -1 ? ( - - ) : ( - - )} - - - {v["target-repo-display-name"]} - - + /> ))} ), @@ -214,6 +197,12 @@ const SubmissionList = ({ data && data.map((d) => ({ // Todo: API needs work and standardisation, also see types. + error: d["targets"].some( + (t) => depositStatus.error.indexOf(t["ingest-status"]) !== -1, + ), + processing: d["targets"].some( + (t) => depositStatus.processing.indexOf(t["ingest-status"]) !== -1, + ), id: d["metadata-id"], // viewLink: '', created: type === "draft" ? d["created-date"] : d["submitted-date"], @@ -273,6 +262,92 @@ const SubmissionList = ({ ); }; +// A separate component for a target, needs to have it's own state to display popover +const SingleTargetStatus = ({ + depositStatus, + target, +}: { + depositStatus: DepositStatus; + target: TargetOutput; +}) => { + const { t } = useTranslation("user"); + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = (event: MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const open = Boolean(anchorEl); + const id = open ? "popover" : undefined; + + return ( + <> + + + {depositStatus.processing.indexOf(target["ingest-status"]) !== -1 ? ( + + ) : depositStatus.error.indexOf(target["ingest-status"]) !== -1 ? ( + + ) : depositStatus.success.indexOf(target["ingest-status"]) !== -1 ? ( + + ) : ( + + )} + + + {target["target-repo-display-name"]} + + + + + {t("errorExplanation")} +
    +            {JSON.stringify(target["target-output"], null, 2)}
    +          
    +
    +
    + + ); +}; + const CustomColumnMenu = (props: GridColumnMenuProps) => { return (