Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance PR layout with tab nav and UI improvements #631

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -396,10 +396,13 @@ export default function PullRequestConversationPage() {
if (prPanelData?.PRStateLoading || changesLoading) {
return <SkeletonList />
}

// TODO: we only need to plug in one component from @harnessio/ui/views
// for the PullRequestConversationView example and pass it the required props
return (
<SandboxLayout.Columns columnWidths="1fr 220px">
<SandboxLayout.Column>
<SandboxLayout.Content className="pl-0">
<SandboxLayout.Content className="pl-0 pt-0">
{/* TODO: fix handleaction for comment section in panel */}
<PullRequestPanel
spaceId={spaceId}
Expand Down Expand Up @@ -448,7 +451,7 @@ export default function PullRequestConversationPage() {
showRestoreBranchButton={showRestoreBranchButton}
headerMsg={errorMsg}
/>
<Spacer size={9} />
<Spacer size={12} />
<PullRequestFilters
activityFilters={activityFilters}
dateFilters={dateFilters}
Expand Down Expand Up @@ -506,7 +509,7 @@ export default function PullRequestConversationPage() {
</SandboxLayout.Content>
</SandboxLayout.Column>
<SandboxLayout.Column>
<SandboxLayout.Content className="px-0">
<SandboxLayout.Content className="px-0 pt-0">
<PullRequestSideBar
addReviewers={handleAddReviewer}
usersList={principals?.map(user => ({ id: user.id, display_name: user.display_name, uid: user.uid }))}
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/components/dropdown-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'

import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { ChevronRightIcon, DotFilledIcon } from '@radix-ui/react-icons'
import { ChevronRightIcon } from '@radix-ui/react-icons'
import { cn } from '@utils/cn'

import { Icon } from './icon'
Expand Down Expand Up @@ -148,9 +148,9 @@ const DropdownMenuRadioItem = React.forwardRef<
)}
{...props}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<span className="absolute left-2 flex size-4 items-center justify-center rounded-full border border-icons-1">
<DropdownMenuPrimitive.ItemIndicator>
<DotFilledIcon className="size-4 fill-current" />
<span className="size-2 bg-icons-2 rounded-full block" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
Expand Down
112 changes: 62 additions & 50 deletions packages/ui/src/views/layouts/PullRequestLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NavLink, Outlet } from 'react-router-dom'

import { Badge, Icon, Spacer } from '@components/index'
import { Badge, Icon, Spacer, Tabs, TabsList, TabsTrigger } from '@components/index'
import { TranslationStore } from '@views/repo'
import { PullRequestHeader } from '@views/repo/pull-request/components/pull-request-header'
import { IPullRequestStore } from '@views/repo/pull-request/pull-request.types'
Expand All @@ -14,6 +14,12 @@ interface PullRequestLayoutProps {
useTranslationStore: () => TranslationStore
}

enum PullRequestTabsKeys {
CONVERSATION = 'conversation',
COMMITS = 'commits',
CHANGES = 'changes'
}

const PullRequestLayout: React.FC<PullRequestLayoutProps> = ({
usePullRequestStore,
useTranslationStore,
Expand All @@ -23,63 +29,69 @@ const PullRequestLayout: React.FC<PullRequestLayoutProps> = ({
const { pullRequest } = usePullRequestStore()
const { t } = useTranslationStore()

const baseClasses =
'inline-flex items-center justify-center px-3 py-1 px-4 items-center gap-2 bg-background hover:text-primary h-[36px] rounded-tl-md rounded-tr-md m-0'
const getLinkClasses = (isActive: boolean) => {
return `${baseClasses} ${isActive ? 'text-primary [&svg]:text-primary tabnav-active' : 'tabnav-inactive'}`
}
return (
<>
<SandboxLayout.Main fullWidth>
<SandboxLayout.Content className="px-6" maxWidth="4xl">
<Spacer size={8} />
<SandboxLayout.Content className="px-6 pt-7" maxWidth="4xl">
{pullRequest && (
<PullRequestHeader
data={{
title: pullRequest?.title,
number: pullRequest?.number,
merged: pullRequest?.merged,
author: pullRequest?.author,
stats: { commits: pullRequest?.stats?.commits },
target_branch: pullRequest?.target_branch,
source_branch: pullRequest?.source_branch,
created: pullRequest?.created,
is_draft: pullRequest?.is_draft,
state: pullRequest?.state,
spaceId,
repoId
}}
/>
<>
<PullRequestHeader
data={{
title: pullRequest?.title,
number: pullRequest?.number,
merged: pullRequest?.merged,
author: pullRequest?.author,
stats: { commits: pullRequest?.stats?.commits },
target_branch: pullRequest?.target_branch,
source_branch: pullRequest?.source_branch,
created: pullRequest?.created,
is_draft: pullRequest?.is_draft,
state: pullRequest?.state,
spaceId,
repoId
}}
/>
<Spacer size={10} />
</>
)}
<div className="relative grid w-full grid-flow-col grid-cols-[auto_1fr] items-end">
<div className="inline-flex h-[36px] w-full items-center justify-start gap-0 text-muted-foreground">
<NavLink to={`conversation`} className={({ isActive }) => getLinkClasses(isActive)}>
<Icon size={16} name="comments" />
{t('views:pullRequests.conversation')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.conversations || 0}
</Badge>
<Tabs variant="tabnav">
<TabsList>
<NavLink to={PullRequestTabsKeys.CONVERSATION}>
{({ isActive }) => (
<TabsTrigger value={PullRequestTabsKeys.CONVERSATION} data-state={isActive ? 'active' : 'inactive'}>
<Icon size={14} name="comments" />
{t('views:pullRequests.conversation')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.conversations || 0}
</Badge>
</TabsTrigger>
)}
</NavLink>
<NavLink to={`commits`} className={({ isActive }) => getLinkClasses(isActive)}>
<Icon size={16} name="tube-sign" />
{t('views:repos.commits')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.commits}
</Badge>
<NavLink to={PullRequestTabsKeys.COMMITS}>
{({ isActive }) => (
<TabsTrigger value={PullRequestTabsKeys.COMMITS} data-state={isActive ? 'active' : 'inactive'}>
<Icon size={14} name="tube-sign" />
{t('views:repos.commits')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.commits}
</Badge>
</TabsTrigger>
)}
</NavLink>
<NavLink to={`changes`} className={({ isActive }) => getLinkClasses(isActive)}>
<Icon size={14} name="changes" />
{t('views:pullRequests.changes')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.files_changed}
</Badge>
<NavLink to={PullRequestTabsKeys.CHANGES}>
{({ isActive }) => (
<TabsTrigger value={PullRequestTabsKeys.CHANGES} data-state={isActive ? 'active' : 'inactive'}>
<Icon size={14} name="changes" />
{t('views:pullRequests.changes')}
<Badge variant="outline" size="xs">
{pullRequest?.stats?.files_changed}
</Badge>
</TabsTrigger>
)}
</NavLink>
</div>
<div className="h-[36px] border-b border-border-background" />
<div className="absolute right-full h-[36px] w-0 border-b border-border-background" />
<div className="absolute left-full h-[36px] w-0 border-b border-border-background" />
</div>
<Spacer size={8} />
</TabsList>
</Tabs>
<Spacer size={7} />
<Outlet />
</SandboxLayout.Content>
</SandboxLayout.Main>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react'
import { Link } from 'react-router-dom'

import { Badge, Button, Icon, Layout, Text } from '@components/index'
import { Badge, Icon } from '@components/index'
import { timeAgo } from '@utils/utils'

import { IconType } from '../pull-request.types'
Expand Down Expand Up @@ -48,58 +48,42 @@ export const PullRequestHeader: React.FC<PullRequestTitleProps> = ({

const stateObject = getPrState(is_draft, merged, state)
return (
<div className="flex flex-col gap-2 pb-8">
<div className="flex items-center py-1">
<Text size={5} weight={'medium'} className="text-primary">
<div className="flex flex-col gap-y-4">
<div className="flex items-center">
<h1 className="text-foreground-1 font-medium text-24 flex gap-x-2.5">
{original}
&nbsp;&nbsp;
<span className="font-normal text-tertiary-background">#{number}</span>
</Text>
<span className="font-normal text-foreground-4">#{number}</span>
</h1>
</div>
<div className="">
<div className="flex space-x-2 text-tertiary-background">
<div className="flex items-center gap-2.5 text-center align-middle">
<Badge
disableHover
borderRadius="full"
className={`select-none justify-center`}
theme={stateObject.theme as ThemeType}
>
<Layout.Horizontal gap="space-x-1" className="flex items-center align-middle">
<Icon name={stateObject.icon as IconType} size={13} />
&nbsp;{stateObject.text}
</Layout.Horizontal>
</Badge>
<div className="flex gap-2">
<Text
size={2}
className="inline-flex flex-wrap items-center gap-1 text-tertiary-background"
weight="normal"
>
<span className="text-primary">{author?.display_name || author?.email || ''}</span>
<span>{merged ? 'merged' : ' wants to merge'}</span>
<span className="text-primary">
{stats?.commits} {stats?.commits === 1 ? 'commit' : 'commits'}
</span>
<span>into</span>
<Button variant="secondary" size="xs" asChild>
<Link to={`/${spaceId}/repos/${repoId}/code/${target_branch}`}>
<Icon name="branch" size={12} className="mr-1 text-tertiary-background" />
{target_branch}
</Link>
</Button>
<span>from</span>
<Button asChild variant="secondary" size="xs">
<Link to={`/${spaceId}/repos/${repoId}/code/${source_branch}`}>
<Icon name="branch" size={12} className="mr-1 text-tertiary-background" />
{source_branch}
</Link>
</Button>
<span>&nbsp;|&nbsp;</span>
<span className="time">{formattedTime}</span>
</Text>
</div>
</div>

<div className="flex items-center gap-x-3">
<Badge className="gap-x-1 font-normal" disableHover borderRadius="full" theme={stateObject.theme as ThemeType}>
<Icon name={stateObject.icon as IconType} size={13} />
{stateObject.text}
</Badge>

<div className="inline-flex flex-wrap items-center gap-1 text-foreground-4">
<span className="text-foreground-1">{author?.display_name || author?.email || ''}</span>
<span>{merged ? 'merged' : ' wants to merge'}</span>
<span className="text-foreground-1">
{stats?.commits} {stats?.commits === 1 ? 'commit' : 'commits'}
</span>
<span>into</span>
<Badge variant="tertiary" size="md" borderRadius="base">
<Link className="flex items-center gap-x-1" to={`/${spaceId}/repos/${repoId}/code/${target_branch}`}>
<Icon name="branch" size={12} className="text-icons-9" />
{target_branch}
</Link>
</Badge>
<span>from</span>
<Badge variant="tertiary" size="md" borderRadius="base">
<Link className="flex items-center gap-x-1" to={`/${spaceId}/repos/${repoId}/code/${source_branch}`}>
<Icon name="branch" size={12} className="text-icons-9" />
{source_branch}
</Link>
</Badge>
<span className="w-px h-4 mx-1.5 bg-borders-2" />
<span className="text-foreground-4">{formattedTime}</span>
</div>
</div>
</div>
Expand Down
Loading