Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"@testing-library/user-event": "^7.1.2",
"axios": "^0.20.0",
"graphql": "^15.3.0",
"highcharts": "^8.2.2",
"highcharts-react-official": "^3.0.0",
"jwt-decode": "^3.0.0",
"prop-types": "^15.7.2",
"react": "^16.13.1",
Expand Down
8 changes: 6 additions & 2 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { fetchIsDrawerOpen } from './redux/slices/drawer';
import { fetchLogin } from './redux/slices/auth';

import './App.css';
import Dashboard from './components/dashboard';

const useStyles = makeStyles((theme) => ({
root: {
Expand Down Expand Up @@ -39,12 +40,12 @@ function App() {
const dispatch = useDispatch();

const snackbarState = useSelector((state) => state.snackbar);

const token = useSelector((state) => state.auth.authToken);
// Fetch the initial drawer open state
useEffect(() => {
dispatch(fetchIsDrawerOpen());
dispatch(fetchLogin());
}, [dispatch]);
}, [dispatch, token]);

return (
<div className={classes.root}>
Expand All @@ -62,6 +63,9 @@ function App() {
<Route exact path='/home'>
<Home />
</Route>
<Route exact path = '/dashboard'>
<Dashboard />
</Route>
{/* Auth related routes */}
<Route path='/auth'>
<Auth/>
Expand Down
2 changes: 2 additions & 0 deletions client/src/components/auth/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import axios from '../../utils/axios';
import loginQuery from '../../graphQl/queries/loginQuery';
import { setIsSnackbarOpen } from '../../redux/slices/snackbar';
import { setLogin } from '../../redux/slices/auth';
import { fetchUrls } from '../../redux/slices/urls';

// Define styles for this component
const useStyles = makeStyles((theme) => ({
Expand Down Expand Up @@ -108,6 +109,7 @@ function Login() {
// Get the jwt and store in redux and local storage
const authToken = response.data.data.login.token;
dispatch(setLogin({ isLoggedIn: true, authToken }));
dispatch(fetchUrls({ token: authToken }));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't required anymore. We discussed, right?


history.replace(prevPath, location.state);
} catch (err) {
Expand Down
83 changes: 83 additions & 0 deletions client/src/components/dashboard/Graph/LineChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import Highcharts from 'highcharts/highstock';
import HighchartsReact from 'highcharts-react-official';
import { useSelector } from 'react-redux';

/**
* Line Chart component
* This renders a line chart plotting the no of clicks made per day
*/

// The component to render the line chart
function LineChart() {
// Retreive the selected Url
const selectedUrl = useSelector((state) => state.urls.selectedUrl);
const clicks = selectedUrl ? selectedUrl.clicks : [];

// Group the Clicks according to date
const groups = clicks.reduce((groups, click) => {
// Retrieve the Date from clicked time
const date = click.time.split('T')[0];
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(click);
return groups;
}, {});
// Generate an array according to the groups made
const groupArrays = Object.keys(groups).map((date) => {
return {
date,
clicks: groups[date],
};
});

// Data to be passed to the chart
const options = {
chart: {
reflow: 'true',
BackgroundColor: '#fbfbfb',
plotBorderWidth: null,
plotShadow: false,
type: 'line',
},
title: {
text: 'Clicks by Date ',
align: 'left',
},
xAxis: {
title: {
text: 'Date',
},
categories: groupArrays.map((groups) => groups.date),
},
yAxis: {
title: {
text: 'Clicks',
},
},
series: [{
name: 'Clicks',
data: groupArrays.map((groups) => groups.clicks.length),
}],
};

return (
<div style={{
display: 'flex',
flexGrow: 1,
flexBasis: '30%',
}}>
<div style={{
width: '100% !important',
backgroundColor: '#fbfbfb',
}}>

<HighchartsReact
highcharts={Highcharts}
options={options} />
</div>
</div>);
}

export default LineChart;
157 changes: 157 additions & 0 deletions client/src/components/dashboard/Graph/PieChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import HighchartsReact from 'highcharts-react-official';
import Highcharts from 'highcharts/highstock';
import PropTypes from 'prop-types';
import React from 'react';
import { useSelector } from 'react-redux';

/**
* This is the Pie Chart component
* This has two variants
* 1. This plots the no of clicks made in the last month per location on a pieChart
* 2. This plots the no of clicks made in the last month per url on a pieChart
* @usage Just import it and pass a variant prop
* whose value should be `CLICKS_BY_LOCATION` for 1 else `CLICKS_BY_URLS` for 2
* @example <PieChart variant = `CLICKS_BY_LOCATION`||`CLICKS_BY_URLS`/>
*/

export default function PieChart(props) {
const { variant } = props;

const urls = useSelector((state) => state.urls.urls);
const selectedUrl = useSelector((state) => state.urls.selectedUrl);
const clicks = selectedUrl ? selectedUrl.clicks : [];
let options = null;

// Check if the variant is one needing location
if (variant === 'CLICKS_BY_LOCATION') {
const groups = clicks.reduce((groups, click) => {
const location = click.location.city + ',' + click.location.state;
if (!groups[location]) {
groups[location] = [];
}
const date1 = new Date();
const date2 = new Date(click.time.split('T')[0]);
const diffDays = Math.ceil((date1 - date2) / (1000 * 60 * 60 * 24));
if (diffDays <= 31) {
groups[location].push(click);
}
return groups;
}, {});
const groupArrays = Object.keys(groups).map((location) => {
return {
name: location,
y: groups[location].length,
};
});
options = {
chart: {
plotBackgroundColor: '#fbfbfb',
plotBorderWidth: null,
plotShadow: false,
type: 'pie',
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
},
title: {
text: 'Clicks by Location',
align: 'left',
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>',
},
plotOptions: {
pie: {
dataLabels: {
enabled: true,
format: '<b>{point.name}<b/>:{point.percentage:.1f} %',
},
},
},
series: [{
name: 'Clicks',
colorByPoint: true,
data: groupArrays,
}],
};
} else if (variant === 'CLICKS_BY_URLS') {
const groups = urls.reduce((groups, url) => {
const clicks = url.clicks ? url.clicks : [];
const title = url.title ? url.title : url.shortUrl;
for (const click of clicks) {
const date1 = new Date();
const date2 = new Date(click.time.split('T')[0]);
const diffDays = Math.ceil((date1 - date2) / (1000 * 60 * 60 * 24));
if (!groups[title]) {
groups[title] = [];
}
if (diffDays <= 31) {
groups[title].push(click);
}
}
return groups;
}, {});
const groupArrays = Object.keys(groups).map((title) => {
return {
name: title,
y: groups[title].length,
};
});
options = {
chart: {
plotBackgroundColor: '#fbfbfb',
plotBorderWidth: null,
plotShadow: false,
type: 'pie',
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
},
title: {
text: 'Clicks last month',
align: 'left',
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>',
},
plotOptions: {
pie: {
dataLabels: {
enabled: true,
format: '<b>{point.name}<b/>:{point.percentage:.1f} %',
},
},
},
series: [{
name: 'Clicks',
colorByPoint: true,
data: groupArrays,
}],
};
}
return (
<div style={{
display: 'flex',
flexGrow: 1,
height: '40%',
}}>
<div style={{
width: '100% !important',
backgroundColor: '#fbfbfb',
}}>
<HighchartsReact
highcharts={Highcharts}
options={options}
/>
</div>
</div>
);
}

PieChart.propTypes = {
variant: PropTypes.string,
};
61 changes: 61 additions & 0 deletions client/src/components/dashboard/RecentUrlcontainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
Card,
CardActions,
CardHeader,
makeStyles,
useMediaQuery,
useTheme,
} from '@material-ui/core';
import React from 'react';
import UrlList from './UrlList';

// Create styles for the component
const useStyles = makeStyles((theme) => ({
container: {
display: 'flex',
flexGrow: 1,
flexBasis: '50%',
},
}));

// Url Container component
const UrlContainer = (props) => {
const theme = useTheme();
const classes = useStyles();
const mediaMinSm = useMediaQuery(theme.breakpoints.up('sm'));

return (
<div className={classes.container}>
<Card style={{
flex: 1,
height: '90%',
}}>
<CardHeader
title = 'My Urls'
style={{
width: '100%',
height: '10%',
color: 'white',
background: 'black',
}} />
<CardActions style={{
display: 'flex',
flexDirection: 'column',
height: '70%',
}}>
<div
style={{
display: 'flex',
flexDirection: 'column',
paddingTop: '4px',
paddingBottom: mediaMinSm ? '16px' : '6px',
width: '100%',
}}>
<UrlList />
</div>
</CardActions>
</Card>
</div>);
};

export default UrlContainer;
10 changes: 10 additions & 0 deletions client/src/components/dashboard/UrlList/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.item{
cursor: 'pointer';
height: '12%';
}
.active{
display: flex;
width: '100%';
background-color: slategray;
color: white;
}
Loading