Skip to content

Commit

Permalink
Quest application single 2 (#1032)
Browse files Browse the repository at this point in the history
Co-authored-by: Mehdi Hamri <[email protected]>
  • Loading branch information
alexbeno and pixelfact authored Feb 21, 2025
1 parent ff09e63 commit 02ee257
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { TypographyP, TypographySmall } from "@/shared/ui/typography";
import { cn } from "@/shared/utils";

import { AmountOfWorkBadgeProps } from "./amount-of-work-badge.types";

export function AmountOfWorkBadge({ value }: AmountOfWorkBadgeProps) {
const numberOfLines = 8;
const isRed = value <= 1;
const isOrange = value <= 2 && value > 1;
const isAmber = value <= 3 && value > 2;
const isYellow = value <= 4 && value > 3;
const isLime = value > 4 && value <= 5;

const lines = Array.from({ length: numberOfLines }, (_, index) => {
const threshold = (index * 5) / numberOfLines;
return index < 1 || value >= threshold;
});

return (
<Tooltip>
<TooltipTrigger>
<div
className={cn(
"flex h-8 w-auto items-center justify-center gap-2 rounded-md border px-2.5 py-0.5 text-xs font-semibold",
"!border-border !bg-transparent",
{
"border-border from-red-950 text-red-600": isRed,
"border-orange-950 from-orange-950 text-orange-600": isOrange,
"border-amber-950 from-amber-950 text-amber-600": isAmber,
"border-yellow-950 from-yellow-950 text-yellow-600": isYellow,
"border-lime-950 from-lime-950 text-lime-600": isLime,
}
)}
>
<TypographySmall className="text-inherit">Amount of work ({value})</TypographySmall>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className={cn("text-inherit")}
>
<path d="M12 2v4" className={cn("text-border", { "text-inherit": lines[0] })} />
<path d="m16.2 7.8 2.9-2.9" className={cn("text-border", { "text-inherit": lines[1] })} />
<path d="M18 12h4" className={cn("text-border", { "text-inherit": lines[2] })} />
<path d="m16.2 16.2 2.9 2.9" className={cn("text-border", { "text-inherit": lines[3] })} />
<path d="M12 18v4" className={cn("text-border", { "text-inherit": lines[4] })} />
<path d="m4.9 19.1 2.9-2.9" className={cn("text-border", { "text-inherit": lines[5] })} />
<path d="M2 12h4" className={cn("text-border", { "text-inherit": lines[6] })} />
<path d="m4.9 4.9 2.9 2.9" className={cn("text-border", { "text-inherit": lines[7] })} />
</svg>
</div>
</TooltipTrigger>
<TooltipContent side="bottom" align="start">
<div className="max-w-[450px]">
<TypographyP>
This amount of work score is a guideline rather than an exact calculation. Compare pull requests with each
other to keep the evaluation relevant.
</TypographyP>
<ul className="list-disc p-2">
<li>
<TypographyP>{"1-2 : Very Small (<2h): Minor fixes or simple changes"}</TypographyP>
</li>
<li>
<TypographyP>{"2-2 : Small (2-5h): Multiple bug fixes, simple feature tweaks"}</TypographyP>
</li>
<li>
<TypographyP>
{"3-4: Medium (5h-1 day): New features using existing patterns, moderate refactoring"}
</TypographyP>
</li>
<li>
<TypographyP>{"4-5 : Large (1-3 days): Complex features, architectural changes"}</TypographyP>
</li>
<li>
<TypographyP>{"5 : Major (3+ days): Critical overhauls, major system rewrites"}</TypographyP>
</li>
</ul>
</div>
</TooltipContent>
</Tooltip>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface AmountOfWorkBadgeProps {
value: number;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,200 @@
import { useCallback } from "react";
import { ScrollArea } from "@radix-ui/react-scroll-area";
import { Sparkles } from "lucide-react";
import Link from "next/link";
import { useCallback, useState } from "react";
import Emoji from "react-emoji-render";

import { ContributionReactQueryAdapter } from "@/core/application/react-query-adapter/contribution";
import { bootstrap } from "@/core/bootstrap";

import { ContributionBadge } from "@/design-system/molecules/contribution-badge";

import { NEXT_ROUTER } from "@/shared/constants/router";
import { Markdown } from "@/shared/features/markdown/markdown";
import { Github } from "@/shared/icons";
import { Avatar, AvatarFallback, AvatarImage } from "@/shared/ui/avatar";
import { Badge } from "@/shared/ui/badge";
import { Button } from "@/shared/ui/button";
import { Card } from "@/shared/ui/card";
import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTrigger } from "@/shared/ui/sheet";
import { Skeleton } from "@/shared/ui/skeleton";
import { TypographyH3, TypographyMuted, TypographyP, TypographySmall } from "@/shared/ui/typography";
import { TypographyH3, TypographyH4, TypographyMuted, TypographyP, TypographySmall } from "@/shared/ui/typography";
import { cn } from "@/shared/utils";

import { AmountOfWorkBadge } from "../amount-of-work-badge/amount-of-work-badge";
import { IssueListProps } from "./issue-list.types";

function IssueListItem({ issue }: { issue: IssueListProps["issues"][number] }) {
return (
<div key={issue.number} className={"w-full text-left transition-opacity hover:opacity-80"}>
<Card className={"flex cursor-pointer flex-col items-start justify-start gap-2 p-3"}>
<div className="flex w-full items-center justify-between gap-px">
<div className={"flex flex-1 items-center gap-3"}>
<ContributionBadge type={issue.type} number={issue.number} githubStatus={issue.githubStatus} />

<TypographySmall className={"line-clamp-1"}>
<Emoji>{issue.title}</Emoji>
</TypographySmall>
</div>
<div className="flex items-center gap-1 px-3 py-1 first:pl-0">
<div className="flex flex-col gap-1">
<AmountOfWorkBadge value={issue.score} />
</div>
</div>
</div>
<TypographyMuted className={"line-clamp-1"}>
<Emoji>
{issue.justifications?.length > 100 ? `${issue.justifications.slice(0, 100)}...` : issue.justifications}
</Emoji>
</TypographyMuted>
</Card>
</div>
);
}

function IssueListItemPanel({
issue,
children,
}: {
issue: IssueListProps["issues"][number];
children: React.ReactNode;
}) {
const dateKernelPort = bootstrap.getDateKernelPort();
const [open, setOpen] = useState(false);

const { data, isLoading, isError } = ContributionReactQueryAdapter.client.useGetContributionById({
pathParams: { contributionUuid: issue.uuid },
options: { enabled: Boolean(issue.uuid) && open },
});

const renderMetrics = useCallback(() => {
if (isLoading) {
return (
<div className="grid grid-cols-3 gap-3">
{Array.from({ length: 3 }).map((_, index) => (
<Skeleton key={index} className="h-[74px]" />
))}
</div>
);
}

if (!data || isError) return null;

const openedSince = parseInt(dateKernelPort.formatDistanceToNow(new Date(data.createdAt), { unit: "day" }));

return (
<div className="grid grid-cols-3 gap-3">
<Card className="p-3">
<TypographyMuted>Applicants</TypographyMuted>
<TypographyH4>{data.applicants.length}</TypographyH4>
</Card>

<Card className="p-3">
<TypographyMuted>Comments</TypographyMuted>
<TypographyH4>{data.githubCommentCount}</TypographyH4>
</Card>

<Card className="p-3">
<TypographyMuted>Opened since</TypographyMuted>
<TypographyH4>{openedSince === 1 ? "1 day" : `${openedSince} days`}</TypographyH4>
</Card>
</div>
);
}, [data, isLoading, isError]);

const renderSummary = useCallback(() => {
if (isLoading) {
return <Skeleton className="h-40" />;
}

if (!data || isError) return null;

const createdSince = dateKernelPort.formatDistanceToNow(new Date(data.createdAt));

return (
<Card className="flex flex-col gap-3 p-3">
<header className={"flex w-full flex-row items-center justify-start gap-3"}>
<ContributionBadge type={data.type} number={data.githubNumber} githubStatus={data.githubStatus} />
<TypographyH4 className="line-clamp-1">{data.githubTitle}</TypographyH4>
</header>

{data.githubBody ? <Markdown content={data.githubBody} /> : null}

<TypographyMuted>{createdSince}</TypographyMuted>

<footer className="flex items-end justify-between gap-3">
<ul className="flex flex-row flex-wrap gap-2">
{data.githubLabels?.map(label => (
<li key={label.name}>
<Badge variant="outline">{label.name}</Badge>
</li>
))}
</ul>

<Link href={NEXT_ROUTER.users.details.root(data.githubAuthor.login)}>
<Avatar className="size-5">
<AvatarImage src={data.githubAuthor.avatarUrl} alt={data.githubAuthor.login} />
<AvatarFallback className="size-5">{data.githubAuthor.login.charAt(0)}</AvatarFallback>
</Avatar>
</Link>
</footer>
</Card>
);
}, [data, isLoading, isError]);

return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger>{children}</SheetTrigger>
<SheetContent className={"flex flex-col"}>
<SheetHeader className="flex flex-row items-center justify-start gap-2">
<AmountOfWorkBadge value={issue.score} />
</SheetHeader>
<ScrollArea className="flex flex-1 flex-col gap-4">
{renderSummary()}
{renderMetrics()}
<Card
className={cn(
"relative flex flex-col gap-4 overflow-hidden bg-gradient-to-br from-purple-950 to-transparent to-20% p-4"
)}
>
<header className={"flex w-full items-center justify-start gap-2"}>
<div className={"flex items-center gap-2"}>
<Sparkles className={"text-purple-700"} />
<TypographyH3>Analysis by OnlyDust</TypographyH3>
</div>
</header>

<div className={"relative h-fit overflow-hidden transition-all"}>
<TypographyP>{issue.justifications}</TypographyP>
</div>
</Card>
{data?.githubBody ? (
<Card className={cn("overflow-hiddenp-4 relative flex flex-col gap-4")}>
<header className={"flex w-full items-center justify-start gap-2"}>
<div className={"flex items-center gap-2"}>
<TypographyH3>Description</TypographyH3>
</div>
</header>

<div className={"relative h-fit overflow-hidden transition-all"}>
<Markdown content={data.githubBody} />
</div>
</Card>
) : null}
</ScrollArea>
<SheetFooter>
<Button variant={"outline"} asChild>
<Link href={issue.url} target="_blank">
<Github />
See on Github
</Link>
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}

export function IssueList({
containerClassName,
title,
Expand Down Expand Up @@ -55,41 +237,13 @@ export function IssueList({
}

return (
<ul className={"flex flex-col gap-3"}>
<div className={"flex flex-col gap-3"}>
{issues.map(issue => (
<li key={issue.number}>
<a
target="_blank"
rel="noreferrer"
href={issue.url}
className={"w-full text-left transition-opacity hover:opacity-80"}
>
<Card className={"flex cursor-pointer flex-col items-start justify-start gap-2 p-3"}>
<div className="flex w-full items-center justify-between gap-px">
<div className={"flex flex-1 items-center gap-3"}>
<ContributionBadge type={"ISSUE"} number={issue.number} githubStatus={issue.githubStatus} />

<TypographySmall className={"line-clamp-1"}>
<Emoji>{issue.title}</Emoji>
</TypographySmall>
</div>
<div className="flex items-center gap-1 px-3 py-1 first:pl-0">
{issue.languages?.map(language => (
<Avatar className="size-5" key={language.name}>
<AvatarImage src={language.logoUrl} />
<AvatarFallback className="rounded-xl">{language.name.charAt(0)}</AvatarFallback>
</Avatar>
))}
</div>
</div>
<TypographyMuted className={"line-clamp-1"}>
<Emoji>{issue.justifications}</Emoji>
</TypographyMuted>
</Card>
</a>
</li>
<IssueListItemPanel key={issue.number} issue={issue}>
<IssueListItem issue={issue} />
</IssueListItemPanel>
))}
</ul>
</div>
);
}, [issues, isLoading, isError, dateKernel]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ interface Issue {
type: ContributionTypeUnion;
githubStatus: ContributionGithubStatusUnion;
number: number;
score: number;
title: string;
createdAt: string;
url: string;
uuid: string;
justifications: string;
languages: {
logoUrl: string;
Expand Down
Loading

0 comments on commit 02ee257

Please sign in to comment.