refactor(claude): drop the redundant Team-tab account budget (T-158)

After live review: the shared account /usage budget read as redundant on
both the Activity and Team tabs. Usage is per-account and can't be split per
member, so one place is enough — it lives only on the Activity tab, next to
the refresh control that fetches it (T-415). Removes the Team-tab account
card, its usage prop wiring, the team.section.usage/team.usage.shared i18n
keys, and the now-obsolete team_tab test. The three-tab card facelift stays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 07:56:09 +02:00
co-authored by Claude Opus 4.8
parent a72906d25d
commit 3cbe0e6ec6
8 changed files with 326 additions and 151 deletions
@@ -19,10 +19,10 @@
/// `meta_sidebar/` (T-395 split). Activity and Config render on the same
/// table geometry (`buildMetaTable`) so switching tabs doesn't visually jump.
///
/// The account budget now surfaces from a forwarded `/usage` (T-415): the
/// Activity tab renders it, and the Team tab repeats it as a single shared
/// ACCOUNT card (T-158) — usage is per-account (one `~/.claude` login), so it's
/// shown once, not split per member.
/// The account budget surfaces from a forwarded `/usage` (T-415): the Activity
/// tab renders it next to its refresh control. It is NOT duplicated on the Team
/// tab — usage is per-account (one `~/.claude` login), so it can't be split per
/// member; one place to see it is enough (T-158).
library;
import 'dart:async';
@@ -313,7 +313,6 @@ class _ClaudeMetaSidebarState extends State<ClaudeMetaSidebar> {
members: _members,
memberStatus: _memberStatus,
orchestrator: _orchestrator,
usage: _usage,
tasks: _tasks,
injectingAgentId: _injectingAgentId,
injectController: _injectCtl,
@@ -4,12 +4,12 @@
/// state, and orchestrator wiring. Split out of claude_meta_sidebar.dart
/// (T-395).
///
/// Also carries the shared account budget (T-158): the `/usage` figures are
/// per-ACCOUNT — every team session shares one `~/.claude` login, so this is a
/// single shared budget shown once and labelled as such, NOT a per-member split.
/// The account `/usage` budget is deliberately NOT shown here: it is
/// per-account (every team session shares one `~/.claude` login), so it can't
/// be split per member — it lives once on the Activity tab, next to the
/// refresh control that fetches it (T-158).
library;
import 'package:clide/builtin/claude/src/claude_status.dart' show ClaudeUsage;
import 'package:clide/builtin/claude/src/meta_sidebar/models.dart';
import 'package:clide/builtin/claude/src/meta_sidebar/roster_row.dart';
import 'package:clide/builtin/claude/src/meta_sidebar/task_row.dart';
@@ -36,7 +36,6 @@ class TeamTabView extends StatelessWidget {
required this.onSetPermissionMode,
required this.onFork,
required this.onOpenChatPane,
this.usage,
});
final List<TeamMemberJoined> members;
@@ -52,32 +51,27 @@ class TeamTabView extends StatelessWidget {
final void Function(String memberName) onFork;
final VoidCallback onOpenChatPane;
/// Parsed `/usage` budget for the account this team shares (T-158). Null until
/// the first `/usage` refresh (driven from the Activity tab control).
final ClaudeUsage? usage;
@override
Widget build(BuildContext context) {
final tokens = ClideSettings.theme.of(context).surface;
if (members.isEmpty) {
return metaPlaceholder(ClideSettings.i18n.string(context, 'team.empty', namespace: 'builtin.claude', placeholder: 'No team active.'));
}
final children = <Widget>[
..._accountSection(context, tokens),
if (members.isEmpty)
metaPlaceholder(ClideSettings.i18n.string(context, 'team.empty', namespace: 'builtin.claude', placeholder: 'No team active.'))
else
for (final m in members)
AgentRosterRow(
key: ValueKey(m.agentId),
member: m,
status: memberStatus[m.agentId],
orchestrator: orchestrator,
injectingAgentId: injectingAgentId,
injectController: injectController,
onToggleInject: onToggleInject,
onInjectSubmit: onInjectSubmit,
onClose: onClose,
onSetPermissionMode: onSetPermissionMode,
onFork: onFork,
),
for (final m in members)
AgentRosterRow(
key: ValueKey(m.agentId),
member: m,
status: memberStatus[m.agentId],
orchestrator: orchestrator,
injectingAgentId: injectingAgentId,
injectController: injectController,
onToggleInject: onToggleInject,
onInjectSubmit: onInjectSubmit,
onClose: onClose,
onSetPermissionMode: onSetPermissionMode,
onFork: onFork,
),
];
if (tasks.isNotEmpty) {
@@ -96,43 +90,6 @@ class TeamTabView extends StatelessWidget {
return ListView(padding: const EdgeInsets.all(12), children: children);
}
/// The shared account-budget card (T-158): one ACCOUNT section with the three
/// `/usage` figures and a caption that it is account-wide, not per-member.
/// Empty when no `/usage` result has arrived yet.
List<Widget> _accountSection(BuildContext context, SurfaceTokens tokens) {
final u = usage;
if (u == null || (u.session == null && u.week == null && u.weekSonnet == null)) return const [];
return [
metaSectionHeader(context, tokens, ClideSettings.i18n.string(context, 'team.section.usage', namespace: 'builtin.claude', placeholder: 'ACCOUNT')),
metaCard(tokens, [
if (u.session != null)
metaCardRow(
tokens,
MetaRow(ClideSettings.i18n.string(context, 'activity.row.session', namespace: 'builtin.claude', placeholder: 'session'), u.session!),
),
if (u.week != null)
metaCardRow(
tokens,
MetaRow(ClideSettings.i18n.string(context, 'activity.row.weekAll', namespace: 'builtin.claude', placeholder: 'week (all)'), u.week!),
),
if (u.weekSonnet != null)
metaCardRow(
tokens,
MetaRow(ClideSettings.i18n.string(context, 'activity.row.weekSonnet', namespace: 'builtin.claude', placeholder: 'week (sonnet)'), u.weekSonnet!),
),
]),
Padding(
padding: const EdgeInsets.only(left: 2, top: 4),
child: ClideText(
ClideSettings.i18n.string(context, 'team.usage.shared', namespace: 'builtin.claude', placeholder: 'Shared across the team'),
muted: true,
fontSize: clideFontCaption,
),
),
const SizedBox(height: 16),
];
}
Widget _taskSection(BuildContext context, SurfaceTokens tokens) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,