1- import React , { useState } from 'react' ;
1+ import React , { useState , useRef , useEffect } from 'react' ;
22import {
33 Box ,
44 TextField ,
@@ -11,13 +11,17 @@ import {
1111import { PDFDownloadLink } from '@react-pdf/renderer' ;
1212import { fetchSecurityAlerts , generateAlertsSummary } from '../services/githubService' ;
1313import SecurityReport from './SecurityReport' ;
14+ import SecurityPieChart from './SecurityPieChart' ;
1415
1516const LoginForm = ( ) => {
1617 const [ token , setToken ] = useState ( '' ) ;
1718 const [ organization , setOrganization ] = useState ( '' ) ;
1819 const [ error , setError ] = useState ( '' ) ;
1920 const [ loading , setLoading ] = useState ( false ) ;
2021 const [ reportData , setReportData ] = useState ( null ) ;
22+ const [ chartImages , setChartImages ] = useState ( null ) ;
23+ const codeScanningChartRef = useRef ( null ) ;
24+ const dependabotChartRef = useRef ( null ) ;
2125
2226 const handleSubmit = async ( e ) => {
2327 e . preventDefault ( ) ;
@@ -43,23 +47,101 @@ const LoginForm = () => {
4347 } else {
4448 setError ( err . message || 'Failed to fetch security alerts' ) ;
4549 }
50+ setReportData ( null ) ;
51+ setChartImages ( null ) ;
4652 } finally {
4753 setLoading ( false ) ;
4854 }
4955 } ;
5056
57+ useEffect ( ( ) => {
58+ if ( reportData ?. summary ) {
59+ setTimeout ( ( ) => {
60+ const codeScanningImage = codeScanningChartRef . current ?. getChartImage ( ) ;
61+ const dependabotImage = dependabotChartRef . current ?. getChartImage ( ) ;
62+
63+ if ( codeScanningImage && dependabotImage ) {
64+ setChartImages ( {
65+ codeScanning : codeScanningImage ,
66+ dependabot : dependabotImage
67+ } ) ;
68+ }
69+ } , 2000 ) ;
70+ }
71+ } , [ reportData ] ) ;
72+
5173 const generatePDF = ( ) => {
5274 return (
5375 < SecurityReport
5476 organization = { reportData . organization }
5577 alerts = { reportData . alerts }
5678 summary = { reportData . summary }
5779 showAllAlerts = { false }
58- chartImages = { null }
80+ chartImages = { chartImages }
5981 />
6082 ) ;
6183 } ;
6284
85+ const renderButton = ( ) => {
86+ if ( ! reportData ) {
87+ return (
88+ < Button
89+ type = "submit"
90+ variant = "contained"
91+ color = "primary"
92+ fullWidth
93+ sx = { { mt : 2 } }
94+ disabled = { loading }
95+ >
96+ { loading ? (
97+ < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
98+ < CircularProgress size = { 20 } color = "inherit" />
99+ < span > Retrieving Alerts...</ span >
100+ </ Box >
101+ ) : (
102+ 'Retrieve Security Alerts'
103+ ) }
104+ </ Button >
105+ ) ;
106+ }
107+
108+ if ( reportData && chartImages ) {
109+ return (
110+ < PDFDownloadLink
111+ document = { generatePDF ( ) }
112+ fileName = { `${ organization } -security-report.pdf` }
113+ >
114+ { ( { loading : pdfLoading } ) => (
115+ < Button
116+ variant = "contained"
117+ color = "success"
118+ fullWidth
119+ sx = { { mt : 2 } }
120+ disabled = { pdfLoading }
121+ >
122+ { pdfLoading ? 'Generating PDF...' : 'Download PDF Report' }
123+ </ Button >
124+ ) }
125+ </ PDFDownloadLink >
126+ ) ;
127+ }
128+
129+ return (
130+ < Button
131+ variant = "contained"
132+ color = "primary"
133+ fullWidth
134+ sx = { { mt : 2 } }
135+ disabled
136+ >
137+ < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
138+ < CircularProgress size = { 20 } color = "inherit" />
139+ < span > Preparing Report...</ span >
140+ </ Box >
141+ </ Button >
142+ ) ;
143+ } ;
144+
63145 return (
64146 < Box
65147 component = "form"
@@ -108,42 +190,33 @@ const LoginForm = () => {
108190 helperText = "Token needs security read permissions"
109191 />
110192
111- { reportData ? (
112- < PDFDownloadLink
113- document = { generatePDF ( ) }
114- fileName = { `${ organization } -security-report.pdf` }
115- >
116- { ( { loading : pdfLoading } ) => (
117- < Button
118- variant = "contained"
119- color = "primary"
120- fullWidth
121- sx = { { mt : 2 } }
122- disabled = { pdfLoading }
123- >
124- { pdfLoading ? 'Generating PDF...' : 'Download PDF Report' }
125- </ Button >
126- ) }
127- </ PDFDownloadLink >
128- ) : (
129- < Button
130- type = "submit"
131- variant = "contained"
132- color = "primary"
133- fullWidth
134- sx = { { mt : 2 } }
135- disabled = { loading }
136- >
137- { loading ? (
138- < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 1 } } >
139- < CircularProgress size = { 20 } color = "inherit" />
140- < span > Generating Report...</ span >
141- </ Box >
142- ) : (
143- 'Download PDF Report'
144- ) }
145- </ Button >
193+ { reportData && (
194+ < Box sx = { {
195+ position : 'absolute' ,
196+ width : '500px' ,
197+ height : '300px' ,
198+ overflow : 'hidden' ,
199+ opacity : 0 ,
200+ pointerEvents : 'none' ,
201+ } } >
202+ < Box sx = { { width : '100%' , height : '100%' } } >
203+ < SecurityPieChart
204+ ref = { codeScanningChartRef }
205+ data = { reportData . summary . codeScanning . severity }
206+ title = "Code Scanning Alerts by Severity"
207+ />
208+ </ Box >
209+ < Box sx = { { width : '100%' , height : '100%' } } >
210+ < SecurityPieChart
211+ ref = { dependabotChartRef }
212+ data = { reportData . summary . dependabot . severity }
213+ title = "Software Composition Analysis Alerts by Severity"
214+ />
215+ </ Box >
216+ </ Box >
146217 ) }
218+
219+ { renderButton ( ) }
147220 </ Paper >
148221 </ Box >
149222 ) ;
0 commit comments