diff --git a/backend/network_scanner/urls.py b/backend/network_scanner/urls.py index 7cbac3e1b148dcd45a820f5c5178c9bed93f7442..08dabd18f72e2394ccefd672f4aa4b9b0cd8198d 100644 --- a/backend/network_scanner/urls.py +++ b/backend/network_scanner/urls.py @@ -1,9 +1,8 @@ from django.urls import path -from .views import recommendations_view, ResultsView #fetch_recommendations +from .views import recommendations_view, ResultsView, recommendation_count urlpatterns = [ path('results/', ResultsView.as_view(), name='results'), path('recommendations/', recommendations_view, name='recommendations_view'), - #path('fetch-recommendations/', fetch_recommendations, name='fetch_recommendations'), - # Add other URL patterns as needed + path('recommendation-count/', recommendation_count, name='recommendation-count'), ] diff --git a/backend/network_scanner/views.py b/backend/network_scanner/views.py index c3d24779617be78c1b080bd40b68f7c18b708862..47c7cd05f789afc197e7cd80a59ceed6b30cd13d 100644 --- a/backend/network_scanner/views.py +++ b/backend/network_scanner/views.py @@ -6,6 +6,7 @@ from rest_framework import status from .models import ScanResult, Recommendation from .serializers import ScanResultSerializer from .utils import get_recommendations_from_openai +from django.http import JsonResponse class ResultsView(APIView): @@ -51,3 +52,8 @@ def recommendations_view(request): else: return Response({'error': 'Failed to generate recommendations for any scans'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +def recommendation_count(request): + total_recommendations = Recommendation.objects.count() + return JsonResponse({"total_recommendations": total_recommendations}) diff --git a/frontend/src/_nav.js b/frontend/src/_nav.js index 735bb4a1c4032135b27139d70916f89f2b593a44..fa233e420dc71e91331b3b436133a9d18c94269c 100644 --- a/frontend/src/_nav.js +++ b/frontend/src/_nav.js @@ -13,6 +13,7 @@ import { cilSpeedometer, cilStar, cilShieldAlt, + cilEnvelopeLetter, } from '@coreui/icons' import { CNavGroup, CNavItem, CNavTitle } from '@coreui/react' @@ -69,11 +70,15 @@ const _nav = [ to: '/scan', icon: <CIcon icon={cilShieldAlt} customClassName="nav-icon" />, }, + { + component: CNavTitle, + name: 'NETWORK', + }, { component: CNavItem, - name: 'Network Recommendations', + name: 'Recommendations', to: '/recommendations', - icon: <CIcon icon={cilShieldAlt} customClassName="nav-icon" />, + icon: <CIcon icon={cilEnvelopeLetter} customClassName="nav-icon" />, }, { component: CNavItem, diff --git a/frontend/src/components/AppHeader.js b/frontend/src/components/AppHeader.js index dd5f544e3106cf0fb859351e6ebec07eb8886f03..49327404c25327275152f35ee0fb5fdc90aa942a 100644 --- a/frontend/src/components/AppHeader.js +++ b/frontend/src/components/AppHeader.js @@ -40,29 +40,6 @@ const AppHeader = () => { Dashboard </CNavLink> </CNavItem> - <CNavItem> - <CNavLink href="#">Users</CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#">Settings</CNavLink> - </CNavItem> - </CHeaderNav> - <CHeaderNav> - <CNavItem> - <CNavLink href="#"> - <CIcon icon={cilBell} size="lg" /> - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#"> - <CIcon icon={cilList} size="lg" /> - </CNavLink> - </CNavItem> - <CNavItem> - <CNavLink href="#"> - <CIcon icon={cilEnvelopeOpen} size="lg" /> - </CNavLink> - </CNavItem> </CHeaderNav> <CHeaderNav className="ms-3"> <AppHeaderDropdown /> diff --git a/frontend/src/views/dashboard/Dashboard.js b/frontend/src/views/dashboard/Dashboard.js index e979a0c163080b1fc15d28fd7f6245832460485b..0aeeeee951b68ea97fffb8dea86bf437c821e90e 100644 --- a/frontend/src/views/dashboard/Dashboard.js +++ b/frontend/src/views/dashboard/Dashboard.js @@ -307,153 +307,6 @@ const Dashboard = () => { </CRow> </CCardFooter> </CCard> - - <WidgetsBrand withCharts /> - - <CRow> - <CCol xs> - <CCard className="mb-4"> - <CCardHeader>Traffic {' & '} Sales</CCardHeader> - <CCardBody> - <CRow> - <CCol xs={12} md={6} xl={6}> - <CRow> - <CCol sm={6}> - <div className="border-start border-start-4 border-start-info py-1 px-3"> - <div className="text-medium-emphasis small">New Clients</div> - <div className="fs-5 fw-semibold">9,123</div> - </div> - </CCol> - <CCol sm={6}> - <div className="border-start border-start-4 border-start-danger py-1 px-3 mb-3"> - <div className="text-medium-emphasis small">Recurring Clients</div> - <div className="fs-5 fw-semibold">22,643</div> - </div> - </CCol> - </CRow> - - <hr className="mt-0" /> - {progressGroupExample1.map((item, index) => ( - <div className="progress-group mb-4" key={index}> - <div className="progress-group-prepend"> - <span className="text-medium-emphasis small">{item.title}</span> - </div> - <div className="progress-group-bars"> - <CProgress thin color="info" value={item.value1} /> - <CProgress thin color="danger" value={item.value2} /> - </div> - </div> - ))} - </CCol> - - <CCol xs={12} md={6} xl={6}> - <CRow> - <CCol sm={6}> - <div className="border-start border-start-4 border-start-warning py-1 px-3 mb-3"> - <div className="text-medium-emphasis small">Pageviews</div> - <div className="fs-5 fw-semibold">78,623</div> - </div> - </CCol> - <CCol sm={6}> - <div className="border-start border-start-4 border-start-success py-1 px-3 mb-3"> - <div className="text-medium-emphasis small">Organic</div> - <div className="fs-5 fw-semibold">49,123</div> - </div> - </CCol> - </CRow> - - <hr className="mt-0" /> - - {progressGroupExample2.map((item, index) => ( - <div className="progress-group mb-4" key={index}> - <div className="progress-group-header"> - <CIcon className="me-2" icon={item.icon} size="lg" /> - <span>{item.title}</span> - <span className="ms-auto fw-semibold">{item.value}%</span> - </div> - <div className="progress-group-bars"> - <CProgress thin color="warning" value={item.value} /> - </div> - </div> - ))} - - <div className="mb-5"></div> - - {progressGroupExample3.map((item, index) => ( - <div className="progress-group" key={index}> - <div className="progress-group-header"> - <CIcon className="me-2" icon={item.icon} size="lg" /> - <span>{item.title}</span> - <span className="ms-auto fw-semibold"> - {item.value}{' '} - <span className="text-medium-emphasis small">({item.percent}%)</span> - </span> - </div> - <div className="progress-group-bars"> - <CProgress thin color="success" value={item.percent} /> - </div> - </div> - ))} - </CCol> - </CRow> - - <br /> - - <CTable align="middle" className="mb-0 border" hover responsive> - <CTableHead color="light"> - <CTableRow> - <CTableHeaderCell className="text-center"> - <CIcon icon={cilPeople} /> - </CTableHeaderCell> - <CTableHeaderCell>User</CTableHeaderCell> - <CTableHeaderCell className="text-center">Country</CTableHeaderCell> - <CTableHeaderCell>Usage</CTableHeaderCell> - <CTableHeaderCell className="text-center">Payment Method</CTableHeaderCell> - <CTableHeaderCell>Activity</CTableHeaderCell> - </CTableRow> - </CTableHead> - <CTableBody> - {tableExample.map((item, index) => ( - <CTableRow v-for="item in tableItems" key={index}> - <CTableDataCell className="text-center"> - <CAvatar size="md" src={item.avatar.src} status={item.avatar.status} /> - </CTableDataCell> - <CTableDataCell> - <div>{item.user.name}</div> - <div className="small text-medium-emphasis"> - <span>{item.user.new ? 'New' : 'Recurring'}</span> | Registered:{' '} - {item.user.registered} - </div> - </CTableDataCell> - <CTableDataCell className="text-center"> - <CIcon size="xl" icon={item.country.flag} title={item.country.name} /> - </CTableDataCell> - <CTableDataCell> - <div className="clearfix"> - <div className="float-start"> - <strong>{item.usage.value}%</strong> - </div> - <div className="float-end"> - <small className="text-medium-emphasis">{item.usage.period}</small> - </div> - </div> - <CProgress thin color={item.usage.color} value={item.usage.value} /> - </CTableDataCell> - <CTableDataCell className="text-center"> - <CIcon size="xl" icon={item.payment.icon} /> - </CTableDataCell> - <CTableDataCell> - <div className="small text-medium-emphasis">Last login</div> - <strong>{item.activity}</strong> - </CTableDataCell> - </CTableRow> - ))} - </CTableBody> - </CTable> - </CCardBody> - </CCard> - </CCol> - </CRow> </> ) } diff --git a/frontend/src/views/widgets/WidgetsDropdown.js b/frontend/src/views/widgets/WidgetsDropdown.js index 67247de60eae8d08b3672250bca95cd82cc597a5..4914227d63b1fd11e16d7b9fd3d8c4437e139705 100644 --- a/frontend/src/views/widgets/WidgetsDropdown.js +++ b/frontend/src/views/widgets/WidgetsDropdown.js @@ -1,3 +1,5 @@ +import React, { useState, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' import { CRow, CCol, @@ -7,50 +9,57 @@ import { CDropdownToggle, CWidgetStatsA, } from '@coreui/react' -import { getStyle } from '@coreui/utils' -import { CChartBar, CChartLine } from '@coreui/react-chartjs' import CIcon from '@coreui/icons-react' -import { cilArrowBottom, cilArrowTop, cilOptions } from '@coreui/icons' -import { countUniqueEmails } from '../emails/BreachesEmails' -import React, { useState, useEffect } from 'react' -import { useNavigate } from 'react-router-dom' +import { cilArrowTop, cilOptions } from '@coreui/icons' + const WidgetsDropdown = () => { const [breachResults, setBreachResults] = useState([]) + const [recommendationCount, setRecommendationCount] = useState(0) const navigate = useNavigate() + useEffect(() => { - // Fetch or set your breachResults data - // Example: Fetching data using an async function const fetchData = async () => { try { const token = localStorage.getItem('token') - const response = await fetch('http://localhost:8000/api/breaches/details/', { + + // Fetch breaches details + const breachesResponse = await fetch('http://localhost:8000/api/breaches/details/', { headers: { Authorization: `Bearer ${token}`, }, }) - - if (response.status === 401) { + if (breachesResponse.status === 401) { console.error('Unauthorized access to the API') navigate('/login') return } + const breachesData = await breachesResponse.json() + setBreachResults(breachesData) - const data = await response.json() - setBreachResults(data) + // Fetch recommendations count + const recommendationsResponse = await fetch( + 'http://localhost:8000/api/networkscanner/recommendation-count/', + { + headers: { + Authorization: `Bearer ${token}`, + }, + }, + ) + const recommendationsData = await recommendationsResponse.json() + setRecommendationCount(recommendationsData.total_recommendations) } catch (error) { - console.error('Error fetching breach results:', error) + console.error('Error fetching data:', error) } } - fetchData() // Call the async function - }, []) + fetchData() + }, [navigate]) + const handleViewBreachesClick = () => { - // Change window location to '/breaches-emails' window.location.href = '/breaches-emails#/view' } const handleAddBreachesClick = () => { - // Change window location to '/breaches-emails' window.location.href = '/breaches-emails#/add' } @@ -60,7 +69,7 @@ const WidgetsDropdown = () => { <CWidgetStatsA className="my-4 pb-4" color="primary" - value={<>{countUniqueEmails(breachResults)}</>} + value={<>{breachResults.length}</>} title="Emails Breached" action={ <CDropdown alignment="end"> @@ -77,17 +86,10 @@ const WidgetsDropdown = () => { </CCol> <CCol sm={6} lg={3}> <CWidgetStatsA - className="mb-4" + className="my-4 pb-4" color="info" - value={ - <> - $6.200{' '} - <span className="fs-6 fw-normal"> - (40.9% <CIcon icon={cilArrowTop} />) - </span> - </> - } - title="Income" + value={<> {recommendationCount}</>} + title="Total Network Recommendations" action={ <CDropdown alignment="end"> <CDropdownToggle color="transparent" caret={false} className="p-0"> @@ -101,231 +103,9 @@ const WidgetsDropdown = () => { </CDropdownMenu> </CDropdown> } - chart={ - <CChartLine - className="mt-3 mx-3" - style={{ height: '70px' }} - data={{ - labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], - datasets: [ - { - label: 'My First dataset', - backgroundColor: 'transparent', - borderColor: 'rgba(255,255,255,.55)', - pointBackgroundColor: getStyle('--cui-info'), - data: [1, 18, 9, 17, 34, 22, 11], - }, - ], - }} - options={{ - plugins: { - legend: { - display: false, - }, - }, - maintainAspectRatio: false, - scales: { - x: { - grid: { - display: false, - drawBorder: false, - }, - ticks: { - display: false, - }, - }, - y: { - min: -9, - max: 39, - display: false, - grid: { - display: false, - }, - ticks: { - display: false, - }, - }, - }, - elements: { - line: { - borderWidth: 1, - }, - point: { - radius: 4, - hitRadius: 10, - hoverRadius: 4, - }, - }, - }} - /> - } - /> - </CCol> - <CCol sm={6} lg={3}> - <CWidgetStatsA - className="mb-4" - color="warning" - value={ - <> - 2.49{' '} - <span className="fs-6 fw-normal"> - (84.7% <CIcon icon={cilArrowTop} />) - </span> - </> - } - title="Conversion Rate" - action={ - <CDropdown alignment="end"> - <CDropdownToggle color="transparent" caret={false} className="p-0"> - <CIcon icon={cilOptions} className="text-high-emphasis-inverse" /> - </CDropdownToggle> - <CDropdownMenu> - <CDropdownItem>Action</CDropdownItem> - <CDropdownItem>Another action</CDropdownItem> - <CDropdownItem>Something else here...</CDropdownItem> - <CDropdownItem disabled>Disabled action</CDropdownItem> - </CDropdownMenu> - </CDropdown> - } - chart={ - <CChartLine - className="mt-3" - style={{ height: '70px' }} - data={{ - labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], - datasets: [ - { - label: 'My First dataset', - backgroundColor: 'rgba(255,255,255,.2)', - borderColor: 'rgba(255,255,255,.55)', - data: [78, 81, 80, 45, 34, 12, 40], - fill: true, - }, - ], - }} - options={{ - plugins: { - legend: { - display: false, - }, - }, - maintainAspectRatio: false, - scales: { - x: { - display: false, - }, - y: { - display: false, - }, - }, - elements: { - line: { - borderWidth: 2, - tension: 0.4, - }, - point: { - radius: 0, - hitRadius: 10, - hoverRadius: 4, - }, - }, - }} - /> - } - /> - </CCol> - <CCol sm={6} lg={3}> - <CWidgetStatsA - className="mb-4" - color="danger" - value={ - <> - 44K{' '} - <span className="fs-6 fw-normal"> - (-23.6% <CIcon icon={cilArrowBottom} />) - </span> - </> - } - title="Sessions" - action={ - <CDropdown alignment="end"> - <CDropdownToggle color="transparent" caret={false} className="p-0"> - <CIcon icon={cilOptions} className="text-high-emphasis-inverse" /> - </CDropdownToggle> - <CDropdownMenu> - <CDropdownItem>Action</CDropdownItem> - <CDropdownItem>Another action</CDropdownItem> - <CDropdownItem>Something else here...</CDropdownItem> - <CDropdownItem disabled>Disabled action</CDropdownItem> - </CDropdownMenu> - </CDropdown> - } - chart={ - <CChartBar - className="mt-3 mx-3" - style={{ height: '70px' }} - data={{ - labels: [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December', - 'January', - 'February', - 'March', - 'April', - ], - datasets: [ - { - label: 'My First dataset', - backgroundColor: 'rgba(255,255,255,.2)', - borderColor: 'rgba(255,255,255,.55)', - data: [78, 81, 80, 45, 34, 12, 40, 85, 65, 23, 12, 98, 34, 84, 67, 82], - barPercentage: 0.6, - }, - ], - }} - options={{ - maintainAspectRatio: false, - plugins: { - legend: { - display: false, - }, - }, - scales: { - x: { - grid: { - display: false, - drawTicks: false, - }, - ticks: { - display: false, - }, - }, - y: { - grid: { - display: false, - drawBorder: false, - drawTicks: false, - }, - ticks: { - display: false, - }, - }, - }, - }} - /> - } /> </CCol> + {/* Other widgets here */} </CRow> ) }