Skip to content

Commit

Permalink
Merge pull request #682 from dali-lab/dev
Browse files Browse the repository at this point in the history
Homepage redesign & blog functionality
  • Loading branch information
wu-ciesielska authored Feb 5, 2024
2 parents 57feda0 + 564b9d5 commit df6dd94
Show file tree
Hide file tree
Showing 102 changed files with 2,581 additions and 973 deletions.
7 changes: 5 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@
],
"max-len": [
"error",
200
200,
{
"ignoreStrings": true
}
],
"no-extra-parens": 0,
"no-restricted-syntax": [
Expand Down Expand Up @@ -78,4 +81,4 @@
"sourceType": "module"
}
}
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ yarn-error.log
.env

.eslintcache
.vscode
9 changes: 7 additions & 2 deletions src/app/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import {
Resources,
TrappingData,
Home,
PlayWithModel,
Prediction,
Blog,
SingleBlogPost,
} from '../screens';

import {
Expand Down Expand Up @@ -58,6 +59,7 @@ const App = (props) => {
getPredictions,
getAvailableStates,
getSparseData,
getAllBlogPosts,
} = props;

const [isMobile, setIsMobile] = useState(window.innerWidth < MIN_WIDTH_THRESHOLD);
Expand Down Expand Up @@ -86,12 +88,14 @@ const App = (props) => {
getAggregateYearData();
getAggregateStateData();
getAggregateLocationData();
getAllBlogPosts();
getSparseData();
getPredictions();
}, [
getAggregateLocationData,
getAggregateStateData,
getAggregateYearData,
getAllBlogPosts,
getPredictions,
getSparseData,
loginUserFromStorage,
Expand Down Expand Up @@ -120,9 +124,10 @@ const App = (props) => {
<Route exact path={ROUTES.HOME} component={Home} />
<Route path={ROUTES.ABOUT} component={About} />
<Route path={ROUTES.ADMIN} component={Admin} />
<Route path={`${ROUTES.BLOG}/:id`} component={SingleBlogPost} />
<Route path={ROUTES.BLOG} component={Blog} />
<Route path={ROUTES.RESOURCES} component={Resources} />
<Route path={ROUTES.TRAPPING_DATA} component={TrappingData} />
<Route path={ROUTES.PLAY_WITH_MODEL} component={PlayWithModel} />
<Route path={ROUTES.PREDICTIONS} component={Prediction} />
{Object.entries(RESOURCE_ROUTES).map(([TYPE, ROUTE]) => (
<Route
Expand Down
4 changes: 4 additions & 0 deletions src/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getAggregateStateData,
getAggregateLocationData,
getAvailableStates,
getAllBlogPosts,
getSparseData,
getPredictions,
getUserFromStorage,
Expand Down Expand Up @@ -44,6 +45,9 @@ const mapDispatchToProps = (dispatch) => {
getAvailableStates: (overrideFilter) => {
dispatch(getAvailableStates(overrideFilter));
},
getAllBlogPosts: () => {
dispatch(getAllBlogPosts());
},
getSparseData: (overrideFilter) => {
dispatch(getSparseData(overrideFilter));
},
Expand Down
13 changes: 13 additions & 0 deletions src/components/button/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

import './style.scss';

const Button = (props) => {
const {
onClick, buttonStyle = 'primary', children, className = '',
} = props;

return <button type="button" onClick={onClick} className={`button animated-button button button-${buttonStyle} ${className}`}>{children}</button>;
};

export default Button;
3 changes: 3 additions & 0 deletions src/components/button/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Button from './component';

export default Button;
18 changes: 18 additions & 0 deletions src/components/button/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.button {
padding: 10px;
border-radius: 6px;
font-size: 18px;
font-weight: 500;
text-decoration: none;

&-primary {
color: #ffffff;
background-color: #424755;
}

&-secondary {
background-color: white;
border: 1px solid #424755 !important;
color: #424755
}
}
33 changes: 23 additions & 10 deletions src/components/header/component.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';

import './style.scss';
import { Link, useLocation, useHistory } from 'react-router-dom';

import { ROUTES } from '../../constants';
import DownloadData from '../download-data';

import pineBeetleImage from '../../assets/icons/black-beetle-logo.png';

const Header = (_props) => {
import './style.scss';

const Header = () => {
const routes = {
[ROUTES.PREDICTIONS]: 'Predictions',
[ROUTES.TRAPPING_DATA]: 'Historical Data',
[ROUTES.PLAY_WITH_MODEL]: 'Play With Model',
[ROUTES.RESOURCES]: 'Resources',
[ROUTES.ABOUT]: 'About',
};

const urlPath = useLocation().pathname;
const location = useLocation();
const history = useHistory();
const urlPath = location.pathname;

const scrollToUrl = '?scrollTo=howItWorks';

const handleHowItWorksButtonClick = () => {
// handle situation when user wants to go back to how does it work section, after already clicking on the button
if (location.pathname === ROUTES.HOME && location.search === scrollToUrl) {
history.push(ROUTES.HOME);
setTimeout(() => {
history.push(`/${scrollToUrl}`);
}, 0);
} else {
history.push(`/${scrollToUrl}`);
}
};

return (
<div id="header">
Expand All @@ -34,14 +46,15 @@ const Header = (_props) => {
<div id="nav-button-area">
<div id="nav-buttons">
<div id="button-container">
<button type="button" onClick={handleHowItWorksButtonClick} className="nav-button inactive-nav">How does it work?</button>
{Object.entries(routes).map(([key, value]) => (
<Link to={key} key={key} className={`${value === 'About' ? 'nav-button-short' : 'nav-button'} ${(urlPath === key) ? 'active-nav' : 'inactive-nav'}`}>
{value}
</Link>
))}
</div>
<div id="download-button-area">
<DownloadData />
{urlPath === ROUTES.TRAPPING_DATA && <DownloadData />}
</div>
</div>
</div>
Expand Down
8 changes: 5 additions & 3 deletions src/components/header/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
justify-content: space-around;
align-items: center;
background-color: #FBFBFD;
max-width: 1200px;
margin: auto;

a {
text-decoration: none;
Expand All @@ -30,7 +32,6 @@
align-items: center;

img {
margin-left: 35px;
margin-top: 13px;
width: 26px;
height: 32px;
Expand All @@ -43,7 +44,7 @@

#nav-button-area {
margin: auto;
margin-right: 100px;
margin-right: 0;
}

#nav-buttons {
Expand Down Expand Up @@ -71,7 +72,6 @@

#download-button-area {
display: flex;
background-color: white;
padding: 5px;
justify-content: center;
align-content: center;
Expand All @@ -88,6 +88,8 @@
line-height: 24px;
letter-spacing: 0em;
text-align: center;
border: none;
background: none;

-o-transition: 0.3s;
-ms-transition: 0.3s;
Expand Down
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Button from './button';
import DownloadData from './download-data';
import Footer from './footer';
import Header from './header';
Expand All @@ -6,6 +7,7 @@ import MobileOverlay from './mobile-overlay';
import ScrollToTop from './scroll-to-top';

export {
Button,
DownloadData,
Footer,
Header,
Expand Down
121 changes: 121 additions & 0 deletions src/components/input-components/file-input/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useState } from 'react';

import './style.scss';

const FileInput = (props) => {
const {
guideURL, component, onResetFiles, fileFormat = '.csv',
} = props;

const [isUploadingFile, setIsUploadingFile] = useState(false);
const [uploadingFileError, setUploadingFileError] = useState('');
const [successMessage, setSuccessMessage] = useState({});

const clearSuccessMessage = () => setSuccessMessage({});

const clearError = () => {
setUploadingFileError('');
onResetFiles();
setIsUploadingFile(false);
setSuccessMessage({});
};

if (isUploadingFile) {
return (
<div className="uploading-message-container">
<h3>Uploading File...</h3>
</div>
);
}

if (uploadingFileError) {
return (
<div id="uploading-error-container" className="uploading-message-container">
{
guideURL
? <h3>{uploadingFileError} Please read <a href={guideURL} target="_blank" rel="noopener noreferrer">this guide</a> for uploading data.</h3>
: <h3>{uploadingFileError}</h3>
}
<button
type="button"
onClick={clearError}
>Try Again
</button>
</div>
);
}

/**
* @description uploads given file
* @param {Function} uploadFunction function to upload file
* @param {File} file file object
* @param {Function} clearFile function to clear the file
* @param {String} id file id
*/
const uploadFile = async (uploadFunction, file, clearFile, id) => {
setIsUploadingFile(true);

try {
await uploadFunction(file);
clearFile();
setSuccessMessage({ [id]: 'Successfully uploaded file' });
setTimeout(clearSuccessMessage, 1000 * 7);
} catch (err) {
const { data, status } = err?.response || {};

const strippedError = data?.error.toString().replace('Error: ', '');

const badRequest = status === 400;
const badColumnNames = strippedError.includes('missing fields in csv');
const wrongFileFormat = strippedError.includes('Invalid file type');

if (badColumnNames) setUploadingFileError('Incorrect column names. Please upload a different CSV.');
else if (wrongFileFormat) setUploadingFileError('Invalid file type. Only PNG, JPG, and JPEG files are allowed! Please, choose a different file.');
else if (badRequest) setUploadingFileError(`Bad request: ${strippedError}`);
else setUploadingFileError(strippedError || data?.error.toString() || 'We encountered an error. Please try again.');
} finally {
setIsUploadingFile(false);
}
};

return (
<div id={component.id} key={component.id}>
<p>{component.name}</p>
<p id="file-selected">
{component.file ? component.file.name : ''}
</p>
{component.file && component.uploadFile ? (
<button
id="upload-button"
className="custom-file-upload"
type="button"
onClick={() => uploadFile(
component.uploadFile,
component.file,
component.selectFile,
component.id,
)}
>
Upload File
</button>
) : (
<>
{successMessage[component.id] && (
<p id="success-message">{successMessage[component.id]}</p>
)}
<label htmlFor={`file-upload-${component.id}`} className="custom-file-upload">
<input
id={`file-upload-${component.id}`}
type="file"
accept={fileFormat}
onChange={(e) => component.selectFile(e.target.files[0]) && clearSuccessMessage()}
/>
Select File
</label>
</>
)}
</div>
);
};

export default FileInput;
2 changes: 2 additions & 0 deletions src/components/input-components/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ChoiceInput from './choice-input';
import TextInput from './text-input';
import MultiSelectInput from './multi-select-input';
import FileInput from './file-input';

export {
ChoiceInput,
TextInput,
MultiSelectInput,
FileInput,
};
1 change: 1 addition & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const CHART_MODES = {
const ROUTES = {
ABOUT: '/about',
ADMIN: '/admin',
BLOG: '/blog',
HOME: '/',
RESOURCES: '/resources',
PLAY_WITH_MODEL: '/play-with-model',
Expand Down
Loading

0 comments on commit df6dd94

Please sign in to comment.