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..73009e13ae 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,118 @@ 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'); + }); + + 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.query(usersQuery).then(({ data: { users } }) => { + const user = users.find((user) => user.adminData.email == Cypress.env('e2eUsername')); + + cy.conditionalIntercept( + '**/graphql', + (req) => req.body.operationName == 'insertChecklist', + 'insertChecklist', + { errors: [testError] } + ); + + 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.visit(url); + + cy.get(newChecklistButtonQuery).click(); + + cy.get('[data-cy="toast"]').contains('Could not create checklist.').should('exist'); + }); + }); }); 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/cypress/e2e/integration/cite.cy.js b/site/gatsby-site/cypress/e2e/integration/cite.cy.js index 3699c3a130..66e740bd03 100644 --- a/site/gatsby-site/cypress/e2e/integration/cite.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/cite.cy.js @@ -199,7 +199,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), }); }); @@ -212,7 +212,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); diff --git a/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js b/site/gatsby-site/cypress/e2e/integration/citeEdit.cy.js index 33009e986b..dff0592ca8 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/cypress/e2e/integration/discover.cy.js b/site/gatsby-site/cypress/e2e/integration/discover.cy.js index acfcac0ec5..3e6f12f24c 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/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 } } } diff --git a/site/gatsby-site/gatsby-node.js b/site/gatsby-site/gatsby-node.js index 20442a1b7f..9bd99f9177 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'); @@ -76,6 +78,7 @@ exports.createPages = async ({ actions, graphql, reporter }) => { createBlogPages, createCitationPages, createWordCountsPages, + createLandingPage, createBackupsPage, createTaxonomyPages, createDownloadIndexPage, diff --git a/site/gatsby-site/i18n/locales/en/popovers.json b/site/gatsby-site/i18n/locales/en/popovers.json index 77e0e186e4..f3bebfbea1 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. 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/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/es/translation.json b/site/gatsby-site/i18n/locales/es/translation.json index 77290f5c55..eaaf4fcbbc 100644 --- a/site/gatsby-site/i18n/locales/es/translation.json +++ b/site/gatsby-site/i18n/locales/es/translation.json @@ -291,6 +291,8 @@ "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}} incidents": "Mostrando {{pageLength}} de {{allResultsCount}} incidentes", "Blog": "Blog", "AI News Digest": "Resumen de noticias de IA", "Remove Duplicate": "Eliminar Duplicado", 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/i18n/locales/fr/translation.json b/site/gatsby-site/i18n/locales/fr/translation.json index 6b3a2eddac..9f050b8dcc 100644 --- a/site/gatsby-site/i18n/locales/fr/translation.json +++ b/site/gatsby-site/i18n/locales/fr/translation.json @@ -279,6 +279,8 @@ "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}} incidents": "Affichage de {{pageLength}} sur {{allResultsCount}} incidents", "Blog": "Blog", "AI News Digest": "Résumé de l’Actualité sur l’IA", "Remove Duplicate": "Supprimer le doublon", diff --git a/site/gatsby-site/page-creators/createLandingPage.js b/site/gatsby-site/page-creators/createLandingPage.js new file mode 100644 index 0000000000..aa81571bf0 --- /dev/null +++ b/site/gatsby-site/page-creators/createLandingPage.js @@ -0,0 +1,86 @@ +const path = require('path'); + +const createLandingPage = async (graphql, createPage) => { + const result = await graphql(` + query LandingPage { + latestIncidents: allMongodbAiidprodIncidents( + filter: { + reports: { elemMatch: { is_incident_report: { eq: true }, quiet: { in: [null, false] } } } + } + sort: { reports: { epoch_date_submitted: DESC } } + limit: 5 + ) { + nodes { + title + incident_id + reports { + cloudinary_id + epoch_date_submitted + image_url + report_number + source_domain + text + title + url + } + } + } + + sponsors: allPrismicSponsor(sort: { data: { order: { text: ASC } } }) { + edges { + node { + data { + title { + text + richText + } + order { + text + } + language { + text + } + items { + name { + text + } + description { + richText + } + logo { + gatsbyImageData + url + } + link { + url + } + } + } + } + } + } + } + `); + + const latestIncidents = result.data.latestIncidents.nodes; + + const latestIncidentsReportNumbers = result.data.latestIncidents.nodes.map((node) => { + const sortedArray = node.reports.sort((a, b) => { + return a.epoch_date_submitted - b.epoch_date_submitted; + }); + + return sortedArray[0].report_number; + }); + + createPage({ + path: '/', + component: path.resolve('./src/templates/landingPage.js'), + context: { + latestIncidents, + latestIncidentsReportNumbers, + sponsors: result.data.sponsors.edges, + }, + }); +}; + +module.exports = createLandingPage; diff --git a/site/gatsby-site/page-creators/createWordCountsPage.js b/site/gatsby-site/page-creators/createWordCountsPage.js index 07cfeeeb91..ac0d5ee541 100644 --- a/site/gatsby-site/page-creators/createWordCountsPage.js +++ b/site/gatsby-site/page-creators/createWordCountsPage.js @@ -11,10 +11,6 @@ const PAGES_WITH_WORDCOUNT = [ path: '/summaries/wordcounts', componentPath: './src/templates/wordcounts.js', }, - { - path: '/', - componentPath: './src/templates/landingPage.js', - }, ]; const createWordCountsPage = async (graphql, createPage) => { @@ -25,59 +21,6 @@ const createWordCountsPage = async (graphql, createPage) => { text } } - latestReport: allMongodbAiidprodReports( - filter: { is_incident_report: { eq: true } } - sort: { epoch_date_submitted: DESC } - limit: 1 - ) { - nodes { - report_number - } - } - latestReports: allMongodbAiidprodIncidents( - filter: { reports: { elemMatch: { is_incident_report: { eq: true } } } } - sort: { reports: { epoch_date_submitted: DESC } } - limit: 5 - ) { - nodes { - reports { - report_number - } - } - } - sponsors: allPrismicSponsor(sort: { data: { order: { text: ASC } } }) { - edges { - node { - data { - title { - text - richText - } - order { - text - } - language { - text - } - items { - name { - text - } - description { - richText - } - logo { - gatsbyImageData - url - } - link { - url - } - } - } - } - } - } } `); @@ -125,14 +68,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; - }); - PAGES_WITH_WORDCOUNT.forEach((page) => { createPage({ path: page.path, @@ -141,12 +76,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, - sponsors: result.data.sponsors.edges, }, }); }); 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'); 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/components/forms/IncidentReportForm.js b/site/gatsby-site/src/components/forms/IncidentReportForm.js index ddb75ab2ca..0fea80a5da 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,21 @@ const IncidentReportForm = () => { {...TextInputGroupProps} /> +
+
+