Skip to content

Commit bc773f1

Browse files
authored
Display branch status in UI + refresh UI for branches list (#7492)
1 parent f520f29 commit bc773f1

File tree

17 files changed

+234
-112
lines changed

17 files changed

+234
-112
lines changed

changelog/+IFC-1905.added.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Branches list page refreshed:
2+
3+
- Improved layout for clearer grouping and labels
4+
- Better accessibility and full keyboard navigation
5+
- New: branch status is now displayed

frontend/app/src/entities/branches/api/get-branches-from-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const GET_BRANCHES = gql`
1010
description
1111
origin_branch
1212
branched_from
13+
status
1314
created_at
1415
sync_with_git
1516
is_default

frontend/app/src/entities/branches/domain/get-branches.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import { branchesState } from "@/entities/branches/stores";
77
export type GetBranches = () => Promise<Array<Branch>>;
88

99
export const getBranches: GetBranches = async () => {
10-
const { data, error } = await getBranchesFromApi();
10+
const { data, errors } = await getBranchesFromApi();
1111

12-
if (error) throw error;
12+
if (errors) {
13+
throw new Error(errors.map((e) => e.message).join("; "));
14+
}
1315

1416
const branches = data?.Branch ?? [];
1517
store.set(branchesState, branches);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Badge, type BadgeProps } from "@/shared/components/ui/badge";
2+
import { classNames } from "@/shared/utils/common";
3+
4+
export function BranchDefaultBadge({ className, ...props }: BadgeProps) {
5+
return (
6+
<Badge className={classNames("rounded-full font-normal text-gray-700", className)} {...props}>
7+
default
8+
</Badge>
9+
);
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { RefreshCwIcon, RefreshCwOffIcon } from "lucide-react";
2+
3+
import { classNames } from "@/shared/utils/common";
4+
5+
export interface BranchGitSyncBadgeProps {
6+
isSyncWithGit: boolean;
7+
}
8+
9+
const pillStyle =
10+
"inline-flex shrink-0 items-center gap-1.5 rounded-full border border-transparent px-2 py-1.25 text-xs";
11+
12+
export function BranchGitSyncBadge({ isSyncWithGit }: BranchGitSyncBadgeProps) {
13+
if (isSyncWithGit) {
14+
return (
15+
<span className={classNames(pillStyle, "bg-custom-blue-700/10 text-custom-blue-700")}>
16+
<RefreshCwIcon className="size-3" /> Synced with Git
17+
</span>
18+
);
19+
}
20+
21+
return (
22+
<span className={classNames(pillStyle, "border-gray-300 border-dashed text-gray-500")}>
23+
<RefreshCwOffIcon className="size-3" /> Not synced
24+
</span>
25+
);
26+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { ListBoxItem, type ListBoxItemProps } from "react-aria-components";
2+
3+
import type { Branch } from "@/shared/api/graphql/generated/graphql";
4+
import { constructPath } from "@/shared/api/rest/fetch";
5+
import { Col, Row } from "@/shared/components/container";
6+
import { focusVisibleStyle } from "@/shared/components/style-rac";
7+
import { classNames } from "@/shared/utils/common";
8+
import { formatFullDate } from "@/shared/utils/date";
9+
10+
import { BranchDefaultBadge } from "@/entities/branches/ui/branch-list-item/branch-default-badge";
11+
import { BranchGitSyncBadge } from "@/entities/branches/ui/branch-list-item/branch-git-sync-badge";
12+
import { BranchMetadata } from "@/entities/branches/ui/branch-list-item/branch-metadata";
13+
import { BranchSchemaChangesBadge } from "@/entities/branches/ui/branch-list-item/branch-schema-changes-badge";
14+
import { BranchStatusBadge } from "@/entities/branches/ui/branch-list-item/branch-status-badge";
15+
16+
interface BranchListItemProps extends ListBoxItemProps {
17+
branch: Branch;
18+
}
19+
20+
export function BranchListItem({ branch, className, ...props }: BranchListItemProps) {
21+
return (
22+
<ListBoxItem
23+
textValue={branch.name}
24+
href={constructPath(`/branches/${branch.name}`)}
25+
className={classNames(
26+
focusVisibleStyle,
27+
"flex flex-wrap items-center gap-6 px-6 py-4",
28+
"border border-transparent not-last:border-b-gray-200",
29+
"first:rounded-t-lg last:rounded-b-lg",
30+
"hover:bg-neutral-100",
31+
className
32+
)}
33+
{...props}
34+
>
35+
<Col className="min-w-1/3 flex-1 gap-0 text-sm">
36+
<Row className="shrink-0">
37+
<span className="font-semibold">{branch.name}</span>
38+
39+
{branch.is_default ? (
40+
<BranchDefaultBadge />
41+
) : branch.status !== "OPEN" ? (
42+
<BranchStatusBadge status={branch.status} />
43+
) : null}
44+
45+
{branch.has_schema_changes && <BranchSchemaChangesBadge />}
46+
</Row>
47+
48+
<p className="truncate text-gray-600 text-xs">{branch.description}</p>
49+
</Col>
50+
51+
<BranchGitSyncBadge isSyncWithGit={!!branch.sync_with_git} />
52+
53+
<Row className="ml-auto gap-6">
54+
<BranchMetadata
55+
label="last rebase"
56+
value={branch.branched_from ? formatFullDate(branch.branched_from) : "-"}
57+
/>
58+
<BranchMetadata
59+
label="created at"
60+
value={branch.created_at ? formatFullDate(branch.created_at) : "-"}
61+
/>
62+
</Row>
63+
</ListBoxItem>
64+
);
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Col } from "@/shared/components/container";
2+
3+
interface BranchMetadataProps {
4+
label: React.ReactNode;
5+
value: React.ReactNode;
6+
}
7+
8+
export function BranchMetadata({ label, value }: BranchMetadataProps) {
9+
return (
10+
<Col className="shrink-0 gap-0 text-xs">
11+
<span className="font-medium">{label}</span>
12+
<span>{value ?? "-"}</span>
13+
</Col>
14+
);
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { BoxIcon } from "lucide-react";
2+
3+
import { Badge } from "@/shared/components/ui/badge";
4+
5+
export function BranchSchemaChangesBadge() {
6+
return (
7+
<Badge className="gap-1 rounded-full font-normal text-gray-600">
8+
<BoxIcon className="size-3" /> schema updated
9+
</Badge>
10+
);
11+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { AlertTriangleIcon, LoaderIcon } from "lucide-react";
2+
3+
import type { BranchStatus } from "@/shared/api/graphql/generated/graphql";
4+
import { Badge, type BadgeProps } from "@/shared/components/ui/badge";
5+
import { classNames, warnUnexpectedType } from "@/shared/utils/common";
6+
7+
const pillStyle = "gap-1 rounded-full font-normal";
8+
9+
interface BranchStatusBadgeProps extends BadgeProps {
10+
status: BranchStatus;
11+
}
12+
13+
export function BranchStatusBadge({ status, className, ...props }: BranchStatusBadgeProps) {
14+
switch (status) {
15+
case "OPEN": {
16+
return (
17+
<Badge className={classNames(pillStyle, className)} variant="green" {...props}>
18+
open
19+
</Badge>
20+
);
21+
}
22+
case "NEED_REBASE": {
23+
return (
24+
<Badge className={classNames(pillStyle, className)} variant="yellow" {...props}>
25+
<AlertTriangleIcon className="size-3" /> Rebase needed
26+
</Badge>
27+
);
28+
}
29+
case "DELETING": {
30+
return (
31+
<Badge className={classNames(pillStyle, className)} variant="red" {...props}>
32+
<LoaderIcon className="size-3 animate-spin" /> Deleting
33+
</Badge>
34+
);
35+
}
36+
default: {
37+
warnUnexpectedType(status);
38+
return null;
39+
}
40+
}
41+
}

frontend/app/src/entities/branches/ui/branches-items.tsx

Lines changed: 0 additions & 105 deletions
This file was deleted.

0 commit comments

Comments
 (0)