Skip to content
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
656 changes: 599 additions & 57 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
"eslint": "8.35.0",
"eslint-config-next": "13.2.3",
"jsonwebtoken": "^9.0.0",
"lucide-react": "^0.260.0",
"next": "13.2.3",
"pg": "^8.10.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.8.0"
"react-icons": "^4.8.0",
"recharts": "^2.7.2"
},
"devDependencies": {
"autoprefixer": "^10.4.13",
Expand Down
29 changes: 29 additions & 0 deletions src/components/Dashboard/DashboardStats.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";
import StatCard from "./StatCard";

export default function DashboardStats({ projects, issues }) {
const unsolvedIssues = issues.filter((issue) => issue.status != "closed");
return (
<section className="flex flex-col gap-4 w-full h-full flex-1">
<h2 className="text-xl">Statistics</h2>
<StatCard
title={"Total Projects"}
stat={`${projects.length} projects`}
color="#f0c816"
navLink={"/projects"}
/>
<StatCard
title={"Open Issues"}
stat={`${unsolvedIssues.length} issues`}
color="#eb4646"
navLink={"/issues"}
/>
<StatCard
title={"Closed Issues"}
stat={`${issues.length - unsolvedIssues.length} issues`}
color="#4f72ff"
navLink={"/issues"}
/>
</section>
);
}
49 changes: 49 additions & 0 deletions src/components/Dashboard/OpenIssuesByProject.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import {
BarChart,
Bar,
XAxis,
YAxis,
Tooltip,
Legend,
} from "recharts";

export default function OpenIssuesByProject({ issues }) {
const getIssuesLengthByProject = () => {
const openIssues = issues.filter((issue) => issue.status != "closed");

return openIssues.reduce((acc, currentIssue) => {
if (acc[currentIssue.project_name]) {
acc[currentIssue.project_name] += 1;
} else {
acc[currentIssue.project_name] = 1;
}
return acc;
}, {});
};

const formattedData = () => {
const array = [];
for (const [key, value] of Object.entries(getIssuesLengthByProject())) {
array.push({ name: key.slice(0,9)+"...", issues: value });
}
return array;
};

return (
<div className="flex-1">
<h2 className="text-xl mb-4">Open Issues by Project</h2>
<BarChart
width={500}
height={300}
data={formattedData()}
>
<XAxis dataKey="name" />
<YAxis allowDecimals={false}/>
<Tooltip />
<Legend />
<Bar dataKey="issues" fill="#8884d8" />
</BarChart>
</div>
);
}
32 changes: 32 additions & 0 deletions src/components/Dashboard/RecentIssueCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";

export default function RecentIssueCard({ issue }) {
const shortenString = (str) =>
str.length > 20 ? str.slice(0, 18) + "..." : str;

const handlePriority = (status) => {
if (status == "high") {
return "HIGH"
} else if (status == "medium"){
return "MED"
} else {
return "LOW"
}
}

return (
<tr className="bg-white border-b dark:bg-gray-900 dark:border-gray-700">
<th
scope="row"
className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{issue.created_by_name}
</th>
<td className="px-6 py-4">
{issue.title && shortenString(issue.title)}
</td>
<td className="px-6 py-4">{issue.priority && handlePriority(issue.priority)}</td>
<td className="px-6 py-4">{issue.status}</td>
</tr>
);
}
35 changes: 35 additions & 0 deletions src/components/Dashboard/RecentIssues.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import RecentIssueCard from "./RecentIssueCard";

export default function RecentIssues({ issues }) {
return (
<div className="h-full w-full flex-1">
<div className="relative overflow-x-auto shadow-md sm:rounded-lg">
<h2 className="text-xl mb-4">Recent Issues</h2>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="px-6 py-3">
User
</th>
<th scope="col" className="px-6 py-3">
Issue
</th>
<th scope="col" className="px-6 py-3">
Priority
</th>
<th scope="col" className="px-6 py-3">
Status
</th>
</tr>
</thead>
<tbody>
{issues.map((issue) => (
<RecentIssueCard key={issue.id} issue={issue} />
))}
</tbody>
</table>
</div>
</div>
);
}
47 changes: 47 additions & 0 deletions src/components/Dashboard/StatCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { BookMarked } from "lucide-react";
import Link from "next/link";
import React from "react";
import { LineChart, Line } from "recharts";

export default function StatCard({ title, stat, data, navLink, color }) {
const datas = [
{ name: "Page A", uv: 400, amt: 2400 },
{ name: "Page B", uv: 600, amt: 2600 },
{ name: "Page C", uv: 300, amt: 2200 },
{ name: "Page D", uv: 400, amt: 2200 },
];

const renderLineChart = (
<LineChart width={120} height={50} data={datas}>
<Line type="monotone" dataKey="uv" stroke={color} />
</LineChart>
);

const textColor = `text-[${color}]`;

console.log(textColor)

return (
<article className="flex min-w-[360px] flex-col gap-4 w-full py-4 px-6 border rounded-md border-gray-300">
<header className="flex justify-between">
<div className="flex gap-2">
<BookMarked color={color} />
<h2 className="text-lg">{title}</h2>
</div>
{renderLineChart}
</header>

<span className="text-2xl font-semibold">{stat}</span>

<div className="flex justify-between items-end">
<Link href={navLink} className="text-gray-200 underline text-sm hover:text-gray-300">
View all
</Link>
<div className="flex flex-col items-end">
<div className="text-lg font-bold text-green-600">21%</div>
<span className="text-sm text-gray-400">this month</span>
</div>
</div>
</article>
);
}
23 changes: 10 additions & 13 deletions src/pages/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import Head from "next/head";
import DashboardCard from "../components/DashboardCard";
import serverAPI from "@/api/axios";
import LoadingScreen from "@/components/LoadingScreen";
import { useRouter } from "next/router";
import { useUser } from "@/contexts/UserContext";
import { useEffect } from "react";
import RecentIssues from "@/components/Dashboard/RecentIssues";
import DashboardStats from "@/components/Dashboard/DashboardStats";
import OpenIssuesByProject from "@/components/Dashboard/OpenIssuesByProject";

export default function Home({ projects, issues }) {
const router = useRouter();
Expand Down Expand Up @@ -34,18 +36,13 @@ export default function Home({ projects, issues }) {
{/* Dashboard Home */}
{!isLoading && (
<main className="h-[90vh] w-full flex flex-col px-4 pt-5 gap-4 text-text">
<h1 className="text-2xl font-bold">Overview</h1>
<section className="w-full flex gap-2 flex-col md:flex-row">
<DashboardCard
title="Projects"
quantity={projects.length}
hrefLink="/projects"
/>
<DashboardCard
title="Issues"
quantity={issues.length}
hrefLink="/issues"
/>
<h1 className="text-2xl font-bold">Dashboard</h1>

{/* Dashboard Contents */}
<section className="w-full flex justify-between gap-4 flex-col md:flex-row">
<RecentIssues issues={issues}/>
<DashboardStats projects={projects} issues={issues}/>
<OpenIssuesByProject issues={issues}/>
</section>
</main>
)}
Expand Down