Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate metrics in detail view #42

Merged
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
1,483 changes: 1,348 additions & 135 deletions packages/firebase/seed.sample.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/firebase/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type SeedData = {
version: string;
projects: Record<string, object>;
metrics: Record<string, object>;
"metricRecords": Record<string, object>;
"project-totals": Record<string, object>;
"globalScore": Record<string, object>;
"etl-log": Record<string, object>;
};
Expand Down Expand Up @@ -63,7 +63,7 @@ export const importSeed = async (database: Firestore) => {
await Promise.all([
importCollectionData(database, "projects", seedToImport.projects),
importCollectionData(database, "metrics", seedToImport.metrics),
importCollectionData(database, "metricRecords", seedToImport["metricRecords"]),
importCollectionData(database, "project-totals", seedToImport["project-totals"]),
importCollectionData(database, "globalScore", seedToImport["globalScore"]),
importCollectionData(database, "etl-log", seedToImport["etl-log"]),
]);
Expand Down
47 changes: 18 additions & 29 deletions packages/nextjs/app/project/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Image from "next/image";
import type { NextPage } from "next";
import { ShareIcon } from "~~/components/assets/ShareIcon";
import CustomButton from "~~/components/onchain-impact-dashboard/CustomButton";
import { ProjectTotalsComponent } from "~~/components/onchain-impact-dashboard/projectTotalsComponent/projectTotalsComponent";
import { ProjectService } from "~~/services/onchainImpactDashboardApi/projectService";

export async function generateStaticParams() {
Expand Down Expand Up @@ -31,22 +32,30 @@ const ProjectDetail: NextPage<{ params: { id: string } }> = async ({ params }) =
<div className="flex flex-col flex-1 ml-4 justify-center">
<h1 className="text-lg m-0">#1 {data?.name}</h1>
</div>
<CustomButton text={"Share"} customClassName="border bg-transparent border-gray-200">
<CustomButton text={"Share"} customClassName="border bg-transparent border-gray-200 bg-base-100">
<ShareIcon />
</CustomButton>
</div>
<main className="leaderboard-content lg:flex">
<div className="mb-3 border border-gray-300 rounded-lg p-4 grow lg:mr-4 lg:7/12 min-h-[300px]">
Graph goes here...
</div>
<div className="mb-3 border border-gray-300 rounded-lg pl-4 pt-4 pr-4 lg:basis-5/12">
<CheckboxItem name="check1" />
<CheckboxItem name="check2" />
<CheckboxItem name="check3" />
</div>
<ProjectTotalsComponent id={params.id} />
</main>
<h2 className="font-semibold mt-8 mb-4">Description</h2>
<p>{data?.description}</p>
<h2 className="font-semibold mt-8 mb-4">Repositories</h2>
{[...data?.github, ...data?.packages].map((item, i) => (
<div key={i} className="flex items-center mb-2">
<a href={`${item}`} className="flex items-center" target="_blank" rel="noreferrer">
{item}
<Image
className="ml-2"
src="/assets/svg/icons/linkArrow.svg"
width={14}
height={14}
alt={`repository link`}
/>
</a>
</div>
))}
{data?.socialLinks?.website && (
<>
<h2 className="font-semibold mt-8 mb-4">Web</h2>
Expand Down Expand Up @@ -117,25 +126,5 @@ const ProjectDetail: NextPage<{ params: { id: string } }> = async ({ params }) =
</>
);
};
interface ICheckboxItem {
name: string;
}
const CheckboxItem = ({ name }: ICheckboxItem) => {
return (
<div className="border border-gray-300 bg-base-200 rounded-lg p-4 mb-4">
<label className="flex justify-center gap-4" htmlFor={name}>
<div className="">
<input id={name} type="checkbox" className=" w-[18px] h-[18px] accent-blue-600" />
</div>
<div>
<h3 className="">Title</h3>
<span className="">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</span>
</div>
</label>
</div>
);
};

export default ProjectDetail;
28 changes: 28 additions & 0 deletions packages/nextjs/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
interface ICheckboxItem {
name: string;
onChange: (arg: boolean) => void;
value: boolean;
}
export const CheckboxItem = ({ value, name, onChange }: ICheckboxItem) => {
return (
<div className="border border-gray-300 bg-base-200 rounded-lg p-4 mb-4">
<label className="flex justify-center gap-4" htmlFor={name}>
<div className="">
<input
id={name}
type="checkbox"
checked={value}
onChange={e => onChange(e.target.checked)}
className=" w-[18px] h-[18px] accent-blue-600"
/>
</div>
<div>
<h3 className="font-bold">{name}</h3>
<span className="">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</span>
</div>
</label>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ const LeaderboardCollapse: React.FC<
customClassName="bg-red-600 mr-3 text-white hover:bg-red-500 border-transparent btn-md"
/>
</Link>
<CustomButton text={"Share"} customClassName="border border-gray-200" onClick={() => shareProject()}>
<CustomButton
text={"Share"}
customClassName="border border-gray-200 bg-base-100"
onClick={() => shareProject()}
>
<ShareIcon />
</CustomButton>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
interface IFilterButton {
filter: string;
value: string;
label: string;
onClick: (val: string) => void;
}
export const FilterButton = ({ filter, label, onClick, value }: IFilterButton) => {
return (
<button
className={`p-2 pl-4 pr-4 ${
filter == value ? "border border-gray-300 bg-customGray rounded-lg" : " border border-transparent"
}`}
onClick={() => onClick(value)}
>
{label}
</button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { useEffect, useState } from "react";
import Leaderboard from "../Leaderboard";
import { FilterButton } from "../filterButton/filterButton";
import { LeaderBoardGraph } from "../leaderboardGraph/leaderBoardGraph";
import { DatePicker } from "~~/components/impact-vector/inputs/datePicker";
import { GlobalScoreDTO } from "~~/pages/api/globalScore";
Expand All @@ -10,9 +11,10 @@ import { GlobalScoreService } from "~~/services/onchainImpactDashboardApi/global
import { ProjectService } from "~~/services/onchainImpactDashboardApi/projectService";

export const LeaderBoardComponent = () => {
const DEFAULT_FILTER = "30";
const [scores, setScores] = useState<GlobalScoreDTO[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [filter, setFilter] = useState("1");
const [filter, setFilter] = useState(DEFAULT_FILTER);
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const { getPaginatedGlobalScores } = GlobalScoreService();
Expand All @@ -24,7 +26,7 @@ export const LeaderBoardComponent = () => {
};
useEffect(() => {
getProjects();
getScores("1");
getScores(DEFAULT_FILTER);
}, []);
useEffect(() => {
setEndDate("");
Expand Down Expand Up @@ -61,10 +63,10 @@ export const LeaderBoardComponent = () => {
<div className="flex flex-col lg:flex-row mb-4">
<div className="flex flex-col xl:flex-row items-center bg-base-300 rounded-lg p-2 pr-2 w-full xl:w-auto">
<div className="flex items-center">
<FilterButton filter={filter} value="1" label="24h" onClick={onFilter} />
<FilterButton filter={filter} value="7" label="7d" onClick={onFilter} />
<FilterButton filter={filter} value="30" label="1m" onClick={onFilter} />
<FilterButton filter={filter} value="365" label="1y" onClick={onFilter} />{" "}
<FilterButton filter={filter} value="90" label="3m" onClick={onFilter} />
<FilterButton filter={filter} value="270" label="6m" onClick={onFilter} />
<FilterButton filter={filter} value="365" label="1y" onClick={onFilter} />
<FilterButton filter={filter} value="range" label="Range" onClick={onFilter} />
</div>
{filter == "range" && (
Expand Down Expand Up @@ -97,20 +99,3 @@ export const LeaderBoardComponent = () => {
</main>
);
};

interface IFilterButton {
filter: string;
value: string;
label: string;
onClick: (val: string) => void;
}
const FilterButton = ({ filter, label, onClick, value }: IFilterButton) => {
return (
<button
className={`p-2 pl-4 pr-4 ${filter == value ? "bg-red-500 rounded-lg text-white" : ""}`}
onClick={() => onClick(value)}
>
{label}
</button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import React from "react";
import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import { GlobalScoreDTO } from "~~/pages/api/globalScore";
import { formatDate } from "~~/utils/onchainImpactDashboard/common";

export const LeaderBoardGraph = ({ scores }: { scores: GlobalScoreDTO[] }) => {
const CustomTooltip = ({ active, payload, label }: any) => {
if (active && payload && payload.length) {
return (
<div className="bg-base-300">
<p className="m-1 label">{`Date : ${label}`}</p>
<div className="bg-base-300 p-4 rounded-lg shadow">
<p className="m-0 p-0 label font-bold">Date</p>
<p className="m-0 p-0 label">{formatDate(label)}</p>
{payload.map((entry: any, index: any) => (
<p key={`item-${index}`} className="m-1" style={{ color: entry.color }}>
{`${entry.name} : ${entry.value}`}
</p>
<div key={`item-${index}`} style={{ color: entry.color }}>
<p className="m-0 mt-4 p-0 label font-bold">Global Score</p>
<p className="m-0 p-0 label">{entry.value}</p>
</div>
))}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import React, { useEffect, useState } from "react";
import { ProjectTotalsGraph } from "../projectTotalsGraph/projectTotalsGraph";
import { CheckboxItem } from "~~/components/checkbox/checkbox";
import { ProjectTotalsService } from "~~/services/onchainImpactDashboardApi/projectTotalsService";

export const ProjectTotalsComponent = ({ id }: { id: string }) => {
const DEFAULT_FILTER = "30";
const [filter, setFilter] = useState(DEFAULT_FILTER);
const [selectedMetrics, setSelectedMetrics] = useState<string[]>([]);
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const { getProjectTotalsByIdAndFilters } = ProjectTotalsService();
const [totalsRecord, setTotalsRecord] = useState<any[]>([]);

const getTotals = async () => {
const data = await getProjectTotalsByIdAndFilters(id, filter);
const graphData = data.timeSeries.map(item => {
const val: any = { date: item.createdAt };
Object.keys(item.metrics).forEach(key => {
val[key] = item.metrics[key].score;
});
return val;
});
if (selectedMetrics.length == 0) {
setSelectedMetrics(["onchain-users"]);
}
setTotalsRecord(graphData);
};
useEffect(() => {
getTotals();
}, []);

const onFilter = (value: string) => {
setFilter(value);
if (value == "range") {
return;
}
getTotals();
};
const keys = totalsRecord?.length > 0 ? Object.keys(totalsRecord[0]).filter(it => it != "date") : [];

return (
<>
<div className="mb-3 border border-gray-300 w-full h-[50vh] rounded-lg p-2 grow min-h-[300px] lg:mr-4 lg:7/12 relative">
<ProjectTotalsGraph
keys={keys}
id={id}
totalsRecord={totalsRecord}
selectedMetrics={selectedMetrics}
filter={filter}
onFilter={val => onFilter(val)}
endDate={endDate}
startDate={startDate}
updateStartDate={val => setStartDate(val)}
updateEndDate={val => setEndDate(val)}
/>
</div>
<div className="mb-3 border border-gray-300 rounded-lg pl-4 pt-4 pr-4 lg:basis-5/12 lg:max-h-[426px] overflow-auto">
{keys.map(key => (
<CheckboxItem
key={key}
name={key}
onChange={(arg: boolean) => {
if (arg) {
setSelectedMetrics(elems => [...elems, key]);
} else {
setSelectedMetrics(ellems => ellems.filter(it => it != key));
}
}}
value={selectedMetrics.includes(key)}
/>
))}
</div>
</>
);
};
Loading
Loading