github.com/lauslim12/expert-systems@v0.0.0-20221115131159-018513aad29c/web/src/components/Modal/ResultModal.tsx (about) 1 import { 2 Alert, 3 AlertIcon, 4 Button, 5 ButtonGroup, 6 chakra, 7 Link, 8 List, 9 ListIcon, 10 ListItem, 11 Modal, 12 ModalBody, 13 ModalCloseButton, 14 ModalContent, 15 ModalFooter, 16 ModalHeader, 17 ModalOverlay, 18 Text, 19 Textarea, 20 VStack, 21 } from '@chakra-ui/react'; 22 import { memo, useState } from 'react'; 23 import { useTranslation } from 'react-i18next'; 24 import { 25 AiFillInfoCircle, 26 AiOutlineClose, 27 AiOutlineCode, 28 } from 'react-icons/ai'; 29 30 import type Response from '../../types/Response'; 31 32 /** 33 * Accepts ChakraUI's modal props, plus the response from the API. 34 */ 35 type Props = { 36 isOpen: boolean; 37 onClose: () => void; 38 results: Response; 39 }; 40 41 /** 42 * This modal will render the results of the inference. 43 * 44 * @param param - ChakraUI's modal props, and the API response 45 * @returns React Functional Component 46 */ 47 const ResultModal = ({ isOpen, onClose, results }: Props) => { 48 const [showRawData, setShowRawData] = useState(false); 49 const { t } = useTranslation(); 50 51 /** 52 * Renders the suggestion of what the user should do based on their probability rate. 53 * 54 * @param probability - The probability of having TB 55 * @returns JSX Element 56 */ 57 const renderSuggestion = (probability: number) => { 58 if (probability <= 30) { 59 return <Text>{t('inference.suggestionOkay')}</Text>; 60 } 61 62 if (probability > 30 && probability < 70) { 63 return <Text>{t('inference.suggestionVisit')}</Text>; 64 } 65 66 return <Text>{t('inference.suggestionDangerous')}</Text>; 67 }; 68 69 return ( 70 <Modal 71 isOpen={isOpen} 72 onClose={onClose} 73 size="5xl" 74 motionPreset="slideInBottom" 75 closeOnEsc={false} 76 closeOnOverlayClick={false} 77 > 78 <ModalOverlay /> 79 80 <ModalContent> 81 <ModalHeader>{t('inference.title')}</ModalHeader> 82 <ModalCloseButton /> 83 84 <ModalBody> 85 <VStack as="article" align="stretch"> 86 <Alert as="section" status="success" variant="solid"> 87 <AlertIcon /> 88 {t('inference.alert')} 89 </Alert> 90 91 <VStack as="section" align="start" spacing={4}> 92 {results.data && ( 93 <> 94 <VStack align="start"> 95 <Text fontSize="lg" fontWeight="bold"> 96 {t('inference.verdict')} 97 </Text> 98 99 <Text 100 color={ 101 results.data.verdict === true 102 ? 'orange.400' 103 : 'twitter.400' 104 } 105 fontWeight="bold" 106 > 107 {results.data.verdict === true 108 ? t('inference.verdictTB') 109 : t('inference.verdictNoTB')} 110 </Text> 111 112 <Text>{t('inference.verdictCalculation')}</Text> 113 </VStack> 114 115 <VStack align="start"> 116 <Text fontSize="lg" fontWeight="bold"> 117 {t('inference.probability')} 118 </Text> 119 120 <Text fontWeight="bold" color="pink.400"> 121 {t('inference.probabilityResult', { 122 probability: (results.data.probability * 100).toFixed( 123 2 124 ), 125 })} 126 </Text> 127 128 <Text>{t('inference.probabilityCalculation')}</Text> 129 </VStack> 130 131 <VStack align="start"> 132 <Text fontSize="lg" fontWeight="bold"> 133 {t('inference.suggestion')} 134 </Text> 135 136 {renderSuggestion( 137 parseFloat((results.data.probability * 100).toFixed(2)) 138 )} 139 </VStack> 140 141 <VStack align="start"> 142 <Text fontSize="lg" fontWeight="bold"> 143 {t('inference.information')} 144 </Text> 145 146 <Text>{results.data.disease.description}</Text> 147 </VStack> 148 149 <VStack align="start"> 150 <Text fontSize="lg" fontWeight="bold"> 151 {t('inference.prevention')} 152 </Text> 153 154 <Text>{results.data.disease.prevention}</Text> 155 </VStack> 156 157 <VStack align="start"> 158 <Text fontSize="lg" fontWeight="bold"> 159 {t('inference.treatment')} 160 </Text> 161 162 <Text>{results.data.disease.treatment}</Text> 163 </VStack> 164 165 <VStack align="start"> 166 <Text fontSize="lg" fontWeight="bold"> 167 {t('inference.sources')} 168 </Text> 169 170 <Text>{t('inference.sourcesBeginning')}</Text> 171 172 <List> 173 {results.data.disease.source.map((source) => ( 174 <ListItem key={source.name}> 175 <ListIcon as={AiFillInfoCircle} color="green.500" /> 176 177 <Link 178 color="twitter.500" 179 href={source.link} 180 isExternal 181 > 182 {source.name} 183 </Link> 184 </ListItem> 185 ))} 186 </List> 187 </VStack> 188 189 {showRawData && ( 190 <VStack align="start" w="full"> 191 <Text fontSize="lg" fontWeight="bold"> 192 {t('inference.json')} 193 </Text> 194 195 <Text>{t('inference.jsonBeginning')}</Text> 196 197 <chakra.code w="full"> 198 <Textarea 199 value={JSON.stringify(results, null, 2)} 200 w="full" 201 h="50vh" 202 readOnly 203 /> 204 </chakra.code> 205 </VStack> 206 )} 207 </> 208 )} 209 </VStack> 210 </VStack> 211 </ModalBody> 212 213 <ModalFooter> 214 <ButtonGroup> 215 <Button 216 colorScheme="twitter" 217 leftIcon={<AiOutlineCode />} 218 onClick={() => setShowRawData(!showRawData)} 219 > 220 {showRawData === true 221 ? t('inference.hideCode') 222 : t('inference.showCode')} 223 </Button> 224 225 <Button 226 colorScheme="red" 227 leftIcon={<AiOutlineClose />} 228 onClick={onClose} 229 > 230 {t('inference.close')} 231 </Button> 232 </ButtonGroup> 233 </ModalFooter> 234 </ModalContent> 235 </Modal> 236 ); 237 }; 238 239 export default memo(ResultModal);