From 61a8653fb169b0910dce1cffeeac6484e50fcc59 Mon Sep 17 00:00:00 2001 From: ebrockainq <112428189+ebrockainq@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:20:05 -0400 Subject: [PATCH 01/34] Site 3912 (#117) * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx --- .../direct/hisp/DocumentSelector.tsx | 28 +-------------- src/components/direct/hisp/TestCard.tsx | 1 - src/components/direct/hisp/XDRTestCard.tsx | 2 -- .../direct/test-by-criteria/ServerActions.ts | 34 +++++++++++++++++++ 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/components/direct/hisp/DocumentSelector.tsx b/src/components/direct/hisp/DocumentSelector.tsx index 1a426cda..8eafe2e6 100644 --- a/src/components/direct/hisp/DocumentSelector.tsx +++ b/src/components/direct/hisp/DocumentSelector.tsx @@ -13,6 +13,7 @@ import DialogActions from '@mui/material/DialogActions' import { SelectChangeEvent } from '@mui/material/Select' import Divider from '@mui/material/Divider' import Typography from '@mui/material/Typography' +import { fetchCCDADocuments } from '../test-by-criteria/ServerActions' import axios from 'axios' import XDR from './XDRTab' @@ -36,13 +37,6 @@ export interface Directory { files: FileDetail[] } -export interface Documents { - [key: string]: { - dirs: Directory[] - files: FileDetail[] - } -} - const DocumentSelector = ({ onConfirm, onClose, receive: receive }: DocumentSelectorProps) => { const [open, setOpen] = useState(true) const [documents, setDocuments] = useState({}) @@ -176,24 +170,4 @@ const DocumentSelector = ({ onConfirm, onClose, receive: receive }: DocumentSele ) } -export async function fetchCCDADocuments(receive: boolean): Promise { - const baseUrl = receive - ? process.env.CCDA_DOCUMENTS_XDR || 'https://ett.healthit.gov/ett/api/ccdadocuments?testCaseType=xdr' - : process.env.CCDA_DOCUMENTS || 'https://ett.healthit.gov/ett/api/ccdadocuments?testCaseType' - - const config = { - method: 'get', - url: baseUrl.toString(), - headers: { 'Content-Type': 'application/json' }, - } - - try { - const response = await axios(config) - return response.data - } catch (error) { - console.error('Error fetching CCDA documents:', error) - throw error - } -} - export default DocumentSelector diff --git a/src/components/direct/hisp/TestCard.tsx b/src/components/direct/hisp/TestCard.tsx index 3cfaf0e5..cb0a340f 100644 --- a/src/components/direct/hisp/TestCard.tsx +++ b/src/components/direct/hisp/TestCard.tsx @@ -468,7 +468,6 @@ const TestCard = ({ {test.criteria && manualValidationCriteria.includes(test.criteria) && - (formattedLogs.length > 0 || criteriaMet.includes('FALSE')) && (criteriaMet.includes('TRUE') || criteriaMet.includes('FALSE')) && ( {test.criteria && - testRequest && - (testRequest.length > 0 || criteriaMet.includes('FALSE') || criteriaMet.includes('ERROR')) && (criteriaMet.includes('TRUE') || criteriaMet.includes('FALSE') || criteriaMet.includes('ERROR') || diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index e0645261..51c92c43 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -28,6 +28,13 @@ interface APICallData { attachmentType?: string } +export interface Documents { + [key: string]: { + dirs: Directory[] + files: FileDetail[] + } +} + interface XDRAPICallData { ip_address?: string port?: string @@ -127,6 +134,7 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise { + const baseUrl = receive + ? process.env.CCDA_DOCUMENTS_XDR || 'https://ett.healthit.gov/ett/api/ccdadocuments?testCaseType=xdr' + : process.env.CCDA_DOCUMENTS || 'https://ett.healthit.gov/ett/api/ccdadocuments?testCaseType' + + const config = { + method: 'get', + url: baseUrl.toString(), + headers: { 'Content-Type': 'application/json' }, + } + + try { + const response = await axios(config) + return response.data + } catch (error) { + if (axios.isAxiosError(error) && error.response) { + console.error('API Error Response:', error.response.data) + console.error('Status:', error.response.status) + console.error('Headers:', error.response.headers) + } else { + console.error('Error') + } + throw error + } +} From e0e09f1ba5e2c960e10429c8d312b453f5188fb5 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Thu, 29 Aug 2024 22:51:24 -0400 Subject: [PATCH 02/34] Support SC sample document downloads (#118) * Support SC sample document downloads * Remove unused import * Remove forced formatting single quotes on CSS color prop --- src/components/c-cda/scorecard/ScorecardResultsDialog.tsx | 8 ++++++++ .../scorecard/next-steps/ScorecardIssueDescription.tsx | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx b/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx index 1d7d26f5..a645f92b 100644 --- a/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx +++ b/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx @@ -47,6 +47,14 @@ export default function ScorecardResultsDialog({ const handleDownloadSampleDocument = () => { console.log('Enter handleDownloadSampleDocument()') + // Create an anchor element since next.js can use that cleanly to download a static asset in the public folder + const locationPrefix = '/c-cda/scorecard/samples/' + const link = document.createElement('a') + link.setAttribute('download', '') // Force download vs nav, no filenmae set so uses default url name + link.href = locationPrefix + (json?.filename ? json.filename : 'UnknownFilename.xml') + document.body.appendChild(link) + link.click() + document.body.removeChild(link) } const isShowSampleDownloadButton: boolean = isTryMeDemo diff --git a/src/components/c-cda/scorecard/next-steps/ScorecardIssueDescription.tsx b/src/components/c-cda/scorecard/next-steps/ScorecardIssueDescription.tsx index 47e5acf5..f81a5295 100644 --- a/src/components/c-cda/scorecard/next-steps/ScorecardIssueDescription.tsx +++ b/src/components/c-cda/scorecard/next-steps/ScorecardIssueDescription.tsx @@ -1,6 +1,5 @@ -import React from 'react' -import Link from 'next/link' import { Box } from '@mui/material' +import React from 'react' interface ScorecardIssueDescriptionProps { description: string @@ -21,9 +20,9 @@ export const ScorecardIssueDescription: React.FC href = href.replace('scorecard', 'c-cda/scorecard') const text = curPart.replace(/<[^>]+>/g, '') // Remove HTML tags to extract text return ( - + {text} - + ) } From 5bc00010f5c1e5eaf27d8b9d2d06ea99560ce39f Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 3 Sep 2024 13:57:51 -0400 Subject: [PATCH 03/34] Update Scorecard demo labels --- src/components/c-cda/scorecard/ScorecardHome.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/c-cda/scorecard/ScorecardHome.tsx b/src/components/c-cda/scorecard/ScorecardHome.tsx index 48328f63..f737f97a 100644 --- a/src/components/c-cda/scorecard/ScorecardHome.tsx +++ b/src/components/c-cda/scorecard/ScorecardHome.tsx @@ -82,11 +82,11 @@ export default function ScorecardHome() { value: 'sampleWithVocabularyErrors.json', }, { - label: 'Sample with Missing Section Data', + label: 'Sample with Empty Sections', value: 'sampleWithEmptySections.json', }, { - label: 'Sample with Missing Sections and Errors', + label: 'Sample with Empty Sections and Errors', value: 'sampleWithEmptySectionsAndErrors.json', }, ] From c3fc404dd9e90ba569efa7923137b98e207f1b02 Mon Sep 17 00:00:00 2001 From: ebrockainq <112428189+ebrockainq@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:55:40 -0400 Subject: [PATCH 04/34] Site 3913 (#119) * Add H1 tab * Configure all links --- src/components/direct/DirectHome.tsx | 10 +- .../direct/send-direct/SendDirect.tsx | 33 ++++++- .../direct/test-by-criteria/B1Tab.tsx | 2 - .../direct/test-by-criteria/H1Tab.tsx | 98 ++++++++++++++----- 4 files changed, 108 insertions(+), 35 deletions(-) diff --git a/src/components/direct/DirectHome.tsx b/src/components/direct/DirectHome.tsx index af1853d5..4fd8b8bd 100644 --- a/src/components/direct/DirectHome.tsx +++ b/src/components/direct/DirectHome.tsx @@ -197,10 +197,12 @@ const DirectHome = () => { /> - +
+ +
{ + const [selectedTab, setSelectedTab] = useState('VERSION V1.3') + const sendDirectTabs: TabInputs[] = [ { tabName: 'VERSION V1.3', tabIndex: 0, tabPanel: }, { tabName: 'VERSION V1.2', tabIndex: 1, tabPanel: }, { tabName: 'Message Status', tabIndex: 2, tabPanel: }, ] + + useEffect(() => { + const hash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() + const tab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === hash) + setSelectedTab(tab ? tab.tabName : 'VERSION V1.3') + window.addEventListener('hashchange', () => { + const updatedHash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() + const updatedTab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === updatedHash) + setSelectedTab(updatedTab ? updatedTab.tabName : 'VERSION V1.3') + }) + + return () => { + window.removeEventListener('hashchange', () => { + const updatedHash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() + const updatedTab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === updatedHash) + setSelectedTab(updatedTab ? updatedTab.tabName : 'VERSION V1.3') + }) + } + }, []) + return ( <> - {/* Global Header */} + Direct , - + Direct Message , ]} heading={'Send Direct Message '} description={<>Send a Direct message from this tool to a HISP of your choosing. Need more text here} /> - {/* Main Content */} - + ) } diff --git a/src/components/direct/test-by-criteria/B1Tab.tsx b/src/components/direct/test-by-criteria/B1Tab.tsx index b6c4f10c..729bcf98 100644 --- a/src/components/direct/test-by-criteria/B1Tab.tsx +++ b/src/components/direct/test-by-criteria/B1Tab.tsx @@ -39,10 +39,8 @@ const B1Component = () => { const handleChange = (event: SelectChangeEvent) => { const newOption = event.target.value as string - // Set showTestCard to false to remove old cards setShowTestCard(false) - // Delay the re-render of test cards to allow the old cards to be removed completely setTimeout(() => { setOption(newOption) setShowTestCard(true) diff --git a/src/components/direct/test-by-criteria/H1Tab.tsx b/src/components/direct/test-by-criteria/H1Tab.tsx index a74316bd..efaf294d 100644 --- a/src/components/direct/test-by-criteria/H1Tab.tsx +++ b/src/components/direct/test-by-criteria/H1Tab.tsx @@ -10,24 +10,60 @@ import { CardContent, Container, } from '@mui/material' +import React, { useState } from 'react' import Profile from '../shared/Profile' -import SMTPTestCard from '../shared/SMTPTestCard' import palette from '@/styles/palette' -import React, { useEffect } from 'react' -import criteria from '@/assets/Criteria' +import TestCard from '../hisp/TestCard' +import testCases from '../../../assets/SMTPTestCases' const H1Component = () => { - const [option, setOption] = React.useState('') - const [showTestCard, setShowTestCard] = React.useState(false) - const handleChange = (event: SelectChangeEvent) => { - setOption(event.target.value as string) + const [option, setOption] = useState('') + const [hostname, setHostname] = useState('') + const [email, setEmail] = useState('') + const [username, setUsername] = useState('') + const [password, setPassword] = useState('') + const [tlsRequired, setTlsRequired] = useState(false) + + const dropdownOptions = [ + { + value: 'directHome', + label: 'Criteria (i) Direct Home - Certificates', + link: '/direct#certification-download', + }, + { + value: 'certificateDiscovery', + label: 'Criteria (i) Certificate Discovery / Hosting', + link: '/direct/dcdt#hosting', + }, + { value: 'registerDirect', label: 'Criteria (i) Register Direct', link: '/direct/register' }, + { value: 'sendDirectMessage', label: 'Criteria (i) Send Direct Message', link: '/direct/senddirect' }, + { + value: 'receiveMessageStatus', + label: 'Criteria (i) Receive - Message Status', + link: '/direct/senddirect#message-status', + }, + { value: 'deliveryNotifications', label: 'Criteria (ii) Delivery Notifications', testCard: true }, + ] + + const h1Criteria = testCases.tests.filter((test) => test.criteria?.includes('h1-1')) + + const selectedTestCases = () => { + switch (option) { + default: + return h1Criteria + } } - const h1CriteriaList = criteria.filter((c) => c.testList === "['h1']") - /* useEffect(() => { - if (option !== '') { - setShowTestCard(true) + + const handleChange = (event: SelectChangeEvent) => { + const selectedOption = dropdownOptions.find((option) => option.value === event.target.value) + if (selectedOption?.testCard) { + setOption(event.target.value) + } else if (selectedOption?.link) { + window.location.href = selectedOption.link } - }, [option]) */ + } + + const showTestCard = option === 'deliveryNotifications' return ( @@ -49,9 +85,9 @@ const H1Component = () => { label="Choose a sub category" onChange={handleChange} > - {h1CriteriaList.map((option) => ( - - {option.name} + {dropdownOptions.map((item) => ( + + {item.label} ))} @@ -61,21 +97,35 @@ const H1Component = () => { - + - {showTestCard && ( - - )} + {showTestCard && + selectedTestCases().map((test, i) => ( + + {' '} + + + ))} ) } + export default H1Component From 0d685d9966eb1a4ab77ff47a26e23ad8371c8d7e Mon Sep 17 00:00:00 2001 From: ebrockainq <112428189+ebrockainq@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:35:37 -0400 Subject: [PATCH 05/34] Site 3912 (#120) * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests --- .prettierrc | 2 +- src/components/direct/hisp/TestCard.tsx | 49 +++++-- src/components/direct/hisp/XDRTestCard.tsx | 130 ++++++++++++------ .../direct/test-by-criteria/ServerActions.ts | 8 +- 4 files changed, 139 insertions(+), 50 deletions(-) diff --git a/.prettierrc b/.prettierrc index 0ba8bf09..d0afb198 100644 --- a/.prettierrc +++ b/.prettierrc @@ -6,7 +6,7 @@ "experimentalTernaries": false, "singleQuote": true, "jsxSingleQuote": false, - "quoteProps": "consistent", + "quoteProps": "as-needed", "trailingComma": "es5", "singleAttributePerLine": false, "htmlWhitespaceSensitivity": "css", diff --git a/src/components/direct/hisp/TestCard.tsx b/src/components/direct/hisp/TestCard.tsx index cb0a340f..edf7b037 100644 --- a/src/components/direct/hisp/TestCard.tsx +++ b/src/components/direct/hisp/TestCard.tsx @@ -1,5 +1,4 @@ import DynamicTable from './DynamicTable' -import InfoIcon from '@mui/icons-material/Info' import _ from 'lodash' import React, { useState } from 'react' import DocumentSelector from './DocumentSelector' @@ -23,6 +22,7 @@ import { Checkbox, TextField, SelectChangeEvent, + Popover, } from '@mui/material' export type TestCaseFields = { @@ -100,6 +100,10 @@ const TestCard = ({ tlsRequired = false, receive, }: TestCardProps) => { + const [popoverAnchorEl, setPopoverAnchorEl] = useState(null) + const popoverOpen = Boolean(popoverAnchorEl) + const popoverId = popoverOpen ? 'ccda-file-required-popover' : undefined + const [autoCloseTimer, setAutoCloseTimer] = useState(null) const attachmentTypeTestIDs = [231, 331] const manualValidationCriteria = [ "['b1-5']", @@ -118,7 +122,6 @@ const TestCard = ({ const [isFinished, setIsFinished] = useState(false) const [apiError, setApiError] = useState(false) const [attachmentType, setAttachmentType] = useState('') - const apiUrl = process.env.CCDA_DOCUMENTS || 'https://ett.healthit.gov/ett/api/ccdadocuments' const handleDocumentConfirm = (selectedData: SelectedDocument) => { console.log('Confirmed Document', selectedData) @@ -194,9 +197,21 @@ const TestCard = ({ setFormData((prev) => ({ ...prev, [name]: value })) } + const handleClosePopover = () => { + if (autoCloseTimer) { + clearTimeout(autoCloseTimer) + setAutoCloseTimer(null) + } + setPopoverAnchorEl(null) + } + const handleRunTest = async () => { if (test.ccdaFileRequired && !documentDetails) { - alert('This test requires a CCDA document to be selected. Please select a document before running the test.') + setPopoverAnchorEl(document.activeElement as HTMLButtonElement) + const timer = setTimeout(() => { + handleClosePopover() + }, 2500) + setAutoCloseTimer(timer) } else { try { setIsLoading(true) @@ -294,7 +309,25 @@ const TestCard = ({ - + {} + + + This test requires a CCDA document to be selected. Please select a document before running the test. + + {showDetail ? ( <> @@ -353,10 +386,10 @@ const TestCard = ({ diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index 51c92c43..e733fb42 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -131,6 +131,7 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Fri, 6 Sep 2024 11:59:27 -0400 Subject: [PATCH 06/34] Add H1 Tab (#121) * Add H1 tab * Configure all links * Improve tab link handling --- .../direct/send-direct/SendDirect.tsx | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/components/direct/send-direct/SendDirect.tsx b/src/components/direct/send-direct/SendDirect.tsx index 018ceaa4..f627bad6 100644 --- a/src/components/direct/send-direct/SendDirect.tsx +++ b/src/components/direct/send-direct/SendDirect.tsx @@ -1,7 +1,6 @@ 'use client' import { useEffect, useState } from 'react' -import { useRouter } from 'next/router' import BannerBox from '@/components/shared/BannerBox' import Link from 'next/link' import styles from '@shared/styles.module.css' @@ -20,21 +19,17 @@ const SendDirect = () => { ] useEffect(() => { - const hash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() - const tab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === hash) - setSelectedTab(tab ? tab.tabName : 'VERSION V1.3') - window.addEventListener('hashchange', () => { - const updatedHash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() - const updatedTab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === updatedHash) - setSelectedTab(updatedTab ? updatedTab.tabName : 'VERSION V1.3') - }) + const handleRouteChange = () => { + const hash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() + const tab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === hash) + setSelectedTab(tab ? tab.tabName : 'VERSION V1.3') + } + + handleRouteChange() + window.addEventListener('hashchange', handleRouteChange) return () => { - window.removeEventListener('hashchange', () => { - const updatedHash = window.location.hash.replace('#', '').replace(/-/g, ' ').toLowerCase() - const updatedTab = sendDirectTabs.find((t) => t.tabName.toLowerCase() === updatedHash) - setSelectedTab(updatedTab ? updatedTab.tabName : 'VERSION V1.3') - }) + window.removeEventListener('hashchange', handleRouteChange) } }, []) @@ -49,8 +44,8 @@ const SendDirect = () => { Direct Message , ]} - heading={'Send Direct Message '} - description={<>Send a Direct message from this tool to a HISP of your choosing. Need more text here} + heading={'Send Direct Message'} + description="Send a Direct message from this tool to a HISP of your choosing. Need more text here" /> From b3e4afaf6d8805424b91cef7f48fe3ea83e938b0 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 9 Sep 2024 13:52:51 -0400 Subject: [PATCH 07/34] Implementation for Register Direct Email (#122) * feat(direct): register direct email addresses * fix: added email validation * fix(direct): added error handling in preparation for implementation of Error Boundry * fix(direct): added loading feedback on page components * fix(direct): added linear progress user feedback * fix(direct): added success and error user feedback --- src/app/api/direct/register/[id]/route.ts | 150 +++++++ src/app/api/direct/register/route.ts | 111 +++++ src/components/direct/register/Register.tsx | 397 +++++++++++++++--- .../direct/shared/AlertSnackbar.tsx | 27 ++ 4 files changed, 638 insertions(+), 47 deletions(-) create mode 100644 src/app/api/direct/register/[id]/route.ts create mode 100644 src/app/api/direct/register/route.ts create mode 100644 src/components/direct/shared/AlertSnackbar.tsx diff --git a/src/app/api/direct/register/[id]/route.ts b/src/app/api/direct/register/[id]/route.ts new file mode 100644 index 00000000..2bcf3aa2 --- /dev/null +++ b/src/app/api/direct/register/[id]/route.ts @@ -0,0 +1,150 @@ +import { NextRequest, NextResponse } from 'next/server' +import _ from 'lodash' +import { authOptions } from '@/lib/auth' +import { getServerSession } from 'next-auth' + +const ETT_API_URL = process.env.ETT_API_URL + +export async function GET(req: NextRequest, { params }: { params: { id: string } }) { + const ettAPIUrl = `${ETT_API_URL}/registration/contact/${params.id}` + try { + const response = await fetch(ettAPIUrl, { + method: 'GET', + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json( + data.map((x: { result: string }) => x.result), + { status: 200 } + ) + } catch (error: unknown) { + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + error: error.message || 'An error occurred', + }, + { status: 500 } + ) + } + } +} + +export async function POST(request: NextRequest, { params }: { params: { id: string } }) { + const body = await request.json() + const emailAddressToRegister = body.contactEmailAddressToAdd?.toLowerCase() + if (_.isEmpty(emailAddressToRegister)) { + return NextResponse.json( + { + success: false, + error: 'Email address is required', + }, + { status: 500 } + ) + } + const ettAPIUrl = `${ETT_API_URL}/registration/contact/${params.id}` + try { + const response = await fetch(ettAPIUrl, { + method: 'POST', + body: emailAddressToRegister, + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json(data, { status: 200 }) + } catch (error: unknown) { + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + error: error.message || 'An error occurred', + }, + { status: 500 } + ) + } + } +} + +export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) { + const session = await getServerSession(authOptions) + const jsessionid = session?.user?.jsessionid ?? '' + if (request.nextUrl.searchParams.has('contact')) { + const contactEmailAddress = request.nextUrl.searchParams.get('contact') + const ettAPIUrl = `${ETT_API_URL}/registration/contact/${params.id}/${contactEmailAddress}` + try { + const response = await fetch(ettAPIUrl, { + method: 'DELETE', + headers: session + ? { 'Content-Type': 'application/json', Cookie: `JSESSIONID=${jsessionid}` } + : { 'Content-Type': 'application/json' }, + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json(data, { status: 200 }) + } catch (error: unknown) { + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + error: error.message || 'An error occurred', + }, + { status: 500 } + ) + } + } + } else { + try { + const ettAPIUrl = `${ETT_API_URL}/registration/direct/${params.id}` + const response = await fetch(ettAPIUrl, { + method: 'DELETE', + headers: session + ? { 'Content-Type': 'application/json', Cookie: `JSESSIONID=${jsessionid}` } + : { 'Content-Type': 'application/json' }, + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json(data, { status: 200 }) + } catch (error: unknown) { + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + error: error.message || 'An error occurred', + }, + { status: 500 } + ) + } + } + } +} diff --git a/src/app/api/direct/register/route.ts b/src/app/api/direct/register/route.ts new file mode 100644 index 00000000..4cbd25a8 --- /dev/null +++ b/src/app/api/direct/register/route.ts @@ -0,0 +1,111 @@ +import { authOptions } from '@/lib/auth' +import { getServerSession } from 'next-auth' +import { NextRequest, NextResponse } from 'next/server' +import _ from 'lodash' +const ETT_API_URL = process.env.ETT_API_URL + +const getETTProperties = async () => { + const response = await fetch(`${ETT_API_URL}/properties`) + return await response.json() +} +async function isEmailDomainValid(emailAddressToRegister: string): Promise { + const emailDomain = emailAddressToRegister.split('@')[1] + const invalidDomains = await getInvalidDomains() + if (invalidDomains.includes(emailDomain)) { + return false + } + return true +} + +export async function POST(request: NextRequest) { + const session = await getServerSession(authOptions) + if (!session) { + return NextResponse.json(false) + } + try { + const jsessionid = session?.user?.jsessionid ?? '' + const body = await request.json() + const emailAddressToRegister = body.directEmailAddress?.toLowerCase() + console.log(`DEBUG: emailAddressToRegister: ${emailAddressToRegister}`) + if (_.isEmpty(emailAddressToRegister)) { + return NextResponse.json( + { + success: false, + error: 'Email address is required', + }, + { status: 500 } + ) + } + const isValidEmailDomain = await isEmailDomainValid(emailAddressToRegister) + if (!isValidEmailDomain) { + return NextResponse.json( + { + success: false, + error: 'Invalid email domain', + }, + { status: 500 } + ) + } + const response = await fetch(`${ETT_API_URL}/registration/direct`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: `JSESSIONID=${jsessionid}`, + }, + body: emailAddressToRegister, + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json(data, { status: 200 }) + } catch (error: unknown) { + if (error instanceof Error) { + return NextResponse.json( + { + success: false, + error: error.message || 'An error occurred', + }, + { status: 500 } + ) + } + } +} + +export async function GET() { + const session = await getServerSession(authOptions) + const jsessionid = session?.user?.jsessionid ?? '' + const response = await fetch(`${ETT_API_URL}/registration/direct`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Cookie: `JSESSIONID=${jsessionid}`, + }, + }) + const data = await response.json() + if (!response.ok) { + return NextResponse.json( + { + success: false, + error: data, + }, + { status: 500 } + ) + } + return NextResponse.json(data, { status: 200 }) +} + +async function getInvalidDomains() { + const properties = await getETTProperties() + const dcdt2014Domain = `${properties?.dcdt2014domain}` + const dcdt2015Domain = `${properties?.dcdt2015domain}` + const dcdtDomain = 'dcdt31' + const invalidDomains = [dcdt2014Domain, dcdt2015Domain, dcdtDomain] + return invalidDomains +} diff --git a/src/components/direct/register/Register.tsx b/src/components/direct/register/Register.tsx index 7e6567a8..99cde11a 100644 --- a/src/components/direct/register/Register.tsx +++ b/src/components/direct/register/Register.tsx @@ -1,9 +1,220 @@ +'use client' import Link from 'next/link' import BannerBox from '../../shared/BannerBox' import styles from '../../shared/styles.module.css' -import { Box, Button, Container, TextField, Typography } from '@mui/material' +import { + Box, + Button, + Container, + Grid, + IconButton, + InputAdornment, + List, + ListItem, + ListItemSecondaryAction, + ListItemText, + MenuItem, + Select, + SelectChangeEvent, + TextField, + LinearProgress, +} from '@mui/material' +import AddBoxIcon from '@mui/icons-material/AddBox' +import DeleteIcon from '@mui/icons-material/Delete' import palette from '@/styles/palette' +import _ from 'lodash' +import { MutableRefObject, useEffect, useRef, useState } from 'react' +import { useSession } from 'next-auth/react' +import ErrorDisplayCard from '@/components/c-cda/validation/results/ErrorDisplay' +import AlertSnackbar from '../shared/AlertSnackbar' + const Register = () => { + const [selectedDirectEmailAddress, setSelectedDirectEmailAddress] = useState('') + const [directEmailAddressList, setDirectEmailAddressList] = useState([]) + const [contactEmailAdressList, setContactEmailAddressList] = useState([]) + const [directEmailFormatError, setDirectEmailFormatError] = useState(false) + const [contactEmailFormatError, setContactEmailFormatError] = useState(false) + const [isFetchingLoggedInUsersDirectEmailAdresses, setIsFetchingLoggedInUsersDirectEmailAdresses] = useState(false) + const [isAddingDirectAddress, setIsAddingDirectAddress] = useState(false) + const [isAddingContactAddress, setIsAddingContactAddress] = useState(false) + const [isDeletingDirectEmailGroup, setIsDeletingDirectEmailGroup] = useState(false) + const [isDeletingContactAddress, setIsDeletingContactAddress] = useState(false) + const [isFetchingContactAddresses, setIsFetchingContactAddresses] = useState(false) + const [errorMessage, setErrorMessage] = useState('') + const [successMessage, setSuccessMessage] = useState('') + const directEmailAddressRef = useRef() as MutableRefObject + const contactEmailAddressRef = useRef() as MutableRefObject + const { data: session, status } = useSession() + + useEffect(() => { + async function fetchLoggedInUsersDirectEmailAdresses() { + const response = await fetch('/api/direct/register', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await response.json() + if (!response.ok) { + setErrorMessage(data.error.message) + throw new Error(`Error fetching direct email addresseses for logged in user: ${data.error.message}`) + } + setDirectEmailAddressList(data) + const contactList = getContactEmailAddressesListForSelectedDirectAddress(data[0]) + setContactEmailAddressList(await contactList) + setSelectedDirectEmailAddress(data[0]) + } + if (status === 'authenticated') { + setIsFetchingLoggedInUsersDirectEmailAdresses(true) + fetchLoggedInUsersDirectEmailAdresses().then(() => { + setIsFetchingLoggedInUsersDirectEmailAdresses(false) + }) + } + }, [session, status]) + + const addDirectAddress = async () => { + const directEmailAddressToAdd = (directEmailAddressRef.current as unknown as HTMLInputElement)?.value || '' + directEmailAddressRef.current.value = '' + setIsAddingDirectAddress(true) + const response = await fetch('/api/direct/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ directEmailAddress: directEmailAddressToAdd }), + }) + const data = await response.json() + if (!response.ok) { + setIsAddingDirectAddress(false) + setErrorMessage(data.error.message) + throw new Error(`Error adding direct address ${directEmailAddressToAdd}: ${data.error.message}`) + } + setDirectEmailAddressList([...directEmailAddressList, directEmailAddressToAdd]) + const contactList = getContactEmailAddressesListForSelectedDirectAddress(directEmailAddressToAdd) + setContactEmailAddressList(await contactList) + setSelectedDirectEmailAddress(directEmailAddressToAdd) + setIsAddingDirectAddress(false) + setSuccessMessage('Direct email address added.') + } + + const addContactAddress = async () => { + const contactEmailAddressToAdd = (contactEmailAddressRef.current as unknown as HTMLInputElement)?.value || '' + contactEmailAddressRef.current.value = '' + setIsAddingContactAddress(true) + const response = await fetch(`/api/direct/register/${selectedDirectEmailAddress}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ contactEmailAddressToAdd }), + }) + const data = await response.json() + if (!response.ok) { + setIsAddingContactAddress(false) + setErrorMessage(data.error.message) + throw new Error(`Error adding contact address ${contactEmailAddressToAdd}: ${data.error.message}`) + } + if (data === true) { + setContactEmailAddressList([...contactEmailAdressList, contactEmailAddressToAdd]) + setIsAddingContactAddress(false) + setSuccessMessage(`Contact email address added to ${selectedDirectEmailAddress}.`) + } + } + + const deleteDirectEmailGroup = async () => { + setIsDeletingDirectEmailGroup(true) + const response = await fetch(`/api/direct/register/${selectedDirectEmailAddress}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await response.json() + if (!response.ok) { + setIsDeletingDirectEmailGroup(false) + setErrorMessage(data.error.message) + throw new Error(`Deleting direct group ${selectedDirectEmailAddress}: ${data.error.message}`) + } + if (data === true) { + setDirectEmailAddressList(directEmailAddressList.filter((x) => x !== selectedDirectEmailAddress)) + setContactEmailAddressList([]) + if (!_.isEmpty(directEmailAddressList)) { + setSelectedDirectEmailAddress(directEmailAddressList[0]) + } + } + setIsDeletingDirectEmailGroup(false) + setSuccessMessage('Direct email address group deleted.') + } + + const deleteContactAddress = async (contactEmailAddress: string) => { + setIsDeletingContactAddress(true) + const response = await fetch(`/api/direct/register/${selectedDirectEmailAddress}?contact=${contactEmailAddress}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await response.json() + if (!response.ok) { + setIsDeletingContactAddress(false) + setErrorMessage(data.error.message) + throw new Error(`Error deleting contact email addresses ${contactEmailAddress}: ${data.error.message}`) + } + if (data === true) { + setContactEmailAddressList(contactEmailAdressList.filter((x) => x !== contactEmailAddress)) + } + setIsDeletingContactAddress(false) + setSuccessMessage(`Contact email address deleted from ${selectedDirectEmailAddress}.`) + } + + const handleDirectEmailAddressChange = async (event: SelectChangeEvent) => { + const selectedDirectEmailAddress = event.target.value as string + setSelectedDirectEmailAddress(selectedDirectEmailAddress) + setIsFetchingContactAddresses(true) + const contactList = await getContactEmailAddressesListForSelectedDirectAddress(selectedDirectEmailAddress) + setContactEmailAddressList(contactList) + setIsFetchingContactAddresses(false) + } + + const getContactEmailAddressesListForSelectedDirectAddress = async (directEmailAddress: string) => { + const response = await fetch(`/api/direct/register/${directEmailAddress}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await response.json() + if (!response.ok) { + setErrorMessage(data.error.message) + throw new Error(`Error fetching contact email addresses ${directEmailAddress}: ${data.error.message}`) + } + return data || [] + } + + const validateDirectEmailFormat: React.ChangeEventHandler = (e) => { + if (!_.isEmpty(e.target.value)) { + if (e.target.validity.valid) { + setDirectEmailFormatError(false) + } else { + setDirectEmailFormatError(true) + } + } else { + setDirectEmailFormatError(false) + } + } + + const validateContactEmailFormat: React.ChangeEventHandler = (e) => { + if (!_.isEmpty(e.target.value)) { + if (e.target.validity.valid) { + setContactEmailFormatError(false) + } else { + setContactEmailFormatError(true) + } + } else { + setContactEmailFormatError(false) + } + } + return ( <> {/* Global Header */} @@ -29,52 +240,144 @@ const Register = () => { /> {/* Main Content */} - - - - Enter an email address to get started then select add. - - - - - - - Manage Your Contact Email Addresses - - {' '} - - - - - - {/* To-Do: List email addressess and delete email addresses with funtionality*/} - - Group Members - - - - + {isFetchingLoggedInUsersDirectEmailAdresses ? ( + + ) : ( + + + {isAddingDirectAddress || isDeletingDirectEmailGroup ? ( + + ) : ( + + <> + + + addDirectAddress()} + disabled={directEmailFormatError || directEmailAddressRef.current?.value === ''} + > + + + + ), + }} + /> + + {!_.isEmpty(directEmailAddressList) && ( + + + + + + + )} + + + )} + {!_.isEmpty(directEmailAddressList) && ( + <> + {isAddingContactAddress || isDeletingContactAddress || isFetchingContactAddresses ? ( + + ) : ( + + <> + + + addContactAddress()} + disabled={contactEmailFormatError || contactEmailAddressRef.current?.value === ''} + > + + + + ), + }} + /> + + + {!_.isEmpty(contactEmailAdressList) && ( + + {contactEmailAdressList.map((x) => { + return ( + + + + deleteContactAddress(x)} + disabled={contactEmailFormatError} + > + + + + + ) + })} + + )} + + + + )} + + )} + + {!_.isEmpty(errorMessage) && ( + setErrorMessage('')} response={{ error: errorMessage }} /> + )} + {!_.isEmpty(successMessage) && ( + setSuccessMessage('')} + /> + )} + + )} ) } diff --git a/src/components/direct/shared/AlertSnackbar.tsx b/src/components/direct/shared/AlertSnackbar.tsx new file mode 100644 index 00000000..de20380f --- /dev/null +++ b/src/components/direct/shared/AlertSnackbar.tsx @@ -0,0 +1,27 @@ +import { Snackbar, Alert } from '@mui/material' + +const AlertSnackbar = ({ + message, + severity, + open, + onClose, +}: { + message: string + severity: 'success' | 'error' | 'warning' | 'info' + open: boolean + onClose: () => void +}) => { + return ( + + + {message} + + + ) +} +export default AlertSnackbar From 23ee49ebc9f472535103a167095a39b67b26f48b Mon Sep 17 00:00:00 2001 From: akanuri9 <118755941+akanuri9@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:55:17 -0400 Subject: [PATCH 08/34] Debug create-deploy-build.yml --- .github/workflows/create-deploy-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/create-deploy-build.yml b/.github/workflows/create-deploy-build.yml index 5c6fca68..7e4d5dfe 100644 --- a/.github/workflows/create-deploy-build.yml +++ b/.github/workflows/create-deploy-build.yml @@ -43,12 +43,15 @@ jobs: run: npm run build - name: Create Build Artifact run: | + ls -la + ls -la ./.next mkdir ./build cp ./.next -pR ./build/.next cp ./next.config.js ./build/next.config.js cp ./public -pR ./build/public cp ./package.json ./build/package.json cp ./package-lock.json ./build/package-lock.json + ls -la ./build - name: Upload build folder uses: actions/upload-artifact@v4 with: From 94f9533993a4ad2402c542aa3c9c62b9dd510cdc Mon Sep 17 00:00:00 2001 From: akanuri9 <118755941+akanuri9@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:12:29 -0400 Subject: [PATCH 09/34] Update create-deploy-build.yml --- .github/workflows/create-deploy-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-deploy-build.yml b/.github/workflows/create-deploy-build.yml index 7e4d5dfe..2f6ff906 100644 --- a/.github/workflows/create-deploy-build.yml +++ b/.github/workflows/create-deploy-build.yml @@ -44,7 +44,6 @@ jobs: - name: Create Build Artifact run: | ls -la - ls -la ./.next mkdir ./build cp ./.next -pR ./build/.next cp ./next.config.js ./build/next.config.js @@ -56,6 +55,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: BuildArtifact + include-hidden-files: true path: ./build retention-days: 7 deploy: From d39fb6e4f4bda92fe37fb75ce0b61720201ed100 Mon Sep 17 00:00:00 2001 From: ebrockainq <112428189+ebrockainq@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:24:39 -0400 Subject: [PATCH 10/34] Add test by criteria B1 tab * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling --- src/components/direct/hisp/XDRTestCard.tsx | 2 +- src/components/direct/test-by-criteria/ServerActions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/direct/hisp/XDRTestCard.tsx b/src/components/direct/hisp/XDRTestCard.tsx index 255918b2..6b003c10 100644 --- a/src/components/direct/hisp/XDRTestCard.tsx +++ b/src/components/direct/hisp/XDRTestCard.tsx @@ -456,7 +456,7 @@ const TestCard = ({ test, receive }: TestCardProps) => { renderMoreInfo() ) : showLogs ? ( - Log for XDR Test {test.name} + Log for {test.name} + setSaveReportError('')} + response={{ error: saveReportError }} + /> {isShowSampleDownloadButton && ( - - - )} - - - )} - {!_.isEmpty(directEmailAddressList) && ( - <> - {isAddingContactAddress || isDeletingContactAddress || isFetchingContactAddresses ? ( + + + ) : ( + <> + {isFetchingLoggedInUsersDirectEmailAdresses ? ( + + ) : ( + + + {isAddingDirectAddress || isDeletingDirectEmailGroup ? ( ) : ( <> + + Add a valid Direct email address to get started. + { addContactAddress()} - disabled={contactEmailFormatError || contactEmailAddressRef.current?.value === ''} + onClick={() => addDirectAddress()} + disabled={directEmailFormatError || directEmailAddressRef.current?.value === ''} + title="Add a Direct email address to manage its contacts." > @@ -336,47 +288,125 @@ const Register = () => { }} /> - - {!_.isEmpty(contactEmailAdressList) && ( - - {contactEmailAdressList.map((x) => { + {!_.isEmpty(directEmailAddressList) && ( + + + + + + + )} )} - - )} - - {!_.isEmpty(errorMessage) && ( - setErrorMessage('')} response={{ error: errorMessage }} /> - )} - {!_.isEmpty(successMessage) && ( - setSuccessMessage('')} - /> + {!_.isEmpty(directEmailAddressList) && ( + <> + {isAddingContactAddress || isDeletingContactAddress || isFetchingContactAddresses ? ( + + ) : ( + + <> + + + Manage contacts for selected email + + + addContactAddress()} + title="Add a contact email address for the selected Direct email address." + disabled={contactEmailFormatError || contactEmailAddressRef.current?.value === ''} + > + + + + ), + }} + /> + + + {!_.isEmpty(contactEmailAdressList) && ( + + {contactEmailAdressList.map((x) => { + return ( + + + + deleteContactAddress(x)} + disabled={contactEmailFormatError} + color="error" + title="Delete this contact email address." + > + + + + + ) + })} + + )} + + + + )} + + )} + + {!_.isEmpty(errorMessage) && ( + setErrorMessage('')} + response={{ error: errorMessage }} + /> + )} + {!_.isEmpty(successMessage) && ( + setSuccessMessage('')} + /> + )} + )} - + )} ) diff --git a/src/components/shared/PageAlertBox.tsx b/src/components/shared/PageAlertBox.tsx new file mode 100644 index 00000000..4e30d394 --- /dev/null +++ b/src/components/shared/PageAlertBox.tsx @@ -0,0 +1,35 @@ +import palette from '@/styles/palette' +import { Box, Typography } from '@mui/material' +import ErrorIcon from '@mui/icons-material/Error' + +type PageAlertBoxProps = { + message: string +} + +const PageAlertBox = (props: PageAlertBoxProps) => { + const message = props.message || null + return ( + + + + {message} + + + ) +} + +export default PageAlertBox From 76daaf1a026b1e03e2d4416a48ab5204df83fe18 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Fri, 13 Sep 2024 16:37:31 -0400 Subject: [PATCH 13/34] Add savescorecard API to .env as temp workaround --- .env | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 6f1a09a2..c27226af 100644 --- a/.env +++ b/.env @@ -5,4 +5,7 @@ NEXT_PUBLIC_CCDA_VALIDATOR_CURES_V1_SENDER_URL=https://api.github.com/repos/onc- NEXT_PUBLIC_CCDA_VALIDATOR_CURES_V1_RECEIVER_URL=https://api.github.com/repos/onc-healthit/2015-edition-cures-update-data/contents/Cures Update Receiver SUT Test Data NEXT_PUBLIC_CCDA_VALIDATOR_CURES_DOWNLOAD_URL=https://codeload.github.com/onc-healthit/2015-edition-cures-update-data/zip/master NEXT_PUBLIC_RELEASE_VERSION_URL=https://raw.githubusercontent.com/onc-healthit/site-content/master/site-ui-4/version.md -NEXT_PUBLIC_RELEASE_DATE_URL=https://raw.githubusercontent.com/onc-healthit/site-content/master/site-ui-4/release-date.md \ No newline at end of file +NEXT_PUBLIC_RELEASE_DATE_URL=https://raw.githubusercontent.com/onc-healthit/site-content/master/site-ui-4/release-date.md + +# TODO: Allow for this to be dynamic per environment file and delete from here. See: https://phase.dev/blog/nextjs-public-runtime-variables/ +NEXT_PUBLIC_SCORECARD_SAVESCORECARDSERVICE_API=https://ccda.healthit.gov/scorecard/savescorecardservice From f1294c390938dd9a8eb720bc40dfd9c879b8ac95 Mon Sep 17 00:00:00 2001 From: akanuri9 <118755941+akanuri9@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:22:34 -0400 Subject: [PATCH 14/34] Add Direct XDM Validator (#125) * XDM Validator Implementation * Update description for XDM * Add generic error constant and update Axios version --- package-lock.json | 8 +- package.json | 2 +- src/app/direct/validate/page.tsx | 7 -- src/app/direct/xdm/page.tsx | 7 ++ src/components/c-cda/validation/actions.ts | 5 +- src/components/direct/DirectHome.tsx | 6 +- src/components/direct/dcdt/actions.ts | 5 +- .../direct/transport-tool/actions.ts | 11 +- .../DirectMessage.tsx} | 24 +--- .../direct/validators/ValidatorHome.tsx | 36 ++++++ .../direct/validators/XDMResults.tsx | 103 ++++++++++++++++++ .../direct/validators/XDMValidator.tsx | 73 +++++++++++++ src/components/direct/validators/actions.ts | 48 ++++++++ src/constants/errorConstants.ts | 1 + 14 files changed, 291 insertions(+), 45 deletions(-) delete mode 100644 src/app/direct/validate/page.tsx create mode 100644 src/app/direct/xdm/page.tsx rename src/components/direct/{validate/Validate.tsx => validators/DirectMessage.tsx} (75%) create mode 100644 src/components/direct/validators/ValidatorHome.tsx create mode 100644 src/components/direct/validators/XDMResults.tsx create mode 100644 src/components/direct/validators/XDMValidator.tsx create mode 100644 src/components/direct/validators/actions.ts create mode 100644 src/constants/errorConstants.ts diff --git a/package-lock.json b/package-lock.json index f52e85d0..0ea236bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@mui/material": "^5.16.0", "@mui/material-nextjs": "^5.15.5", "@mui/x-data-grid": "^7.12.1", - "axios": "^1.6.8", + "axios": "^1.7.7", "dompurify": "^3.1.2", "lodash": "^4.17.21", "marked": "^12.0.2", @@ -1971,9 +1971,9 @@ } }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 14d17d92..42686678 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@mui/material": "^5.16.0", "@mui/material-nextjs": "^5.15.5", "@mui/x-data-grid": "^7.12.1", - "axios": "^1.6.8", + "axios": "^1.7.7", "dompurify": "^3.1.2", "lodash": "^4.17.21", "marked": "^12.0.2", diff --git a/src/app/direct/validate/page.tsx b/src/app/direct/validate/page.tsx deleted file mode 100644 index 4f11e2d1..00000000 --- a/src/app/direct/validate/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import ValidateComponent from '@/components/direct/validate/Validate' - -const Validate = () => { - return -} - -export default Validate diff --git a/src/app/direct/xdm/page.tsx b/src/app/direct/xdm/page.tsx new file mode 100644 index 00000000..841d934c --- /dev/null +++ b/src/app/direct/xdm/page.tsx @@ -0,0 +1,7 @@ +import XDMComponent from '@/components/direct/validators/XDMValidator' + +const Validate = () => { + return +} + +export default Validate diff --git a/src/components/c-cda/validation/actions.ts b/src/components/c-cda/validation/actions.ts index dab608f1..dc8495a8 100644 --- a/src/components/c-cda/validation/actions.ts +++ b/src/components/c-cda/validation/actions.ts @@ -1,6 +1,7 @@ 'use server' import https from 'https' import axios from 'axios' +import { GENERIC_ERROR_MESSAGE } from '@/constants/errorConstants' //Get Scenario file options export async function getScenarioOptions(criteriaUrl: string) { const res = await fetch(criteriaUrl) @@ -77,7 +78,7 @@ export async function postToValidatorV3(prevState: object | undefined, formData: console.error(error.response?.data) return { response: { - error: 'Validator Service had an issue, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -126,7 +127,7 @@ export async function postToValidatorV1(prevState: object | undefined, formData: console.error(error.response?.data) return { response: { - error: 'Validator Service had an issue, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } diff --git a/src/components/direct/DirectHome.tsx b/src/components/direct/DirectHome.tsx index 4fd8b8bd..b8289683 100644 --- a/src/components/direct/DirectHome.tsx +++ b/src/components/direct/DirectHome.tsx @@ -162,10 +162,10 @@ const DirectHome = () => { cardWidthPercent={100} /> } cardWidthPercent={100} /> diff --git a/src/components/direct/dcdt/actions.ts b/src/components/direct/dcdt/actions.ts index 6cdfd9e4..d65e1257 100644 --- a/src/components/direct/dcdt/actions.ts +++ b/src/components/direct/dcdt/actions.ts @@ -1,4 +1,5 @@ 'use server' +import { GENERIC_ERROR_MESSAGE } from '@/constants/errorConstants' import axios from 'axios' export async function handleSubmitHosting(prevState: object | undefined, formData: FormData) { const hostingApi = process.env.DCDT_HOSTING_API @@ -27,7 +28,7 @@ export async function handleSubmitHosting(prevState: object | undefined, formDat console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -65,7 +66,7 @@ export async function handleSubmitDiscover(prevState: object | undefined, formDa console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } diff --git a/src/components/direct/transport-tool/actions.ts b/src/components/direct/transport-tool/actions.ts index 8e93f8df..feb03b5a 100644 --- a/src/components/direct/transport-tool/actions.ts +++ b/src/components/direct/transport-tool/actions.ts @@ -1,4 +1,5 @@ 'use server' +import { GENERIC_ERROR_MESSAGE } from '@/constants/errorConstants' import axios from 'axios' export async function getSampleCCDAFiles(sampleCCDAFilesEndpoint: string) { @@ -29,7 +30,7 @@ export async function handleSendMessageWithAttachmentFilepath(prevState: object console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -60,7 +61,7 @@ export async function handleSendMessageWithAttachmentFile(prevState: object | un console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -91,7 +92,7 @@ export async function handleSearchSITEInbox(prevState: object | undefined, formD console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -120,7 +121,7 @@ export async function handleSearchHHSInbox(prevState: object | undefined, formDa console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } @@ -151,7 +152,7 @@ export async function handleUploadTrustAnchor(prevState: object | undefined, for console.error(error.response?.data) return { response: { - error: 'They was an error connecting to the backend, Please try again later!', + error: GENERIC_ERROR_MESSAGE, errorStatus: error.response?.status, }, } diff --git a/src/components/direct/validate/Validate.tsx b/src/components/direct/validators/DirectMessage.tsx similarity index 75% rename from src/components/direct/validate/Validate.tsx rename to src/components/direct/validators/DirectMessage.tsx index e49f5721..10a9ef33 100644 --- a/src/components/direct/validate/Validate.tsx +++ b/src/components/direct/validators/DirectMessage.tsx @@ -1,4 +1,4 @@ -'use client' +/* 'use client' import BannerBox from '@/components/shared/BannerBox' import styles from '@/components/shared/styles.module.css' import { Box, Button, Container, Stack, TextField, Tooltip, Typography } from '@mui/material' @@ -10,26 +10,7 @@ import palette from '@/styles/palette' const Validate = () => { return ( <> - {/* Global Header */} - - Direct - , - - Validate - , - ]} - heading={'Validate Direct Message'} - description={ - <> - Ensure that your direct messaging system complies with legal and regulatory requirements, including HIPAA - regulations in the United States. Stay updated with any changes in the regulations that might impact the way - direct messages need to be handled and secured. - - } - /> - {/* Main Content */} + @@ -87,3 +68,4 @@ const Validate = () => { } export default Validate + */ diff --git a/src/components/direct/validators/ValidatorHome.tsx b/src/components/direct/validators/ValidatorHome.tsx new file mode 100644 index 00000000..487702bf --- /dev/null +++ b/src/components/direct/validators/ValidatorHome.tsx @@ -0,0 +1,36 @@ +/* 'use client' +import BannerBox from '@/components/shared/BannerBox' +import Link from 'next/link' +import styles from '@shared/styles.module.css' +import TabsComponent, { TabInputs } from '@/components/shared/TabsComponent' +import DirectMessage from './DirectMessage' +import XDM from './XDMValidator' + +const ValidatorHome = () => { + const validatorTabs: TabInputs[] = [ + { tabName: 'XDM VALIDATOR', tabIndex: 0, tabPanel: }, + { tabName: 'DIRECT MESSAGE', tabIndex: 1, tabPanel: }, + ] + return ( + <> + + + Direct + , + + Message Validators + , + ]} + heading={'Message Validators '} + description={<>} + /> + + + + ) +} + +export default ValidatorHome + */ diff --git a/src/components/direct/validators/XDMResults.tsx b/src/components/direct/validators/XDMResults.tsx new file mode 100644 index 00000000..a7c08825 --- /dev/null +++ b/src/components/direct/validators/XDMResults.tsx @@ -0,0 +1,103 @@ +import { Box, Button, Divider, Typography } from '@mui/material' +import _ from 'lodash' + +import ErrorDisplayCard from '@/components/c-cda/validation/results/ErrorDisplay' +import { useFormStatus } from 'react-dom' +import { useState, useEffect } from 'react' +import { CircularProgress } from '@mui/material' +import palette from '@/styles/palette' +import { Check } from '@mui/icons-material' +import ErrorIcon from '@mui/icons-material/Error' +interface ResultsComponentProps { + response: ContentProps + disabled?: boolean + buttonTitle: string +} +type ContentProps = { + pass: boolean + report: string + uploadedTrustAnchorFileName: string + error?: string + errorStatus?: number +} +const XDMResults = ({ response, buttonTitle }: ResultsComponentProps) => { + const [errorOpen, setErrorOpen] = useState(false) + const { pending } = useFormStatus() + + const handleErrorClose = () => { + setErrorOpen(false) + } + + useEffect(() => { + if (!pending && _.has(response, 'error')) { + setErrorOpen(true) + } + }, [pending, response]) + + return ( + <> + + + {!pending && _.has(response, 'error') && ( + + )} + + {!pending && !_.has(response, 'error') && !_.isEmpty(response) && ( + + + + Validator Results + + {response.pass && ( + + + +
{response.report}
+
+
+ )} + {!response.pass && ( + + + +
{response.report}
+
+
+ )} +
+ )} + + ) +} + +export default XDMResults diff --git a/src/components/direct/validators/XDMValidator.tsx b/src/components/direct/validators/XDMValidator.tsx new file mode 100644 index 00000000..7ba4707c --- /dev/null +++ b/src/components/direct/validators/XDMValidator.tsx @@ -0,0 +1,73 @@ +'use client' +import BannerBox from '@/components/shared/BannerBox' +import styles from '@/components/shared/styles.module.css' +import { Box, Container, Stack, Typography } from '@mui/material' +import Link from 'next/link' +import DragandDropFile from '@components/shared/DragandDropFile' +import { handleXDMUpload } from './actions' +import { useFormState } from 'react-dom' +import { useEffect, useState } from 'react' +import XDMResults from './XDMResults' + +const XDM = () => { + const [base64, setBase64] = useState(null) + const [data, handleSubmit] = useFormState(handleXDMUpload, { response: {} }) + const [fileContent, setFileContent] = useState(null) + + const formData = new FormData() + formData.append('zip', base64 || '') + const handleSubmitwithBase64 = handleSubmit.bind(null, formData) + + useEffect(() => { + const reader = new FileReader() + reader.onloadend = () => { + const result = reader.result as string + const base64String = result.split(',')[1] // Extract base64 part + setBase64(base64String) + } + if (fileContent) { + reader.readAsDataURL(fileContent) + } + }, [fileContent]) + + const getFileName = (data: File[]) => { + if (data !== undefined) { + setFileContent(data[0]) + } + } + + return ( + <> + {/* Global Header */} + + Direct + , + + XDM Validator + , + ]} + heading={'XDM Validator'} + description={<>Upload your XDM file to validate.} + /> + + {/* Main Content */} + +
+ + + + Upload your XDM file: + + + + + + +
+ + ) +} + +export default XDM diff --git a/src/components/direct/validators/actions.ts b/src/components/direct/validators/actions.ts new file mode 100644 index 00000000..5d15fb49 --- /dev/null +++ b/src/components/direct/validators/actions.ts @@ -0,0 +1,48 @@ +'use server' +import axios from 'axios' +import _ from 'lodash' + +export async function handleXDMUpload(prevState: object | undefined, formData: FormData) { + const Api = process.env.XDM_VALIDATOR_API + const uploadFile = formData.get('zip') + // console.log('uploaded File', uploadFile) + if (_.isEmpty(uploadFile)) { + return { + response: { + error: 'Please upload a file', + errorStatus: 400, + }, + } + } else { + const body = JSON.stringify({ + zip: uploadFile, + }) + const config = { + method: 'post', + url: Api, + data: body, + headers: { + 'Content-Type': 'application/json', + }, + } + console.log('Submitted data XDM Validator ', config) + try { + const response = await axios.request(config) + console.log('XDM Validator response status', response.status) + //console.log('XDM Validator response status', response.data) + return { response: response.data } + } catch (error) { + if (axios.isAxiosError(error)) { + console.error(error.response?.data) + return { + response: { + error: 'There was an error completing the request, Please try again later!', + errorStatus: error.response?.status, + }, + } + } else { + console.error(error) + } + } + } +} diff --git a/src/constants/errorConstants.ts b/src/constants/errorConstants.ts new file mode 100644 index 00000000..2117521e --- /dev/null +++ b/src/constants/errorConstants.ts @@ -0,0 +1 @@ +export const GENERIC_ERROR_MESSAGE: string = 'There was an error completing the request, Please try again later!' From d8c4e13fcc348a864fa21a31a362d0e837a30574 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 16 Sep 2024 22:55:34 -0400 Subject: [PATCH 15/34] Add Scorecard compare chart --- package-lock.json | 326 +++++++++++++++++- package.json | 3 +- .../scorecard/ScorecardResultsDialog.tsx | 2 +- .../next-steps/ScorecardNextSteps.tsx | 26 +- .../summary/ScorecardCompareChartSummary.tsx | 107 +++++- src/components/shared/SwitchWIthLabel.tsx | 32 ++ .../shared/dialog/DialogTemplate.tsx | 2 +- 7 files changed, 463 insertions(+), 35 deletions(-) create mode 100644 src/components/shared/SwitchWIthLabel.tsx diff --git a/package-lock.json b/package-lock.json index 0ea236bf..29a67fa1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,10 @@ "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.0", "@mui/material-nextjs": "^5.15.5", + "@mui/x-charts": "^7.17.0", "@mui/x-data-grid": "^7.12.1", "axios": "^1.7.7", - "dompurify": "^3.1.2", + "dompurify": "^3.1.6", "lodash": "^4.17.21", "marked": "^12.0.2", "next": "14.2.3", @@ -246,9 +247,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -916,6 +917,81 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/@mui/x-charts": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.17.0.tgz", + "integrity": "sha512-xDH/lOnb57+VBIA7q+1KlC0Ht1O46d/N2MEl1tUq1JYIXhA2Owi5cp+bcaof8Rvw5ApCmkoBxyUIjqT0guNIwA==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/utils": "^5.16.6", + "@mui/x-charts-vendor": "7.16.0", + "@mui/x-internals": "7.17.0", + "@react-spring/rafz": "^9.7.4", + "@react-spring/web": "^9.7.4", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-charts-vendor": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.16.0.tgz", + "integrity": "sha512-MyMCCl7eAM53rLbjqP4zbMy5hYtdeqCjAYCH2jpvBKdgugm2eaPLKOPM8bUVfen0wHA8BXleQrIrNceytFPyZA==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@types/d3-color": "^3.1.3", + "@types/d3-delaunay": "^6.0.4", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-scale": "^4.0.8", + "@types/d3-shape": "^3.1.6", + "@types/d3-time": "^3.0.3", + "d3-color": "^3.1.0", + "d3-delaunay": "^6.0.4", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "d3-time": "^3.1.0", + "delaunator": "^5.0.1", + "robust-predicates": "^3.0.2" + } + }, + "node_modules/@mui/x-charts/node_modules/@mui/x-internals": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.17.0.tgz", + "integrity": "sha512-FLlAGSJl/vsuaA/8hPGazXFppyzIzxApJJDZMoTS0geUmHd0hyooISV2ltllLmrZ/DGtHhI08m8GGnHL6/vVeg==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/utils": "^5.16.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, "node_modules/@mui/x-data-grid": { "version": "7.12.1", "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.12.1.tgz", @@ -1172,6 +1248,72 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-spring/animated": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.4.tgz", + "integrity": "sha512-7As+8Pty2QlemJ9O5ecsuPKjmO0NKvmVkRR1n6mEotFgWar8FKuQt2xgxz3RTgxcccghpx1YdS1FCdElQNexmQ==", + "dependencies": { + "@react-spring/shared": "~9.7.4", + "@react-spring/types": "~9.7.4" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.4.tgz", + "integrity": "sha512-GzjA44niEJBFUe9jN3zubRDDDP2E4tBlhNlSIkTChiNf9p4ZQlgXBg50qbXfSXHQPHak/ExYxwhipKVsQ/sUTw==", + "dependencies": { + "@react-spring/animated": "~9.7.4", + "@react-spring/shared": "~9.7.4", + "@react-spring/types": "~9.7.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.4.tgz", + "integrity": "sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA==" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.4.tgz", + "integrity": "sha512-bEPI7cQp94dOtCFSEYpxvLxj0+xQfB5r9Ru1h8OMycsIq7zFZon1G0sHrBLaLQIWeMCllc4tVDYRTLIRv70C8w==", + "dependencies": { + "@react-spring/rafz": "~9.7.4", + "@react-spring/types": "~9.7.4" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.4.tgz", + "integrity": "sha512-iQVztO09ZVfsletMiY+DpT/JRiBntdsdJ4uqk3UJFhrhS8mIC9ZOZbmfGSRs/kdbNPQkVyzucceDicQ/3Mlj9g==" + }, + "node_modules/@react-spring/web": { + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.4.tgz", + "integrity": "sha512-UMvCZp7I5HCVIleSa4BwbNxynqvj+mJjG2m20VO2yPoi2pnCYANy58flvz9v/YcXTAvsmL655FV3pm5fbr6akA==", + "dependencies": { + "@react-spring/animated": "~9.7.4", + "@react-spring/core": "~9.7.4", + "@react-spring/shared": "~9.7.4", + "@react-spring/types": "~9.7.4" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.6.1.tgz", @@ -1202,6 +1344,50 @@ "cypress": "*" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2554,6 +2740,111 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2656,6 +2947,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2718,9 +3017,9 @@ } }, "node_modules/dompurify": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", - "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/ecc-jsbn": { "version": "0.1.2", @@ -4289,6 +4588,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -7333,6 +7640,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/package.json b/package.json index 42686678..f354e741 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,10 @@ "@mui/lab": "^5.0.0-alpha.173", "@mui/material": "^5.16.0", "@mui/material-nextjs": "^5.15.5", + "@mui/x-charts": "^7.17.0", "@mui/x-data-grid": "^7.12.1", "axios": "^1.7.7", - "dompurify": "^3.1.2", + "dompurify": "^3.1.6", "lodash": "^4.17.21", "marked": "^12.0.2", "next": "14.2.3", diff --git a/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx b/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx index 57fdffd1..dbb729be 100644 --- a/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx +++ b/src/components/c-cda/scorecard/ScorecardResultsDialog.tsx @@ -145,7 +145,7 @@ export default function ScorecardResultsDialog({
- + diff --git a/src/components/c-cda/scorecard/next-steps/ScorecardNextSteps.tsx b/src/components/c-cda/scorecard/next-steps/ScorecardNextSteps.tsx index b266fddd..a90c09ab 100644 --- a/src/components/c-cda/scorecard/next-steps/ScorecardNextSteps.tsx +++ b/src/components/c-cda/scorecard/next-steps/ScorecardNextSteps.tsx @@ -2,11 +2,11 @@ import { ScorecardReferenceResultType, ScorecardResultsType, } from '@/components/c-cda/scorecard/types/ScorecardJsonResponseType' -import { Box, Divider, FormControlLabel, styled, Switch, Typography } from '@mui/material' +import { Box, Divider, Typography } from '@mui/material' import { useEffect, useState } from 'react' import { removeHashtagToUseHrefLinkAsIdForAnchor } from '../serverside/scorecardHelperService' import { HrefLinkValueEnum, SORT_ORDER_STARTING_VALUE } from '../types/ScorecardConstants' -// import { SortOrderEnum } from '../types/ScorecardConstants' +import SwitchWithLabel from '@/components/shared/SwitchWIthLabel' import ScorecardDetailedResults from './ScorecardDetailedResults' import ScorecardHeatMap from './ScorecardHeatMap' @@ -35,11 +35,6 @@ export default function ScorecardNextSteps({ setIsAscendingOrderChecked((prevIsAscendingOrderChecked) => !prevIsAscendingOrderChecked) } - const StyledFormControlLabelWithLabelOnLeft = styled(FormControlLabel)` - display: flex; - flex-direction: row; - ` - return ( @@ -47,18 +42,11 @@ export default function ScorecardNextSteps({ Next Steps - - } - // label={`Sorted by: ${isAscendingOrderChecked ? SortOrderEnum.ASCENDING : SortOrderEnum.DESCENDING} order`} - label="Toggle Sort Order" - name="wrapped" + diff --git a/src/components/c-cda/scorecard/summary/ScorecardCompareChartSummary.tsx b/src/components/c-cda/scorecard/summary/ScorecardCompareChartSummary.tsx index 9524c9b9..f28df6f9 100644 --- a/src/components/c-cda/scorecard/summary/ScorecardCompareChartSummary.tsx +++ b/src/components/c-cda/scorecard/summary/ScorecardCompareChartSummary.tsx @@ -1,16 +1,111 @@ +import SwitchWithLabel from '@/components/shared/SwitchWIthLabel' import { Box, Typography } from '@mui/material' +import { BarChart } from '@mui/x-charts/BarChart' +import { useState } from 'react' +import { GradeEnum, gradeStyleMap } from '../types/ScorecardConstants' +import { ScorecardResultsType, TotalGradesGiven } from '../types/ScorecardJsonResponseType' + +interface ScorecardCompareChartSummaryProps { + results: ScorecardResultsType | undefined +} +export default function ScorecardCompareChartSummary({ results }: ScorecardCompareChartSummaryProps) { + const initialGradeCategoryLabels = ['A+', 'A-', 'B+', 'B-', 'C', 'D'] + + const totalGradesGiven: TotalGradesGiven | undefined = results?.totalGradesGiven + + // Set grade data for each bar based on index order + // TODO: Consider if totalGradesGiven is undefined, don't show the chart instead of just setting to 0 as we are now. + // Instead, display an inline error. This inline error is template is in the shared folder (see Register Direct Email) + const initialGradeTotalCountData: number[] = [ + totalGradesGiven?.aPlusGrades ? totalGradesGiven.aPlusGrades : 0, + totalGradesGiven?.aMinusGrades ? totalGradesGiven.aMinusGrades : 0, + totalGradesGiven?.bPlusGrades ? totalGradesGiven.bPlusGrades : 0, + totalGradesGiven?.bMinusGrades ? totalGradesGiven.bMinusGrades : 0, + totalGradesGiven?.cGrades ? totalGradesGiven.cGrades : 0, + totalGradesGiven?.dGrades ? totalGradesGiven.dGrades : 0, + ] + + // Define the colors for each bar based on index order + const initialGradeColors: string[] = [ + gradeStyleMap[GradeEnum.A_PLUS].backgroundColor, + gradeStyleMap[GradeEnum.A_MINUS].backgroundColor, + gradeStyleMap[GradeEnum.B_PLUS].backgroundColor, + gradeStyleMap[GradeEnum.B_MINUS].backgroundColor, + gradeStyleMap[GradeEnum.C].backgroundColor, + gradeStyleMap[GradeEnum.D].backgroundColor, + ] + + const [isGradeOrderFlipped, setIsGradeOrderFlipped] = useState(false) + + const gradeTotalCountData: number[] = isGradeOrderFlipped + ? [...initialGradeTotalCountData].reverse() + : initialGradeTotalCountData + const gradeCategoryLabels: string[] = isGradeOrderFlipped + ? [...initialGradeCategoryLabels].reverse() + : initialGradeCategoryLabels + const gradeColors: string[] = isGradeOrderFlipped ? [...initialGradeColors].reverse() : initialGradeColors + + const handleFlipGradeOrder = () => { + setIsGradeOrderFlipped(!isGradeOrderFlipped) + } -export default function ScorecardCompareChartSummary() { return ( - - Compare - - - CHART + + + Compare + + + + + The following chart displays the total count for each possible grade received when compared with all C-CDA + Scorecard validations run since we started tracking data. It is intended to give a general idea of how your + grade compares to others, as well as to provide an assessment of the community as a whole. +
+ Note: To view exact total count data, please hover over a relevant grade bar.
+ + + {/* TODO: Make labels more bold? */} + +
) } diff --git a/src/components/shared/SwitchWIthLabel.tsx b/src/components/shared/SwitchWIthLabel.tsx new file mode 100644 index 00000000..6b0dad5e --- /dev/null +++ b/src/components/shared/SwitchWIthLabel.tsx @@ -0,0 +1,32 @@ +import { FormControlLabel, styled, Switch } from '@mui/material' + +const StyledFormControlLabelWithLabelOnRight = styled(FormControlLabel)` + display: flex; + flex-direction: row; +` +const StyledFormControlLabelWithLabelOnLeft = styled(FormControlLabel)` + display: flex; + flex-direction: row-reverse; +` + +interface SwitchWithLabelProps { + isChecked: boolean + handleToggleSwitch: (event: React.ChangeEvent) => void + labelText: string + labelOnRight?: boolean +} + +export default function SwitchWithLabel({ + isChecked, + handleToggleSwitch: handleChangeSwitch, + labelText, + labelOnRight, +}: SwitchWithLabelProps) { + const SwitchControl = + + return labelOnRight ? ( + + ) : ( + + ) +} diff --git a/src/components/shared/dialog/DialogTemplate.tsx b/src/components/shared/dialog/DialogTemplate.tsx index 78583457..228e21a8 100644 --- a/src/components/shared/dialog/DialogTemplate.tsx +++ b/src/components/shared/dialog/DialogTemplate.tsx @@ -43,7 +43,7 @@ const DialogTemplate: FC = ({ actionsContent, }) => { return ( - + {title} Date: Tue, 17 Sep 2024 09:27:57 -0400 Subject: [PATCH 16/34] Add B1 Tab * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling * Update replacement values --- src/components/direct/test-by-criteria/ServerActions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index ef5f4b5d..15715b5c 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -135,7 +135,6 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Tue, 17 Sep 2024 12:27:06 -0400 Subject: [PATCH 17/34] Add B1 Tab * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling * Update replacement values * Modify jessionid formation * Update ServerActions.ts --- src/components/direct/test-by-criteria/ServerActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index 15715b5c..538c95fe 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -107,6 +107,7 @@ export async function handleAPICall(data: APICallData): Promise { export async function handleXDRAPICall(data: XDRAPICallData): Promise { const apiUrl = process.env.XDR_TEST_BY_CRITERIA_ENDPOINT + data.id + '/run' + const jsessionstring = 'JSESSIONID=' + data.jsession const formattedData = { targetEndpointTLS: data.targetEndpointTLS, ip_address: data.ip_address, @@ -131,8 +132,7 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Tue, 17 Sep 2024 13:41:24 -0400 Subject: [PATCH 18/34] Site 3912 (#128) * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling * Update replacement values * Modify jessionid formation * Update ServerActions.ts * Alternative auth method --- src/components/direct/test-by-criteria/ServerActions.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index 538c95fe..9bacb774 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -1,5 +1,9 @@ 'use server' import axios from 'axios' +import { authOptions } from '@/lib/auth' +import { NextRequest, NextResponse } from 'next/server' +import _ from 'lodash' +import { getServerSession } from 'next-auth' interface APICallData { testCaseNumber: number | string @@ -107,7 +111,8 @@ export async function handleAPICall(data: APICallData): Promise { export async function handleXDRAPICall(data: XDRAPICallData): Promise { const apiUrl = process.env.XDR_TEST_BY_CRITERIA_ENDPOINT + data.id + '/run' - const jsessionstring = 'JSESSIONID=' + data.jsession + const session = await getServerSession(authOptions) + const jsessionid = session?.user?.jsessionid ?? '' const formattedData = { targetEndpointTLS: data.targetEndpointTLS, ip_address: data.ip_address, @@ -132,7 +137,7 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Wed, 18 Sep 2024 09:31:24 -0400 Subject: [PATCH 19/34] Add B1 Tab * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling * Update replacement values * Modify jessionid formation * Update ServerActions.ts * Alternative auth method * Improve error handling --- .../direct/test-by-criteria/ServerActions.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index 9bacb774..b581907b 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -1,7 +1,6 @@ 'use server' import axios from 'axios' import { authOptions } from '@/lib/auth' -import { NextRequest, NextResponse } from 'next/server' import _ from 'lodash' import { getServerSession } from 'next-auth' @@ -135,10 +134,9 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Wed, 18 Sep 2024 11:06:14 -0400 Subject: [PATCH 20/34] Add text for XDM results --- .../direct/validators/XDMResults.tsx | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/components/direct/validators/XDMResults.tsx b/src/components/direct/validators/XDMResults.tsx index a7c08825..2e1220ee 100644 --- a/src/components/direct/validators/XDMResults.tsx +++ b/src/components/direct/validators/XDMResults.tsx @@ -51,50 +51,50 @@ const XDMResults = ({ response, buttonTitle }: ResultsComponentProps) => { Validator Results
{response.pass && ( - - + + + + Validation Passed +
{response.report}
)} {!response.pass && ( - - + + + + Validation Failed +
{response.report}
- )} -
)} ) From 8b72fbb5aa2d8dbbf0aa7b4273931fe5086c3e03 Mon Sep 17 00:00:00 2001 From: akanuri9 <118755941+akanuri9@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:17:00 -0400 Subject: [PATCH 21/34] Update XDMResults.tsx --- src/components/direct/validators/XDMResults.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/direct/validators/XDMResults.tsx b/src/components/direct/validators/XDMResults.tsx index 2e1220ee..b8ea65f3 100644 --- a/src/components/direct/validators/XDMResults.tsx +++ b/src/components/direct/validators/XDMResults.tsx @@ -95,6 +95,8 @@ const XDMResults = ({ response, buttonTitle }: ResultsComponentProps) => {
{response.report}
+ )} +
)} ) From 85da15d998ce490b81d10e1c9efa4456dc7393fb Mon Sep 17 00:00:00 2001 From: ebrockainq <112428189+ebrockainq@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:07:08 -0400 Subject: [PATCH 22/34] Add B1 Tab * Changes from SITE-3986 * Update B1Tab.tsx * Create SMTPTestCases.tsx * Remove duplicate files * Add env variables and move API calls to ServerActions * Improve environment variable name * Remove env variables * Add Loading Button, Remove Default Profile Values * add IMAP/POP3 manual validation, attachment type field * Fix tab data carryover, fix include filter * Add XDR endpoint fields * Add XDR DocumentSelector handling * Fix incorrect emails * Fix missing doc select on XDR 3 * Add logs formatting * Fix reversed IMAP and POP3 icons * Add tooltip * Fix XDR logs and missing manual validations * Add attachment type tooltip * Remove Tooltips add XDR API call * Add error condition, add clear button * Fix more info sections, censor profile password * Add clear selected document * Remove accept/reject on API error * Fix XDR more info page * add tooltip and pop over for endpoint copy * Fix incorrect xdr email addresses * Replace popover system and fix error handling * Language improvements and fix clear button visibility trigger * Fix language errors * Add clear button to XDR send * Basic xdr functionality * Add manual validation, request/response, remining request fields, * add xdr document selector * Add XDR Document Select test functionality, add colorized XML display * Correct document selection per sub criteria * Fix failure detection on empty responses * Banish type errors * Less restrictive clear button * Fix CORS issue with document select * Update DocumentSelector.tsx * add prettier ignore to bugged line * Add improved warning triggers * Add backup content, fix criteriaMet icon on non manual tests * Language fix and null value handling * Update replacement values * Modify jessionid formation * Update ServerActions.ts * Alternative auth method * Improve error handling * Remove client session in request --- src/components/direct/hisp/XDRTestCard.tsx | 8 +------- src/components/direct/test-by-criteria/ServerActions.ts | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/direct/hisp/XDRTestCard.tsx b/src/components/direct/hisp/XDRTestCard.tsx index 6b003c10..0fa63d24 100644 --- a/src/components/direct/hisp/XDRTestCard.tsx +++ b/src/components/direct/hisp/XDRTestCard.tsx @@ -20,7 +20,6 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle' import CancelIcon from '@mui/icons-material/Cancel' import LoadingButton from '../shared/LoadingButton' import DocumentSelector from './DocumentSelector' -import { useSession } from 'next-auth/react' import XMLDisplay from '../shared/colorizeXML' export type TestCaseFields = { @@ -159,7 +158,6 @@ const TestCard = ({ test, receive }: TestCardProps) => { const [logType, setLogType] = useState<'request' | 'response'>('request') const manualValidationCriteria = ["['b1-3']", "['b1-3','su1-3']"] - const { data: session } = useSession() const subHeader = 'Description' const subDesc = test['Purpose/Description'] @@ -246,10 +244,6 @@ const TestCard = ({ test, receive }: TestCardProps) => { } const handleRunTest = async () => { - if (!session) { - showPopover('You must be logged in and have a valid session to perform this action.', null) - return - } console.log('ccda required' + test.ccdaFileRequired) console.log('doc details ' + documentDetails) if (isCCDADocumentRequired && !documentDetails) { @@ -281,7 +275,7 @@ const TestCard = ({ test, receive }: TestCardProps) => { path: documentDetails ? documentDetails.directory : '', link: documentDetails ? documentDetails.fileLink : '', id: test.id.toString(), - jsession: session.user.jsessionid, + jsession: '', cures: false, itemNumber: '12', selected: true, diff --git a/src/components/direct/test-by-criteria/ServerActions.ts b/src/components/direct/test-by-criteria/ServerActions.ts index b581907b..8d78ade9 100644 --- a/src/components/direct/test-by-criteria/ServerActions.ts +++ b/src/components/direct/test-by-criteria/ServerActions.ts @@ -2,6 +2,7 @@ import axios from 'axios' import { authOptions } from '@/lib/auth' import _ from 'lodash' +import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' interface APICallData { @@ -144,7 +145,7 @@ export async function handleXDRAPICall(data: XDRAPICallData): Promise Date: Wed, 18 Sep 2024 19:03:10 -0400 Subject: [PATCH 23/34] Add Direct XDR Edge Test Tool UI components (#129) --- src/app/direct/xdr-edge/page.tsx | 7 + src/components/direct/DirectHome.tsx | 8 + src/components/direct/shared/SubTabs.tsx | 84 +++++++++ src/components/direct/xdr-edge/SendTab.tsx | 64 +++++++ .../direct/xdr-edge/ValidateTab.tsx | 64 +++++++ .../direct/xdr-edge/XDREdgeTestTool.tsx | 59 ++++++ .../direct/xdr-edge/send/ContentTab.tsx | 6 + .../direct/xdr-edge/send/SendForm.tsx | 169 ++++++++++++++++++ .../direct/xdr-edge/send/TemplateTab.tsx | 6 + 9 files changed, 467 insertions(+) create mode 100644 src/app/direct/xdr-edge/page.tsx create mode 100644 src/components/direct/shared/SubTabs.tsx create mode 100644 src/components/direct/xdr-edge/SendTab.tsx create mode 100644 src/components/direct/xdr-edge/ValidateTab.tsx create mode 100644 src/components/direct/xdr-edge/XDREdgeTestTool.tsx create mode 100644 src/components/direct/xdr-edge/send/ContentTab.tsx create mode 100644 src/components/direct/xdr-edge/send/SendForm.tsx create mode 100644 src/components/direct/xdr-edge/send/TemplateTab.tsx diff --git a/src/app/direct/xdr-edge/page.tsx b/src/app/direct/xdr-edge/page.tsx new file mode 100644 index 00000000..8fa1c65d --- /dev/null +++ b/src/app/direct/xdr-edge/page.tsx @@ -0,0 +1,7 @@ +import XDREdgeComponent from '@/components/direct/xdr-edge/XDREdgeTestTool' + +const XDREdgeTest = () => { + return +} + +export default XDREdgeTest diff --git a/src/components/direct/DirectHome.tsx b/src/components/direct/DirectHome.tsx index b8289683..72920905 100644 --- a/src/components/direct/DirectHome.tsx +++ b/src/components/direct/DirectHome.tsx @@ -187,6 +187,14 @@ const DirectHome = () => { buttonIcon={} cardWidthPercent={100} /> + } + cardWidthPercent={100} + /> { + const [value, setValue] = React.useState(0) + const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue) + } + + useEffect(() => { + if (selectedTab !== '') { + const selectedTabIndex = tabs.filter((c) => c.tabName === selectedTab) + setValue(selectedTabIndex[0].tabIndex) + } + }, [selectedTab, tabs]) + + function CustomTabPanel(props: TabPanelProps) { + const { children, value, index } = props + + return
{value === index && <>{children}}
+ } + + const StyledTab = styled(Tab)(() => ({ + backgroundColor: palette.white, + color: palette.primary, + '&.Mui-selected, &:hover': { + backgroundColor: palette.white, + color: palette.primary, + }, + })) + + return ( + <> + + + + {tabs.map((tab) => ( + + ))} + + + + + {tabs.map((tab) => ( + + {tab.tabPanel} + + ))} + + ) +} +export default SubTabsComponent diff --git a/src/components/direct/xdr-edge/SendTab.tsx b/src/components/direct/xdr-edge/SendTab.tsx new file mode 100644 index 00000000..ad6a34a5 --- /dev/null +++ b/src/components/direct/xdr-edge/SendTab.tsx @@ -0,0 +1,64 @@ +import { useEffect, useState } from 'react' +import SubTabsComponent, { TabInputs } from '../shared/SubTabs' +import Template from './send/TemplateTab' +import Content from './send/ContentTab' +import bulletedList from '../shared/BulletList' +import { Container, Typography, List, ListItem } from '@mui/material' + +const SendTab = () => { + const [selectedTab, setSelectedTab] = useState('Choose template') + + const sendTabs: TabInputs[] = [ + { tabName: 'Choose template', tabIndex: 0, tabPanel: