Skip to content
Merged
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
12 changes: 12 additions & 0 deletions src/web-ui/src/app/scenes/agents/components/AgentTeamCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
}

&__body {
display: flex;
flex-direction: column;
justify-content: flex-start;
flex: 1;
padding: $size-gap-2 $size-gap-3;
position: relative;
Expand Down Expand Up @@ -130,6 +133,15 @@
}
}

// Shared with ReviewTeamPage for summary metric chips (BEM class reuse).
.agent-team-card__metrics {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
min-width: 0;
}

@media (max-width: 720px) {
.agent-team-card {
width: 100%;
Expand Down
123 changes: 89 additions & 34 deletions src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
.review-team-page {
--config-page-content-max-width: 1480px;
--config-page-content-inline-padding: clamp(40px, 6vw, 80px);
/* Aligns overview + member rows with `AgentTeamCard` / gallery team cards */
--review-team-accent: var(--color-accent-400, #0ea5e9);

.bitfun-config-page-content__inner {
max-width: 1480px;
}

&__agent-team-metrics-wrap {
margin-bottom: $size-gap-3;
width: 100%;
min-width: 0;
}

&__summary-grid {
display: grid;
gap: 8px;
Expand All @@ -17,12 +25,45 @@
&__summary-card {
display: flex;
flex-direction: column;
gap: 8px;
gap: 10px;
min-height: 118px;
padding: 16px;
border: 1px solid var(--border-subtle);
border-radius: 8px;
background: var(--color-surface);
padding: 12px 14px;
border-radius: 12px;
border: 1px solid color-mix(in srgb, var(--review-team-accent) 16%, var(--border-subtle));
background:
linear-gradient(180deg, color-mix(in srgb, var(--review-team-accent) 8%, transparent), transparent),
color-mix(in srgb, var(--element-bg-soft) 86%, transparent);
}

&__summary-card--primary {
background:
radial-gradient(circle at top left, color-mix(in srgb, var(--review-team-accent) 22%, transparent), transparent 62%),
color-mix(in srgb, var(--review-team-accent) 9%, var(--element-bg-soft));
}

&__summary-card-head {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}

&__summary-card-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 10px;
flex-shrink: 0;
color: color-mix(in srgb, var(--review-team-accent) 78%, var(--color-text-primary));
background: color-mix(in srgb, var(--review-team-accent) 14%, transparent);
border: 1px solid color-mix(in srgb, var(--review-team-accent) 24%, transparent);
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);

svg {
flex-shrink: 0;
}
}

&__header-actions,
Expand All @@ -46,15 +87,16 @@
color: var(--color-text-primary);
text-align: left;
cursor: pointer;
transition:
background $motion-fast $easing-standard,
box-shadow $motion-fast $easing-standard;
transition: background $motion-fast $easing-standard;

&:hover {
background: color-mix(in srgb, var(--element-bg-medium) 68%, transparent);
}

&:hover,
&:focus-visible {
background: color-mix(in srgb, var(--element-bg-medium) 68%, transparent);
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--color-accent-500) 74%, transparent);
outline: none;
outline: 2px solid color-mix(in srgb, var(--color-accent-500) 45%, var(--border-medium));
outline-offset: 2px;
}
}

Expand Down Expand Up @@ -94,9 +136,11 @@
min-width: 0;
min-height: 58px;
padding: 9px 10px;
border: 1px solid var(--border-subtle);
border-radius: 8px;
background: color-mix(in srgb, var(--element-bg-soft) 74%, transparent);
border: 1px solid color-mix(in srgb, var(--review-team-accent) 16%, var(--border-subtle));
border-radius: 12px;
background:
linear-gradient(180deg, color-mix(in srgb, var(--review-team-accent) 8%, transparent), transparent),
color-mix(in srgb, var(--element-bg-soft) 86%, transparent);

span {
overflow: hidden;
Expand Down Expand Up @@ -136,6 +180,8 @@
}

&__summary-kicker {
flex: 1;
min-width: 0;
font-size: 11px;
font-weight: $font-weight-semibold;
text-transform: uppercase;
Expand All @@ -152,8 +198,9 @@

&__section-badges {
display: inline-flex;
gap: 8px;
gap: 6px;
flex-wrap: wrap;
justify-content: flex-end;
}

&__row-control {
Expand Down Expand Up @@ -292,9 +339,11 @@
gap: 10px;
width: 100%;
padding: 10px 12px;
border: 1px solid var(--border-subtle);
border-radius: 8px;
background: color-mix(in srgb, var(--element-bg-soft) 78%, transparent);
border: 1px solid color-mix(in srgb, var(--review-team-accent) 16%, var(--border-subtle));
border-radius: 12px;
background:
linear-gradient(180deg, color-mix(in srgb, var(--member-accent, #64748b) 8%, transparent), transparent),
color-mix(in srgb, var(--element-bg-soft) 86%, transparent);
color: var(--color-text-primary);
text-align: left;
cursor: pointer;
Expand All @@ -305,20 +354,22 @@

&:hover {
border-color: color-mix(in srgb, var(--member-accent, #64748b) 34%, var(--border-medium));
background: color-mix(in srgb, var(--element-bg-soft) 100%, transparent);
background:
linear-gradient(180deg, color-mix(in srgb, var(--member-accent, #64748b) 10%, transparent), transparent),
color-mix(in srgb, var(--element-bg-soft) 94%, transparent);
}

&.is-selected {
border-color: color-mix(in srgb, var(--member-accent, #64748b) 54%, var(--color-bg-primary));
background: color-mix(in srgb, var(--member-accent, #64748b) 10%, var(--element-bg-soft));
box-shadow:
0 0 0 1px color-mix(in srgb, var(--member-accent, #64748b) 24%, transparent),
inset 3px 0 0 color-mix(in srgb, var(--member-accent, #64748b) 60%, transparent);
background:
radial-gradient(circle at top left, color-mix(in srgb, var(--member-accent, #64748b) 22%, transparent), transparent 62%),
color-mix(in srgb, var(--member-accent, #64748b) 9%, var(--element-bg-soft));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--member-accent, #64748b) 24%, transparent);
}

&:focus-visible {
outline: none;
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--member-accent, #64748b) 50%, transparent);
outline: 2px solid color-mix(in srgb, var(--member-accent, #64748b) 50%, var(--border-medium));
outline-offset: 2px;
}
}

Expand All @@ -328,10 +379,11 @@
justify-content: center;
width: 32px;
height: 32px;
border-radius: 6px;
background: color-mix(in srgb, var(--member-accent, #64748b) 16%, transparent);
border-radius: 10px;
background: color-mix(in srgb, var(--member-accent, #64748b) 14%, transparent);
color: color-mix(in srgb, var(--member-accent, #64748b) 78%, var(--color-text-primary));
border: 1px solid color-mix(in srgb, var(--member-accent, #64748b) 20%, transparent);
border: 1px solid color-mix(in srgb, var(--member-accent, #64748b) 24%, transparent);
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
flex-shrink: 0;
}

Expand Down Expand Up @@ -372,9 +424,11 @@
flex-direction: column;
gap: 16px;
padding: 20px;
border: 1px solid var(--border-subtle);
border-radius: 8px;
background: color-mix(in srgb, var(--element-bg-soft) 78%, transparent);
border: 1px solid color-mix(in srgb, var(--review-team-accent) 16%, var(--border-subtle));
border-radius: 12px;
background:
linear-gradient(180deg, color-mix(in srgb, var(--member-accent, #64748b) 8%, transparent), transparent),
color-mix(in srgb, var(--element-bg-soft) 86%, transparent);
animation: review-team-card-expand $motion-base $easing-standard forwards;
}

Expand All @@ -392,10 +446,11 @@
justify-content: center;
width: 44px;
height: 44px;
border-radius: 8px;
background: color-mix(in srgb, var(--member-accent, #64748b) 20%, transparent);
color: color-mix(in srgb, var(--member-accent, #64748b) 76%, var(--color-text-primary));
border-radius: 10px;
background: color-mix(in srgb, var(--member-accent, #64748b) 14%, transparent);
color: color-mix(in srgb, var(--member-accent, #64748b) 78%, var(--color-text-primary));
border: 1px solid color-mix(in srgb, var(--member-accent, #64748b) 24%, transparent);
box-shadow: inset 0 1px 0 color-mix(in srgb, white 8%, transparent);
flex-shrink: 0;
}

Expand Down
86 changes: 68 additions & 18 deletions src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Lock,
Settings,
Shield,
Users,
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Badge, Button, ConfigPageLoading } from '@/component-library';
Expand Down Expand Up @@ -39,6 +40,7 @@ import {
type ReviewTeamMember,
} from '@/shared/services/reviewTeamService';
import '../AgentsView.scss';
import './AgentTeamCard.scss';
import './ReviewTeamPage.scss';

const rtLog = createLogger('ReviewTeamPage');
Expand Down Expand Up @@ -248,6 +250,28 @@ const ReviewTeamPage: React.FC = () => {
return match ? getModelDisplayName(match) : modelId;
}, [models, tModel]);

const reviewTeamCoreMemberNames = useMemo(
() => (team?.coreMembers ?? []).map((member) =>
member.definitionKey
? t(`reviewTeams.members.${member.definitionKey}.role`, {
defaultValue: member.roleName,
})
: member.displayName,
),
[team?.coreMembers, t],
);

const reviewTeamMembersLabel = useMemo(
() =>
team
? t('reviewTeams.default.members', {
count: team.members.length,
defaultValue: `${team.members.length} members`,
})
: '',
[team, t],
);

const openReviewSettings = useCallback(() => {
setSettingsTab('review');
openScene('settings');
Expand Down Expand Up @@ -312,40 +336,66 @@ const ReviewTeamPage: React.FC = () => {
description={t('reviewTeams.detail.summaryDescription', {
defaultValue: 'The code review team launches reviewers in parallel and finishes with a quality-gate pass.',
})}
titleSuffix={(
<Badge variant="neutral">
{t('reviewTeams.detail.membersCount', {
count: team.members.length,
defaultValue: `${team.members.length} members`,
})}
</Badge>
)}
>
<div className="review-team-page__summary-grid">
<div className="review-team-page__summary-card">
<span className="review-team-page__summary-kicker">
<div className="review-team-page__agent-team-metrics-wrap">
<div
className="agent-team-card__metrics"
aria-label={reviewTeamCoreMemberNames.join(', ')}
>
<Badge variant="neutral">
<Users size={10} />
{reviewTeamMembersLabel}
</Badge>
<Badge variant="accent">
<GitBranch size={10} />
{t('reviewTeams.detail.localOnly', { defaultValue: 'Code review' })}
</span>
</Badge>
<Badge variant="purple">
<BadgeCheck size={10} />
{t('reviewTeams.detail.qualityGate', { defaultValue: 'Quality gate' })}
</Badge>
</div>
</div>
<div className="review-team-page__summary-grid">
<div className="review-team-page__summary-card review-team-page__summary-card--primary">
<div className="review-team-page__summary-card-head">
<span className="review-team-page__summary-card-icon" aria-hidden>
<GitBranch size={14} strokeWidth={1.8} />
</span>
<span className="review-team-page__summary-kicker">
{t('reviewTeams.detail.localOnly', { defaultValue: 'Code review' })}
</span>
</div>
<p className="review-team-page__summary-value">
{t('reviewTeams.detail.localOnlyDescription', {
defaultValue: 'Reviewers run as BitFun subagents and report through the same review workflow.',
})}
</p>
</div>
<div className="review-team-page__summary-card">
<span className="review-team-page__summary-kicker">
{t('reviewTeams.detail.parallelLabel', { defaultValue: 'Parallel reviewers' })}
</span>
<div className="review-team-page__summary-card-head">
<span className="review-team-page__summary-card-icon" aria-hidden>
<Users size={14} strokeWidth={1.8} />
</span>
<span className="review-team-page__summary-kicker">
{t('reviewTeams.detail.parallelLabel', { defaultValue: 'Parallel reviewers' })}
</span>
</div>
<p className="review-team-page__summary-value">
{t('reviewTeams.detail.parallelDescription', {
defaultValue: 'Business logic, performance, security, and extra reviewers run concurrently before the judge verifies them.',
})}
</p>
</div>
<div className="review-team-page__summary-card">
<span className="review-team-page__summary-kicker">
{t('reviewTeams.detail.qualityGate', { defaultValue: 'Quality gate' })}
</span>
<div className="review-team-page__summary-card-head">
<span className="review-team-page__summary-card-icon" aria-hidden>
<BadgeCheck size={14} strokeWidth={1.8} />
</span>
<span className="review-team-page__summary-kicker">
{t('reviewTeams.detail.qualityGate', { defaultValue: 'Quality gate' })}
</span>
</div>
<p className="review-team-page__summary-value">
{t('reviewTeams.detail.warning', { defaultValue: team.warning })}
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
line-height: 1.65;
cursor: pointer;
user-select: none;
transition: color 0.15s ease;
border-radius: var(--radius-sm, 6px);
transition: color 0.15s ease, background 0.15s ease;

&:hover {
background: var(--bg-hover, rgba(255, 255, 255, 0.05));
Expand Down
Loading
Loading