From 1084130c60e42daec8d636fb6a2142820c7c1bdd Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 4 Mar 2024 11:23:06 -0300 Subject: [PATCH 01/24] Add quiet field to submissions and reports --- site/gatsby-site/i18n/locales/en/popovers.json | 4 ++++ site/gatsby-site/i18n/locales/es/popovers.json | 4 ++++ site/gatsby-site/i18n/locales/fr/popovers.json | 4 ++++ .../page-creators/createWordCountsPage.js | 4 +++- .../src/components/submissions/SubmissionForm.js | 16 +++++++++++++++- site/gatsby-site/src/graphql/reports.js | 3 +++ site/gatsby-site/src/graphql/submissions.js | 2 ++ site/gatsby-site/typeDefs.js | 1 + .../mongodb-atlas/aiidprod/reports/schema.json | 3 +++ .../aiidprod/submissions/schema.json | 3 +++ .../mongodb-atlas/history/reports/schema.json | 3 +++ .../realm/functions/promoteSubmissionToReport.js | 1 + 12 files changed, 46 insertions(+), 2 deletions(-) diff --git a/site/gatsby-site/i18n/locales/en/popovers.json b/site/gatsby-site/i18n/locales/en/popovers.json index 77e0e186e4..0710f950be 100644 --- a/site/gatsby-site/i18n/locales/en/popovers.json +++ b/site/gatsby-site/i18n/locales/en/popovers.json @@ -74,5 +74,9 @@ "submittersLoggedIn": { "title": "This is you!", "text": "You are currently logged in so this submission will automatically be associated with your account. If you would like to remain anonymous, please open a private browsing window to submit." + }, + "quiet": { + "title": "Quiet", + "text": "Quiet reports are those that will not be published in the 'Latest Reports' section of the homepage. Silent reports are used for reports that are useful for internal data but do not need to be promoted. If you are unsure, leave this field blank." } } diff --git a/site/gatsby-site/i18n/locales/es/popovers.json b/site/gatsby-site/i18n/locales/es/popovers.json index afcb8c27bc..a7a0c6179f 100644 --- a/site/gatsby-site/i18n/locales/es/popovers.json +++ b/site/gatsby-site/i18n/locales/es/popovers.json @@ -74,5 +74,9 @@ "submittersLoggedIn": { "title": "¡Este eres tú!", "text": "Actualmente estás conectado, por lo que este reporte se asociará automáticamente con tu cuenta. Si deseas permanecer anónimo, por favor abre una ventana de navegación privada." + }, + "quiet": { + "title": "¿Es un informe silencioso?", + "text": "Los informes silenciosos son aquellos que no se publicarán en la sección 'Últimos informes' de la página de inicio. Los informes silenciosos se utilizan para informes que son útiles para datos internos pero que no necesitan promocionarse. Si no está seguro, deje este campo en blanco." } } diff --git a/site/gatsby-site/i18n/locales/fr/popovers.json b/site/gatsby-site/i18n/locales/fr/popovers.json index 385218d842..206584a553 100644 --- a/site/gatsby-site/i18n/locales/fr/popovers.json +++ b/site/gatsby-site/i18n/locales/fr/popovers.json @@ -62,5 +62,9 @@ "submittersLoggedIn": { "title": "C'est vous !", "text": "Vous êtes actuellement connecté, donc cette soumission sera automatiquement associée à votre compte. Si vous souhaitez rester anonyme, veuillez ouvrir une fenêtre de navigation privée pour soumettre." + }, + "quiet": { + "title": "Est-ce un rapport silencieux ?", + "text": "Les rapports silencieux sont ceux qui ne seront pas publiés dans la section « Derniers rapports » de la page d'accueil. Les rapports silencieux sont utilisés pour les rapports utiles pour les données internes mais qui n'ont pas besoin d'être promus. Si vous n'êtes pas sûr, laissez ce champ vide." } } \ No newline at end of file diff --git a/site/gatsby-site/page-creators/createWordCountsPage.js b/site/gatsby-site/page-creators/createWordCountsPage.js index 07cfeeeb91..29e4c6b4a2 100644 --- a/site/gatsby-site/page-creators/createWordCountsPage.js +++ b/site/gatsby-site/page-creators/createWordCountsPage.js @@ -35,7 +35,9 @@ const createWordCountsPage = async (graphql, createPage) => { } } latestReports: allMongodbAiidprodIncidents( - filter: { reports: { elemMatch: { is_incident_report: { eq: true } } } } + filter: { + reports: { elemMatch: { is_incident_report: { eq: true } }, quiet: { in: [null, false] } } + } sort: { reports: { epoch_date_submitted: DESC } } limit: 5 ) { diff --git a/site/gatsby-site/src/components/submissions/SubmissionForm.js b/site/gatsby-site/src/components/submissions/SubmissionForm.js index 889e222582..6a350eb142 100644 --- a/site/gatsby-site/src/components/submissions/SubmissionForm.js +++ b/site/gatsby-site/src/components/submissions/SubmissionForm.js @@ -34,7 +34,7 @@ import { faTenge, } from '@fortawesome/free-solid-svg-icons'; import FlowbiteSearchInput from 'components/forms/FlowbiteSearchInput'; -import { Select } from 'flowbite-react'; +import { Checkbox, Select } from 'flowbite-react'; import IncidentsField from 'components/incidents/IncidentsField'; import { graphql, useStaticQuery } from 'gatsby'; @@ -348,6 +348,20 @@ const SubmissionForm = ({ onChange = null }) => { columns={['byIncidentId']} /> +
+
+
+
+ { + setFieldValue('quiet', e.target.checked); + }} + /> +
+
+ {(!values.incident_ids || values.incident_ids.length === 0) && (

diff --git a/site/gatsby-site/src/graphql/reports.js b/site/gatsby-site/src/graphql/reports.js index 7883a4b029..68c2dcc4ae 100644 --- a/site/gatsby-site/src/graphql/reports.js +++ b/site/gatsby-site/src/graphql/reports.js @@ -34,6 +34,7 @@ export const FIND_REPORT = gql` from_text_hash vector } + quiet } } `; @@ -70,6 +71,7 @@ export const FIND_REPORT_WITH_TRANSLATIONS = gql` title text } + quiet } } `; @@ -172,6 +174,7 @@ export const FIND_REPORT_HISTORY = gql` url source_domain user + quiet } } `; diff --git a/site/gatsby-site/src/graphql/submissions.js b/site/gatsby-site/src/graphql/submissions.js index 731f84671e..249384d4e9 100644 --- a/site/gatsby-site/src/graphql/submissions.js +++ b/site/gatsby-site/src/graphql/submissions.js @@ -59,6 +59,7 @@ export const FIND_SUBMISSIONS = gql` user { userId } + quiet } } `; @@ -110,6 +111,7 @@ export const FIND_SUBMISSION = gql` editor_similar_incidents editor_dissimilar_incidents status + quiet } } `; diff --git a/site/gatsby-site/typeDefs.js b/site/gatsby-site/typeDefs.js index 4a2df4f977..16c92b9c44 100644 --- a/site/gatsby-site/typeDefs.js +++ b/site/gatsby-site/typeDefs.js @@ -83,6 +83,7 @@ const typeDefs = ` report_number: Int is_incident_report: Boolean flag: Boolean + quiet: Boolean } type mongodbAiidprodTaxaField_list implements Node { diff --git a/site/realm/data_sources/mongodb-atlas/aiidprod/reports/schema.json b/site/realm/data_sources/mongodb-atlas/aiidprod/reports/schema.json index 7ddd931ff9..a8f2951459 100644 --- a/site/realm/data_sources/mongodb-atlas/aiidprod/reports/schema.json +++ b/site/realm/data_sources/mongodb-atlas/aiidprod/reports/schema.json @@ -106,6 +106,9 @@ }, "user": { "bsonType": "string" + }, + "quiet": { + "bsonType": "bool" } }, "required": [ diff --git a/site/realm/data_sources/mongodb-atlas/aiidprod/submissions/schema.json b/site/realm/data_sources/mongodb-atlas/aiidprod/submissions/schema.json index 25c0c79fea..b3866addfd 100644 --- a/site/realm/data_sources/mongodb-atlas/aiidprod/submissions/schema.json +++ b/site/realm/data_sources/mongodb-atlas/aiidprod/submissions/schema.json @@ -147,6 +147,9 @@ }, "status": { "bsonType": "string" + }, + "quiet": { + "bsonType": "bool" } }, "required": [ diff --git a/site/realm/data_sources/mongodb-atlas/history/reports/schema.json b/site/realm/data_sources/mongodb-atlas/history/reports/schema.json index d250f7343f..899cff521d 100644 --- a/site/realm/data_sources/mongodb-atlas/history/reports/schema.json +++ b/site/realm/data_sources/mongodb-atlas/history/reports/schema.json @@ -109,6 +109,9 @@ }, "user": { "bsonType": "string" + }, + "quiet": { + "bsonType": "bool" } }, "required": [ diff --git a/site/realm/functions/promoteSubmissionToReport.js b/site/realm/functions/promoteSubmissionToReport.js index 7cd0189b39..c31e1c6965 100644 --- a/site/realm/functions/promoteSubmissionToReport.js +++ b/site/realm/functions/promoteSubmissionToReport.js @@ -153,6 +153,7 @@ exports = async (input) => { source_domain: submission.source_domain, language: submission.language, tags: submission.tags, + quiet: submission.quiet }; if (submission.embedding) { newReport.embedding = submission.embedding; From fee84dd9945de73422164e68c0be083781e1c8f7 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 4 Mar 2024 13:21:56 -0300 Subject: [PATCH 02/24] Filter quiet in landing and add field to report edit --- site/gatsby-site/i18n/locales/en/popovers.json | 2 +- .../page-creators/createWordCountsPage.js | 2 +- .../src/components/forms/IncidentReportForm.js | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/site/gatsby-site/i18n/locales/en/popovers.json b/site/gatsby-site/i18n/locales/en/popovers.json index 0710f950be..f3bebfbea1 100644 --- a/site/gatsby-site/i18n/locales/en/popovers.json +++ b/site/gatsby-site/i18n/locales/en/popovers.json @@ -77,6 +77,6 @@ }, "quiet": { "title": "Quiet", - "text": "Quiet reports are those that will not be published in the 'Latest Reports' section of the homepage. Silent reports are used for reports that are useful for internal data but do not need to be promoted. If you are unsure, leave this field blank." + "text": "Quiet reports are those that will not be published in the 'Latest Reports' section of the homepage. Quiet reports are used for reports that are useful for internal data but do not need to be promoted. If you are unsure, leave this field blank." } } diff --git a/site/gatsby-site/page-creators/createWordCountsPage.js b/site/gatsby-site/page-creators/createWordCountsPage.js index 29e4c6b4a2..40768453d1 100644 --- a/site/gatsby-site/page-creators/createWordCountsPage.js +++ b/site/gatsby-site/page-creators/createWordCountsPage.js @@ -36,7 +36,7 @@ const createWordCountsPage = async (graphql, createPage) => { } latestReports: allMongodbAiidprodIncidents( filter: { - reports: { elemMatch: { is_incident_report: { eq: true } }, quiet: { in: [null, false] } } + reports: { elemMatch: { is_incident_report: { eq: true }, quiet: { in: [null, false] } } } } sort: { reports: { epoch_date_submitted: DESC } } limit: 5 diff --git a/site/gatsby-site/src/components/forms/IncidentReportForm.js b/site/gatsby-site/src/components/forms/IncidentReportForm.js index ddb75ab2ca..356bacd182 100644 --- a/site/gatsby-site/src/components/forms/IncidentReportForm.js +++ b/site/gatsby-site/src/components/forms/IncidentReportForm.js @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { Select } from 'flowbite-react'; +import { Checkbox, Select } from 'flowbite-react'; import { Form, useFormikContext } from 'formik'; import * as yup from 'yup'; import TextInputGroup from '../../components/forms/TextInputGroup'; @@ -425,6 +425,20 @@ const IncidentReportForm = () => { {...TextInputGroupProps} /> +
+
+
+
+ { + setFieldValue('quiet', e.target.checked); + }} + /> +
+
+

Translations

{config From 3cf1308fc3cab6bbfe5f5ca0bbee666de84f4f89 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 4 Mar 2024 16:14:16 -0300 Subject: [PATCH 03/24] Fix tests --- site/gatsby-site/cypress/e2e/integration/discover.cy.js | 6 +++--- .../e2e/unit/functions/promoteSubmissionToReport.cy.js | 6 ++++++ site/gatsby-site/src/components/discover/Actions.js | 4 ++-- site/gatsby-site/src/graphql/reports.js | 1 + site/realm/functions/promoteSubmissionToReport.js | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/site/gatsby-site/cypress/e2e/integration/discover.cy.js b/site/gatsby-site/cypress/e2e/integration/discover.cy.js index 970739a438..f7387230f6 100644 --- a/site/gatsby-site/cypress/e2e/integration/discover.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/discover.cy.js @@ -2,7 +2,7 @@ import flaggedReport from '../../fixtures/reports/flagged.json'; import unflaggedReport from '../../fixtures/reports/unflagged.json'; import config from '../../../config'; import path from 'path'; -import { format, getUnixTime } from 'date-fns'; +import { getUnixTime } from 'date-fns'; import { deleteReportTypenames, transformReportData } from '../../../src/utils/reports'; import { conditionalIt } from '../../support/utils'; @@ -267,7 +267,7 @@ describe('The Discover app', () => { expect(variables.query.report_number).to.equal(23); expect(variables.set).deep.eq({ flag: true, - date_modified: format(now, 'yyyy-MM-dd'), + date_modified: now.toISOString(), epoch_date_modified: getUnixTime(now), }); }); @@ -280,7 +280,7 @@ describe('The Discover app', () => { ); expectedReport.modifiedBy = ''; - expectedReport.date_modified = format(now, 'yyyy-MM-dd'); + expectedReport.date_modified = now.toISOString(); expectedReport.epoch_date_modified = getUnixTime(now); expect(input).to.deep.eq(expectedReport); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js index 6bc67a8a00..4a6be30bd9 100644 --- a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js @@ -35,6 +35,7 @@ const submission = { editor_similar_incidents: [], tags: [], user: 'user1', + quiet: false, }; const submission_with_embedding = { @@ -68,6 +69,7 @@ const submission_with_embedding = { editor_similar_incidents: [], tags: [], user: 'user1', + quiet: false, embedding: { vector: [1, 2, 3], from_reports: [1], @@ -244,6 +246,7 @@ describe('Functions', () => { language: 'en', tags: [], user: 'user1', + quiet: false, }; expect(reportsCollection.insertOne.firstCall.args[0]).to.deep.eq(expectedReport); @@ -404,6 +407,7 @@ describe('Functions', () => { language: 'en', tags: [], user: 'user1', + quiet: false, embedding: { vector: [1, 2, 3], from_reports: [1], @@ -561,6 +565,7 @@ describe('Functions', () => { language: 'en', tags: [], user: 'user1', + quiet: false, }; expect(reportsCollection.insertOne.firstCall.args[0]).to.deep.eq(expectedReport); @@ -862,6 +867,7 @@ describe('Functions', () => { source_domain: 'projects.tampabay.com', language: 'en', tags: [], + quiet: false, }; expect(reportsCollection.insertOne.firstCall.args[0]).to.deep.eq(expectedReport); diff --git a/site/gatsby-site/src/components/discover/Actions.js b/site/gatsby-site/src/components/discover/Actions.js index 970f8d607d..93d64aeee6 100644 --- a/site/gatsby-site/src/components/discover/Actions.js +++ b/site/gatsby-site/src/components/discover/Actions.js @@ -17,7 +17,7 @@ import CustomButton from '../../elements/Button'; import { Modal } from 'flowbite-react'; import { useUserContext } from 'contexts/userContext'; import { useLogReportHistory } from '../../hooks/useLogReportHistory'; -import { format, getUnixTime } from 'date-fns'; +import { getUnixTime } from 'date-fns'; import useLocalizePath from 'components/i18n/useLocalizePath'; function FlagModalContent({ reportNumber }) { @@ -36,7 +36,7 @@ function FlagModalContent({ reportNumber }) { const updated = { flag: true, - date_modified: format(now, 'yyyy-MM-dd'), + date_modified: now, epoch_date_modified: getUnixTime(now), }; diff --git a/site/gatsby-site/src/graphql/reports.js b/site/gatsby-site/src/graphql/reports.js index 68c2dcc4ae..8103cb482e 100644 --- a/site/gatsby-site/src/graphql/reports.js +++ b/site/gatsby-site/src/graphql/reports.js @@ -97,6 +97,7 @@ export const UPDATE_REPORT = gql` report_number editor_notes language + quiet } } `; diff --git a/site/realm/functions/promoteSubmissionToReport.js b/site/realm/functions/promoteSubmissionToReport.js index c31e1c6965..398e3a7656 100644 --- a/site/realm/functions/promoteSubmissionToReport.js +++ b/site/realm/functions/promoteSubmissionToReport.js @@ -153,7 +153,7 @@ exports = async (input) => { source_domain: submission.source_domain, language: submission.language, tags: submission.tags, - quiet: submission.quiet + quiet: submission.quiet || false }; if (submission.embedding) { newReport.embedding = submission.embedding; From ba76967787450fabe32e1c6a53a523045078afc7 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 4 Mar 2024 16:41:04 -0300 Subject: [PATCH 04/24] Fix should flag report --- site/gatsby-site/cypress/fixtures/reports/flagged.json | 3 ++- site/gatsby-site/cypress/fixtures/reports/unflagged.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/site/gatsby-site/cypress/fixtures/reports/flagged.json b/site/gatsby-site/cypress/fixtures/reports/flagged.json index c743218911..f68ddbb23e 100644 --- a/site/gatsby-site/cypress/fixtures/reports/flagged.json +++ b/site/gatsby-site/cypress/fixtures/reports/flagged.json @@ -33,7 +33,8 @@ "epoch_date_downloaded": 1559347200, "date_submitted": "2019-06-01", "date_modified": "2019-06-02", - "language": "en" + "language": "en", + "quiet": false } } } diff --git a/site/gatsby-site/cypress/fixtures/reports/unflagged.json b/site/gatsby-site/cypress/fixtures/reports/unflagged.json index 4bda34cb45..ac899774f1 100644 --- a/site/gatsby-site/cypress/fixtures/reports/unflagged.json +++ b/site/gatsby-site/cypress/fixtures/reports/unflagged.json @@ -33,7 +33,8 @@ "epoch_date_modified": 1559347200, "epoch_date_downloaded": 1559347200, "date_submitted": "2019-06-01", - "date_modified": "2019-06-02" + "date_modified": "2019-06-02", + "quiet": false } } } From fe2b0a4ece750fd063a87316a347bc7b09c9b06c Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Tue, 5 Mar 2024 09:15:24 -0300 Subject: [PATCH 05/24] Fix cite flag a report --- site/gatsby-site/cypress/e2e/integration/cite.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/gatsby-site/cypress/e2e/integration/cite.cy.js b/site/gatsby-site/cypress/e2e/integration/cite.cy.js index bb4f40c0d8..c96e2b94a0 100644 --- a/site/gatsby-site/cypress/e2e/integration/cite.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/cite.cy.js @@ -198,7 +198,7 @@ describe('Cite pages', () => { expect(variables.query.report_number).to.equal(23); expect(variables.set).deep.eq({ flag: true, - date_modified: format(now, 'yyyy-MM-dd'), + date_modified: now.toISOString(), epoch_date_modified: getUnixTime(now), }); }); @@ -211,7 +211,7 @@ describe('Cite pages', () => { ); expectedReport.modifiedBy = ''; - expectedReport.date_modified = format(now, 'yyyy-MM-dd'); + expectedReport.date_modified = now.toISOString(); expectedReport.epoch_date_modified = getUnixTime(now); expect(input).to.deep.eq(expectedReport); From b65e60723652a969720f886f0065b2681aa1bfb9 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Tue, 5 Mar 2024 14:39:05 -0300 Subject: [PATCH 06/24] Fix report save with quiet field --- site/gatsby-site/src/pages/cite/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/site/gatsby-site/src/pages/cite/edit.js b/site/gatsby-site/src/pages/cite/edit.js index b0e41f8da7..bf5dc70d72 100644 --- a/site/gatsby-site/src/pages/cite/edit.js +++ b/site/gatsby-site/src/pages/cite/edit.js @@ -57,6 +57,7 @@ const reportFields = [ 'url', 'embedding', 'inputs_outputs', + 'quiet', ]; function EditCitePage(props) { From a7b9bd91c21ce6d490f8e1ec07ca76d01f0df7c8 Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Wed, 6 Mar 2024 12:12:21 -0500 Subject: [PATCH 07/24] wip --- site/gatsby-site/src/templates/landingPage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/site/gatsby-site/src/templates/landingPage.js b/site/gatsby-site/src/templates/landingPage.js index 35ef90e823..b5f4cb4171 100644 --- a/site/gatsby-site/src/templates/landingPage.js +++ b/site/gatsby-site/src/templates/landingPage.js @@ -67,6 +67,8 @@ const LandingPage = (props) => { return updatedIncident; }); + console.log(`props`, props); + return ( // Tailwind has max-w-6xl but no plain w-6xl... 72rem = 6xl
@@ -82,7 +84,10 @@ const LandingPage = (props) => { {latestReports.length > 0 && (
- +
)} From cfa6130bfddb02e427e7c57aa869250f5f38c5ff Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Wed, 6 Mar 2024 12:25:02 -0500 Subject: [PATCH 08/24] inner card --- site/gatsby-site/gatsby-config.js | 2 +- .../page-creators/createWordCountsPage.js | 4 + .../landing/LatestIncidentReport.js | 213 ++++++++++++------ .../src/components/landing/LatestReports.js | 4 +- 4 files changed, 153 insertions(+), 70 deletions(-) diff --git a/site/gatsby-site/gatsby-config.js b/site/gatsby-site/gatsby-config.js index 3e0879042e..bff04a2488 100755 --- a/site/gatsby-site/gatsby-config.js +++ b/site/gatsby-site/gatsby-config.js @@ -305,6 +305,6 @@ module.exports = { plugins: plugins, trailingSlash: `always`, flags: { - DEV_SSR: true, + DEV_SSR: false, }, }; diff --git a/site/gatsby-site/page-creators/createWordCountsPage.js b/site/gatsby-site/page-creators/createWordCountsPage.js index 07cfeeeb91..afd98a32a8 100644 --- a/site/gatsby-site/page-creators/createWordCountsPage.js +++ b/site/gatsby-site/page-creators/createWordCountsPage.js @@ -40,6 +40,7 @@ const createWordCountsPage = async (graphql, createPage) => { limit: 5 ) { nodes { + title reports { report_number } @@ -133,6 +134,8 @@ const createWordCountsPage = async (graphql, createPage) => { return sortedArray[0].report_number; }); + const fiveLatestIncidents = result.data.latestReports.nodes; + PAGES_WITH_WORDCOUNT.forEach((page) => { createPage({ path: page.path, @@ -146,6 +149,7 @@ const createWordCountsPage = async (graphql, createPage) => { ? result.data.latestReport.nodes[0].report_number : 0, latestReportNumbers, + fiveLatestIncidents, sponsors: result.data.sponsors.edges, }, }); diff --git a/site/gatsby-site/src/components/landing/LatestIncidentReport.js b/site/gatsby-site/src/components/landing/LatestIncidentReport.js index 04ede8c7d6..e163b41276 100644 --- a/site/gatsby-site/src/components/landing/LatestIncidentReport.js +++ b/site/gatsby-site/src/components/landing/LatestIncidentReport.js @@ -7,7 +7,7 @@ import { LocalizedLink } from 'plugins/gatsby-theme-i18n'; import DateLabel from 'components/ui/DateLabel'; import Link from 'components/ui/Link'; -const LatestIncidentReport = ({ report, key, isLatest = false }) => { +const LatestIncidentReport = ({ report, key, isLatest = false, fiveLatestIncidents }) => { const { image_url, cloudinary_id, @@ -24,75 +24,154 @@ const LatestIncidentReport = ({ report, key, isLatest = false }) => { const reportLink = `/cite/${incident_id}#r${report_number}`; + const incident = fiveLatestIncidents.find((incident) => + incident.reports.map((r) => r.report_number).includes(report.report_number) + ); + + console.log(`incident`, incident); + return ( -
-
- - {title} - -
-
-
- -
- {title} - {isLatest && ( - - Latest Incident Report - - )} -
-
- - - {source_domain} - -
-
- -
-
- - Read More - - -
-
-
-
+ + + + + ); +}; + +const Card = (props) => { + const className = ` + flex flex-col lg:flex-row items-center + bg-white rounded-lg border shadow-md dark:border-gray-700 dark:bg-gray-800 h-full + `; + + return
{props.children}
; +}; + +const CardImage = ({ reportLink, cloudinary_id, image_url, title, t, incident_id }) => { + return ( +
+ + {title} + +
+ ); +}; + +const CardBody = ({ + report, + incident, + isLatest, + reportLink, + epoch_date_submitted, + url, + source_domain, + text, +}) => { + return ( +
+

+ Incident {incident.incident_id || '###'}: {incident.title} +

+
); }; +const ReportPreview = ({ + reportLink, + report, + isLatest, + source_domain, + url, + epoch_date_submitted, + text, +}) => ( +
+ +
+ “{report.title}”{isLatest && } +
+
+
+ + {source_domain} + + +
+
+ +
+
+ +
+
+); + +const ReadMoreButton = ({ reportLink, className }) => ( + + Read More + + +); + +const LatestIncidentBadge = () => ( + + Latest Incident Report + +); + export default LatestIncidentReport; diff --git a/site/gatsby-site/src/components/landing/LatestReports.js b/site/gatsby-site/src/components/landing/LatestReports.js index a1f7ab16ae..710315f735 100644 --- a/site/gatsby-site/src/components/landing/LatestReports.js +++ b/site/gatsby-site/src/components/landing/LatestReports.js @@ -4,7 +4,7 @@ import { Carousel } from 'flowbite-react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faArrowCircleLeft, faArrowCircleRight } from '@fortawesome/free-solid-svg-icons'; -export default function LatestReports({ latestReports }) { +export default function LatestReports({ latestReports, fiveLatestIncidents }) { return ( <> {latestReports.map((report, index) => ( ))} From 38769906ff28f9e5b57372c00b28140212ae4e96 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Mar 2024 10:40:29 -0300 Subject: [PATCH 09/24] Add reports to Table view --- .../src/components/reports/ReportEditModal.js | 169 ++++++++++++++ .../src/components/reports/ReportsTable.js | 163 ++++++++++++++ site/gatsby-site/src/graphql/incidents.js | 3 + site/gatsby-site/src/graphql/reports.js | 27 +++ site/gatsby-site/src/pages/apps/incidents.js | 211 +++++++++++++++--- 5 files changed, 538 insertions(+), 35 deletions(-) create mode 100644 site/gatsby-site/src/components/reports/ReportEditModal.js create mode 100644 site/gatsby-site/src/components/reports/ReportsTable.js diff --git a/site/gatsby-site/src/components/reports/ReportEditModal.js b/site/gatsby-site/src/components/reports/ReportEditModal.js new file mode 100644 index 0000000000..45eae4a605 --- /dev/null +++ b/site/gatsby-site/src/components/reports/ReportEditModal.js @@ -0,0 +1,169 @@ +import React, { useEffect, useState } from 'react'; +import { useMutation, useQuery } from '@apollo/client'; +import { FIND_INCIDENTS } from '../../graphql/incidents'; +import { FIND_ENTITIES } from '../../graphql/entities'; +import useToastContext, { SEVERITY } from '../../hooks/useToast'; +import { Spinner, Modal, Button } from 'flowbite-react'; +import { Formik } from 'formik'; +import { LocalizedLink } from 'plugins/gatsby-theme-i18n'; +import { useTranslation, Trans } from 'react-i18next'; +import { getUnixTime } from 'date-fns'; +// import { useUserContext } from 'contexts/userContext'; +// import { useLogIncidentHistory } from '../../hooks/useLogIncidentHistory'; +import { FIND_REPORT_WITH_TRANSLATIONS, UPDATE_REPORT } from '../../graphql/reports'; +import IncidentReportForm, { schema } from 'components/forms/IncidentReportForm'; + +export default function ReportEditModal({ show, onClose, report_number }) { + // const { user } = useUserContext(); + + const { t, i18n } = useTranslation(); + + const [report, setReport] = useState(null); + + const { data: reportData } = useQuery(FIND_REPORT_WITH_TRANSLATIONS, { + variables: { query: { report_number: report_number } }, + }); + + const { + data: incidentsData, + // loading: loadingIncident, + // refetch: refetchIncidents, + } = useQuery(FIND_INCIDENTS, { + variables: { + query: { + reports_in: { + report_number: report_number, + }, + }, + }, + }); + + const incident_ids = incidentsData?.incidents.map((incident) => incident.incident_id); + + const { data: entitiesData } = useQuery(FIND_ENTITIES); + + const [UpdateReport] = useMutation(UPDATE_REPORT); + + // const [createEntityMutation] = useMutation(UPSERT_ENTITY); + + const addToast = useToastContext(); + + // const { logIncidentHistory } = useLogIncidentHistory(); + + useEffect(() => { + if (reportData?.report) { + setReport({ ...reportData.report }); + } else { + setReport(undefined); + } + }, [reportData]); + + const updateSuccessToast = ({ report_number }) => ({ + message: ( + + Incident {{ report_number }} updated successfully.{' '} + + View report {{ report_number }} + + . + + ), + severity: SEVERITY.success, + }); + + const updateErrorToast = ({ report_number, error }) => ({ + message: t('Error updating report {{report}}. \n {{message}}', { + report_number, + message: error.message, + }), + severity: SEVERITY.danger, + error, + }); + + const handleSubmit = async (values) => { + try { + const updated = { + ...values, + __typename: undefined, + }; + + updated.epoch_date_modified = getUnixTime(new Date()); + + await UpdateReport({ + variables: { + query: { + report_number: report_number, + }, + set: { + ...updated, + }, + }, + }); + + addToast(updateSuccessToast({ report_number })); + + onClose(); + } catch (error) { + addToast(updateErrorToast({ report_number, error })); + } + }; + + if (!show) { + return null; + } + + return ( + + Edit Report {report?.report_number} + + {!report && ( + + {report === undefined && ( +
+ +
+ )} + {report === null &&
Report not found
} +
+ )} + + {report && entitiesData?.entities && ( + + {({ isValid, isSubmitting, submitForm }) => ( + <> + + + + + + + + + )} + + )} +
+ ); +} diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js new file mode 100644 index 0000000000..eefbde656c --- /dev/null +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -0,0 +1,163 @@ +import { useUserContext } from 'contexts/userContext'; +import React, { useState } from 'react'; +import { useFilters, usePagination, useSortBy, useTable } from 'react-table'; +import { useTranslation } from 'react-i18next'; +import { Button, ToggleSwitch } from 'flowbite-react'; +import Table, { + DefaultColumnFilter, + DefaultColumnHeader, + SelectColumnFilter, + SelectDatePickerFilter, +} from 'components/ui/Table'; +import ReportEditModal from './ReportEditModal'; +import { format } from 'date-fns'; + +export default function ReportsTable({ data, isLiveData, setIsLiveData }) { + const [reportNumberToEdit, setReportNumberToEdit] = useState(0); + + const { isLoggedIn, isRole } = useUserContext(); + + const { t } = useTranslation(); + + const defaultColumn = React.useMemo( + () => ({ + className: 'min-w-[120px]', + Filter: DefaultColumnFilter, + Header: DefaultColumnHeader, + }), + [] + ); + + const columns = React.useMemo(() => { + const columns = [ + { + title: t('Report Number'), + accessor: 'report_number', + Cell: ({ row: { values } }) => ( + + Report {values.report_number} + + ), + }, + { + className: 'min-w-[340px]', + title: t('Title'), + accessor: 'title', + }, + { + className: 'min-w-[340px]', + title: t('Description'), + accessor: 'description', + }, + { + title: t('Date Submitted'), + accessor: 'date_submitted', + Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Filter: SelectDatePickerFilter, + }, + { + title: t('Date Published'), + accessor: 'date_published', + Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Filter: SelectDatePickerFilter, + }, + { + title: t('Date Modified'), + accessor: 'date_modified', + Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Filter: SelectDatePickerFilter, + }, + { + title: t('Language'), + accessor: 'language', + Filter: SelectColumnFilter, + }, + { + title: t('Submitters'), + accessor: 'submitters', + Filter: SelectColumnFilter, + }, + { + title: t('Authors'), + id: 'authors', + accessor: 'authors', + Filter: SelectColumnFilter, + }, + { + title: t('Tags'), + id: 'tags', + accessor: 'tags', + Filter: SelectColumnFilter, + }, + { + title: t('Flagged'), + accessor: 'flag', + Filter: SelectColumnFilter, + Cell: ({ row: { values } }) => { + return <>{values.flag}; + }, + }, + ]; + + if (isRole('incident_editor')) { + columns.push( + { + title: t('Editor Notes'), //TODO: only show if user is editor + accessor: 'editor_notes', + }, + { + title: t('Actions'), + id: 'actions', + className: 'min-w-[120px]', + Cell: ({ row: { values } }) => ( + + ), + } + ); + } + + return columns; + }, [isLoggedIn]); + + const table = useTable( + { + columns, + data, + defaultColumn, + }, + useFilters, + useSortBy, + usePagination + ); + + return ( + <> +
+ { + setIsLiveData(checked); + }} + name="live-data-switch" + /> +
+ + + {reportNumberToEdit !== 0 && ( + setReportNumberToEdit(0)} + report_number={reportNumberToEdit} + /> + )} + + ); +} diff --git a/site/gatsby-site/src/graphql/incidents.js b/site/gatsby-site/src/graphql/incidents.js index 87302391ff..084183e780 100644 --- a/site/gatsby-site/src/graphql/incidents.js +++ b/site/gatsby-site/src/graphql/incidents.js @@ -67,6 +67,9 @@ export const FIND_INCIDENTS_TABLE = gql` entity_id name } + reports { + report_number + } } } `; diff --git a/site/gatsby-site/src/graphql/reports.js b/site/gatsby-site/src/graphql/reports.js index 7883a4b029..c7090ef555 100644 --- a/site/gatsby-site/src/graphql/reports.js +++ b/site/gatsby-site/src/graphql/reports.js @@ -198,3 +198,30 @@ export const FIND_REPORTS = gql` } } `; + +export const FIND_REPORTS_TABLE = gql` + query FindReports($query: ReportQueryInput!) { + reports(query: $query, limit: 999) { + _id + submitters + date_published + date_downloaded + date_submitted + date_modified + report_number + title + description + url + image_url + cloudinary_id + source_domain + text + authors + epoch_date_submitted + language + tags + inputs_outputs + editor_notes + } + } +`; diff --git a/site/gatsby-site/src/pages/apps/incidents.js b/site/gatsby-site/src/pages/apps/incidents.js index 6bbad492a6..addfb60cec 100644 --- a/site/gatsby-site/src/pages/apps/incidents.js +++ b/site/gatsby-site/src/pages/apps/incidents.js @@ -7,41 +7,84 @@ import AiidHelmet from '../../components/AiidHelmet'; import ListSkeleton from 'elements/Skeletons/List'; import { graphql } from 'gatsby'; import { makeEntitiesHash } from 'utils/entities'; +import { Button } from 'flowbite-react'; +import ReportsTable from 'components/reports/ReportsTable'; +import { FIND_REPORTS_TABLE } from '../../graphql/reports'; const IncidentsPage = ({ data, ...props }) => { - const { data: incidents, loading } = useQuery(FIND_INCIDENTS_TABLE); + const { data: incidents, loading: incidentsLoading } = useQuery(FIND_INCIDENTS_TABLE); + + const { data: reports, loading: reportsLoading } = useQuery(FIND_REPORTS_TABLE, { + variables: { + query: { + is_incident_report: true, + }, + }, + }); + + const { data: issueReports, loading: issueReportsLoading } = useQuery(FIND_REPORTS_TABLE, { + variables: { + query: { + is_incident_report: false, + }, + }, + }); const [incidentsData, setIncidentsData] = useState(null); + const [issueReportsData, setIssueReportsData] = useState(null); + + const [reportsData, setReportsData] = useState(null); + const [isLiveData, setIsLiveData] = useState(false); + const [selectedView, setSelectedView] = useState('incidents'); + useEffect(() => { - if (isLiveData) { - if (incidents) { - setIncidentsData(incidents.incidents); - } - } else if (data?.incidents?.nodes) { - const entitiesHash = makeEntitiesHash(data?.entities?.nodes); - - const incidents = []; - - for (const incident of data.incidents.nodes) { - incident.AllegedDeployerOfAISystem = incident.Alleged_deployer_of_AI_system.map( - (entity_id) => entitiesHash[entity_id] - ); - incident.AllegedDeveloperOfAISystem = incident.Alleged_developer_of_AI_system.map( - (entity_id) => entitiesHash[entity_id] - ); - incident.AllegedHarmedOrNearlyHarmedParties = - incident.Alleged_harmed_or_nearly_harmed_parties.map( + if (selectedView === 'incidents') { + if (isLiveData) { + if (incidents) { + setIncidentsData(incidents.incidents); + } + } else if (data?.incidents?.nodes) { + const entitiesHash = makeEntitiesHash(data?.entities?.nodes); + + const incidents = []; + + for (const incident of data.incidents.nodes) { + incident.AllegedDeployerOfAISystem = incident.Alleged_deployer_of_AI_system.map( (entity_id) => entitiesHash[entity_id] ); - incidents.push(incident); - } + incident.AllegedDeveloperOfAISystem = incident.Alleged_developer_of_AI_system.map( + (entity_id) => entitiesHash[entity_id] + ); + incident.AllegedHarmedOrNearlyHarmedParties = + incident.Alleged_harmed_or_nearly_harmed_parties.map( + (entity_id) => entitiesHash[entity_id] + ); + incidents.push(incident); + } - setIncidentsData(incidents); + setIncidentsData(incidents); + } + } else if (selectedView === 'issueReports') { + if (isLiveData) { + if (issueReports) { + setIssueReportsData(issueReports.reports); + } + } else if (data?.issueReports?.nodes) { + setIssueReportsData(data?.issueReports?.nodes); + } + } else if (selectedView === 'reports') { + if (isLiveData) { + if (reports) { + setReportsData(reports.reports); + } + } else if (data?.reports?.nodes) { + setReportsData(data?.reports?.nodes); + } } - }, [isLiveData, incidents, data]); + }, [isLiveData, incidents, data, selectedView]); const { t } = useTranslation(); @@ -51,18 +94,79 @@ const IncidentsPage = ({ data, ...props }) => { {t('Incidents')}
- {(incidentsData && !isLiveData) || (incidentsData && isLiveData && !loading) ? ( -
- -
- ) : ( -
- -
+ + + + + + {selectedView === 'incidents' && ( + <> + {(incidentsData && !isLiveData) || + (incidentsData && isLiveData && !incidentsLoading) ? ( +
+ +
+ ) : ( +
+ +
+ )} + + )} + {selectedView === 'issueReports' && ( + <> + {(issueReportsData && !isLiveData) || + (issueReportsData && isLiveData && !issueReportsLoading) ? ( +
+ +
+ ) : ( +
+ +
+ )} + + )} + {selectedView === 'reports' && ( + <> + {(reportsData && !isLiveData) || (reportsData && isLiveData && !reportsLoading) ? ( +
+ +
+ ) : ( +
+ +
+ )} + )}
@@ -93,6 +197,43 @@ export const query = graphql` name } } + reports: allMongodbAiidprodReports(filter: { is_incident_report: { eq: true } }) { + nodes { + report_number + title + url + authors + date_downloaded + date_modified + date_published + date_submitted + description + flag + image_url + language + source_domain + submitters + } + } + + issueReports: allMongodbAiidprodReports(filter: { is_incident_report: { eq: false } }) { + nodes { + report_number + title + url + authors + date_downloaded + date_modified + date_published + date_submitted + description + flag + image_url + language + source_domain + submitters + } + } } `; From 6b1fa8a90c3a8b135595fc064e96cb1a57ca7892 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Mar 2024 14:37:33 -0300 Subject: [PATCH 10/24] Add live data option for reports --- .../src/components/reports/ReportEditModal.js | 169 ------------------ .../src/components/reports/ReportsTable.js | 61 +++---- site/gatsby-site/src/graphql/reports.js | 2 +- site/gatsby-site/src/pages/apps/incidents.js | 124 ++++++++----- site/gatsby-site/src/pages/cite/edit.js | 25 ++- 5 files changed, 126 insertions(+), 255 deletions(-) delete mode 100644 site/gatsby-site/src/components/reports/ReportEditModal.js diff --git a/site/gatsby-site/src/components/reports/ReportEditModal.js b/site/gatsby-site/src/components/reports/ReportEditModal.js deleted file mode 100644 index 45eae4a605..0000000000 --- a/site/gatsby-site/src/components/reports/ReportEditModal.js +++ /dev/null @@ -1,169 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useMutation, useQuery } from '@apollo/client'; -import { FIND_INCIDENTS } from '../../graphql/incidents'; -import { FIND_ENTITIES } from '../../graphql/entities'; -import useToastContext, { SEVERITY } from '../../hooks/useToast'; -import { Spinner, Modal, Button } from 'flowbite-react'; -import { Formik } from 'formik'; -import { LocalizedLink } from 'plugins/gatsby-theme-i18n'; -import { useTranslation, Trans } from 'react-i18next'; -import { getUnixTime } from 'date-fns'; -// import { useUserContext } from 'contexts/userContext'; -// import { useLogIncidentHistory } from '../../hooks/useLogIncidentHistory'; -import { FIND_REPORT_WITH_TRANSLATIONS, UPDATE_REPORT } from '../../graphql/reports'; -import IncidentReportForm, { schema } from 'components/forms/IncidentReportForm'; - -export default function ReportEditModal({ show, onClose, report_number }) { - // const { user } = useUserContext(); - - const { t, i18n } = useTranslation(); - - const [report, setReport] = useState(null); - - const { data: reportData } = useQuery(FIND_REPORT_WITH_TRANSLATIONS, { - variables: { query: { report_number: report_number } }, - }); - - const { - data: incidentsData, - // loading: loadingIncident, - // refetch: refetchIncidents, - } = useQuery(FIND_INCIDENTS, { - variables: { - query: { - reports_in: { - report_number: report_number, - }, - }, - }, - }); - - const incident_ids = incidentsData?.incidents.map((incident) => incident.incident_id); - - const { data: entitiesData } = useQuery(FIND_ENTITIES); - - const [UpdateReport] = useMutation(UPDATE_REPORT); - - // const [createEntityMutation] = useMutation(UPSERT_ENTITY); - - const addToast = useToastContext(); - - // const { logIncidentHistory } = useLogIncidentHistory(); - - useEffect(() => { - if (reportData?.report) { - setReport({ ...reportData.report }); - } else { - setReport(undefined); - } - }, [reportData]); - - const updateSuccessToast = ({ report_number }) => ({ - message: ( - - Incident {{ report_number }} updated successfully.{' '} - - View report {{ report_number }} - - . - - ), - severity: SEVERITY.success, - }); - - const updateErrorToast = ({ report_number, error }) => ({ - message: t('Error updating report {{report}}. \n {{message}}', { - report_number, - message: error.message, - }), - severity: SEVERITY.danger, - error, - }); - - const handleSubmit = async (values) => { - try { - const updated = { - ...values, - __typename: undefined, - }; - - updated.epoch_date_modified = getUnixTime(new Date()); - - await UpdateReport({ - variables: { - query: { - report_number: report_number, - }, - set: { - ...updated, - }, - }, - }); - - addToast(updateSuccessToast({ report_number })); - - onClose(); - } catch (error) { - addToast(updateErrorToast({ report_number, error })); - } - }; - - if (!show) { - return null; - } - - return ( - - Edit Report {report?.report_number} - - {!report && ( - - {report === undefined && ( -
- -
- )} - {report === null &&
Report not found
} -
- )} - - {report && entitiesData?.entities && ( - - {({ isValid, isSubmitting, submitForm }) => ( - <> - - - - - - - - - )} - - )} -
- ); -} diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js index eefbde656c..dd9b553420 100644 --- a/site/gatsby-site/src/components/reports/ReportsTable.js +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -1,20 +1,17 @@ import { useUserContext } from 'contexts/userContext'; -import React, { useState } from 'react'; +import React from 'react'; import { useFilters, usePagination, useSortBy, useTable } from 'react-table'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { Button, ToggleSwitch } from 'flowbite-react'; import Table, { DefaultColumnFilter, DefaultColumnHeader, SelectColumnFilter, SelectDatePickerFilter, + formatDateField, } from 'components/ui/Table'; -import ReportEditModal from './ReportEditModal'; -import { format } from 'date-fns'; export default function ReportsTable({ data, isLiveData, setIsLiveData }) { - const [reportNumberToEdit, setReportNumberToEdit] = useState(0); - const { isLoggedIn, isRole } = useUserContext(); const { t } = useTranslation(); @@ -44,27 +41,22 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { title: t('Title'), accessor: 'title', }, - { - className: 'min-w-[340px]', - title: t('Description'), - accessor: 'description', - }, { title: t('Date Submitted'), accessor: 'date_submitted', - Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, }, { title: t('Date Published'), accessor: 'date_published', - Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, }, { title: t('Date Modified'), accessor: 'date_modified', - Cell: ({ value }) => format(new Date(value), 'yyyy-MM-dd'), + Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, }, { @@ -111,12 +103,12 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { className: 'min-w-[120px]', Cell: ({ row: { values } }) => ( ), } @@ -139,25 +131,22 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { return ( <> -
- { - setIsLiveData(checked); - }} - name="live-data-switch" - /> +
+
+ { + setIsLiveData(checked); + }} + name="live-data-switch" + /> +
+
-
- {reportNumberToEdit !== 0 && ( - setReportNumberToEdit(0)} - report_number={reportNumberToEdit} - /> - )} ); } diff --git a/site/gatsby-site/src/graphql/reports.js b/site/gatsby-site/src/graphql/reports.js index c7090ef555..cda89240de 100644 --- a/site/gatsby-site/src/graphql/reports.js +++ b/site/gatsby-site/src/graphql/reports.js @@ -201,7 +201,7 @@ export const FIND_REPORTS = gql` export const FIND_REPORTS_TABLE = gql` query FindReports($query: ReportQueryInput!) { - reports(query: $query, limit: 999) { + reports(query: $query, sort: { report_number: DESC }, limit: 999) { _id submitters date_published diff --git a/site/gatsby-site/src/pages/apps/incidents.js b/site/gatsby-site/src/pages/apps/incidents.js index addfb60cec..3fe273e467 100644 --- a/site/gatsby-site/src/pages/apps/incidents.js +++ b/site/gatsby-site/src/pages/apps/incidents.js @@ -10,8 +10,11 @@ import { makeEntitiesHash } from 'utils/entities'; import { Button } from 'flowbite-react'; import ReportsTable from 'components/reports/ReportsTable'; import { FIND_REPORTS_TABLE } from '../../graphql/reports'; +import { useQueryParam } from 'use-query-params'; const IncidentsPage = ({ data, ...props }) => { + const [view] = useQueryParam('view'); + const { data: incidents, loading: incidentsLoading } = useQuery(FIND_INCIDENTS_TABLE); const { data: reports, loading: reportsLoading } = useQuery(FIND_REPORTS_TABLE, { @@ -38,54 +41,65 @@ const IncidentsPage = ({ data, ...props }) => { const [isLiveData, setIsLiveData] = useState(false); - const [selectedView, setSelectedView] = useState('incidents'); + const [selectedView, setSelectedView] = useState(null); + + useEffect(() => { + if (view) { + setSelectedView(view); + } else { + setSelectedView('incidents'); + } + }, [view]); useEffect(() => { - if (selectedView === 'incidents') { - if (isLiveData) { - if (incidents) { + switch (selectedView) { + case 'incidents': + if (isLiveData && incidents) { setIncidentsData(incidents.incidents); + } else if (data?.incidents?.nodes) { + const entitiesHash = makeEntitiesHash(data?.entities?.nodes); + + const processedIncidents = data.incidents.nodes.map((incident) => ({ + ...incident, + AllegedDeployerOfAISystem: incident.Alleged_deployer_of_AI_system.map( + (id) => entitiesHash[id] + ), + AllegedDeveloperOfAISystem: incident.Alleged_developer_of_AI_system.map( + (id) => entitiesHash[id] + ), + AllegedHarmedOrNearlyHarmedParties: + incident.Alleged_harmed_or_nearly_harmed_parties.map((id) => entitiesHash[id]), + })); + + setIncidentsData(processedIncidents); } - } else if (data?.incidents?.nodes) { - const entitiesHash = makeEntitiesHash(data?.entities?.nodes); - - const incidents = []; - - for (const incident of data.incidents.nodes) { - incident.AllegedDeployerOfAISystem = incident.Alleged_deployer_of_AI_system.map( - (entity_id) => entitiesHash[entity_id] - ); - incident.AllegedDeveloperOfAISystem = incident.Alleged_developer_of_AI_system.map( - (entity_id) => entitiesHash[entity_id] - ); - incident.AllegedHarmedOrNearlyHarmedParties = - incident.Alleged_harmed_or_nearly_harmed_parties.map( - (entity_id) => entitiesHash[entity_id] - ); - incidents.push(incident); + break; + case 'issueReports': + if (isLiveData && issueReports) { + setIssueReportsData(transformReports(issueReports.reports)); + } else if (data?.issueReports?.nodes) { + setIssueReportsData(transformReports(data.issueReports.nodes)); } + break; - setIncidentsData(incidents); - } - } else if (selectedView === 'issueReports') { - if (isLiveData) { - if (issueReports) { - setIssueReportsData(issueReports.reports); + case 'reports': + if (isLiveData && reports) { + setReportsData(transformReports(reports.reports)); + } else if (data?.reports?.nodes) { + setReportsData(transformReports(data.reports.nodes)); } - } else if (data?.issueReports?.nodes) { - setIssueReportsData(data?.issueReports?.nodes); - } - } else if (selectedView === 'reports') { - if (isLiveData) { - if (reports) { - setReportsData(reports.reports); - } - } else if (data?.reports?.nodes) { - setReportsData(data?.reports?.nodes); - } + break; + default: } }, [isLiveData, incidents, data, selectedView]); + function transformReports(reports) { + return reports.map((report) => ({ + ...report, + flag: report.flag ? 'Yes' : 'No', + })); + } + const { t } = useTranslation(); return ( @@ -97,20 +111,38 @@ const IncidentsPage = ({ data, ...props }) => { @@ -197,7 +229,10 @@ export const query = graphql` name } } - reports: allMongodbAiidprodReports(filter: { is_incident_report: { eq: true } }) { + reports: allMongodbAiidprodReports( + filter: { is_incident_report: { eq: true } } + sort: { report_number: DESC } + ) { nodes { report_number title @@ -216,7 +251,10 @@ export const query = graphql` } } - issueReports: allMongodbAiidprodReports(filter: { is_incident_report: { eq: false } }) { + issueReports: allMongodbAiidprodReports( + filter: { is_incident_report: { eq: false } } + sort: { report_number: DESC } + ) { nodes { report_number title diff --git a/site/gatsby-site/src/pages/cite/edit.js b/site/gatsby-site/src/pages/cite/edit.js index b0e41f8da7..0bdde7140f 100644 --- a/site/gatsby-site/src/pages/cite/edit.js +++ b/site/gatsby-site/src/pages/cite/edit.js @@ -66,7 +66,7 @@ function EditCitePage(props) { const [reportNumber] = useQueryParam('report_number', withDefault(NumberParam, 1)); - const [incidentId] = useQueryParam('incident_id', withDefault(NumberParam, 1)); + const [incidentId] = useQueryParam('incident_id'); const { data: reportData, @@ -280,11 +280,24 @@ function EditCitePage(props) {

Editing Incident Report {{ reportNumber }}

- - - + {incidentId ? ( + + + + ) : ( + + + + )} )} From ada97e91a00836252f3fdaa672861cec45f76e8e Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Mar 2024 15:14:15 -0300 Subject: [PATCH 11/24] Remove reports apps, display result count and fix tests --- .../e2e/integration/apps/reports.cy.js | 21 +- .../i18n/locales/es/translation.json | 3 +- .../i18n/locales/fr/translation.json | 3 +- .../src/components/reports/ReportsTable.js | 13 + site/gatsby-site/src/graphql/reports.js | 1 + site/gatsby-site/src/pages/apps/incidents.js | 3 + site/gatsby-site/src/pages/apps/reports.js | 230 ------------------ 7 files changed, 29 insertions(+), 245 deletions(-) delete mode 100644 site/gatsby-site/src/pages/apps/reports.js diff --git a/site/gatsby-site/cypress/e2e/integration/apps/reports.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/reports.cy.js index 10688207a0..5e2f740336 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/reports.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/reports.cy.js @@ -1,21 +1,16 @@ -import reports from '../../../fixtures/reports/reports.json'; - describe('Reports App', () => { - const url = '/apps/reports'; + const url = '/apps/incidents'; - it('Successfully loads', () => { - cy.visit(url); + it('Successfully loads reports', () => { + cy.visit(url + '?view=reports'); }); - it('Filters a report by title ', () => { - cy.visit(url); + it('Successfully loads issue reports', () => { + cy.visit(url + '?view=issueReports'); + }); - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName == 'ReportsQuery', - 'ReportsQuery', - reports - ); + it('Filters a report by title ', () => { + cy.visit(url + '?view=reports'); cy.get('[data-cy="filter"]', { timeout: 15000 }) .eq(1) diff --git a/site/gatsby-site/i18n/locales/es/translation.json b/site/gatsby-site/i18n/locales/es/translation.json index a70b35360d..26801e397e 100644 --- a/site/gatsby-site/i18n/locales/es/translation.json +++ b/site/gatsby-site/i18n/locales/es/translation.json @@ -290,5 +290,6 @@ "Incident and Issue Reports": "Incidentes e Informes de Problemas", "Issue Reports": "Informes de Problemas", "found": "encontrados", - "results found": "resultados encontrados" + "results found": "resultados encontrados", + "Displaying {{pageLength}} of {{allResultsCount}} reports": "Mostrando {{pageLength}} de {{allResultsCount}} informes" } diff --git a/site/gatsby-site/i18n/locales/fr/translation.json b/site/gatsby-site/i18n/locales/fr/translation.json index cb173ddbe1..1571e2b51e 100644 --- a/site/gatsby-site/i18n/locales/fr/translation.json +++ b/site/gatsby-site/i18n/locales/fr/translation.json @@ -278,5 +278,6 @@ "Incident and Issue Reports": "Incidents et rapports de problèmes", "Issue Reports": "Rapports de problèmes", "found": "trouvés", - "results found": "résultats trouvés" + "results found": "résultats trouvés", + "Displaying {{pageLength}} of {{allResultsCount}} reports": "Affichage de {{pageLength}} sur {{allResultsCount}} rapports" } diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js index dd9b553420..6f849f8d77 100644 --- a/site/gatsby-site/src/components/reports/ReportsTable.js +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -89,6 +89,10 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { return <>{values.flag}; }, }, + { + title: t('Is Issue Report?'), + accessor: 'is_incident_report', + }, ]; if (isRole('incident_editor')) { @@ -129,6 +133,10 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { usePagination ); + const pageLength = table.page.length; + + const allResultsCount = data.length; + return ( <>
@@ -146,6 +154,11 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { Reset filters
+

+ + Displaying {{ pageLength }} of {{ allResultsCount }} reports + +

); diff --git a/site/gatsby-site/src/graphql/reports.js b/site/gatsby-site/src/graphql/reports.js index cda89240de..a8d1c29930 100644 --- a/site/gatsby-site/src/graphql/reports.js +++ b/site/gatsby-site/src/graphql/reports.js @@ -222,6 +222,7 @@ export const FIND_REPORTS_TABLE = gql` tags inputs_outputs editor_notes + is_incident_report } } `; diff --git a/site/gatsby-site/src/pages/apps/incidents.js b/site/gatsby-site/src/pages/apps/incidents.js index 3fe273e467..2392bf684f 100644 --- a/site/gatsby-site/src/pages/apps/incidents.js +++ b/site/gatsby-site/src/pages/apps/incidents.js @@ -97,6 +97,7 @@ const IncidentsPage = ({ data, ...props }) => { return reports.map((report) => ({ ...report, flag: report.flag ? 'Yes' : 'No', + is_incident_report: report.is_incident_report ? 'No' : 'Yes', })); } @@ -248,6 +249,7 @@ export const query = graphql` language source_domain submitters + is_incident_report } } @@ -270,6 +272,7 @@ export const query = graphql` language source_domain submitters + is_incident_report } } } diff --git a/site/gatsby-site/src/pages/apps/reports.js b/site/gatsby-site/src/pages/apps/reports.js deleted file mode 100644 index 614c82f01d..0000000000 --- a/site/gatsby-site/src/pages/apps/reports.js +++ /dev/null @@ -1,230 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import AiidHelmet from '../../components/AiidHelmet'; -import Link from '../../components/ui/Link'; -import { faLink } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { useTable, useFilters, usePagination, useSortBy } from 'react-table'; -import { gql, useQuery } from '@apollo/client'; -import { useMenuContext } from 'contexts/MenuContext'; -import ListSkeleton from 'elements/Skeletons/List'; -import { useTranslation } from 'react-i18next'; -import { Button } from 'flowbite-react'; -import Table, { - DefaultColumnFilter, - DefaultColumnHeader, - formatDateField, - SelectColumnFilter, - SelectDatePickerFilter, -} from 'components/ui/Table'; - -const query = gql` - query ReportsQuery { - incidents(sortBy: INCIDENT_ID_ASC, limit: 9999) { - incident_id - reports { - title - source_domain - url - authors - submitters - epoch_date_submitted - epoch_date_modified - epoch_date_downloaded - report_number - flag - date_downloaded - date_modified - date_submitted - } - } - } -`; - -export default function Incidents(props) { - const [tableData, setTableData] = useState([]); - - const { data, loading } = useQuery(query); - - const { t } = useTranslation(); - - useEffect(() => { - if (!data) return; - - const incidentDataToCellMap = (data) => { - const tableData = data.incidents.reduce((accumulator, incident) => { - for (const report of incident.reports) { - accumulator.push({ - ...report, - incident_id: incident.incident_id, - flag: report.flag === true ? 'Yes' : 'No', - authors: report.authors.join(', '), - __typename: undefined, - }); - } - return accumulator; - }, []); - - return tableData; - }; - - setTableData(incidentDataToCellMap(data)); - }, [data]); - - const cellData = React.useMemo(() => tableData, [tableData]); - - const columns = React.useMemo(() => { - const columns = [ - { - title: t('Incident ID'), - accessor: 'incident_id', - Cell: ({ row: { values } }) => ( - - Incident {values.incident_id} - - - ), - }, - { - className: 'min-w-[240px] max-w-[400px]', - title: t('Incident Title'), - accessor: 'title', - Cell: (cell) => ( -
- {cell.row.values.title} -
- ), - }, - { - className: 'min-w-[240px]', - title: t('Source Domain'), - accessor: 'source_domain', - width: 240, - }, - { - className: 'min-w-[240px]', - title: t('Authors'), - accessor: 'authors', - width: 240, - }, - { - className: 'min-w-[240px]', - title: t('Submitters'), - accessor: 'submitters', - width: 240, - Filter: SelectColumnFilter, - }, - { - className: 'min-w-[240px]', - title: t('Date Submitted'), - accessor: 'date_submitted', - width: 240, - Filter: SelectDatePickerFilter, - Cell: ({ row: { values } }) => { - return <>{formatDateField(values.date_submitted)}; - }, - }, - { - className: 'min-w-[240px]', - title: t('Date Modified'), - accessor: 'date_modified', - width: 240, - Filter: SelectDatePickerFilter, - Cell: ({ row: { values } }) => { - return <>{formatDateField(values.date_modified)}; - }, - }, - { - className: 'min-w-[240px]', - title: t('Date Downloaded'), - accessor: 'date_downloaded', - width: 240, - Filter: SelectDatePickerFilter, - Cell: ({ row: { values } }) => { - return <>{formatDateField(values.date_downloaded)}; - }, - }, - { - className: 'min-w-[240px]', - title: t('Report ID'), - accessor: 'report_number', - width: 240, - }, - { - className: 'min-w-[240px]', - title: t('Flagged'), - accessor: 'flag', - width: 240, - Filter: SelectColumnFilter, - Cell: ({ row: { values } }) => { - return <>{values.flag}; - }, - }, - ]; - - return columns; - }, []); - - const defaultColumn = React.useMemo( - () => ({ - Filter: DefaultColumnFilter, - Header: DefaultColumnHeader, - }), - [] - ); - - const filterDateFunction = (rows, id, filterValue) => { - const start = filterValue[0]; - - const end = filterValue[1]; - - return rows.filter((val) => { - return val.original[id] >= start && val.original[id] <= end; - }); - }; - - const filterTypes = { - epoch_date_submitted: filterDateFunction, - epoch_date_modified: filterDateFunction, - date_downloaded: filterDateFunction, - }; - - const table = useTable( - { - columns, - data: cellData, - defaultColumn, - filterTypes, - initialState: { pageIndex: 0, pageSize: 100 }, - }, - useFilters, - useSortBy, - usePagination - ); - - const { isCollapsed } = useMenuContext(); - - return ( -
- - Incident List - - - {loading && } - {!loading && ( -
-

Incident Report Table

- -
-
- - - )} - - ); -} From 3667d8c523afd42d9ab40ee3ca5f2cab9729dc3a Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Mar 2024 15:58:51 -0300 Subject: [PATCH 12/24] Display issue report in table --- site/gatsby-site/src/components/reports/ReportsTable.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js index 6f849f8d77..8b92ecf797 100644 --- a/site/gatsby-site/src/components/reports/ReportsTable.js +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -92,6 +92,10 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { { title: t('Is Issue Report?'), accessor: 'is_incident_report', + Filter: SelectColumnFilter, + Cell: ({ row: { values } }) => { + return <>{values.is_incident_report}; + }, }, ]; From 1879b74e03b4dd84f119132eb36608ca57ebc507 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Thu, 14 Mar 2024 11:41:51 -0300 Subject: [PATCH 13/24] Add quiet to test --- site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js | 3 +++ site/gatsby-site/src/components/forms/IncidentReportForm.js | 1 + 2 files changed, 4 insertions(+) diff --git a/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js b/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js index 3f79f81cea..49e2345b83 100644 --- a/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js @@ -156,6 +156,8 @@ describe('Edit report', () => { cy.get(`[name=${key}]`).clear().type(updates[key]); }); + cy.get(`[name="quiet"]`).click(); + cy.setEditorText( '## This is text in English\n\nthat is longer that eighty characters, yes eighty characters!', '[data-cy="text"] .CodeMirror' @@ -215,6 +217,7 @@ describe('Edit report', () => { source_domain: 'test.com', editor_notes: 'Pro iustitia tantum', language: 'en', + quiet: true, }; cy.wait('@updateReport').then((xhr) => { diff --git a/site/gatsby-site/src/components/forms/IncidentReportForm.js b/site/gatsby-site/src/components/forms/IncidentReportForm.js index 356bacd182..0fea80a5da 100644 --- a/site/gatsby-site/src/components/forms/IncidentReportForm.js +++ b/site/gatsby-site/src/components/forms/IncidentReportForm.js @@ -431,6 +431,7 @@ const IncidentReportForm = () => {
{ setFieldValue('quiet', e.target.checked); From 9590c491ee36197f03c67bae53d78b3cd96488c9 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Thu, 14 Mar 2024 12:02:47 -0300 Subject: [PATCH 14/24] Add reset filters to incidents page --- .../i18n/locales/es/translation.json | 3 +- .../i18n/locales/fr/translation.json | 3 +- .../components/incidents/IncidentsTable.js | 34 +++++++++++++------ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/site/gatsby-site/i18n/locales/es/translation.json b/site/gatsby-site/i18n/locales/es/translation.json index 26801e397e..88aeb88b1b 100644 --- a/site/gatsby-site/i18n/locales/es/translation.json +++ b/site/gatsby-site/i18n/locales/es/translation.json @@ -291,5 +291,6 @@ "Issue Reports": "Informes de Problemas", "found": "encontrados", "results found": "resultados encontrados", - "Displaying {{pageLength}} of {{allResultsCount}} reports": "Mostrando {{pageLength}} de {{allResultsCount}} informes" + "Displaying {{pageLength}} of {{allResultsCount}} reports": "Mostrando {{pageLength}} de {{allResultsCount}} informes", + "Displaying {{pageLength}} of {{allResultsCount}} incidents": "Mostrando {{pageLength}} de {{allResultsCount}} incidentes" } diff --git a/site/gatsby-site/i18n/locales/fr/translation.json b/site/gatsby-site/i18n/locales/fr/translation.json index 1571e2b51e..889ffe625d 100644 --- a/site/gatsby-site/i18n/locales/fr/translation.json +++ b/site/gatsby-site/i18n/locales/fr/translation.json @@ -279,5 +279,6 @@ "Issue Reports": "Rapports de problèmes", "found": "trouvés", "results found": "résultats trouvés", - "Displaying {{pageLength}} of {{allResultsCount}} reports": "Affichage de {{pageLength}} sur {{allResultsCount}} rapports" + "Displaying {{pageLength}} of {{allResultsCount}} reports": "Affichage de {{pageLength}} sur {{allResultsCount}} rapports", + "Displaying {{pageLength}} of {{allResultsCount}} incidents": "Affichage de {{pageLength}} sur {{allResultsCount}} incidents" } diff --git a/site/gatsby-site/src/components/incidents/IncidentsTable.js b/site/gatsby-site/src/components/incidents/IncidentsTable.js index 2a6674dd69..e7ee13971e 100644 --- a/site/gatsby-site/src/components/incidents/IncidentsTable.js +++ b/site/gatsby-site/src/components/incidents/IncidentsTable.js @@ -2,7 +2,7 @@ import { useUserContext } from 'contexts/userContext'; import React, { useState } from 'react'; import { useFilters, usePagination, useSortBy, useTable } from 'react-table'; import IncidentEditModal from './IncidentEditModal'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import Link from 'components/ui/Link'; import { Button, ToggleSwitch } from 'flowbite-react'; import Table, { DefaultColumnFilter, DefaultColumnHeader } from 'components/ui/Table'; @@ -134,18 +134,32 @@ export default function IncidentsTable({ data, isLiveData, setIsLiveData }) { usePagination ); + const pageLength = table.page.length; + + const allResultsCount = data.length; + return ( <> -
- { - setIsLiveData(checked); - }} - name="live-data-switch" - /> +
+
+ { + setIsLiveData(checked); + }} + name="live-data-switch" + /> +
+
+

+ + Displaying {{ pageLength }} of {{ allResultsCount }} incidents + +

{incidentIdToEdit !== 0 && ( From a66a19f06922c1c37a4cb78cc8e2b6e060f28520 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 18 Mar 2024 10:02:13 -0300 Subject: [PATCH 15/24] Fix table date sorting --- .../src/components/reports/ReportsTable.js | 10 ++++++++ site/gatsby-site/src/components/ui/Table.js | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js index 8b92ecf797..412e18cba4 100644 --- a/site/gatsby-site/src/components/reports/ReportsTable.js +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -9,6 +9,7 @@ import Table, { SelectColumnFilter, SelectDatePickerFilter, formatDateField, + sortDateField, } from 'components/ui/Table'; export default function ReportsTable({ data, isLiveData, setIsLiveData }) { @@ -46,18 +47,27 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { accessor: 'date_submitted', Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, + sortType: (rowA, rowB) => { + return sortDateField(rowA, rowB, 'date_submitted'); + }, }, { title: t('Date Published'), accessor: 'date_published', Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, + sortType: (rowA, rowB) => { + return sortDateField(rowA, rowB, 'date_published'); + }, }, { title: t('Date Modified'), accessor: 'date_modified', Cell: ({ value }) => formatDateField(value), Filter: SelectDatePickerFilter, + sortType: (rowA, rowB) => { + return sortDateField(rowA, rowB, 'date_modified'); + }, }, { title: t('Language'), diff --git a/site/gatsby-site/src/components/ui/Table.js b/site/gatsby-site/src/components/ui/Table.js index 548e91d0ef..8cf2102c60 100644 --- a/site/gatsby-site/src/components/ui/Table.js +++ b/site/gatsby-site/src/components/ui/Table.js @@ -230,6 +230,29 @@ export function formatDateField(date) { } } +export function sortDateField(rowA, rowB, fieldName) { + if ( + rowA.original[fieldName] && + rowA.original[fieldName] !== '' && + rowB.original[fieldName] && + rowB.original[fieldName] !== '' + ) { + const dateRowA = new Date(rowA.original[fieldName]); + + const dateRowB = new Date(rowB.original[fieldName]); + + if (dateRowA > dateRowB) { + return 1; + } + + if (dateRowA < dateRowB) { + return -1; + } + + return 0; + } +} + export default function Table({ table, showPagination = true, From 7d85dc85c6009e178120c2abe6f7717e7648fff8 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 18 Mar 2024 10:13:33 -0300 Subject: [PATCH 16/24] Fix date filter --- .../src/components/incidents/IncidentsTable.js | 15 ++++++++++++++- .../src/components/reports/ReportsTable.js | 4 ++++ site/gatsby-site/src/components/ui/Table.js | 11 +++++++++++ .../src/components/users/UsersTable.js | 11 +---------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/site/gatsby-site/src/components/incidents/IncidentsTable.js b/site/gatsby-site/src/components/incidents/IncidentsTable.js index e7ee13971e..75eaf0724c 100644 --- a/site/gatsby-site/src/components/incidents/IncidentsTable.js +++ b/site/gatsby-site/src/components/incidents/IncidentsTable.js @@ -5,7 +5,14 @@ import IncidentEditModal from './IncidentEditModal'; import { Trans, useTranslation } from 'react-i18next'; import Link from 'components/ui/Link'; import { Button, ToggleSwitch } from 'flowbite-react'; -import Table, { DefaultColumnFilter, DefaultColumnHeader } from 'components/ui/Table'; +import Table, { + DefaultColumnFilter, + DefaultColumnHeader, + SelectDatePickerFilter, + filterDate, + formatDateField, + sortDateField, +} from 'components/ui/Table'; function ListCell({ cell }) { return ( @@ -78,6 +85,12 @@ export default function IncidentsTable({ data, isLiveData, setIsLiveData }) { { title: t('Date'), accessor: 'date', + Cell: ({ value }) => formatDateField(value), + Filter: SelectDatePickerFilter, + sortType: (rowA, rowB) => { + return sortDateField(rowA, rowB, 'date'); + }, + filter: (rows, id, filterValue) => filterDate(rows, id, filterValue), }, { title: t('Alleged Deployer of AI System'), diff --git a/site/gatsby-site/src/components/reports/ReportsTable.js b/site/gatsby-site/src/components/reports/ReportsTable.js index 412e18cba4..b460ffd8c7 100644 --- a/site/gatsby-site/src/components/reports/ReportsTable.js +++ b/site/gatsby-site/src/components/reports/ReportsTable.js @@ -8,6 +8,7 @@ import Table, { DefaultColumnHeader, SelectColumnFilter, SelectDatePickerFilter, + filterDate, formatDateField, sortDateField, } from 'components/ui/Table'; @@ -50,6 +51,7 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { sortType: (rowA, rowB) => { return sortDateField(rowA, rowB, 'date_submitted'); }, + filter: (rows, id, filterValue) => filterDate(rows, id, filterValue), }, { title: t('Date Published'), @@ -59,6 +61,7 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { sortType: (rowA, rowB) => { return sortDateField(rowA, rowB, 'date_published'); }, + filter: (rows, id, filterValue) => filterDate(rows, id, filterValue), }, { title: t('Date Modified'), @@ -68,6 +71,7 @@ export default function ReportsTable({ data, isLiveData, setIsLiveData }) { sortType: (rowA, rowB) => { return sortDateField(rowA, rowB, 'date_modified'); }, + filter: (rows, id, filterValue) => filterDate(rows, id, filterValue), }, { title: t('Language'), diff --git a/site/gatsby-site/src/components/ui/Table.js b/site/gatsby-site/src/components/ui/Table.js index 8cf2102c60..8678633edb 100644 --- a/site/gatsby-site/src/components/ui/Table.js +++ b/site/gatsby-site/src/components/ui/Table.js @@ -253,6 +253,17 @@ export function sortDateField(rowA, rowB, fieldName) { } } +export function filterDate(rows, id, filterValue) { + return rows.filter((row) => { + const rowValue = row.values[id]; + + if (!rowValue) return false; + const filterValueDate = new Date(rowValue).getTime(); + + return filterValueDate >= filterValue[0] && filterValueDate <= filterValue[1]; + }); +} + export default function Table({ table, showPagination = true, diff --git a/site/gatsby-site/src/components/users/UsersTable.js b/site/gatsby-site/src/components/users/UsersTable.js index fe476c8fb0..7c513be1f3 100644 --- a/site/gatsby-site/src/components/users/UsersTable.js +++ b/site/gatsby-site/src/components/users/UsersTable.js @@ -4,6 +4,7 @@ import Table, { DefaultColumnFilter, DefaultColumnHeader, SelectDatePickerFilter, + filterDate, } from 'components/ui/Table'; import { Badge, Button } from 'flowbite-react'; import UserEditModal from './UserEditModal'; @@ -40,16 +41,6 @@ export default function UsersTable({ data, className = '', ...props }) { const client = useApolloClient(); - const filterDate = (rows, id, filterValue) => - rows.filter((row) => { - const rowValue = row.values[id]; - - if (!rowValue) return false; - const filterValueDate = new Date(rowValue).getTime(); - - return filterValueDate >= filterValue[0] && filterValueDate <= filterValue[1]; - }); - useEffect(() => { const fetchUserAdminData = async () => { if (data) { From 1b1d72ee18018230d43533d1210efe45dc0fbf67 Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Tue, 19 Mar 2024 17:28:11 -0400 Subject: [PATCH 17/24] wip --- .../page-creators/createWordCountsPage.js | 21 +++++-------- .../landing/LatestIncidentReport.js | 11 ++++--- .../src/components/landing/LatestReports.js | 8 ++--- site/gatsby-site/src/templates/landingPage.js | 31 ++----------------- 4 files changed, 21 insertions(+), 50 deletions(-) diff --git a/site/gatsby-site/page-creators/createWordCountsPage.js b/site/gatsby-site/page-creators/createWordCountsPage.js index afd98a32a8..3d1d5de943 100644 --- a/site/gatsby-site/page-creators/createWordCountsPage.js +++ b/site/gatsby-site/page-creators/createWordCountsPage.js @@ -41,8 +41,16 @@ const createWordCountsPage = async (graphql, createPage) => { ) { nodes { title + incident_id reports { + cloudinary_id + epoch_date_submitted + image_url report_number + source_domain + text + title + url } } } @@ -126,14 +134,6 @@ const createWordCountsPage = async (graphql, createPage) => { } } - const latestReportNumbers = result.data.latestReports.nodes.map((node) => { - const sortedArray = node.reports.sort((a, b) => { - return a.epoch_date_submitted - b.epoch_date_submitted; - }); - - return sortedArray[0].report_number; - }); - const fiveLatestIncidents = result.data.latestReports.nodes; PAGES_WITH_WORDCOUNT.forEach((page) => { @@ -144,11 +144,6 @@ const createWordCountsPage = async (graphql, createPage) => { wordClouds, wordCountsSorted, wordsPerCloud, - latestReportNumber: - result.data.latestReport.nodes.length > 0 - ? result.data.latestReport.nodes[0].report_number - : 0, - latestReportNumbers, fiveLatestIncidents, sponsors: result.data.sponsors.edges, }, diff --git a/site/gatsby-site/src/components/landing/LatestIncidentReport.js b/site/gatsby-site/src/components/landing/LatestIncidentReport.js index e163b41276..1c05fa01c9 100644 --- a/site/gatsby-site/src/components/landing/LatestIncidentReport.js +++ b/site/gatsby-site/src/components/landing/LatestIncidentReport.js @@ -7,7 +7,10 @@ import { LocalizedLink } from 'plugins/gatsby-theme-i18n'; import DateLabel from 'components/ui/DateLabel'; import Link from 'components/ui/Link'; -const LatestIncidentReport = ({ report, key, isLatest = false, fiveLatestIncidents }) => { +const LatestIncidentReport = ({ incident, key, isLatest = false }) => { + + const report = incident.reports[0]; + const { image_url, cloudinary_id, @@ -24,9 +27,9 @@ const LatestIncidentReport = ({ report, key, isLatest = false, fiveLatestInciden const reportLink = `/cite/${incident_id}#r${report_number}`; - const incident = fiveLatestIncidents.find((incident) => - incident.reports.map((r) => r.report_number).includes(report.report_number) - ); +// const incident = fiveLatestIncidents.find((incident) => +// incident.reports.map((r) => r.report_number).includes(report.report_number) +// ); console.log(`incident`, incident); diff --git a/site/gatsby-site/src/components/landing/LatestReports.js b/site/gatsby-site/src/components/landing/LatestReports.js index 710315f735..307bc06698 100644 --- a/site/gatsby-site/src/components/landing/LatestReports.js +++ b/site/gatsby-site/src/components/landing/LatestReports.js @@ -4,7 +4,7 @@ import { Carousel } from 'flowbite-react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faArrowCircleLeft, faArrowCircleRight } from '@fortawesome/free-solid-svg-icons'; -export default function LatestReports({ latestReports, fiveLatestIncidents }) { +export default function LatestReports({ fiveLatestIncidents }) { return ( <> } > - {latestReports.map((report, index) => ( + {fiveLatestIncidents.map((incident, index) => ( ))} diff --git a/site/gatsby-site/src/templates/landingPage.js b/site/gatsby-site/src/templates/landingPage.js index b5f4cb4171..6d2c5f3479 100644 --- a/site/gatsby-site/src/templates/landingPage.js +++ b/site/gatsby-site/src/templates/landingPage.js @@ -23,8 +23,6 @@ const LandingPage = (props) => { let { latestPrismicPost, latestPostOld, latestReportIncidents } = data; - const { latestReportNumbers } = props.pageContext; - let latestBlogPost = null; if (!latestPostOld || latestPostOld.nodes.length === 0) { @@ -44,30 +42,6 @@ const LandingPage = (props) => { const { locale: language } = useLocalization(); - const latestReports = latestReportIncidents.edges.map((incident) => { - const report = incident.node.reports.find((r) => latestReportNumbers.includes(r.report_number)); - - if (report.language !== language) { - const translation = data[`latestReports_${language}`]?.edges.find( - (translation) => translation.node.report_number === report.report_number - ); - - if (translation) { - report.title = translation.node.title; - report.text = translation.node.text; - } else { - console.warn(`No latestReports_${language}`); - } - } - const updatedIncident = { - incident_id: incident.node.incident_id, - ...report, - }; - - return updatedIncident; - }); - - console.log(`props`, props); return ( // Tailwind has max-w-6xl but no plain w-6xl... 72rem = 6xl @@ -81,11 +55,10 @@ const LandingPage = (props) => { - {latestReports.length > 0 && ( + {props.pageContext.fiveLatestIncidents.length > 0 && (
@@ -131,7 +104,7 @@ const LandingPage = (props) => {
- {latestReports.length > 0 && ( + {props.pageContext.fiveLatestIncidents.length > 0 && (
From 1b7da05d13fdadf4db3dcca0c5ac5a597d156622 Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Tue, 26 Mar 2024 18:26:24 -0400 Subject: [PATCH 18/24] update gatsby-node.js --- site/gatsby-site/gatsby-node.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/gatsby-site/gatsby-node.js b/site/gatsby-site/gatsby-node.js index 96b0983083..53d076fe81 100644 --- a/site/gatsby-site/gatsby-node.js +++ b/site/gatsby-site/gatsby-node.js @@ -14,6 +14,8 @@ const createCitationPages = require('./page-creators/createCitationPages'); const createWordCountsPages = require('./page-creators/createWordCountsPage'); +const createLandingPage = require('./page-creators/createLandingPage'); + const createBackupsPage = require('./page-creators/createBackupsPage'); const createTaxonomyPages = require('./page-creators/createTaxonomyPages'); @@ -74,6 +76,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { createBlogPages, createCitationPages, createWordCountsPages, + createLandingPage, createBackupsPage, createTaxonomyPages, createDownloadIndexPage, From ffa6011fa81c8800ca9756fc564371622c5ba6bf Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Wed, 27 Mar 2024 12:22:56 -0400 Subject: [PATCH 19/24] Don't touch gatsby-config --- site/gatsby-site/gatsby-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/gatsby-site/gatsby-config.js b/site/gatsby-site/gatsby-config.js index bff04a2488..3e0879042e 100755 --- a/site/gatsby-site/gatsby-config.js +++ b/site/gatsby-site/gatsby-config.js @@ -305,6 +305,6 @@ module.exports = { plugins: plugins, trailingSlash: `always`, flags: { - DEV_SSR: false, + DEV_SSR: true, }, }; From 4357581ee85ef79cc0998ccc974d27b97cd9d082 Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Wed, 27 Mar 2024 19:50:58 -0400 Subject: [PATCH 20/24] Add toasts on query failures --- .../components/checklists/CheckListForm.js | 3 ++- .../components/checklists/ChecklistsIndex.js | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/site/gatsby-site/src/components/checklists/CheckListForm.js b/site/gatsby-site/src/components/checklists/CheckListForm.js index 4c5b0bee63..0d2871afe5 100644 --- a/site/gatsby-site/src/components/checklists/CheckListForm.js +++ b/site/gatsby-site/src/components/checklists/CheckListForm.js @@ -101,7 +101,7 @@ export default function CheckListForm({ const { data: generatedRisksData, loading: generatedRisksLoading, - errors: generatedRisksErrors, + error: generatedRisksErrors, } = useQuery( gql` query { @@ -124,6 +124,7 @@ export default function CheckListForm({ addToast({ message: t('Failure searching for risks.'), severity: SEVERITY.danger, + error: generatedRisksErrors, }); } diff --git a/site/gatsby-site/src/components/checklists/ChecklistsIndex.js b/site/gatsby-site/src/components/checklists/ChecklistsIndex.js index 4e5115a6ff..563d3fc457 100644 --- a/site/gatsby-site/src/components/checklists/ChecklistsIndex.js +++ b/site/gatsby-site/src/components/checklists/ChecklistsIndex.js @@ -40,11 +40,23 @@ const ChecklistsIndex = ({ users }) => { const [insertChecklist] = useMutation(INSERT_CHECKLIST); /************************** Get Checklists **************************/ - const { data: checklistsData, loading: checklistsLoading } = useQuery(FIND_CHECKLISTS, { + const { + data: checklistsData, + loading: checklistsLoading, + error: checklistsErrors, + } = useQuery(FIND_CHECKLISTS, { variables: { query: { owner_id: user?.id } }, skip: !user?.id, }); + if (checklistsErrors) { + addToast({ + message: t('Could not fetch checklists'), + severity: SEVERITY.danger, + error: checklistsErrors, + }); + } + // In useState so that on deleting a checklist, // we can remove it from display immediately. const [checklists, setChecklists] = useState([]); @@ -104,7 +116,17 @@ const ChecklistsIndex = ({ users }) => { } `; - const { data: risksData } = useQuery(gql(riskQuery), { skip: skipRisksQuery }); + const { data: risksData, error: risksErrors } = useQuery(gql(riskQuery), { + skip: skipRisksQuery, + }); + + if (risksErrors) { + addToast({ + message: t('Failure searching for risks.'), + severity: SEVERITY.danger, + error: risksErrors, + }); + } /************************ Prepare Display ***************************/ const [sortBy, setSortBy] = useState('alphabetical'); From 0c92f2f142cfe6257727cc7be54ac31e0c42c5bb Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Thu, 28 Mar 2024 17:22:30 -0400 Subject: [PATCH 21/24] Add tests for toasts --- .../integration/apps/checklistsIndex.cy.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js index 760edaffd4..07cbf686e1 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js @@ -7,6 +7,13 @@ describe('Checklists App Index', () => { const newChecklistButtonQuery = '#new-checklist-button'; + const testError = { + 0: { + message: 'Test error', + locations: [{ line: 1, column: 1 }], + }, + }; + const usersQuery = { query: gql` { @@ -91,4 +98,34 @@ describe('Checklists App Index', () => { cy.get('[data-cy="checklist-card"]:last-child button').contains('Delete').should('not.exist'); }); }); + + it('Should show toast on error fetching checklists', () => { + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'findChecklists', + 'findChecklists', + { errors: [testError] } + ); + + cy.visit(url); + + cy.get('[data-cy="toast"]').contains('Could not fetch checklists').should('exist'); + }); + + maybeIt('Should show toast on error creating checklist', () => { + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'insertChecklist', + 'insertChecklist', + { errors: [testError] } + ); + + cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); + + cy.visit(url); + + cy.get(newChecklistButtonQuery).click(); + + cy.get('[data-cy="toast"]').contains('Could not create checklist.').should('exist'); + }); }); From 57da1a9309f9187d9fc1856f3c347367e8907c2e Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Thu, 28 Mar 2024 18:38:26 -0400 Subject: [PATCH 22/24] Add tests for toasts --- .../integration/apps/checklistsIndex.cy.js | 104 ++++++++++++++++-- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js index 07cbf686e1..73009e13ae 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/checklistsIndex.cy.js @@ -112,20 +112,104 @@ describe('Checklists App Index', () => { cy.get('[data-cy="toast"]').contains('Could not fetch checklists').should('exist'); }); + it('Should show toast on error fetching risks', () => { + cy.query(usersQuery).then(({ data: { users } }) => { + const user = users.find((user) => user.adminData.email == Cypress.env('e2eUsername')); + + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'findChecklists', + 'findChecklists', + { + data: { + checklists: [ + { + about: '', + id: 'fakeChecklist1', + name: 'My Checklist', + owner_id: user.userId, + risks: [], + tags_goals: ['GMF:Known AI Goal:Translation'], + tags_methods: [], + tags_other: [], + }, + { + about: '', + id: 'fakeChecklist2', + name: "Somebody Else's Checklist", + owner_id: 'aFakeUserId', + risks: [], + tags_goals: [], + tags_methods: [], + tags_other: [], + }, + ], + }, + } + ); + + cy.conditionalIntercept('**/graphql', (req) => req.body.query.includes('GMF'), 'risks', { + errors: [testError], + }); + + cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); + + cy.visit(url); + + cy.get('[data-cy="toast"]').contains('Failure searching for risks').should('exist'); + }); + }); + maybeIt('Should show toast on error creating checklist', () => { - cy.conditionalIntercept( - '**/graphql', - (req) => req.body.operationName == 'insertChecklist', - 'insertChecklist', - { errors: [testError] } - ); + cy.query(usersQuery).then(({ data: { users } }) => { + const user = users.find((user) => user.adminData.email == Cypress.env('e2eUsername')); - cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'insertChecklist', + 'insertChecklist', + { errors: [testError] } + ); - cy.visit(url); + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'findChecklists', + 'findChecklists', + { + data: { + checklists: [ + { + about: '', + id: 'fakeChecklist1', + name: 'My Checklist', + owner_id: user.userId, + risks: [], + tags_goals: [], + tags_methods: [], + tags_other: [], + }, + { + about: '', + id: 'fakeChecklist2', + name: "Somebody Else's Checklist", + owner_id: 'aFakeUserId', + risks: [], + tags_goals: [], + tags_methods: [], + tags_other: [], + }, + ], + }, + } + ); + + cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); - cy.get(newChecklistButtonQuery).click(); + cy.visit(url); - cy.get('[data-cy="toast"]').contains('Could not create checklist.').should('exist'); + cy.get(newChecklistButtonQuery).click(); + + cy.get('[data-cy="toast"]').contains('Could not create checklist.').should('exist'); + }); }); }); From ee136b3e14df84aec3268179ac5f681c4f9f2af0 Mon Sep 17 00:00:00 2001 From: Luna McNulty Date: Fri, 29 Mar 2024 12:00:57 -0400 Subject: [PATCH 23/24] Change query name --- site/gatsby-site/page-creators/createLandingPage.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/site/gatsby-site/page-creators/createLandingPage.js b/site/gatsby-site/page-creators/createLandingPage.js index bbb0734bb8..cdc384686d 100644 --- a/site/gatsby-site/page-creators/createLandingPage.js +++ b/site/gatsby-site/page-creators/createLandingPage.js @@ -2,8 +2,7 @@ const path = require('path'); const createLandingPage = async (graphql, createPage) => { const result = await graphql(` - query WordCounts { - + query LandingPage { latestIncidents: allMongodbAiidprodIncidents( filter: { reports: { elemMatch: { is_incident_report: { eq: true } } } } sort: { reports: { epoch_date_submitted: DESC } } @@ -73,7 +72,7 @@ const createLandingPage = async (graphql, createPage) => { createPage({ path: '/', - component: path.resolve( './src/templates/landingPage.js'), + component: path.resolve('./src/templates/landingPage.js'), context: { latestIncidents, latestIncidentsReportNumbers, From 7533d2125a2991f48b870fa21390128482a9130c Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Mon, 1 Apr 2024 09:07:27 -0300 Subject: [PATCH 24/24] Add quiet to createLandingPage latests incidents --- site/gatsby-site/page-creators/createLandingPage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/gatsby-site/page-creators/createLandingPage.js b/site/gatsby-site/page-creators/createLandingPage.js index cdc384686d..aa81571bf0 100644 --- a/site/gatsby-site/page-creators/createLandingPage.js +++ b/site/gatsby-site/page-creators/createLandingPage.js @@ -4,7 +4,9 @@ const createLandingPage = async (graphql, createPage) => { const result = await graphql(` query LandingPage { latestIncidents: allMongodbAiidprodIncidents( - filter: { reports: { elemMatch: { is_incident_report: { eq: true } } } } + filter: { + reports: { elemMatch: { is_incident_report: { eq: true }, quiet: { in: [null, false] } } } + } sort: { reports: { epoch_date_submitted: DESC } } limit: 5 ) {