Skip to content

Commit 9be2851

Browse files
authored
Merge pull request #37 from kleros/feat/cooperative-page
feat(frontend): cooperative-page
2 parents 0b8ff5b + 5ccaa75 commit 9be2851

File tree

18 files changed

+19531
-13252
lines changed

18 files changed

+19531
-13252
lines changed

frontend/.husky/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
cd frontend
22
yarn tsc
3-
yarn lint
3+
yarn lint --fix
Lines changed: 10 additions & 0 deletions
Loading

frontend/src/components/Button.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,45 @@ const baseStyle = clsx("px-8 py-2 rounded-full transition duration-75");
77
const primaryStyle = clsx(
88
baseStyle,
99
"bg-primary-blue",
10-
"hover:bg-primary-blue/90"
10+
"hover:bg-primary-blue/90",
11+
"disabled:bg-stroke"
1112
);
1213

1314
const secondaryStyle = clsx(
15+
baseStyle,
16+
"bg-transparent border-2 border-white",
17+
"hover:bg-primary-blue hover:border-primary-blue hover:text-background-2"
18+
);
19+
20+
const tertiaryStyle = clsx(
1421
baseStyle,
1522
"bg-transparent border-2 border-white",
1623
"hover:bg-white/10"
1724
);
1825

1926

20-
interface IButton {
27+
interface IButton extends React.ComponentProps<"button"> {
2128
children: React.ReactNode;
2229
onClick?: () => void;
23-
variant?: "primary" | "secondary";
30+
variant?: "primary" | "secondary" | "tertiary";
2431
className?: string;
2532
}
2633

2734
const Button: React.FC<IButton> = ({
2835
children,
2936
onClick,
3037
className,
31-
variant = "primary"
38+
variant = "primary",
39+
...props
3240
}) => (
3341
<button
34-
className={
35-
clsx(variant === "primary" ? primaryStyle : secondaryStyle, className)
36-
}
37-
{...{ onClick }}
42+
className={clsx(
43+
variant === "primary" && primaryStyle,
44+
variant === "secondary" && secondaryStyle,
45+
variant === "tertiary" && tertiaryStyle,
46+
className
47+
)}
48+
{...{ onClick, ...props }}
3849
>
3950
{children}
4051
</button>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from "react";
2+
3+
import Image from "next/image";
4+
import Link from "next/link";
5+
6+
import Button from "@/components/Button";
7+
import { CooperativeLearnMoreSection } from "@/queries/cooperative/member-section";
8+
9+
const LearnMore: React.FC<CooperativeLearnMoreSection> = ({
10+
title,
11+
button,
12+
background,
13+
}) => {
14+
return (
15+
<div className={
16+
"relative w-full flex flex-col items-center justify-center mt-16 p-8"
17+
}>
18+
<h2 className="text-primary-text text-xl mb-8 z-[1]">{title}</h2>
19+
<Link
20+
href={button.link.url}
21+
target="_blank"
22+
rel="noopener noreferrer"
23+
className="z-[1]"
24+
>
25+
<Button>
26+
<span className="text-background-2">{button.text}</span>
27+
</Button>
28+
</Link>
29+
<Image
30+
src={background.url}
31+
alt="Learn more Image Background"
32+
fill
33+
className="object-cover rounded-2xl"
34+
/>
35+
</div>
36+
);
37+
};
38+
export default LearnMore;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import ExternalLink from "@/components/ExternalLink";
2+
import { CooperativePageMemberQueryType } from "@/queries/cooperative/member-section";
3+
import LearnMore from "./LearnMore";
4+
5+
interface IMemberSection {
6+
memberData: CooperativePageMemberQueryType["cooperativePageMemberSection"];
7+
}
8+
9+
const MemberSection: React.FC<IMemberSection> = ({ memberData }) => {
10+
return (
11+
<div className="bg-background-1 py-12 lg:py-24 px-6 lg:px-32">
12+
<h1 className="text-2xl lg:text-3xl text-primary-text font-medium mb-8">
13+
{memberData.header}
14+
</h1>
15+
<p className="text-lg text-secondary-text mb-16">{memberData.subtitle}</p>
16+
<LearnMore {...memberData.learnMoreSection} />
17+
<h1 className="text-2xl lg:text-3xl text-primary-text font-medium mb-8 mt-16">
18+
{memberData.secondaryHeader}
19+
</h1>
20+
<ExternalLink
21+
url={memberData.arrowLink.link.url}
22+
text={memberData.arrowLink.text}
23+
className="!gap-2"
24+
/>
25+
</div>
26+
);
27+
};
28+
29+
export default MemberSection;
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { useEffect, useMemo, useState } from "react";
2+
3+
import Dropdown from "@/components/Dropdown";
4+
import { Report } from "@/queries/cooperative/report-section";
5+
6+
import { Reports } from "./ReportCard";
7+
8+
interface IDropdownContainer
9+
extends Pick<Report, "yearDropdownLabel" | "monthDropdownLabel"> {
10+
reports: Reports;
11+
setReportUrl: (url?: string) => void;
12+
}
13+
14+
type IProcessedReports = Record<number, Array<string>>;
15+
16+
const DropdownContainer: React.FC<IDropdownContainer> = ({
17+
reports,
18+
setReportUrl,
19+
yearDropdownLabel,
20+
monthDropdownLabel,
21+
}) => {
22+
const processedReports = useMemo<IProcessedReports>(
23+
() =>
24+
reports.reduce<IProcessedReports>((acc, report: Reports[number]) => {
25+
const months = acc[report.year] ?? [];
26+
if (report.month) {
27+
acc[report.year] = [...months, report.month];
28+
} else {
29+
acc[report.year] = months;
30+
}
31+
return acc;
32+
}, {}),
33+
[reports]
34+
);
35+
36+
const years = useMemo(
37+
() =>
38+
Object.keys(processedReports)
39+
.map((key) => parseInt(key))
40+
.sort((a, b) => b - a)
41+
.map((year) => ({
42+
key: year,
43+
value: year,
44+
})),
45+
[processedReports]
46+
);
47+
48+
const firstYear = years[0].value;
49+
50+
const [year, setYear] = useState<number>(firstYear);
51+
52+
const months = useMemo(
53+
() =>
54+
processedReports[year]
55+
.sort((a, b) => monthOrder.indexOf(a) - monthOrder.indexOf(b))
56+
.map((month) => ({
57+
key: month,
58+
value: month,
59+
})),
60+
[year, processedReports]
61+
);
62+
63+
const [month, setMonth] = useState<string>();
64+
65+
const isMonthInfo = months.length > 0;
66+
67+
useEffect(() => {
68+
setMonth(undefined);
69+
}, [year]);
70+
71+
useEffect(() => {
72+
const selectedReport = reports.find(
73+
(report) =>
74+
(isMonthInfo ? report.month === month : true) && report.year === year
75+
);
76+
setReportUrl(selectedReport?.url);
77+
}, [isMonthInfo, month, year, reports, setReportUrl]);
78+
79+
return (
80+
<div className={
81+
"flex flex-col md:flex-row gap-8 items-start md:items-center"
82+
}>
83+
<div className="flex gap-4 items-center">
84+
<label className="text-lg text-secondary-text">
85+
{yearDropdownLabel}
86+
</label>
87+
<Dropdown
88+
items={years}
89+
value={year}
90+
onChange={(val) => setYear(Number(val))}
91+
/>
92+
</div>
93+
{isMonthInfo ? (
94+
<div className="flex gap-4 items-center">
95+
<label className="text-lg text-secondary-text">
96+
{monthDropdownLabel}
97+
</label>
98+
<Dropdown
99+
items={months}
100+
value={month}
101+
onChange={(val) => setMonth(val.toString())}
102+
/>
103+
</div>
104+
) : null}
105+
</div>
106+
);
107+
};
108+
109+
const monthOrder = [
110+
"January",
111+
"February",
112+
"March",
113+
"April",
114+
"May",
115+
"June",
116+
"July",
117+
"August",
118+
"September",
119+
"October",
120+
"November",
121+
"December",
122+
];
123+
124+
export default DropdownContainer;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useState } from "react";
2+
3+
import clsx from "clsx";
4+
import Image from "next/image";
5+
import Link from "next/link";
6+
7+
import Button from "@/components/Button";
8+
import { Report } from "@/queries/cooperative/report-section";
9+
10+
import DropdownContainer from "./DropdownContainer";
11+
12+
export type Reports = {
13+
url: string;
14+
month?: string;
15+
year: number;
16+
}[];
17+
18+
interface IReportCard extends Report {
19+
reports: Reports;
20+
}
21+
const ReportCard: React.FC<IReportCard> = ({
22+
title,
23+
subtitle,
24+
icon,
25+
reports,
26+
yearDropdownLabel,
27+
monthDropdownLabel,
28+
downloadButtonText,
29+
}) => {
30+
const [reportUrl, setReportUrl] = useState<string>();
31+
32+
return (
33+
<div className={clsx(
34+
"bg-background-2 md:pb-7",
35+
"flex flex-col md:flex-row gap-16 justify-between"
36+
)}>
37+
<div className="flex flex-col gap-8 items-start">
38+
<h2 className="text-2xl md:text-3xl font-medium text-primary-text">
39+
{title}
40+
</h2>
41+
<p className="text-lg text-secondary-text">{subtitle}</p>
42+
43+
<DropdownContainer
44+
{...{
45+
reports,
46+
setReportUrl,
47+
yearDropdownLabel,
48+
monthDropdownLabel,
49+
}}
50+
/>
51+
52+
<Link href={reportUrl ?? ""} target="_blank" rel="noreferrer noopenner">
53+
<Button
54+
variant="primary"
55+
className="text-background-1"
56+
disabled={typeof reportUrl === "undefined"}
57+
>
58+
{downloadButtonText}
59+
</Button>
60+
</Link>
61+
</div>
62+
63+
<div className={
64+
"relative w-32 h-32 md:w-56 md:h-56 flex-shrink-0 hidden md:block"
65+
}>
66+
<Image src={icon.url} alt={icon.name} fill className="object-contain" />
67+
</div>
68+
</div>
69+
);
70+
};
71+
72+
export default ReportCard;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useCallback } from "react";
2+
3+
import clsx from "clsx";
4+
5+
import {
6+
CooperativePageReportQueryType,
7+
ReportType,
8+
} from "@/queries/cooperative/report-section";
9+
10+
import ReportCard from "./ReportCard";
11+
12+
interface IReportSection {
13+
reportsData: CooperativePageReportQueryType;
14+
}
15+
16+
const ReportSection: React.FC<IReportSection> = ({ reportsData }) => {
17+
const getReports = useCallback((reportType: ReportType) => {
18+
switch (reportType) {
19+
case "annual":
20+
return reportsData.annualReports;
21+
case "risk":
22+
return reportsData.riskReports;
23+
default:
24+
return reportsData.treasuryReports;
25+
}
26+
}, [reportsData]);
27+
28+
return (
29+
<div className={clsx(
30+
"bg-background-2 py-12 lg:py-24 px-6 lg:px-32",
31+
"flex flex-col gap-12 md:gap-24"
32+
)}>
33+
{reportsData.cooperativePageReportSection.reports.map((report, i) => (
34+
<ReportCard
35+
key={i}
36+
{...report}
37+
reports={getReports(report.reportType)}
38+
/>
39+
))}
40+
</div>
41+
);
42+
};
43+
44+
export default ReportSection;

0 commit comments

Comments
 (0)