Skip to content

Commit

Permalink
feat: Add download CSV funtionality to Reports page (#286)
Browse files Browse the repository at this point in the history
* Initial commit to add download csv functionality from reports.

* Added filter options to reports filter options dropdown.

* Added react-csv to project.

* Added logic to generate a report.

* Set up error handling for select reports.

* Successfully added download CSV functionality to reports.

* Added proper date to reports.

* Added current state of reports to localstorage.

* Updated report history story to correspond to changes in report interface.

* Adding PRs text to contributors profile.

* Adding functionality to display s in PRs when totalPRs is not 1.

* Fixed mobile issue where design broke down in mobile up to 375px width.
  • Loading branch information
chadstewart committed Sep 3, 2022
1 parent 65c3349 commit ada79d3
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 45 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NEXT_PUBLIC_SUPABASE_URL=https://lkkownkrbkmblczeoyqb.supabase.co
NEXT_PUBLIC_SUPABASE_API_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNjQyNDU2MTc0LCJleHAiOjE5NTgwMzIxNzR9.c6nlkT05GnNacQ6OYuGcjBsILmGsSDwEEtN2zZVXFgY
NEXT_PUBLIC_GS_API_URL=https://gs-api.opensauced.pizza/v1
2 changes: 1 addition & 1 deletion components/atoms/Card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Card: React.FC<CardProps> = ({ className, children, heading }) => {
{
heading ?
<>
<div className="px-6 py-3 rounded-t-lg bg-light-slate-3">
<div className="px-3 md:px-6 py-3 rounded-t-lg bg-light-slate-3">
{heading}
</div>
<div>
Expand Down
2 changes: 1 addition & 1 deletion components/molecules/CardProfile/card-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const CardProfile = ({ githubAvatar, githubName, totalPRs, dateOfFirstPR }: Card
</div>
<div className="flex gap-2 font-medium text-sm">
<div className="flex items-center gap-1 text-light-slate-11">
<Icon alt="Total Pull-Requests" IconImage={ForkIcon} /> {totalPRs}
<Icon alt="Total Pull-Requests" IconImage={ForkIcon} /> {totalPRs} PR{totalPRs === 1 ? "" : "s"}
</div>
<div className="flex items-center gap-1 text-light-slate-11">
<Icon alt="Time from First Pull Request" IconImage={FirstPRIcon} /> {dateOfFirstPR}
Expand Down
24 changes: 13 additions & 11 deletions components/molecules/ReportsHistory/reports-history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Card from "components/atoms/Card/card";
import Title from "components/atoms/Typography/title";
import Text from "components/atoms/Typography/text";
import { Report } from "interfaces/report-type";
import { CSVLink } from "react-csv";

interface ReportsHistoryProps {
reportList?: Report[];
Expand All @@ -11,7 +12,7 @@ interface ReportsHistoryProps {
const ReportsHistory = ({ reportList }: ReportsHistoryProps): JSX.Element => {
const heading = <Title level={5} className="!text-light-slate-9 uppercase">
<div className="flex justify-between w-full gap-2">
<div className="w-2/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
<div className="w-2/5 md:w-2/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
report name
</div>
<div className="w-1/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
Expand All @@ -20,7 +21,7 @@ const ReportsHistory = ({ reportList }: ReportsHistoryProps): JSX.Element => {
<div className="w-1/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
format
</div>
<div className="w-1/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
<div className="w-2/5 text-xs font-semibold text-slate-400 tracking-wide uppercase">
{null}
</div>
</div>
Expand All @@ -29,10 +30,10 @@ const ReportsHistory = ({ reportList }: ReportsHistoryProps): JSX.Element => {
return (
<Card heading={heading}>
<div className="flex flex-col justify-between w-full gap-2">
{reportList ?
reportList.map(({reportDate, reportName, reportFormat, isGenerated}, index) =>
<div className={`flex items-center py-3 px-6 ${index % 2 === 0 ? "bg-slate-50" : "bg-white"} gap-2`} key={index}>
<div className="w-2/5 text-sm font-medium text-slate-900 tracking-tight">
{reportList &&
reportList.map(({reportDate, reportName, reportFormat, isGenerated, data}, index) =>
<div className={`flex items-center py-3 px-3 md:px-6 ${index % 2 === 0 ? "bg-slate-50" : "bg-white"} gap-2`} key={index}>
<div className="w-2/5 md:w-2/5 text-sm font-medium text-slate-900 tracking-tight">
{reportName}
</div>
<div className="w-1/5 text-sm font-medium text-slate-900 tracking-tight">
Expand All @@ -41,9 +42,11 @@ const ReportsHistory = ({ reportList }: ReportsHistoryProps): JSX.Element => {
<div className="w-1/5 text-sm font-medium text-slate-900 tracking-tight">
{reportFormat}
</div>
<div className="w-1/5 text-sm font-medium text-slate-900 tracking-tight text-right">
<div className="w-2/5 text-sm font-medium text-slate-900 tracking-tight text-right">
{isGenerated ?
<Button type="link" >Download</Button>
<CSVLink data={data}>
<Button type="link" >Download</Button>
</CSVLink>

:

Expand All @@ -52,9 +55,8 @@ const ReportsHistory = ({ reportList }: ReportsHistoryProps): JSX.Element => {
</div>
</div>
)

:

}
{!reportList || reportList.length === 0 &&
<Text className="py-3 px-6">
Currently there are no reports
</Text>
Expand Down
15 changes: 11 additions & 4 deletions components/molecules/SelectReportsFilter/select-reports-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,32 @@ interface SelectReportsFilterProps {

const SelectReportsFilter = ({ filterList, callback }: SelectReportsFilterProps): JSX.Element => {
const [selectedValue, setSelectedValue] = useState("");
const [error, setError] = useState("");

const handleButtonClick = () => {
if(selectedValue === "") return setError("Please select a filter.");
if(error) setError("");
callback(selectedValue);
};

return (
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2 min-h-20">
<Title level={4}>Select a Filter</Title>
<Text>
Download the filtered contributions from the last 30 days as a CSV. Selecting a filter will remove all the added
repositories.
</Text>
<div className="flex flex-col md:flex-row gap-2">
<Select onChange={(event) => setSelectedValue(event.target.value)} className="w-full">
<Select error={error} onChange={(event) => setSelectedValue(event.target.value)} className="w-full">
<SelectOption value="">Select a Filter</SelectOption>
{filterList?.map(({ filterName, filterValue }, index) => (
<SelectOption key={index} value={filterValue}>
{filterName}
</SelectOption>
))}
</Select>
<Button type="primary" onClick={() => callback(selectedValue)} className="w-52">
Download CSV
<Button type="primary" onClick={handleButtonClick} className="w-52 h-[38px]">
Generate CSV
</Button>
</div>
</div>
Expand Down
65 changes: 41 additions & 24 deletions components/organisms/Reports/reports.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,62 @@ import Title from "components/atoms/Typography/title";
import ReportsHistory from "components/molecules/ReportsHistory/reports-history";
import SelectReportsFilter from "components/molecules/SelectReportsFilter/select-reports-filter";
import { Report } from "interfaces/report-type";
import useFilterOptions from "lib/hooks/useFilterOptions";
import { useRepositoriesList } from "lib/hooks/useRepositoriesList";
import getCurrentDate from "lib/utils/get-current-date";
import { useState } from "react";

const USERDEVICESTORAGENAME = "reportState";

const Reports = (): JSX.Element => {
const userDeviceState = localStorage.getItem(USERDEVICESTORAGENAME);
const initialState = userDeviceState ? JSON.parse(userDeviceState as string) : [];
const { data, isLoading, isError } = useRepositoriesList();
const [ reports, setReports ] = useState<Report[]>(initialState);

const reportList: Report[] = [
{
reportName: "Top Ten",
reportDate: "Jun 3, 2022",
reportFormat: "CSV",
isGenerated: true
},
{
reportName: "Top Five",
reportDate: "Jun 3, 2022",
const filterOptions = useFilterOptions();
const filterList = filterOptions.map(filter => {
return {
filterName: filter,
filterValue: filter.toLowerCase().replaceAll(" ", "-")
};
});

const handleFilterClick = (selectedFilter: string) => {
const dataReady = !isLoading && !isError;

const constructedReport = {
reportName: selectedFilter,
reportDate: getCurrentDate(),
reportFormat: "CSV",
isGenerated: false
}
];
isGenerated: dataReady,
data : dataReady ? data : []
};

setReports(prevState => {
const newState = [ ...prevState, constructedReport ];
localStorage.setItem(USERDEVICESTORAGENAME, JSON.stringify(newState));

return newState;
});
};

return (
<section className="w-full py-4 px-8 flex justify-center">
<section className="flex flex-col w-full py-4 px-2 md:px-4 justify-center">
<div className="max-w-4xl">
<Title className="!font-medium relative" level={3}>
Contributions Insights
</Title>
<hr className="border-light-slate-6 my-4" />
<div className="!text-left">
<SelectReportsFilter
callback={function () {
throw new Error("Function not implemented.");
}}
/>
</div>
<SelectReportsFilter
filterList={filterList}
callback={handleFilterClick}
/>
<Title className="!font-medium relative mt-16" level={3}>
Download History
</Title>
<hr className="border-light-slate-6 my-4" />
<div>
<ReportsHistory reportList={reportList}/>
</div>
<ReportsHistory reportList={reports}/>
</div>
</section>
);
Expand Down
3 changes: 2 additions & 1 deletion interfaces/report-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type Report = {
reportName: string;
reportDate: string;
reportFormat: "CSV";
reportFormat: string;
isGenerated: boolean;
data: object[];
}
2 changes: 1 addition & 1 deletion lib/hooks/useSWR.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Fetcher } from "swr";

const baseUrl = "https://gs-api.opensauced.pizza/v1";
const baseUrl = process.env.NEXT_PUBLIC_GS_API_URL;

const apiFetcher: Fetcher = async (apiUrl: string) => {
const res = await fetch(`${baseUrl}/${apiUrl}`, { headers: { accept: "application/json" } });
Expand Down
8 changes: 8 additions & 0 deletions lib/utils/get-current-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const getCurrentDate = () => {
const month = ["January","February","March","April","May","June","July","August","September","October","November","December"];
const today = new Date;

return `${month[today.getMonth()]} ${today.getDay()}, ${today.getFullYear()}`;
};

export default getCurrentDate;
16 changes: 16 additions & 0 deletions npm-shrinkwrap.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"echarts-for-react": "^3.0.2",
"next": "^12.2.4",
"react": "^17.0.2",
"react-csv": "^2.2.2",
"react-dom": "^17.0.2",
"react-icons": "^4.4.0",
"swr": "^1.3.0"
Expand All @@ -72,6 +73,7 @@
"@types/jest": "^28.1.6",
"@types/node": "18.7.1",
"@types/react": "18.0.17",
"@types/react-csv": "^1.1.3",
"@types/react-dom": "18.0.6",
"autoprefixer": "^10.4.8",
"babel-loader": "^8.2.5",
Expand Down
6 changes: 4 additions & 2 deletions stories/molecules/reports-history.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ const testReportList: Report[] = [
reportName: "Top Ten",
reportDate: "Jun 3, 2022",
reportFormat: "CSV",
isGenerated: true
isGenerated: true,
data: []
},
{
reportName: "Top Five",
reportDate: "Jun 3, 2022",
reportFormat: "CSV",
isGenerated: false
isGenerated: false,
data: []
}
];

Expand Down

0 comments on commit ada79d3

Please sign in to comment.