Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit 8af4482

Browse files
authored
adds checker icon labels (#11)
added checker icon labels
1 parent 83d5793 commit 8af4482

File tree

12 files changed

+339
-1
lines changed

12 files changed

+339
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Meta, Title, Primary, Stories, Controls, Story } from "@storybook/blocks";
2+
import * as CheckerAILabelStories from "./CheckerAILabel.stories";
3+
import { CheckerAILabel } from "./CheckerAILabel";
4+
5+
# CheckerAILabel
6+
7+
<Meta of={CheckerAILabelStories} />
8+
9+
The `CheckerAILabel` component is designed to visually represent the evaluation of a given percentage. It displays an icon alongside a label that indicates whether the percentage is considered "Approve", "Uncertain", or "Reject".
10+
11+
## Props
12+
13+
- **`percent`**: A number representing the percentage to evaluate. The component will display:
14+
- "Approve" if the percentage is greater than or equal to 60.
15+
- "Uncertain" if the percentage is between 41 and 59.
16+
- "Reject" if the percentage is 40 or below.
17+
- **`className`**: An optional string to apply additional CSS classes for styling.
18+
19+
## Example
20+
21+
### Default Usage
22+
23+
<Story of={CheckerAILabelStories.Default} />
24+
25+
This example demonstrates the default usage of the `CheckerAILabel` component, showcasing how it dynamically updates the label based on the `percent` prop.
26+
27+
## Controls
28+
29+
<Controls />
30+
31+
Use the controls to dynamically adjust the `percent` prop and observe how the component's output changes.
32+
33+
## Other Variations
34+
35+
<Stories />
36+
37+
Explore other variations and use cases of the `CheckerAILabel` component to see how it can be integrated into different scenarios.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { CheckerAILabel } from "./CheckerAILabel";
3+
4+
const meta = {
5+
title: "Features/Checker/components/CheckerAILabel",
6+
component: CheckerAILabel,
7+
args: {
8+
percent: 77,
9+
},
10+
argTypes: {
11+
percent: {
12+
control: "number",
13+
},
14+
},
15+
} satisfies Meta;
16+
17+
export default meta;
18+
19+
type Story = StoryObj<typeof CheckerAILabel>;
20+
21+
export const Default: Story = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Icon, IconType } from "@/primitives/Icon";
2+
import { cn } from "@/lib/utils";
3+
4+
export type CheckerAILabelProps = React.HTMLAttributes<HTMLDivElement> & {
5+
percent: number;
6+
};
7+
8+
export const CheckerAILabelComponents: React.FC<CheckerAILabelProps> = ({ className, percent }) => {
9+
const evaluationMessage = getEvaluationMessage(percent);
10+
return (
11+
<div className={cn("flex items-center gap-1", className)}>
12+
<Icon type={IconType.SPARKLES} className={"size-5 fill-green-600"} />
13+
<span className="text-[16px]/[24px]">{`${evaluationMessage}`}</span>
14+
</div>
15+
);
16+
};
17+
export const CheckerAILabel: React.FC<CheckerAILabelProps> = ({ percent, className, ...props }) => {
18+
return <CheckerAILabelComponents className={className} percent={percent} {...props} />;
19+
};
20+
21+
function getEvaluationMessage(percent: number): string {
22+
if (percent > 100) {
23+
percent = 100;
24+
}
25+
if (percent < 0) {
26+
percent = 0;
27+
}
28+
if (percent > 60) {
29+
return `Approve (${percent}%)`;
30+
} else if (percent > 50 && percent <= 60) {
31+
return `Uncertain (${percent}%)`;
32+
} else if (percent > 40 && percent <= 50) {
33+
return `Uncertain (${percent}%)`;
34+
} else {
35+
return `Reject (${percent}%)`;
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Meta, Title, Primary, Stories, Controls, Story } from "@storybook/blocks";
2+
import * as CheckerDateLabelStories from "./CheckerDateLabel.stories";
3+
import { CheckerDateLabel } from "./CheckerDateLabel";
4+
5+
# CheckerDateLabel
6+
7+
<Meta of={CheckerDateLabelStories} />
8+
9+
The `CheckerDateLabel` component is designed to display a formatted date alongside a clock icon.
10+
11+
## Props
12+
13+
- **`date`**: A `Date` object representing the date to be displayed. The date is formatted using a default date format.
14+
- **`className`**: An optional string to apply additional CSS classes for styling.
15+
16+
## Example
17+
18+
### Default Usage
19+
20+
<Story of={CheckerDateLabelStories.Default} />
21+
22+
This example demonstrates the default usage of the `CheckerDateLabel` component, showing how it formats and displays a date.
23+
24+
## Controls
25+
26+
<Controls />
27+
28+
Use the controls to dynamically adjust the `date` prop and observe how the component's output changes.
29+
30+
## Other Variations
31+
32+
<Stories />
33+
34+
Explore other variations and use cases of the `CheckerDateLabel` component to see how it can be integrated into different scenarios.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { CheckerDateLabel } from "./CheckerDateLabel";
3+
4+
const meta = {
5+
title: "Features/Checker/components/CheckerDateLabel",
6+
component: CheckerDateLabel,
7+
args: {
8+
date: new Date(),
9+
},
10+
argTypes: {
11+
date: {
12+
control: "date",
13+
},
14+
},
15+
} satisfies Meta;
16+
17+
export default meta;
18+
19+
type Story = StoryObj<typeof CheckerDateLabel>;
20+
21+
export const Default: Story = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Icon, IconType } from "@/primitives/Icon";
2+
import { cn } from "@/lib/utils";
3+
import { formatDate, DateFormat } from "@/lib/dates/formatDate";
4+
5+
export type CheckerDateLabelProps = React.HTMLAttributes<HTMLDivElement> & {
6+
date: Date;
7+
};
8+
9+
export const CheckerDateLabelComponents: React.FC<CheckerDateLabelProps> = ({
10+
className,
11+
date,
12+
}) => {
13+
const formattedDate = formatDate(date, DateFormat["Default"]);
14+
return (
15+
<div className={cn("flex items-center gap-2", className)}>
16+
<Icon type={IconType.CLOCK} className={"size-5"} />
17+
<span className="text-[16px]/[24px]">{formattedDate}</span>
18+
</div>
19+
);
20+
};
21+
export const CheckerDateLabel: React.FC<CheckerDateLabelProps> = ({
22+
date,
23+
className,
24+
...props
25+
}) => {
26+
return <CheckerDateLabelComponents className={className} date={date} {...props} />;
27+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Meta, Title, Primary, Stories, Controls, Story } from "@storybook/blocks";
2+
import * as CheckerReviewsLabelStories from "./CheckerReviewsLabel.stories";
3+
import { CheckerReviewsLabel } from "./CheckerReviewsLabel";
4+
5+
# CheckerReviewsLabel
6+
7+
<Meta of={CheckerReviewsLabelStories} />
8+
9+
The `CheckerReviewsLabel` component is designed to visually represent the number of positive and negative reviews using icons. It provides a quick visual summary of reviews.
10+
11+
## Props
12+
13+
- **`posReviews`**: A number representing the count of positive reviews. If the value is negative, it will be treated as zero.
14+
- **`negReviews`**: A number representing the count of negative reviews. If the value is negative, it will be treated as zero.
15+
- **`className`**: An optional string to apply additional CSS classes for styling.
16+
17+
## Icon Representation
18+
19+
- **Positive Reviews**: Represented by check icons with a green fill.
20+
- **Negative Reviews**: Represented by cross icons with an orange fill.
21+
22+
## Example
23+
24+
### Default Usage
25+
26+
<Story of={CheckerReviewsLabelStories.Default} />
27+
28+
This example demonstrates the default usage of the `CheckerReviewsLabel` component, showing how it displays the total number of reviews and the breakdown of positive and negative reviews.
29+
30+
## Controls
31+
32+
<Controls />
33+
34+
Use the controls to dynamically adjust the `posReviews` and `negReviews` props and observe how the component's output changes.
35+
36+
## Other Variations
37+
38+
<Stories />
39+
40+
Explore other variations and use cases of the `CheckerReviewsLabel` component to see how it can be integrated into different scenarios.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { CheckerReviewsLabel } from "./CheckerReviewsLabel";
3+
4+
const meta = {
5+
title: "Features/Checker/components/CheckerReviewsLabel",
6+
component: CheckerReviewsLabel,
7+
args: {
8+
posReviews: 2,
9+
negReviews: 2,
10+
},
11+
argTypes: {
12+
posReviews: {
13+
control: "number",
14+
},
15+
negReviews: {
16+
control: "number",
17+
},
18+
},
19+
} satisfies Meta;
20+
21+
export default meta;
22+
23+
type Story = StoryObj<typeof CheckerReviewsLabel>;
24+
25+
export const Default: Story = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Icon, IconType } from "@/primitives/Icon";
2+
import { cn } from "@/lib/utils";
3+
4+
export type CheckerReviewsLabelProps = React.SVGProps<SVGSVGElement> & {
5+
posReviews: number;
6+
negReviews: number;
7+
};
8+
9+
const ICON_CLASSES = {
10+
base: "size-7 bg-white",
11+
positive: " fill-green-600",
12+
negative: "fill-orange-200",
13+
border: "border border-gray-100 rounded-full",
14+
};
15+
16+
const renderIcons = (countPos: number, countNeg: number, props: React.SVGProps<SVGSVGElement>) => {
17+
const negProps = { ...props, type: IconType.X };
18+
const posProps = { ...props, type: IconType.CHECK };
19+
const totalReviews = countPos + countNeg;
20+
21+
return Array.from({ length: totalReviews }).map((_, index) => (
22+
<Icon
23+
key={index}
24+
{...(index < countPos ? posProps : negProps)}
25+
className={cn(
26+
ICON_CLASSES.base,
27+
ICON_CLASSES.border,
28+
index >= countPos ? ICON_CLASSES.negative : ICON_CLASSES.positive,
29+
index !== 0 ? "-ml-2" : "",
30+
)}
31+
/>
32+
));
33+
};
34+
35+
export const CheckerReviewsLabelComponents: React.FC<CheckerReviewsLabelProps> = ({
36+
className,
37+
posReviews,
38+
negReviews,
39+
...props
40+
}) => {
41+
if (posReviews < 0) {
42+
posReviews = 0;
43+
}
44+
if (negReviews < 0) {
45+
negReviews = 0;
46+
}
47+
const totalReviews = posReviews + negReviews;
48+
return (
49+
<div className={cn("flex items-center gap-2", className)}>
50+
<div className="flex items-center">{renderIcons(posReviews, negReviews, { ...props })}</div>
51+
<span className="text-[16px]/[24px]">{`${totalReviews} Reviews`}</span>
52+
</div>
53+
);
54+
};
55+
56+
export const CheckerReviewsLabel: React.FC<CheckerReviewsLabelProps> = ({
57+
posReviews,
58+
negReviews,
59+
className,
60+
...props
61+
}) => (
62+
<CheckerReviewsLabelComponents
63+
className={className}
64+
posReviews={posReviews}
65+
negReviews={negReviews}
66+
{...props}
67+
/>
68+
);

src/features/checker/pages/OverviewPage/components/OverviewPageTitle/OverviewPageTitle.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
22
import { OverviewPageTitle } from "@/features/checker/pages/OverviewPage/components";
33

44
const meta = {
5-
title: "Features/Checker/Components",
5+
title: "Features/Checker/Components/OverviewPageTitle",
66
component: OverviewPageTitle,
77
} satisfies Meta;
88

src/lib/dates/formatDate.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export enum DateFormat {
2+
Default = "default",
3+
}
4+
5+
export const formatDate = (date: Date, format: DateFormat): string => {
6+
switch (format) {
7+
case DateFormat.Default:
8+
const options = {
9+
day: "numeric",
10+
month: "long",
11+
year: "numeric",
12+
hour: "2-digit",
13+
minute: "2-digit",
14+
hour12: false,
15+
} as Intl.DateTimeFormatOptions;
16+
const formattedDate = date.toLocaleString("en-GB", options);
17+
return formattedDate.replace(/ at /, " ").trim();
18+
default:
19+
return date.toISOString(); // Default format
20+
}
21+
};

src/tokens/colors.ts

+7
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@ import { Colors } from "./types";
33
export const colors: Colors = {
44
grey: {
55
"50": "#f7f7f7",
6+
"100": "#EBEBEB",
67
"400": "#555555",
78
"500": "#000000",
89
},
910
white: "#ffffff",
1011
black: "#000000",
12+
orange: {
13+
"200": "#F68561",
14+
},
15+
green: {
16+
"600": "#558A17",
17+
},
1118
};

0 commit comments

Comments
 (0)