This repository was archived by the owner on Feb 10, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
feat: project review list #17
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
2ea8182
feat: implemented generic ListGrid component
hussmartinez 772a52c
chore: install moment library
hussmartinez 47ce89d
chore: added grey-image for temporary placeholder
hussmartinez ddacbde
feat: implemented ProjectReviewList
hussmartinez bf0bb69
Merge branch 'main' into feat/project-review-list
hussmartinez 2b5557f
Merge branch 'main' into feat/project-review-list
hussmartinez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions
19
src/features/checker/components/ProjectReviewList/ProjectReviewList.stories.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import { ProjectReviewList } from "./ProjectReviewList"; | ||
import { mockPendingReview0, mockReadyToSubmit0 } from "./mocks"; | ||
|
||
const meta = { | ||
title: "Features/Checker/ProjectReviewList", | ||
component: ProjectReviewList, | ||
args: { | ||
reviewer: "0x1234567890123456789012345678901234567890", | ||
}, | ||
} satisfies Meta; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof ProjectReviewList>; | ||
|
||
export const ReadyToSubmit: Story = { args: { projects: mockReadyToSubmit0 } }; | ||
export const PendingReview: Story = { args: { projects: mockPendingReview0 } }; |
89 changes: 89 additions & 0 deletions
89
src/features/checker/components/ProjectReviewList/ProjectReviewList.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { IconLabel } from "@/components/IconLabel"; | ||
import { Button } from "@/primitives/Button"; | ||
import { CircleStat } from "@/primitives/Indicators"; | ||
import { ListGrid, ListGridColumn } from "@/primitives/ListGrid"; | ||
|
||
import { getReviewsCount } from "../../utils/getReviewsCount"; | ||
import { ProjectReview } from "./types"; | ||
|
||
export interface ProjectReviewListProps { | ||
reviewer: `0x${string}`; | ||
projects: ProjectReview[]; | ||
} | ||
|
||
export const ProjectReviewList = ({ reviewer, projects }: ProjectReviewListProps) => { | ||
const columns: ListGridColumn<ProjectReview>[] = [ | ||
{ | ||
header: "Project", | ||
key: "project", | ||
width: "2fr", | ||
render: (item) => ( | ||
<div className="flex items-center gap-4"> | ||
<img | ||
src={item.avatarUrl} | ||
alt={item.name} | ||
className="aspect-square size-12 rounded-sm" | ||
onError={(event: React.SyntheticEvent<HTMLImageElement, Event>) => { | ||
event.currentTarget.src = "/images/grey-image.png"; | ||
}} | ||
/> | ||
<span>{item.name}</span> | ||
</div> | ||
), | ||
}, | ||
{ | ||
header: "Date Submitted", | ||
key: "date", | ||
width: "1fr", | ||
render: (item) => <IconLabel type="date" date={item.date} />, | ||
}, | ||
{ | ||
header: "Reviews", | ||
key: "reviews", | ||
width: "1fr", | ||
render: (item) => { | ||
const { nApproved, nRejected } = getReviewsCount(item.reviews); | ||
return <IconLabel type="reviews" posReviews={nApproved} negReviews={nRejected} />; | ||
}, | ||
}, | ||
{ | ||
header: "AI Suggestion", | ||
key: "aiSuggestion", | ||
width: "1fr", | ||
render: (item) => <IconLabel type="ai-evaluation" percent={item.aiSuggestion} />, | ||
}, | ||
{ | ||
header: "Score Average", | ||
key: "scoreAverage", | ||
width: "1fr", | ||
position: "center", | ||
render: (item) => ( | ||
<div className="flex items-center justify-center"> | ||
<CircleStat value={item.scoreAverage} /> | ||
</div> | ||
), | ||
}, | ||
{ | ||
header: "Action", | ||
key: "action", | ||
width: "1fr", | ||
position: "center", | ||
render: (item) => { | ||
const isReviewed = item.reviews.some((review) => review.reviewer === reviewer); | ||
return ( | ||
<div className="flex items-center justify-center"> | ||
<Button variant="secondary" value="Evaluate project" disabled={isReviewed} /> | ||
</div> | ||
); | ||
}, | ||
}, | ||
]; | ||
return ( | ||
<ListGrid | ||
data={projects} | ||
columns={columns} | ||
rowClassName="h-[72px]" | ||
getRowKey={(item: ProjectReview) => item.id.toString()} | ||
/> | ||
); | ||
}; |
71 changes: 71 additions & 0 deletions
71
src/features/checker/components/ProjectReviewList/mocks.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { ProjectReview } from "./types"; | ||
|
||
export const mockPendingReview0: ProjectReview[] = [ | ||
{ | ||
id: 1, | ||
name: "project title", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [], | ||
aiSuggestion: 60, | ||
scoreAverage: 60, | ||
}, | ||
{ | ||
id: 2, | ||
name: "project title", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [], | ||
aiSuggestion: 23, | ||
scoreAverage: 23, | ||
}, | ||
{ | ||
id: 3, | ||
name: "project title", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [], | ||
aiSuggestion: 54, | ||
scoreAverage: 54, | ||
}, | ||
]; | ||
|
||
export const mockReadyToSubmit0: ProjectReview[] = [ | ||
{ | ||
id: 1, | ||
name: "cool project", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [ | ||
{ approved: true, reviewer: "0xJohnDoe" }, | ||
{ approved: false, reviewer: "0xJaneDoe" }, | ||
{ approved: true, reviewer: "0xJoneDoe" }, | ||
], | ||
aiSuggestion: 72, | ||
scoreAverage: 88, | ||
}, | ||
{ | ||
id: 2, | ||
name: "project title", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [ | ||
{ approved: true, reviewer: "0xJohnDoe" }, | ||
{ approved: true, reviewer: "0xJoneDoe" }, | ||
], | ||
aiSuggestion: 80, | ||
scoreAverage: 92, | ||
}, | ||
{ | ||
id: 3, | ||
name: "project title", | ||
date: new Date(2024, 5, 3, 15, 0, 0), | ||
avatarUrl: "", | ||
reviews: [ | ||
{ approved: true, reviewer: "0xJohnDoe" }, | ||
{ approved: true, reviewer: "0xJoneDoe" }, | ||
], | ||
aiSuggestion: 80, | ||
scoreAverage: 92, | ||
}, | ||
]; |
14 changes: 14 additions & 0 deletions
14
src/features/checker/components/ProjectReviewList/types.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export interface Review { | ||
reviewer: `0x${string}`; | ||
approved: boolean; | ||
} | ||
|
||
export interface ProjectReview { | ||
id: number; | ||
name: string; | ||
date: Date; | ||
avatarUrl: string; | ||
reviews: Review[]; | ||
aiSuggestion: number; | ||
scoreAverage: number; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Review } from "../components/ProjectReviewList/types"; | ||
|
||
export const getReviewsCount = (reviews: Review[]) => { | ||
const { nApproved, nRejected } = reviews.reduce( | ||
(acc, review) => { | ||
acc.nApproved += review.approved ? 1 : 0; | ||
acc.nRejected += review.approved ? 0 : 1; | ||
return acc; | ||
}, | ||
{ nApproved: 0, nRejected: 0 }, | ||
); | ||
return { nApproved, nRejected }; | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import { ListGrid } from "./ListGrid"; | ||
import { mockColumns0, mockGetRowKey0, TMockData0 } from "./mocks"; | ||
import { mockData0 } from "./mocks"; | ||
|
||
const meta = { | ||
title: "Primitives/ListGrid", | ||
component: ListGrid, | ||
} satisfies Meta; | ||
|
||
export default meta; | ||
|
||
type Story<T> = StoryObj<typeof ListGrid<T>>; | ||
|
||
export const Default: Story<TMockData0> = { | ||
args: { | ||
data: mockData0, | ||
columns: mockColumns0, | ||
getRowKey: mockGetRowKey0, | ||
}, | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import React from "react"; | ||
|
||
import { tv } from "tailwind-variants"; | ||
|
||
const listGridVariants = tv({ | ||
slots: { | ||
root: "mx-auto flex flex-col gap-4 overflow-x-auto", | ||
header: "grid gap-4 px-4 py-2", | ||
headerElement: "flex items-center", | ||
row: "grid items-center gap-4 px-4 py-2", | ||
}, | ||
variants: { | ||
variant: { | ||
default: { | ||
header: "font-sans text-base font-bold text-black", | ||
row: "font-sans text-base font-normal text-black", | ||
}, | ||
}, | ||
position: { | ||
center: { headerElement: "justify-center" }, | ||
left: { headerElement: "justify-start" }, | ||
right: { headerElement: "justify-end" }, | ||
}, | ||
}, | ||
defaultVariants: { | ||
variant: "default", | ||
position: "left", | ||
}, | ||
}); | ||
|
||
export interface ListGridColumn<T> { | ||
header: React.ReactNode; | ||
key: keyof T | string; | ||
position?: "center" | "left" | "right"; | ||
width?: string; | ||
render: (item: T) => React.ReactNode; | ||
} | ||
|
||
export interface ListGridProps<T> { | ||
data: T[]; | ||
columns: ListGridColumn<T>[]; | ||
getRowKey: (item: T) => string | number; | ||
className?: string; | ||
rowClassName?: string; | ||
} | ||
|
||
export const ListGrid = <T,>({ | ||
data, | ||
columns, | ||
getRowKey, | ||
className, | ||
rowClassName, | ||
}: ListGridProps<T>) => { | ||
const { root, header, headerElement, row } = listGridVariants(); | ||
|
||
// Generate grid-template-columns style based on column widths | ||
const gridTemplateColumns = columns | ||
.map((column) => column.width || "1fr") // Default to '1fr' if no width is specified | ||
.join(" "); | ||
|
||
return ( | ||
<div className={root({ className })}> | ||
<div className={header()} style={{ gridTemplateColumns }}> | ||
{columns.map((column, index) => ( | ||
<div key={index} className={headerElement({ position: column.position })}> | ||
{column.header} | ||
</div> | ||
))} | ||
</div> | ||
{data.map((item, index) => ( | ||
<div | ||
key={getRowKey ? getRowKey(item) : index} | ||
className={row({ className: rowClassName })} | ||
style={{ gridTemplateColumns }} | ||
> | ||
{columns.map((column) => ( | ||
<React.Fragment key={column.key as string}>{column.render(item)}</React.Fragment> | ||
))} | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./ListGrid"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ListGridColumn } from "./ListGrid"; | ||
|
||
export interface TMockData0 { | ||
id: number; | ||
name: string; | ||
description: string; | ||
} | ||
|
||
export const mockData0: TMockData0[] = [ | ||
{ id: 1, name: "Item 1", description: "Description 1" }, | ||
{ id: 2, name: "Item 2", description: "Description 2" }, | ||
{ id: 3, name: "Item 3", description: "Description 3" }, | ||
]; | ||
|
||
export const mockColumns0: ListGridColumn<TMockData0>[] = [ | ||
{ | ||
header: "Name", | ||
key: "name", | ||
render: (item: TMockData0) => <span>{item.name}</span>, | ||
}, | ||
{ | ||
header: "Description", | ||
key: "description", | ||
render: (item: TMockData0) => <span>{item.description}</span>, | ||
}, | ||
]; | ||
|
||
export const mockGetRowKey0 = (item: TMockData0) => item.id; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.