Skip to content

Commit 1ee96bb

Browse files
committed
use tag for diff summary
1 parent 2e422ec commit 1ee96bb

File tree

6 files changed

+182
-99
lines changed

6 files changed

+182
-99
lines changed

frontend/app/src/entities/proposed-changes/ui/diff-summary.tsx

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Row } from "@/shared/components/container";
2+
3+
export function DiffSummarySkeleton() {
4+
return (
5+
<Row>
6+
{[...Array(4)].map((_, index) => (
7+
<div key={index} className="h-6 w-9 animate-pulse rounded-full bg-gray-200" />
8+
))}
9+
</Row>
10+
);
11+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { cva, type VariantProps } from "class-variance-authority";
2+
import { CircleMinusIcon, CirclePlusIcon, RefreshCcwIcon, TriangleAlertIcon } from "lucide-react";
3+
import {
4+
Tag,
5+
TagGroup,
6+
type TagGroupProps,
7+
TagList,
8+
type TagListProps,
9+
type TagProps,
10+
} from "react-aria-components";
11+
12+
import { focusVisibleStyle } from "@/shared/components/style-rac";
13+
import { classNames } from "@/shared/utils/common";
14+
15+
export interface DiffSummaryProps<T>
16+
extends Omit<TagGroupProps, "children">,
17+
Pick<TagListProps<T>, "items" | "children" | "renderEmptyState"> {}
18+
19+
export function DiffSummaryTagGroup<T extends object>({
20+
items,
21+
children,
22+
renderEmptyState,
23+
...props
24+
}: DiffSummaryProps<T>) {
25+
return (
26+
<TagGroup aria-label="Diff summary" {...props}>
27+
<TagList className="inline-flex items-center gap-2">
28+
<TagList items={items} renderEmptyState={renderEmptyState}>
29+
{children}
30+
</TagList>
31+
</TagList>
32+
</TagGroup>
33+
);
34+
}
35+
36+
const diffSummaryTagVariants = cva(
37+
[
38+
focusVisibleStyle,
39+
"inline-flex cursor-pointer items-center gap-1 rounded-full border border-transparent p-1 text-xs",
40+
],
41+
{
42+
variants: {
43+
variant: {
44+
added: "bg-green-200 text-green-800",
45+
removed: "bg-red-200 text-red-800",
46+
updated: "bg-blue-200 text-blue-800",
47+
conflicts: "bg-yellow-200 text-yellow-800",
48+
},
49+
},
50+
}
51+
);
52+
53+
export interface DiffSummaryTagProps extends VariantProps<typeof diffSummaryTagVariants>, TagProps {
54+
count: number;
55+
}
56+
57+
export function DiffSummaryTag({ count, variant, ...props }: DiffSummaryTagProps) {
58+
return (
59+
<Tag
60+
className={classNames(diffSummaryTagVariants({ variant }))}
61+
textValue={`diff ${variant} count`}
62+
{...props}
63+
>
64+
<DiffSummaryIcon variant={variant} />
65+
{count}
66+
</Tag>
67+
);
68+
}
69+
70+
export function DiffSummaryIcon({
71+
variant,
72+
...props
73+
}: Pick<VariantProps<typeof diffSummaryTagVariants>, "variant">) {
74+
const className = "size-3";
75+
76+
switch (variant) {
77+
case "added":
78+
return <CirclePlusIcon className={className} {...props} />;
79+
case "removed":
80+
return <CircleMinusIcon className={className} {...props} />;
81+
case "updated":
82+
return <RefreshCcwIcon className={className} {...props} />;
83+
case "conflicts":
84+
return <TriangleAlertIcon className={className} {...props} />;
85+
default:
86+
return null;
87+
}
88+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { QSP } from "@/config/qsp";
2+
3+
import { constructPath } from "@/shared/api/rest/fetch";
4+
import ErrorScreen from "@/shared/components/errors/error-screen";
5+
6+
import { useGetDiffSummary } from "@/entities/diff/domain/get-diff-summary.query";
7+
import { DIFF_STATUS } from "@/entities/diff/node-diff/types";
8+
import { DiffSummarySkeleton } from "@/entities/proposed-changes/ui/diff-summary/diff-summary-skeleton";
9+
import {
10+
DiffSummaryTag,
11+
DiffSummaryTagGroup,
12+
} from "@/entities/proposed-changes/ui/diff-summary/diff-summary-tag-group";
13+
14+
interface ProposedChangeDiffSummaryProps {
15+
branchName: string;
16+
proposedChangeId: string;
17+
}
18+
19+
export function ProposedChangeDiffSummary({
20+
proposedChangeId,
21+
branchName,
22+
}: ProposedChangeDiffSummaryProps) {
23+
const { error, data, isPending } = useGetDiffSummary({ branchName });
24+
25+
if (isPending) {
26+
return <DiffSummarySkeleton />;
27+
}
28+
29+
if (error) {
30+
return (
31+
<ErrorScreen
32+
message={error?.message ?? "No diff summary available."}
33+
hideIcon
34+
className="items-start p-0 text-gray-600 text-sm"
35+
/>
36+
);
37+
}
38+
39+
if (!data) {
40+
return null;
41+
}
42+
43+
const proposedChangeDetailsPath = `/proposed-changes/${proposedChangeId}`;
44+
45+
return (
46+
<DiffSummaryTagGroup>
47+
<DiffSummaryTag
48+
variant="added"
49+
count={data.num_added}
50+
href={constructPath(proposedChangeDetailsPath, [
51+
{ name: QSP.PROPOSED_CHANGES_TAB, value: "data" },
52+
{ name: QSP.STATUS, value: DIFF_STATUS.ADDED },
53+
])}
54+
/>
55+
<DiffSummaryTag
56+
variant="removed"
57+
count={data.num_removed}
58+
href={constructPath(proposedChangeDetailsPath, [
59+
{ name: QSP.PROPOSED_CHANGES_TAB, value: "data" },
60+
{ name: QSP.STATUS, value: DIFF_STATUS.REMOVED },
61+
])}
62+
/>
63+
<DiffSummaryTag
64+
variant="updated"
65+
count={data.num_updated}
66+
href={constructPath(proposedChangeDetailsPath, [
67+
{ name: QSP.PROPOSED_CHANGES_TAB, value: "data" },
68+
{ name: QSP.STATUS, value: DIFF_STATUS.UPDATED },
69+
])}
70+
/>
71+
<DiffSummaryTag
72+
variant="conflicts"
73+
count={data.num_conflicts}
74+
href={constructPath(proposedChangeDetailsPath, [
75+
{ name: QSP.PROPOSED_CHANGES_TAB, value: "data" },
76+
{ name: QSP.STATUS, value: DIFF_STATUS.CONFLICT },
77+
])}
78+
/>
79+
</DiffSummaryTagGroup>
80+
);
81+
}

frontend/app/src/entities/proposed-changes/ui/proposed-change-item.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { classNames } from "@/shared/utils/common";
1212
import { useObjectsCount } from "@/entities/nodes/object/domain/get-objects-count.query";
1313
import { useObjectTableContext } from "@/entities/nodes/object/ui/object-table/object-table-context";
1414
import type { ProposedChangeItem } from "@/entities/proposed-changes/domain/get-proposed-changes";
15-
import { ProposedChangeDiffSummary } from "@/entities/proposed-changes/ui/diff-summary";
15+
import { ProposedChangeDiffSummary } from "@/entities/proposed-changes/ui/diff-summary/proposed-change-diff-summary";
1616
import { ProposedChangesActionCell } from "@/entities/proposed-changes/ui/proposed-changes-actions-cell";
1717
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
1818

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export const disabledStyle = "data-disabled:pointer-events-none data-disabled:opacity-50";
22

33
export const focusVisibleStyle =
4-
"transition-colors data-focus-visible:outline-hidden data-focus-visible:ring-2 data-focus-visible:ring-custom-blue-600/25 data-focus-visible:border-custom-blue-600";
4+
"transition-all data-focus-visible:outline-hidden data-focus-visible:ring-2 data-focus-visible:ring-custom-blue-600/25 data-focus-visible:border-custom-blue-600";

0 commit comments

Comments
 (0)