From c57a3b02737c7c3fc4257ebb77ada179f1e843fd Mon Sep 17 00:00:00 2001 From: scgomez Date: Sun, 3 Mar 2024 17:37:11 -0600 Subject: [PATCH 1/3] Completed first ticket --- .../pages/Landing/RenderLandingPage.jsx | 112 ++++++- src/styles/RenderLandingPage.less | 289 +++++++++++++----- 2 files changed, 315 insertions(+), 86 deletions(-) diff --git a/src/components/pages/Landing/RenderLandingPage.jsx b/src/components/pages/Landing/RenderLandingPage.jsx index 6be6b69a4..11929150c 100644 --- a/src/components/pages/Landing/RenderLandingPage.jsx +++ b/src/components/pages/Landing/RenderLandingPage.jsx @@ -1,25 +1,31 @@ import React from 'react'; // ADD IMPORTS BACK FOR GRAPHS SECTION -// import GrantRatesByOfficeImg from '../../../styles/Images/bar-graph-no-text.png'; -// import GrantRatesByNationalityImg from '../../../styles/Images/pie-chart-no-text.png'; -// import GrantRatesOverTimeImg from '../../../styles/Images/line-graph-no-text.png'; + +// SR - imported styling images +import GrantRatesByOfficeImg from '../../../styles/Images/bar-graph-no-text.png'; +import GrantRatesByNationalityImg from '../../../styles/Images/pie-chart-no-text.png'; +import GrantRatesOverTimeImg from '../../../styles/Images/line-graph-no-text.png'; import HrfPhoto from '../../../styles/Images/paper-stack.jpg'; import '../../../styles/RenderLandingPage.less'; import { Button } from 'antd'; import { useHistory } from 'react-router-dom'; + // for the purposes of testing PageNav -// import PageNav from '../../common/PageNav'; +//import PageNav from '../../common/PageNav'; function RenderLandingPage(props) { + // declare function for scroll back to top const scrollToTop = () => { document.body.scrollTop = 0; document.documentElement.scrollTop = 0; }; + // declare history function for navigation const history = useHistory(); return (
+ {/* Header Section */}

Asylum Office Grant Rate Tracker

@@ -31,18 +37,64 @@ function RenderLandingPage(props) {
- {/* Graphs Section: Add code here for the graphs section for your first ticket */} - {/*
*/} + {/* Graphs Section (SR 3/3)*/} +
+
+ Grant rates by office +

Search Grant Rates By Office

+
+
+ Grant rates by nationality +

Search Grant Rates By Nationality

+
+
+ Grant rates over time +

Search Grant Rates Over Time

+
+
+ + {/* Button container and buttons */}
+ + {/* Download Data Button (SR 3/3) */} +
+ {/* Middle Section (SR 3/3)*/}
Human Rights First @@ -55,13 +107,53 @@ function RenderLandingPage(props) { through a Freedom of Information Act request. You can search for information on asylum grant rates by year, nationality, and asylum office, visualize the data with charts and heat maps, and download - the data set + the data set.
- {/* Bottom Section: Add code here for the graphs section for your first ticket */} - {/*
*/} + {/* Bottom Section (SR 3/3) */} +
+

Systemic Disparity Insights

+
+
+

36%

+

+ By the end of the Trump administration, the average asylum + office grant rate had fallen 36 percent from an average of 44 + percent in fiscal year 2016 to 28 percent in fiscal year 2020. +

+
+
+

5%

+

+ The New York asylum office grant rate dropped to 5 percent in + fiscal year 2020. +

+
+
+

6x Lower

+

+ Between fiscal year 2017 and 2020, the New York asylum office’s + average grant rate was six times lower than the San Francisco + asylum office. +

+
+
+ + {/* Read More button takes user to article on humanrightsfirst.org (SR 3/3) */} + +

scrollToTop()} className="back-to-top"> Back To Top ^

diff --git a/src/styles/RenderLandingPage.less b/src/styles/RenderLandingPage.less index 70ee07c37..d68332d72 100644 --- a/src/styles/RenderLandingPage.less +++ b/src/styles/RenderLandingPage.less @@ -10,7 +10,7 @@ .header { background-color: @main-color; - display:flex; + display: flex; text-align: center; width: 100%; height: 20vh; @@ -40,19 +40,100 @@ color: @white; font-size: 1.2rem; } +// Added a few general styles for html elements +img { + vertical-align: middle; + border-style: none; + overflow-clip-margin: content-box; +} + +p { + display: block; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; +} + +a { + color: #1890ff; + text-decoration: none; + background-color: initial; + outline: none; + cursor: pointer; + transition: color .3s; +} + + +button { + padding-block: 1px; + padding-inline: 6px; +} + +// Added graphs section styling +.graphs-section { + display: flex; + justify-content: space-evenly; + +} + +// Added grant rates by office graph styling +.grant-rates-by-office-container { + text-align: center; + height: 165px; + font-size: 1.5rem; + font-family: "acumin"; +} + + +.gr-office-img { + height: 165px; + width: 320px; + margin-bottom: 10%; +} + +// Added grant rates by nationality graph styling +.grant-rates-by-nationality-container { + text-align: center; + margin-bottom: 3%; + margin-left: -15px; + font-size: 1.5rem; + font-family: "acumin"; +} + +.gr-nationality-img { + height: 175px; + width: 200px; + margin-bottom: 7%; + margin-left: -15px; +} -// .graphs-section { -// ADD THIS BACK IN WHEN YOU HAVE THE GRAPHS SECTION -// } +// Added grant rates over time graph styling +.grant-rates-over-time-container { + text-align: center; + height: 165px; + font-size: 1.5rem; + font-family: "acumin"; +} +.gr-overtime-img { + height: 165px; + width: 300px; + margin-bottom: 10%; +} + +// Update width of button container to match example landing page .view-more-data-btn-container { height: 44px; - width: 180px; + width: 300px; margin: 0 auto; text-align: center; margin-bottom: 10%; + display: flex; + justify-content: space-between; } + .middle-section { display: flex; justify-content: space-evenly; @@ -75,9 +156,53 @@ font-size: 1.4rem; } -// .bottom-section { -// ADD THIS BACK IN WHEN YOU HAVE A BOTTOM SECTION -// } +// Added bottom section styling +.bottom-section { + text-align: center; + margin-top: 10%; +} + +.bottom-section h1 { + font-family: "moneta"; + font-size: 3.5rem; + margin-bottom: 10%; +} + +// Added data and data point container styling for bottom section +.data-container { + display: flex; + justify-content: space-evenly; + margin-bottom: 5%; +} + +.data-point-container { + width: 433px; +} + +.data-point-container h2 { + font-size: 3rem; +} + +.data-container h3 { + font-family: "acumin"; + font-size: 1.2rem; +} + +// Added specific fonts for each data point container to match example landing page +.first-data-point-container h2 { + font-family: "moneta"; +} + + +.second-data-point-container h2 { + font-family: "beatrice_regular"; +} + + +.third-data-point-container h2 { + font-family: "beatrice"; +} + .back-to-top { width: 8%; @@ -95,29 +220,30 @@ } .ant-btn:hover { - border-color: @secondary-color !important; + border-color: @secondary-color !important; } @media @mobile-small { .header { height: 30vh; - display: block; - position: relative; + display: block; + position: relative; } - .hrf-logo-img-container{ - height:fit-content; - position: absolute; - top: 0; - left: 0; - margin: 2% 2%; + + .hrf-logo-img-container { + height: fit-content; + position: absolute; + top: 0; + left: 0; + margin: 2% 2%; } .hrf-logo-img-container img { height: 30px; width: 48px; - + } - + .header-text-container h1 { font-size: 1.5rem; padding-top: 5%; @@ -170,7 +296,7 @@ width: 85%; text-align: center; margin: 0 auto; - + } .middle-section-text-container h3 { @@ -213,23 +339,24 @@ @media @mobile-medium { .header { height: 20vh; - display: block; - position: relative; + display: block; + position: relative; } - .hrf-logo-img-container{ - height:fit-content; - position: absolute; - top: 0; - left: 0; - margin: 2% 2%; + + .hrf-logo-img-container { + height: fit-content; + position: absolute; + top: 0; + left: 0; + margin: 2% 2%; } .hrf-logo-img-container img { height: 30px; width: 48px; - + } - + .header-text-container h1 { font-size: 1.5rem; padding-top: 5%; @@ -271,7 +398,7 @@ background-color: @secondary-color; padding-top: 5%; text-align: center; - + } .hrf-img { @@ -281,7 +408,7 @@ .middle-section-text-container { width: 85%; - text-align: center; + text-align: center; margin: 0 auto; } @@ -328,22 +455,23 @@ @media @mobile-large { .header { height: 20vh; - display: block; - position: relative; + display: block; + position: relative; } - .hrf-logo-img-container{ - height:fit-content; - position: absolute; - top: 0; - left: 0; - margin: 2% 2%; + + .hrf-logo-img-container { + height: fit-content; + position: absolute; + top: 0; + left: 0; + margin: 2% 2%; } .hrf-logo-img-container img { height: 30px; width: 48px; } - + .header-text-container h1 { font-size: 1.5rem; padding-top: 5%; @@ -394,7 +522,7 @@ .middle-section-text-container { width: 85%; - text-align: center; + text-align: center; margin: 0 auto; } @@ -436,29 +564,30 @@ display: flex; flex-wrap: wrap; } - + } @media @tablet-small { .header { height: 30vh; - display: block; - position: relative; + display: block; + position: relative; } - .hrf-logo-img-container{ - height:fit-content; - position: absolute; - top: 0; - left: 0; - margin: 1% 1%; + + .hrf-logo-img-container { + height: fit-content; + position: absolute; + top: 0; + left: 0; + margin: 1% 1%; } .hrf-logo-img-container img { height: 66px; width: 108px; - + } - + .header-text-container h1 { font-size: 2.5rem; padding-top: 5%; @@ -556,23 +685,24 @@ @media @tablet-large { .header { height: 30vh; - display: block; - position: relative; + display: block; + position: relative; } - .hrf-logo-img-container{ - height:fit-content; - position: absolute; - top: 0; - left: 0; - + + .hrf-logo-img-container { + height: fit-content; + position: absolute; + top: 0; + left: 0; + } .hrf-logo-img-container img { height: 83px; width: 136px; - + } - + .header-text-container h1 { font-size: 3.5rem; padding-top: 5%; @@ -625,6 +755,7 @@ height: 232px; width: 450px; } + .grant-rates-over-time-container p { font-size: 2.5rem; } @@ -674,9 +805,11 @@ width: 85%; margin-bottom: 5%; } + .first-data-point-container h2 { - font-size: 3.5rem; + font-size: 3.5rem; } + .first-data-point-container h3 { font-size: 1.5rem; } @@ -685,23 +818,27 @@ width: 85%; margin-bottom: 5%; } + .second-data-point-container h2 { font-size: 3.5rem; - } - .second-data-point-container h3 { - font-size: 1.5rem; - } + } + + .second-data-point-container h3 { + font-size: 1.5rem; + } .third-data-point-container { width: 85%; margin-bottom: 5%; } + .third-data-point-container h2 { font-size: 3.5rem; - } - .third-data-point-container h3 { - font-size: 1.5rem; - } + } + + .third-data-point-container h3 { + font-size: 1.5rem; + } .back-to-top { width: 35%; @@ -733,7 +870,7 @@ } .middle-section { - display:flex; + display: flex; width: 100%; justify-content: space-evenly; padding: 0 2%; @@ -751,8 +888,8 @@ padding: 0 2%; } - - .first-data-point-container { + + .first-data-point-container { width: 23%; } @@ -763,7 +900,7 @@ .third-data-point-container { width: 23%; } - + .back-to-top { width: 35%; } From cb6f60317385cf28c2372ab349b6a037d43003af Mon Sep 17 00:00:00 2001 From: scgomez Date: Sun, 17 Mar 2024 14:54:33 -0500 Subject: [PATCH 2/3] shipping labs build sprint ticket #2 --- .../pages/DataVisualizations/GraphWrapper.jsx | 31 +- .../Graphs/CitizenshipMapAll.jsx | 10 +- .../Graphs/CitizenshipMapSingleOffice.jsx | 12 +- .../Graphs/TableComponents/TableRow.jsx | 37 +- .../DataVisualizations/GraphsContainer.jsx | 24 +- src/index.jsx | 6 +- src/utils/rawApiDataToPlotlyReadyInfo.js | 658 ++++++++++++------ 7 files changed, 519 insertions(+), 259 deletions(-) diff --git a/src/components/pages/DataVisualizations/GraphWrapper.jsx b/src/components/pages/DataVisualizations/GraphWrapper.jsx index 2d25a26e8..c082f4d92 100644 --- a/src/components/pages/DataVisualizations/GraphWrapper.jsx +++ b/src/components/pages/DataVisualizations/GraphWrapper.jsx @@ -10,7 +10,7 @@ import YearLimitsSelect from './YearLimitsSelect'; import ViewSelect from './ViewSelect'; import axios from 'axios'; import { resetVisualizationQuery } from '../../../state/actionCreators'; -import test_data from '../../../data/test_data.json'; +//import test_data from '../../../data/test_data.json'; import { colors } from '../../../styles/data_vis_colors'; import ScrollToTopOnMount from '../../../utils/scrollToTopOnMount'; @@ -19,10 +19,12 @@ const { background_color } = colors; function GraphWrapper(props) { const { set_view, dispatch } = props; let { office, view } = useParams(); + if (!view) { set_view('time-series'); view = 'time-series'; } + let map_to_render; if (!office) { switch (view) { @@ -50,6 +52,7 @@ function GraphWrapper(props) { break; } } + function updateStateWithNewData(years, view, office, stateSettingCallback) { /* _ _ @@ -73,9 +76,15 @@ function GraphWrapper(props) { */ + // updated test data to API data (SR 3/16/24) + const baseURL = `https://hrf-asylum-be-b.herokuapp.com/cases`; + let endpoint = + view === 'citizenship' ? '/citizenshipSummary' : '/fiscalSummary'; + if (office === 'all' || !office) { axios - .get(process.env.REACT_APP_API_URI, { + //.get(process.env.REACT_APP_API_URI, { + .get(`${baseURL}${endpoint}`, { // mock URL, can be simply replaced by `${Real_Production_URL}/summary` in prod! params: { from: years[0], @@ -83,15 +92,21 @@ function GraphWrapper(props) { }, }) .then(result => { - stateSettingCallback(view, office, test_data); // <-- `test_data` here can be simply replaced by `result.data` in prod! + //stateSettingCallback(view, office, view === 'citizenship' ? result.data : [result.data]); // <-- `test_data` here can be simply replaced by `result.data` in prod! + stateSettingCallback( + view, + office, + view === 'citizenship' ? result.data : [result.data] + ); }) .catch(err => { console.error(err); }); } else { axios - .get(process.env.REACT_APP_API_URI, { - // mock URL, can be simply replaced by `${Real_Production_URL}/summary` in prod! + //.get(process.env.REACT_APP_API_URI, { + // mock URL, can be simply replaced by `${Real_Production_URL}/summary` in prod! + .get(`${baseURL}${endpoint}`, { params: { from: years[0], to: years[1], @@ -99,7 +114,11 @@ function GraphWrapper(props) { }, }) .then(result => { - stateSettingCallback(view, office, test_data); // <-- `test_data` here can be simply replaced by `result.data` in prod! + stateSettingCallback( + view, + office, + view === 'citizenship' ? result.data : [result.data] + ); // <-- `test_data` here can be simply replaced by `result.data` in prod! }) .catch(err => { console.error(err); diff --git a/src/components/pages/DataVisualizations/Graphs/CitizenshipMapAll.jsx b/src/components/pages/DataVisualizations/Graphs/CitizenshipMapAll.jsx index a94c202a9..67483827c 100644 --- a/src/components/pages/DataVisualizations/Graphs/CitizenshipMapAll.jsx +++ b/src/components/pages/DataVisualizations/Graphs/CitizenshipMapAll.jsx @@ -58,7 +58,7 @@ function CitizenshipMapAll(props) { 'Citizenship', 'Total Cases', '% Granted', - '% Admin Close / Dismissal', + '% Admin Closed / Dismissal', '% Denied', ]; @@ -115,8 +115,12 @@ function CitizenshipMapAll(props) { />

Table view

diff --git a/src/components/pages/DataVisualizations/Graphs/CitizenshipMapSingleOffice.jsx b/src/components/pages/DataVisualizations/Graphs/CitizenshipMapSingleOffice.jsx index 18305f99e..bed68a48c 100644 --- a/src/components/pages/DataVisualizations/Graphs/CitizenshipMapSingleOffice.jsx +++ b/src/components/pages/DataVisualizations/Graphs/CitizenshipMapSingleOffice.jsx @@ -57,7 +57,7 @@ function CitizenshipMapSingleOffice(props) { 'Citizenship', 'Total Cases', '% Granted', - '% Admin Close / Dismissal', + '% Admin Closed / Dismissal', '% Denied', ]; return ( @@ -107,10 +107,14 @@ function CitizenshipMapSingleOffice(props) { }} style={{ width: '100%', fontWeight: '900' }} /> - +

Table view

diff --git a/src/components/pages/DataVisualizations/Graphs/TableComponents/TableRow.jsx b/src/components/pages/DataVisualizations/Graphs/TableComponents/TableRow.jsx index 2ede5a306..066d70cc8 100644 --- a/src/components/pages/DataVisualizations/Graphs/TableComponents/TableRow.jsx +++ b/src/components/pages/DataVisualizations/Graphs/TableComponents/TableRow.jsx @@ -17,25 +17,26 @@ function TableRow(props) { }} > {columns.map((property, idx) => { - if (row) { - if (typeof row[property] === 'object') { - return ( - + ); + } else { + return ( +
+ - ); - } else { - return ( -
- -
- ); - } +
+ ); } })}
diff --git a/src/components/pages/DataVisualizations/GraphsContainer.jsx b/src/components/pages/DataVisualizations/GraphsContainer.jsx index 9dc39a163..4715a8979 100644 --- a/src/components/pages/DataVisualizations/GraphsContainer.jsx +++ b/src/components/pages/DataVisualizations/GraphsContainer.jsx @@ -26,17 +26,17 @@ function GraphsContainer() { 'New Orleans, LA', ]; function handle_office_select(value) { - // if (view === 'office-heat-map') { - // set_view('time-series'); - // } - // if (value === 'All') { - // history.push( - // `/graphs/all/${view === 'office-heat-map' ? 'time-series' : view}` - // ); - // } - // history.push( - // `/graphs/${value}/${view === 'office-heat-map' ? 'time-series' : view}` - // ); + if (view === 'office-heat-map') { + set_view('time-series'); + } + if (value === 'All') { + history.push( + `/graphs/all/${view === 'office-heat-map' ? 'time-series' : view}` + ); + } + history.push( + `/graphs/${value}/${view === 'office-heat-map' ? 'time-series' : view}` + ); switch (value) { case 'All Offices': @@ -152,7 +152,7 @@ function GraphsContainer() { > {offices.map((office, idx) => office === 'All' ? ( - ) : ( diff --git a/src/index.jsx b/src/index.jsx index 73962baa5..124896cb5 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -29,9 +29,9 @@ const store = configureStore({ reducer: reducer }); ReactDOM.render( - - - + {/* */} + + {/* */} , document.getElementById('root') diff --git a/src/utils/rawApiDataToPlotlyReadyInfo.js b/src/utils/rawApiDataToPlotlyReadyInfo.js index 45c4c81c5..a85c58b70 100644 --- a/src/utils/rawApiDataToPlotlyReadyInfo.js +++ b/src/utils/rawApiDataToPlotlyReadyInfo.js @@ -1,4 +1,6 @@ const rawApiDataToPlotlyReadyInfo = (view, office, data) => { + let rowsForTable; + let rowItem; const officeNames = [ 'Los Angeles, CA', 'San Francisco, CA', @@ -11,239 +13,469 @@ const rawApiDataToPlotlyReadyInfo = (view, office, data) => { 'Miami, FL', 'New Orleans, LA', ]; - let rowItem; - let rowsForTable; - let yearMinMax = []; //variable to set minYear and MaxYear - for (let yearResults of data[0]['yearResults']) { - yearMinMax.push(yearResults['fiscal_year']); - } + // Handling for 'citizenship' view first (SR 3/16/24) + if (view === 'citizenship') { + const rowsForTable = data.map(item => ({ + Citizenship: item.citizenship, + 'Total Cases': item.totalCases, + '% Granted': Number(item.granted).toFixed(2), + '% Admin Closed / Dismissal': Number(item.adminClosed).toFixed(2), + '% Denied': Number(item.denied).toFixed(2), + })); + + const countryGrantRateObj = { + countries: data.map(item => item.citizenship), + countriesPercentGranteds: data.map(item => item.granted), + }; + + return { rowsForTable, countryGrantRateObj }; + } else { + const yearMinMax = []; + const yearByOfficeByGrant = {}; - const yearByOfficeByGrant = {}; //Object that contacts year by Office by grant rate information - for (let office of data[0]['yearResults']) { - if (!yearByOfficeByGrant[office['fiscal_year']]) - yearByOfficeByGrant[office['fiscal_year']] = {}; //if year not existing set to empty object - for (let yearData of office['yearData']) { - yearByOfficeByGrant[office['fiscal_year']][yearData['office']] = { - //assign rates to year:{office:{}} - granted: yearData['granted'], - adminClosed: yearData['adminClosed'], - denied: yearData['denied'], + for (let office of data[0]['yearResults']) { + if (!yearByOfficeByGrant[office['fiscal_year']]) { + yearByOfficeByGrant[office['fiscal_year']] = {}; + } + for (let yearData of office['yearData']) { + yearByOfficeByGrant[office['fiscal_year']][yearData['office']] = { + granted: yearData['granted'], + adminClosed: yearData['adminClosed'], + denied: yearData['denied'], + }; + } + } + + const officeData = {}; + for (let officeName of officeNames) { + officeData[officeName] = { + xYears: [], + totals: [], + yTotalPercentGranteds: [], + totalPercentAdminCloseds: [], + totalPercentDenieds: [], }; } - } - const officeData = {}; //object that holds each % as a key of array value - for (let officeName of officeNames) { - officeData[officeName] = { - xYears: [], - totals: [], - yTotalPercentGranteds: [], - totalPercentAdminCloseds: [], - totalPercentDenieds: [], - }; - } - for (let yearResults of data[0]['yearResults']) { - for (let yearData of yearResults['yearData']) { - officeData[yearData['office']]['xYears'].push(yearResults['fiscal_year']); - officeData[yearData['office']]['totals'].push(yearData['totalCases']); - officeData[yearData['office']]['yTotalPercentGranteds'].push( - yearData['granted'] - ); - officeData[yearData['office']]['totalPercentAdminCloseds'].push( - yearData['adminClosed'] - ); - officeData[yearData['office']]['totalPercentDenieds'].push( - yearData['denied'] - ); + for (let yearResults of data[0]['yearResults']) { + yearMinMax.push(yearResults['fiscal_year']); + for (let yearData of yearResults['yearData']) { + officeData[yearData['office']]['xYears'].push( + yearResults['fiscal_year'] + ); + officeData[yearData['office']]['totals'].push(yearData['totalCases']); + officeData[yearData['office']]['yTotalPercentGranteds'].push( + yearData['granted'] + ); + officeData[yearData['office']]['totalPercentAdminCloseds'].push( + yearData['adminClosed'] + ); + officeData[yearData['office']]['totalPercentDenieds'].push( + yearData['denied'] + ); + } } - } - if (!office || office === 'all') { - switch (view) { - case 'time-series': - const rowsForAllDisplay = []; - for (let yearResults of data[0].yearResults) { - rowItem = { - 'Fiscal Year': yearResults.fiscal_year, - 'Total Cases': yearResults.totalCases, - '% Granted': Number(yearResults.granted).toFixed(2), - '% Admin Close / Dismissal': Number( - yearResults.adminClosed - ).toFixed(2), - '% Denied': Number(yearResults.denied).toFixed(2), + if (!office || office === 'all') { + switch (view) { + case 'time-series': + const rowsForAllDisplay = []; + for (let yearResults of data[0].yearResults) { + rowItem = { + 'Fiscal Year': yearResults.fiscal_year, + 'Total Cases': yearResults.totalCases, + '% Granted': Number(yearResults.granted).toFixed(2), + '% Admin Close / Dismissal': Number( + yearResults.adminClosed + ).toFixed(2), + '% Denied': Number(yearResults.denied).toFixed(2), + }; + rowsForAllDisplay.push(rowItem); + } + + const finalData = { + xYears: [], + totals: [], + yTotalPercentGranteds: [], + totalPercentAdminCloseds: [], + totalPercentDenieds: [], }; - rowsForAllDisplay.push(rowItem); - } - - const finalData = { - xYears: [], - totals: [], - yTotalPercentGranteds: [], - totalPercentAdminCloseds: [], - totalPercentDenieds: [], - }; - for (let officeName of data[0]['yearResults']) { - finalData['xYears'].push(officeName['fiscal_year']); - finalData['totals'].push(officeName['totalCases']); - finalData['yTotalPercentGranteds'].push(officeName['granted']); - finalData['totalPercentAdminCloseds'].push(officeName['adminClosed']); - finalData['totalPercentDenieds'].push(officeName['denied']); - } - - return { ...finalData, rowsForAllDisplay, officeData }; - - case 'office-heat-map': - rowsForTable = []; - for (let yearResults of data[0].yearResults) { - for (let officeKey of officeNames) { + for (let officeName of data[0]['yearResults']) { + finalData['xYears'].push(officeName['fiscal_year']); + finalData['totals'].push(officeName['totalCases']); + finalData['yTotalPercentGranteds'].push(officeName['granted']); + finalData['totalPercentAdminCloseds'].push( + officeName['adminClosed'] + ); + finalData['totalPercentDenieds'].push(officeName['denied']); + } + + return { ...finalData, rowsForAllDisplay, officeData }; + + case 'office-heat-map': + rowsForTable = []; + for (let yearResults of data[0].yearResults) { + for (let officeKey of officeNames) { + if ( + yearResults.yearData.filter( + yearItem => yearItem.office === officeKey + ).length > 0 + ) { + rowItem = { + 'Year [Office]': + String(yearResults.fiscal_year) + + ' [' + + String(officeKey) + + ']', + 'Total Cases': yearResults.yearData.filter( + yearItem => yearItem.office === officeKey + )[0].totalCases, + '% Granted': Number( + yearResults.yearData.filter( + yearItem => yearItem.office === officeKey + )[0].granted + ).toFixed(2), + '% Admin Close / Dismissal': Number( + yearResults.yearData.filter( + yearItem => yearItem.office === officeKey + )[0].adminClosed + ).toFixed(2), + '% Denied': Number( + yearResults.yearData.filter( + yearItem => yearItem.office === officeKey + )[0].denied + ).toFixed(2), + }; + rowsForTable.push(rowItem); + } + } + } + const officeHeatMapDataObject = { + //declare helper object to construct data for heatmap plotly + x: officeNames, //office + y: [], //year + z: [], //rate + }; + for (let fiscal_year in yearByOfficeByGrant) { + //loop through + officeHeatMapDataObject['y'].push(fiscal_year); //include year into y axis + let zAxisArray = []; //Array to hold each row for z axis + for (let officeName of officeNames) { + //loop using unique office names + zAxisArray.push( + yearByOfficeByGrant[fiscal_year][officeName] + ? yearByOfficeByGrant[fiscal_year][officeName]['granted'] + : 0 + ); + } + officeHeatMapDataObject['z'].push(zAxisArray); //push to zaxis array + } + return { officeHeatMapDataObject, rowsForTable }; + // switch(view){ + // case 'time-series': + // case 'office-heat-map': + // const rowsForAllDisplay = data[0].yearResults.map(yearResult => ({ + // 'Fiscal Year': yearResult.fiscal_year, + // 'Total Cases': yearResult.totalCases, + // '% Granted': Number(yearResult.granted).toFixed(2), + // '% Admin Close / Dismissal': Number(yearResult.adminClosed).toFixed(2), + // '% Denied': Number(yearResult.denied).toFixed(2), + // })); + // return { + // rowsForTable: rowsForAllDisplay, + // officeData + // }; + + default: + return {}; + } + } else { + let rowsForTable = []; + switch (view) { + case 'time-series': + data[0].yearResults.sort((a, b) => a.fiscal_year - b.fiscal_year); + for (let i = 0; i < data[0].yearResults.length; i++) { if ( - yearResults.yearData.filter( - yearItem => yearItem.office === officeKey - ).length > 0 + data[0].yearResults[i].yearData.filter( + dataItem => dataItem.office === office + )[0] ) { + const officeObj = data[0].yearResults[i].yearData.filter( + dataItem => dataItem.office === office + )[0]; rowItem = { - 'Year [Office]': - String(yearResults.fiscal_year) + - ' [' + - String(officeKey) + - ']', - 'Total Cases': yearResults.yearData.filter( - yearItem => yearItem.office === officeKey - )[0].totalCases, - '% Granted': Number( - yearResults.yearData.filter( - yearItem => yearItem.office === officeKey - )[0].granted - ).toFixed(2), + 'Fiscal Year': data[0].yearResults[i].fiscal_year, + 'Total Cases': officeObj.totalCases, + '% Granted': Number(officeObj.granted).toFixed(2), '% Admin Close / Dismissal': Number( - yearResults.yearData.filter( - yearItem => yearItem.office === officeKey - )[0].adminClosed - ).toFixed(2), - '% Denied': Number( - yearResults.yearData.filter( - yearItem => yearItem.office === officeKey - )[0].denied + officeObj.adminClosed ).toFixed(2), + '% Denied': Number(officeObj.denied).toFixed(2), }; rowsForTable.push(rowItem); } } - } - const officeHeatMapDataObject = { - //declare helper object to construct data for heatmap plotly - x: officeNames, //office - y: [], //year - z: [], //rate - }; - for (let fiscal_year in yearByOfficeByGrant) { - //loop through - officeHeatMapDataObject['y'].push(fiscal_year); //include year into y axis - let zAxisArray = []; //Array to hold each row for z axis - for (let officeName of officeNames) { - //loop using unique office names - zAxisArray.push( - yearByOfficeByGrant[fiscal_year][officeName] - ? yearByOfficeByGrant[fiscal_year][officeName]['granted'] - : 0 - ); - } - officeHeatMapDataObject['z'].push(zAxisArray); //push to zaxis array - } - return { officeHeatMapDataObject, rowsForTable }; - - case 'citizenship': - rowsForTable = []; - for (let item of data[0].citizenshipResults) { - rowItem = { - Citizenship: item.citizenship, - 'Total Cases': item.totalCases, - '% Granted': Number(item.granted).toFixed(2), - '% Admin Close / Dismissal': Number(item.adminClosed).toFixed(2), - '% Denied': Number(item.denied).toFixed(2), + const singleOfficeDataObject = officeData[office]; + return { + rowsForTable, + singleOfficeDataObject, }; - rowsForTable.push(rowItem); - } - const countryGrantRateObj = { - countries: [], - countriesPercentGranteds: [], - }; - for (let country of data[0]['citizenshipResults']) { - countryGrantRateObj['countries'].push(country['citizenship']); - countryGrantRateObj['countriesPercentGranteds'].push( - country['granted'] - ); - } - return { - rowsForTable, - countryGrantRateObj, - }; - default: - return {}; - } - } else { - switch (view) { - case 'time-series': - rowsForTable = []; - data[0].yearResults.sort((a, b) => a.fiscal_year - b.fiscal_year); - for (let i = 0; i < data[0].yearResults.length; i++) { - if ( - data[0].yearResults[i].yearData.filter( - dataItem => dataItem.office === office - )[0] - ) { - const officeObj = data[0].yearResults[i].yearData.filter( - dataItem => dataItem.office === office - )[0]; - rowItem = { - 'Fiscal Year': data[0].yearResults[i].fiscal_year, - 'Total Cases': officeObj.totalCases, - '% Granted': Number(officeObj.granted).toFixed(2), - '% Admin Close / Dismissal': Number( - officeObj.adminClosed - ).toFixed(2), - '% Denied': Number(officeObj.denied).toFixed(2), - }; - rowsForTable.push(rowItem); - } - } - const singleOfficeDataObject = officeData[office]; - return { - rowsForTable, - singleOfficeDataObject, - }; - case 'citizenship': - rowsForTable = []; - for (let item of data[0].citizenshipResults) { - rowItem = { - Citizenship: item.citizenship, - 'Total Cases': item.totalCases, - '% Granted': Number(item.granted).toFixed(2), - '% Admin Close / Dismissal': Number(item.adminClosed).toFixed(2), - '% Denied': Number(item.denied).toFixed(2), - }; - rowsForTable.push(rowItem); - } - const countryGrantRateObj = { - countries: [], - countriesPercentGranteds: [], - }; - for (let country of data[0]['citizenshipResults']) { - countryGrantRateObj['countries'].push(country['citizenship']); - countryGrantRateObj['countriesPercentGranteds'].push( - country['granted'] - ); - } - return { - rowsForTable, - countryGrantRateObj, - }; - default: - return {}; + default: + return {}; + } } } }; - export { rawApiDataToPlotlyReadyInfo }; + +// const rawApiDataToPlotlyReadyInfo = (view, office, data) => { +// const officeNames = [ +// 'Los Angeles, CA', +// 'San Francisco, CA', +// 'New York, NY', +// 'Houston, TX', +// 'Chicago, IL', +// 'Newark, NJ', +// 'Arlington, VA', +// 'Boston, MA', +// 'Miami, FL', +// 'New Orleans, LA', +// ]; +// let rowItem; +// let rowsForTable; + +// let yearMinMax = []; //variable to set minYear and MaxYear +// for (let yearResults of data[0]['yearResults']) { +// yearMinMax.push(yearResults['fiscal_year']); +// } + +// const yearByOfficeByGrant = {}; //Object that contacts year by Office by grant rate information +// for (let office of data[0]['yearResults']) { +// if (!yearByOfficeByGrant[office['fiscal_year']]) +// yearByOfficeByGrant[office['fiscal_year']] = {}; //if year not existing set to empty object +// for (let yearData of office['yearData']) { +// yearByOfficeByGrant[office['fiscal_year']][yearData['office']] = { +// //assign rates to year:{office:{}} +// granted: yearData['granted'], +// adminClosed: yearData['adminClosed'], +// denied: yearData['denied'], +// }; +// } +// } + +// const officeData = {}; //object that holds each % as a key of array value +// for (let officeName of officeNames) { +// officeData[officeName] = { +// xYears: [], +// totals: [], +// yTotalPercentGranteds: [], +// totalPercentAdminCloseds: [], +// totalPercentDenieds: [], +// }; +// } +// for (let yearResults of data[0]['yearResults']) { +// for (let yearData of yearResults['yearData']) { +// officeData[yearData['office']]['xYears'].push(yearResults['fiscal_year']); +// officeData[yearData['office']]['totals'].push(yearData['totalCases']); +// officeData[yearData['office']]['yTotalPercentGranteds'].push( +// yearData['granted'] +// ); +// officeData[yearData['office']]['totalPercentAdminCloseds'].push( +// yearData['adminClosed'] +// ); +// officeData[yearData['office']]['totalPercentDenieds'].push( +// yearData['denied'] +// ); +// } +// } + +// if (!office || office === 'all') { +// switch (view) { +// case 'time-series': +// const rowsForAllDisplay = []; +// for (let yearResults of data[0].yearResults) { +// rowItem = { +// 'Fiscal Year': yearResults.fiscal_year, +// 'Total Cases': yearResults.totalCases, +// '% Granted': Number(yearResults.granted).toFixed(2), +// '% Admin Close / Dismissal': Number( +// yearResults.adminClosed +// ).toFixed(2), +// '% Denied': Number(yearResults.denied).toFixed(2), +// }; +// rowsForAllDisplay.push(rowItem); +// } + +// const finalData = { +// xYears: [], +// totals: [], +// yTotalPercentGranteds: [], +// totalPercentAdminCloseds: [], +// totalPercentDenieds: [], +// }; +// for (let officeName of data[0]['yearResults']) { +// finalData['xYears'].push(officeName['fiscal_year']); +// finalData['totals'].push(officeName['totalCases']); +// finalData['yTotalPercentGranteds'].push(officeName['granted']); +// finalData['totalPercentAdminCloseds'].push(officeName['adminClosed']); +// finalData['totalPercentDenieds'].push(officeName['denied']); +// } + +// return { ...finalData, rowsForAllDisplay, officeData }; + +// case 'office-heat-map': +// rowsForTable = []; +// for (let yearResults of data[0].yearResults) { +// for (let officeKey of officeNames) { +// if ( +// yearResults.yearData.filter( +// yearItem => yearItem.office === officeKey +// ).length > 0 +// ) { +// rowItem = { +// 'Year [Office]': +// String(yearResults.fiscal_year) + +// ' [' + +// String(officeKey) + +// ']', +// 'Total Cases': yearResults.yearData.filter( +// yearItem => yearItem.office === officeKey +// )[0].totalCases, +// '% Granted': Number( +// yearResults.yearData.filter( +// yearItem => yearItem.office === officeKey +// )[0].granted +// ).toFixed(2), +// '% Admin Close / Dismissal': Number( +// yearResults.yearData.filter( +// yearItem => yearItem.office === officeKey +// )[0].adminClosed +// ).toFixed(2), +// '% Denied': Number( +// yearResults.yearData.filter( +// yearItem => yearItem.office === officeKey +// )[0].denied +// ).toFixed(2), +// }; +// rowsForTable.push(rowItem); +// } +// } +// } +// const officeHeatMapDataObject = { +// //declare helper object to construct data for heatmap plotly +// x: officeNames, //office +// y: [], //year +// z: [], //rate +// }; +// for (let fiscal_year in yearByOfficeByGrant) { +// //loop through +// officeHeatMapDataObject['y'].push(fiscal_year); //include year into y axis +// let zAxisArray = []; //Array to hold each row for z axis +// for (let officeName of officeNames) { +// //loop using unique office names +// zAxisArray.push( +// yearByOfficeByGrant[fiscal_year][officeName] +// ? yearByOfficeByGrant[fiscal_year][officeName]['granted'] +// : 0 +// ); +// } +// officeHeatMapDataObject['z'].push(zAxisArray); //push to zaxis array +// } +// return { officeHeatMapDataObject, rowsForTable }; + +// case 'citizenship': +// rowsForTable = []; +// for (let item of data[0].citizenshipResults) { +// rowItem = { +// Citizenship: item.citizenship, +// 'Total Cases': item.totalCases, +// '% Granted': Number(item.granted).toFixed(2), +// '% Admin Close / Dismissal': Number(item.adminClosed).toFixed(2), +// '% Denied': Number(item.denied).toFixed(2), +// }; +// rowsForTable.push(rowItem); +// } +// const countryGrantRateObj = { +// countries: [], +// countriesPercentGranteds: [], +// }; +// for (let country of data[0]['citizenshipResults']) { +// countryGrantRateObj['countries'].push(country['citizenship']); +// countryGrantRateObj['countriesPercentGranteds'].push( +// country['granted'] +// ); +// } +// return { +// rowsForTable, +// countryGrantRateObj, +// }; +// default: +// return {}; +// } +// } else { +// switch (view) { +// case 'time-series': +// rowsForTable = []; +// data[0].yearResults.sort((a, b) => a.fiscal_year - b.fiscal_year); +// for (let i = 0; i < data[0].yearResults.length; i++) { +// if ( +// data[0].yearResults[i].yearData.filter( +// dataItem => dataItem.office === office +// )[0] +// ) { +// const officeObj = data[0].yearResults[i].yearData.filter( +// dataItem => dataItem.office === office +// )[0]; +// rowItem = { +// 'Fiscal Year': data[0].yearResults[i].fiscal_year, +// 'Total Cases': officeObj.totalCases, +// '% Granted': Number(officeObj.granted).toFixed(2), +// '% Admin Close / Dismissal': Number( +// officeObj.adminClosed +// ).toFixed(2), +// '% Denied': Number(officeObj.denied).toFixed(2), +// }; +// rowsForTable.push(rowItem); +// } +// } +// const singleOfficeDataObject = officeData[office]; +// return { +// rowsForTable, +// singleOfficeDataObject, +// }; + +// case 'citizenship': +// rowsForTable = []; +// for (let item of data[0].citizenshipResults) { +// rowItem = { +// Citizenship: item.citizenship, +// 'Total Cases': item.totalCases, +// '% Granted': Number(item.granted).toFixed(2), +// '% Admin Close / Dismissal': Number(item.adminClosed).toFixed(2), +// '% Denied': Number(item.denied).toFixed(2), +// }; +// rowsForTable.push(rowItem); +// } +// const countryGrantRateObj = { +// countries: [], +// countriesPercentGranteds: [], +// }; +// for (let country of data[0]['citizenshipResults']) { +// countryGrantRateObj['countries'].push(country['citizenship']); +// countryGrantRateObj['countriesPercentGranteds'].push( +// country['granted'] +// ); +// } +// return { +// rowsForTable, +// countryGrantRateObj, +// }; +// default: +// return {}; +// } +// } +// }; + +// export { rawApiDataToPlotlyReadyInfo }; From 7ba40a14f2dbc363a3e67b5782ef00d3b540ef36 Mon Sep 17 00:00:00 2001 From: scgomez Date: Sat, 23 Mar 2024 20:47:30 -0500 Subject: [PATCH 3/3] completed third ticket --- package-lock.json | 31 ++++++++++++++++++++++++ package.json | 1 + src/auth0-provider-with-history.js | 32 +++++++++++++++++++++++++ src/callback-page.js | 12 ++++++++++ src/components/Layout/Header.jsx | 25 ++++++++++++++++++- src/components/common/login-button.js | 32 +++++++++++++++++++++++++ src/components/common/logout-button.js | 29 ++++++++++++++++++++++ src/components/common/signup-button.js | 33 ++++++++++++++++++++++++++ src/components/pages/profile-page.js | 33 ++++++++++++++++++++++++++ src/components/protected-route.js | 17 +++++++++++++ src/index.jsx | 15 ++++++++---- src/styles/RenderLandingPage.less | 26 ++++++++++++++++++++ 12 files changed, 281 insertions(+), 5 deletions(-) create mode 100644 src/auth0-provider-with-history.js create mode 100644 src/callback-page.js create mode 100644 src/components/common/login-button.js create mode 100644 src/components/common/logout-button.js create mode 100644 src/components/common/signup-button.js create mode 100644 src/components/pages/profile-page.js create mode 100644 src/components/protected-route.js diff --git a/package-lock.json b/package-lock.json index d04b7c094..69331046c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@ant-design/icons": "^4.7.0", "@ant-design/pro-form": "^1.52.13", "@ant-design/pro-table": "^2.62.7", + "@auth0/auth0-react": "^2.2.4", "@craco/craco": "^6.4.3", "@material-ui/core": "^4.12.4", "@reduxjs/toolkit": "^1.8.2", @@ -245,6 +246,23 @@ "react": ">=16.9.0" } }, + "node_modules/@auth0/auth0-react": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.2.4.tgz", + "integrity": "sha512-l29PQC0WdgkCoOc6WeMAY26gsy/yXJICW0jHfj0nz8rZZphYKrLNqTRWFFCMJY+sagza9tSgB1kG/UvQYgGh9A==", + "dependencies": { + "@auth0/auth0-spa-js": "^2.1.3" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17 || ^18", + "react-dom": "^16.11.0 || ^17 || ^18" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", + "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==" + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -30233,6 +30251,19 @@ "resize-observer-polyfill": "^1.5.1" } }, + "@auth0/auth0-react": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.2.4.tgz", + "integrity": "sha512-l29PQC0WdgkCoOc6WeMAY26gsy/yXJICW0jHfj0nz8rZZphYKrLNqTRWFFCMJY+sagza9tSgB1kG/UvQYgGh9A==", + "requires": { + "@auth0/auth0-spa-js": "^2.1.3" + } + }, + "@auth0/auth0-spa-js": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", + "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==" + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", diff --git a/package.json b/package.json index 284588c52..325790713 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@ant-design/icons": "^4.7.0", "@ant-design/pro-form": "^1.52.13", "@ant-design/pro-table": "^2.62.7", + "@auth0/auth0-react": "^2.2.4", "@craco/craco": "^6.4.3", "@material-ui/core": "^4.12.4", "@reduxjs/toolkit": "^1.8.2", diff --git a/src/auth0-provider-with-history.js b/src/auth0-provider-with-history.js new file mode 100644 index 000000000..b4575683e --- /dev/null +++ b/src/auth0-provider-with-history.js @@ -0,0 +1,32 @@ +import { Auth0Provider } from '@auth0/auth0-react'; +import React from 'react'; +import { useHistory } from 'react-router-dom'; + +export const Auth0ProviderWithHistory = ({ children }) => { + const history = useHistory(); + + const domain = process.env.REACT_APP_AUTH0_DOMAIN; + const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID; + const redirectUri = process.env.REACT_APP_AUTH0_CALLBACK_URL; + + const onRedirectCallback = appState => { + history.push(appState?.returnTo || window.location.pathname); + }; + + if (!(domain && clientId)) { + return null; + } + + return ( + + {children} + + ); +}; diff --git a/src/callback-page.js b/src/callback-page.js new file mode 100644 index 000000000..12163da8d --- /dev/null +++ b/src/callback-page.js @@ -0,0 +1,12 @@ +import React from 'react'; +// import { NavBar } from "../components/navigation/desktop/nav-bar"; +// import { MobileNavBar } from "../components/navigation/mobile/mobile-nav-bar"; + +export const CallbackPage = () => { + return ( +
+

CALLBACK PAGE

+
+
+ ); +}; diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 3346cbfe4..0cee057a5 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -1,12 +1,18 @@ +import { useAuth0 } from '@auth0/auth0-react'; import React from 'react'; import { Image } from 'antd'; import { Link } from 'react-router-dom'; import Logo from '../../styles/Images/WhiteLogo.png'; import { colors } from '../../styles/data_vis_colors'; +import { LoginButton } from '../common/login-button'; +import { LogoutButton } from '../common/logout-button'; +import { SignupButton } from '../common/signup-button'; const { primary_accent_color } = colors; function HeaderContent() { + const { isAuthenticated } = useAuth0(); + return (
Home - + Graphs + {!isAuthenticated && ( + <> + + + + )} + {isAuthenticated && ( + <> + + Profile + + + + )}
); diff --git a/src/components/common/login-button.js b/src/components/common/login-button.js new file mode 100644 index 000000000..088c6a4fc --- /dev/null +++ b/src/components/common/login-button.js @@ -0,0 +1,32 @@ +import { useAuth0 } from '@auth0/auth0-react'; +import React from 'react'; + +export const LoginButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleLogin = async () => { + await loginWithRedirect({ + appState: { + returnTo: '/profile', + }, + authorizationParams: { + screen_hint: 'signup', + }, + }); + }; + + return ( + + ); +}; diff --git a/src/components/common/logout-button.js b/src/components/common/logout-button.js new file mode 100644 index 000000000..f6520823b --- /dev/null +++ b/src/components/common/logout-button.js @@ -0,0 +1,29 @@ +import { useAuth0 } from '@auth0/auth0-react'; +import React from 'react'; + +export const LogoutButton = () => { + const { logout } = useAuth0(); + + const handleLogout = () => { + logout({ + logoutParams: { + returnTo: window.location.origin, + }, + }); + }; + + return ( + + ); +}; diff --git a/src/components/common/signup-button.js b/src/components/common/signup-button.js new file mode 100644 index 000000000..66484376c --- /dev/null +++ b/src/components/common/signup-button.js @@ -0,0 +1,33 @@ +import { useAuth0 } from '@auth0/auth0-react'; +import React from 'react'; + +export const SignupButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleSignUp = async () => { + await loginWithRedirect({ + appState: { + returnTo: '/profile', + }, + authorizationParams: { + screen_hint: 'signup', + }, + }); + }; + + return ( + + ); +}; diff --git a/src/components/pages/profile-page.js b/src/components/pages/profile-page.js new file mode 100644 index 000000000..a6370ed71 --- /dev/null +++ b/src/components/pages/profile-page.js @@ -0,0 +1,33 @@ +import { useAuth0 } from '@auth0/auth0-react'; +import React from 'react'; +// import { NavBar } from "../components/navigation/desktop/nav-bar"; +// import { MobileNavBar } from "../components/navigation/mobile/mobile-nav-bar"; + +export const ProfilePage = () => { + const { user } = useAuth0(); + + if (!user) { + return null; + } + + return ( +
+

+ Profile Page +

+
+
+ Profile +
+
+

+ Username: {user.name} +

+

+ Email: {user.email} +

+
+
+
+ ); +}; diff --git a/src/components/protected-route.js b/src/components/protected-route.js new file mode 100644 index 000000000..4a14de930 --- /dev/null +++ b/src/components/protected-route.js @@ -0,0 +1,17 @@ +import { withAuthenticationRequired } from '@auth0/auth0-react'; +import React from 'react'; +import { Route } from 'react-router-dom'; +import LoadingComponent from './common/LoadingComponent'; + +export const ProtectedRoute = ({ component, ...args }) => ( + ( +
+ +
+ ), + })} + {...args} + /> +); diff --git a/src/index.jsx b/src/index.jsx index 124896cb5..80e594339 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -10,7 +10,7 @@ import { import 'antd/dist/antd.less'; import { NotFoundPage } from './components/pages/NotFound'; import { LandingPage } from './components/pages/Landing'; - +import { ProtectedRoute } from './components/protected-route'; import { FooterContent, SubFooter } from './components/Layout/Footer'; import { HeaderContent } from './components/Layout/Header'; @@ -22,6 +22,9 @@ import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; import reducer from './state/reducers'; import { colors } from './styles/data_vis_colors'; +import { CallbackPage } from './callback-page'; +import { ProfilePage } from './components/pages/profile-page'; +import { Auth0ProviderWithHistory } from './auth0-provider-with-history'; const { primary_accent_color } = colors; @@ -29,9 +32,11 @@ const store = configureStore({ reducer: reducer }); ReactDOM.render( - {/* */} - - {/* */} + + + + + , document.getElementById('root') @@ -54,6 +59,8 @@ export function App() { + +