Website improvements 4 (#3182)

* Add contributor individual page

* Improve mobile menu

* Fix

* Remove yarn.lock from twenty-website

* Add yarn to gitingore

* Fix linter
This commit is contained in:
Félix Malfait
2023-12-31 10:41:53 +01:00
committed by GitHub
parent 97f83b55b0
commit 858c294f14
60 changed files with 571 additions and 9797 deletions

View File

@ -0,0 +1,9 @@
'use client'
import { ResponsiveTimeRange } from '@nivo/calendar'
export const ActivityLog = ({ data }: { data: { value: number, day: string }[] }) => {
return <ResponsiveTimeRange
data={data}
/>;
}

View File

@ -0,0 +1,100 @@
import Image from 'next/image';
import Database from 'better-sqlite3';
import AvatarGrid from '@/app/components/AvatarGrid';
import { ResponsiveTimeRange } from '@nivo/calendar'
import { ActivityLog } from './components/ActivityLog';
import { Metadata } from 'next';
interface Contributor {
login: string;
avatarUrl: string;
pullRequestCount: number;
}
export function generateMetadata({ params }: { params: { slug: string } }): Metadata {
return {
title: params.slug + ' | Contributors',
};
}
export default async function (
{ params }: { params: { slug: string } }) {
const db = new Database('db.sqlite', { readonly: true });
const contributor = db.prepare(`
SELECT
u.login,
u.avatarUrl,
(SELECT COUNT(*) FROM pullRequests WHERE authorId = u.id) AS pullRequestCount,
(SELECT COUNT(*) FROM issues WHERE authorId = u.id) AS issuesCount
FROM
users u
WHERE
u.login = :user_id
`).get({'user_id' : params.slug}) as Contributor;
const pullRequestActivity = db.prepare(`
SELECT
COUNT(*) as value,
DATE(createdAt) as day
FROM
pullRequests
WHERE
authorId = (SELECT id FROM users WHERE login = :user_id)
GROUP BY
DATE(createdAt)
ORDER BY
DATE(createdAt)
`).all({'user_id': params.slug}) as { value: number, day: string }[];
const pullRequestList = db.prepare(`
SELECT
id,
title,
body,
url,
createdAt,
updatedAt,
closedAt,
mergedAt
FROM
pullRequests
WHERE
authorId = (SELECT id FROM users WHERE login = :user_id)
ORDER BY
DATE(createdAt) DESC
`).all({'user_id': params.slug}) as { title: string, createdAt: string, url: string }[];
db.close();
return (
<div style={{maxWidth: '900px', display: 'flex', padding: '40px', gap: '24px'}}>
<div style={{ flexDirection: 'column', width: '240px'}}>
<Image src={contributor.avatarUrl} alt={contributor.login} width={240} height={240} />
<h1>{contributor.login}</h1>
</div>
<div style={{flexDirection: 'column'}}>
<div style={{width: '450px', height: '200px'}}>
<ActivityLog
data={pullRequestActivity}
/>
</div>
<div style={{width: '450px'}}>
{pullRequestList.map(pr => (
<div>
<a href={pr.url}>{pr.title}</a>
</div>
))}
</div>
</div>
</div>
);
};

View File

@ -7,7 +7,7 @@ export async function GET(
{ params }: { params: { slug: string } }) {
const db = new Database('db.sqlite', { readonly: true });
if(params.slug !== 'users' && params.slug !== 'labels' && params.slug !== 'pullRequests') {
if(params.slug !== 'users' && params.slug !== 'labels' && params.slug !== 'pullRequests' && params.slug !== 'issues') {
return Response.json({ error: 'Invalid table name' }, { status: 400 });
}

View File

@ -21,6 +21,7 @@ interface PullRequestNode {
id: string;
title: string;
body: string;
url: string;
createdAt: string;
updatedAt: string;
closedAt: string;
@ -35,6 +36,7 @@ interface IssueNode {
id: string;
title: string;
body: string;
url: string;
createdAt: string;
updatedAt: string;
closedAt: string;
@ -90,6 +92,7 @@ async function fetchData(cursor: string | null = null, isIssues: boolean = false
id
title
body
url
createdAt
updatedAt
closedAt
@ -119,6 +122,7 @@ async function fetchData(cursor: string | null = null, isIssues: boolean = false
id
title
body
url
createdAt
updatedAt
closedAt
@ -178,6 +182,7 @@ const initDb = () => {
id TEXT PRIMARY KEY,
title TEXT,
body TEXT,
url TEXT,
createdAt TEXT,
updatedAt TEXT,
closedAt TEXT,
@ -192,7 +197,8 @@ const initDb = () => {
id TEXT PRIMARY KEY,
title TEXT,
body TEXT,
createdAt TEXT,
url TEXT,
createdAt TEXT,
updatedAt TEXT,
closedAt TEXT,
authorId TEXT,
@ -249,10 +255,10 @@ export async function GET() {
const assignableUsers = await fetchAssignableUsers();
const prs = await fetchData(lastPRCursor) as Array<PullRequestNode>;
const issues = await fetchData(lastIssueCursor) as Array<IssueNode>;
const issues = await fetchData(lastIssueCursor, true) as Array<IssueNode>;
const insertPR = db.prepare('INSERT INTO pullRequests (id, title, body, createdAt, updatedAt, closedAt, mergedAt, authorId) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertIssue = db.prepare('INSERT INTO issues (id, title, body, createdAt, updatedAt, closedAt, authorId) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertPR = db.prepare('INSERT INTO pullRequests (id, title, body, url, createdAt, updatedAt, closedAt, mergedAt, authorId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertIssue = db.prepare('INSERT INTO issues (id, title, body, url, createdAt, updatedAt, closedAt, authorId) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertUser = db.prepare('INSERT INTO users (id, login, avatarUrl, url, isEmployee) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertLabel = db.prepare('INSERT INTO labels (id, name, color, description) VALUES (?, ?, ?, ?) ON CONFLICT(id) DO NOTHING');
const insertPullRequestLabel = db.prepare('INSERT INTO pullRequestLabels (pullRequestId, labelId) VALUES (?, ?)');
@ -262,7 +268,7 @@ export async function GET() {
console.log(pr);
if(pr.author == null) { continue; }
insertUser.run(pr.author.resourcePath, pr.author.login, pr.author.avatarUrl, pr.author.url, assignableUsers.has(pr.author.login) ? 1 : 0);
insertPR.run(pr.id, pr.title, pr.body, pr.createdAt, pr.updatedAt, pr.closedAt, pr.mergedAt, pr.author.resourcePath);
insertPR.run(pr.id, pr.title, pr.body, pr.url, pr.createdAt, pr.updatedAt, pr.closedAt, pr.mergedAt, pr.author.resourcePath);
for (const label of pr.labels.nodes) {
insertLabel.run(label.id, label.name, label.color, label.description);
@ -274,7 +280,7 @@ export async function GET() {
if(issue.author == null) { continue; }
insertUser.run(issue.author.resourcePath, issue.author.login, issue.author.avatarUrl, issue.author.url, assignableUsers.has(issue.author.login) ? 1 : 0);
insertIssue.run(issue.id, issue.title, issue.body, issue.createdAt, issue.updatedAt, issue.closedAt, issue.author.resourcePath);
insertIssue.run(issue.id, issue.title, issue.body, issue.url, issue.createdAt, issue.updatedAt, issue.closedAt, issue.author.resourcePath);
for (const label of issue.labels.nodes) {
insertLabel.run(label.id, label.name, label.color, label.description);

View File

@ -26,12 +26,12 @@ const Contributors = async () => {
ORDER BY
pullRequestCount DESC;
`).all() as Contributor[];
db.close();
return (
<div>
<h1>Top Contributors</h1>
<AvatarGrid users={contributors} />
</div>
);