diff --git a/client/src/core/client/admin/routes/Reports/RelatedReports.css b/client/src/core/client/admin/routes/Reports/RelatedReports.css new file mode 100644 index 0000000000..e8ebec42b1 --- /dev/null +++ b/client/src/core/client/admin/routes/Reports/RelatedReports.css @@ -0,0 +1,23 @@ +.label { + font-size: var(--font-size-1); + text-transform: uppercase; +} + +.button { + background-color: var(--palette-grey-200); + color: var(--palette-grey-500); + text-align: left; +} + +.reportIDLabel { + text-transform: uppercase; +} + +.referenceID { + margin-left: var(--spacing-2); + text-decoration: underline; +} + +.arrowIcon { + margin-left: auto; +} diff --git a/client/src/core/client/admin/routes/Reports/RelatedReports.tsx b/client/src/core/client/admin/routes/Reports/RelatedReports.tsx new file mode 100644 index 0000000000..d647fecc13 --- /dev/null +++ b/client/src/core/client/admin/routes/Reports/RelatedReports.tsx @@ -0,0 +1,85 @@ +import { Localized } from "@fluent/react/compat"; +import React, { FunctionComponent } from "react"; +import { graphql } from "react-relay"; + +import { withFragmentContainer } from "coral-framework/lib/relay"; +import { ArrowRightIcon, SvgIcon } from "coral-ui/components/icons"; +import { Flex } from "coral-ui/components/v2"; +import { Button } from "coral-ui/components/v3"; + +import { RelatedReports_dsaReport } from "coral-admin/__generated__/RelatedReports_dsaReport.graphql"; + +import styles from "./RelatedReports.css"; + +interface Props { + dsaReport: RelatedReports_dsaReport; +} + +const RelatedReports: FunctionComponent = ({ dsaReport }) => { + const relatedReports = dsaReport.relatedReports.edges.map( + (edge) => edge.node + ); + + if (relatedReports.length === 0) { + return null; + } + + return ( + + +
Related Reports
+
+ {relatedReports.map((report) => { + return ( + + + + ); + })} +
+ ); +}; + +const enhanced = withFragmentContainer({ + dsaReport: graphql` + fragment RelatedReports_dsaReport on DSAReport + @argumentDefinitions( + count: { type: "Int", defaultValue: 20 } + cursor: { type: "Cursor" } + orderBy: { type: "REPORT_SORT", defaultValue: CREATED_AT_DESC } + ) { + id + relatedReports(first: $count, after: $cursor, orderBy: $orderBy) + @connection(key: "RelatedReports_relatedReports") { + edges { + node { + id + referenceID + } + } + } + } + `, +})(RelatedReports); + +export default enhanced; diff --git a/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx b/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx index 605e71b167..78914c36ae 100644 --- a/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx +++ b/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx @@ -36,6 +36,7 @@ import styles from "./SingleReportRoute.css"; import NotFound from "../NotFound"; import ChangeStatusModal from "./ChangeStatusModal"; +import RelatedReports from "./RelatedReports"; import ReportedComment from "./ReportedComment"; import ReportHistory from "./ReportHistory"; import ReportMakeDecisionModal from "./ReportMakeDecisionModal"; @@ -235,6 +236,7 @@ const SingleReportRoute: FunctionComponent & { dsaReport={dsaReport} onShowUserDrawer={onShowUserDrawer} /> + {dsaReport.decision && ( <> @@ -381,6 +383,7 @@ SingleReportRoute.routeConfig = createRouteConfig< ...ReportShareButton_dsaReport ...ReportMakeDecisionModal_dsaReport ...ReportedComment_dsaReport + ...RelatedReports_dsaReport } } `, diff --git a/locales/en-US/admin.ftl b/locales/en-US/admin.ftl index d30171666f..c5fe98cc65 100644 --- a/locales/en-US/admin.ftl +++ b/locales/en-US/admin.ftl @@ -1909,6 +1909,9 @@ reports-decisionModal-detailedExplanationLabel = Detailed explanation reports-decisionModal-detailedExplanationTextarea = .placeholder = Add explanation... +reports-relatedReports-label = Related reports +reports-relatedReports-reportIDLabel = Report ID + # Control panel controlPanel-redis-redis = Redis diff --git a/server/src/core/server/graph/loaders/DSAReports.ts b/server/src/core/server/graph/loaders/DSAReports.ts index b84b932206..e21a65827d 100644 --- a/server/src/core/server/graph/loaders/DSAReports.ts +++ b/server/src/core/server/graph/loaders/DSAReports.ts @@ -5,12 +5,14 @@ import { DSAReportConnectionInput, find, retrieveDSAReportConnection, + retrieveDSAReportRelatedReportsConnection, } from "coral-server/models/dsaReport"; import GraphContext from "../context"; import { createManyBatchLoadFn } from "./util"; import { + DSAReportToRelatedReportsArgs, GQLDSAREPORT_STATUS_FILTER, GQLREPORT_SORT, QueryToDsaReportsArgs, @@ -53,4 +55,19 @@ export default (ctx: GraphContext) => ({ cache: !ctx.disableCaching, } ), + relatedReports: ( + submissionID: string, + id: string, + { first, orderBy }: DSAReportToRelatedReportsArgs + ) => + retrieveDSAReportRelatedReportsConnection( + ctx.mongo, + ctx.tenant.id, + submissionID, + id, + { + first: defaultTo(first, 10), + orderBy: defaultTo(orderBy, GQLREPORT_SORT.CREATED_AT_DESC), + } + ), }); diff --git a/server/src/core/server/graph/resolvers/DSAReport.ts b/server/src/core/server/graph/resolvers/DSAReport.ts index a479aebdfa..17ac248b6b 100644 --- a/server/src/core/server/graph/resolvers/DSAReport.ts +++ b/server/src/core/server/graph/resolvers/DSAReport.ts @@ -1,6 +1,11 @@ +import { defaultTo } from "lodash"; + import * as dsaReport from "coral-server/models/dsaReport"; -import { GQLDSAReportTypeResolver } from "coral-server/graph/schema/__generated__/types"; +import { + GQLDSAReportTypeResolver, + GQLREPORT_SORT, +} from "coral-server/graph/schema/__generated__/types"; export const DSAReport: GQLDSAReportTypeResolver = { reporter: (report, args, ctx) => { @@ -33,4 +38,11 @@ export const DSAReport: GQLDSAReportTypeResolver = { return null; } }, + relatedReports: ({ submissionID, id }, { first, after, orderBy }, ctx) => { + return ctx.loaders.DSAReports.relatedReports(submissionID, id, { + first: defaultTo(first, 10), + after, + orderBy: defaultTo(orderBy, GQLREPORT_SORT.CREATED_AT_DESC), + }); + }, }; diff --git a/server/src/core/server/graph/schema/schema.graphql b/server/src/core/server/graph/schema/schema.graphql index c2ab69385a..d9a4e0b8e7 100644 --- a/server/src/core/server/graph/schema/schema.graphql +++ b/server/src/core/server/graph/schema/schema.graphql @@ -3661,6 +3661,12 @@ type DSAReport { such as a note added, status changed, or decision made """ lastUpdated: Time + + relatedReports( + first: Int = 10 @constraint(max: 50) + orderBy: REPORT_SORT = CREATED_AT_DESC + after: Cursor + ): DSAReportsConnection! } type CommentRevisionPerspectiveMetadata { diff --git a/server/src/core/server/models/dsaReport/report.ts b/server/src/core/server/models/dsaReport/report.ts index 4261b705a0..4b5b6d18a2 100644 --- a/server/src/core/server/models/dsaReport/report.ts +++ b/server/src/core/server/models/dsaReport/report.ts @@ -167,6 +167,23 @@ export async function retrieveDSAReportConnection( return retrieveConnection(input, query); } +export async function retrieveDSAReportRelatedReportsConnection( + mongo: MongoContext, + tenantID: string, + submissionID: string, + id: string, + input: DSAReportConnectionInput +): Promise>>> { + // Create the query. + const query = new Query(mongo.dsaReports()).where({ + tenantID, + submissionID, + id: { $ne: id }, + }); + + return retrieveConnection(input, query); +} + export async function retrieveDSAReport( mongo: MongoContext, tenantID: string,