mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-26 06:16:09 +00:00
fix(cloud): surface runtime and workspace plan status
This commit is contained in:
@@ -1916,7 +1916,7 @@ export default function HomeSidebar({
|
||||
</SidebarHeader>
|
||||
|
||||
<div className="px-2 group-data-[collapsible=icon]:px-0">
|
||||
<WorkspaceSwitcher className="w-full group-data-[collapsible=icon]:min-w-0 group-data-[collapsible=icon]:px-2" />
|
||||
<WorkspaceSwitcher className="w-1/2 max-w-36 group-data-[collapsible=icon]:min-w-0 group-data-[collapsible=icon]:px-2" />
|
||||
</div>
|
||||
|
||||
{/* Navigation items grouped by section */}
|
||||
|
||||
@@ -225,22 +225,13 @@ export default function WorkspaceSettingsPanel({
|
||||
</Badge>
|
||||
)}
|
||||
{isCloudProjection && workspaceInfo && (
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<Button asChild size="sm">
|
||||
<a href={cloudPortalURL} target="_blank" rel="noopener noreferrer">
|
||||
{t('workspace.upgradePlan')}
|
||||
<ExternalLink className="size-3.5" />
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
{canManageCloudMembers && (
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<a href={cloudMembersURL} target="_blank" rel="noopener noreferrer">
|
||||
<UserPlus />
|
||||
{t('workspace.inviteMember')}
|
||||
<ExternalLink className="size-3.5" />
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
</PanelToolbar>
|
||||
|
||||
<PanelBody className="space-y-6">
|
||||
@@ -315,7 +306,24 @@ export default function WorkspaceSettingsPanel({
|
||||
|
||||
{canViewMembers && (
|
||||
<section className="space-y-3">
|
||||
<h3 className="text-sm font-semibold">{t('workspace.members')}</h3>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('workspace.members')}
|
||||
</h3>
|
||||
{canManageCloudMembers && (
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<a
|
||||
href={cloudMembersURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<UserPlus className="size-4" />
|
||||
{t('workspace.inviteMember')}
|
||||
<ExternalLink className="size-3.5" />
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{members.map((member) => {
|
||||
const isSelf =
|
||||
|
||||
@@ -39,17 +39,14 @@ export default function WorkspaceSwitcher({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className={cn(
|
||||
'h-11 min-w-52 max-w-72 justify-start px-3 text-[15px]',
|
||||
className,
|
||||
)}
|
||||
className={cn('h-9 min-w-0 justify-start px-2.5 text-sm', className)}
|
||||
aria-label={t('workspace.switchWorkspace')}
|
||||
>
|
||||
<Building2 className="size-4 shrink-0" />
|
||||
<span className="truncate">{currentWorkspace.workspace.name}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="min-w-80 p-2">
|
||||
<DropdownMenuContent align="start" className="w-64 p-1.5">
|
||||
<DropdownMenuLabel className="px-3 py-2 text-sm">
|
||||
{t('workspace.switchWorkspace')}
|
||||
</DropdownMenuLabel>
|
||||
@@ -59,14 +56,14 @@ export default function WorkspaceSwitcher({
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={entry.workspace.uuid}
|
||||
className="min-h-14 gap-3 px-3 py-2"
|
||||
className="min-h-11 gap-2 px-2 py-1.5"
|
||||
onClick={() => {
|
||||
if (!selected)
|
||||
void switchWorkspaceAndReload(entry.workspace.uuid);
|
||||
}}
|
||||
>
|
||||
<Building2 className="size-5" />
|
||||
<span className="min-w-0 flex-1 truncate font-medium">
|
||||
<Building2 className="size-4 shrink-0" />
|
||||
<span className="max-w-[7rem] min-w-0 flex-1 truncate font-medium">
|
||||
{entry.workspace.name}
|
||||
</span>
|
||||
{entry.workspace.source === 'cloud_projection' && (
|
||||
|
||||
@@ -67,7 +67,7 @@ export default function SystemStatusCard({
|
||||
try {
|
||||
const [plugin, box] = await Promise.all([
|
||||
httpClient.getPluginSystemStatus().catch(() => null),
|
||||
httpClient.getBoxStatus().catch(() => null),
|
||||
httpClient.getBoxRuntimeStatus().catch(() => null),
|
||||
]);
|
||||
const sessions = box?.hidden
|
||||
? []
|
||||
|
||||
@@ -1067,6 +1067,10 @@ export class BackendClient extends BaseHttpClient {
|
||||
return this.get('/api/v1/plugins/debug-info');
|
||||
}
|
||||
|
||||
public getBoxRuntimeStatus(): Promise<ApiRespBoxStatus> {
|
||||
return this.get('/api/v1/box/runtime-status');
|
||||
}
|
||||
|
||||
public getBoxStatus(): Promise<ApiRespBoxStatus> {
|
||||
return this.get('/api/v1/box/status');
|
||||
}
|
||||
|
||||
@@ -83,9 +83,28 @@ test('moves Cloud plan upgrades into Workspace Settings', () => {
|
||||
assert.match(workspaceSettingsPanelSource, /workspace\.upgradePlan/);
|
||||
assert.match(workspaceSettingsPanelSource, /cloudPortalURL/);
|
||||
assert.match(workspaceSettingsPanelSource, /step=plan/);
|
||||
const toolbarEnd = workspaceSettingsPanelSource.indexOf('</PanelToolbar>');
|
||||
const membersHeading =
|
||||
workspaceSettingsPanelSource.indexOf('workspace.members');
|
||||
const cloudInvite = workspaceSettingsPanelSource.indexOf(
|
||||
'href={cloudMembersURL}',
|
||||
);
|
||||
assert.ok(toolbarEnd < membersHeading && membersHeading < cloudInvite);
|
||||
});
|
||||
|
||||
test('uses a larger workspace trigger and menu surface', () => {
|
||||
assert.match(workspaceSwitcherSource, /h-11/);
|
||||
assert.match(workspaceSwitcherSource, /min-w-80/);
|
||||
test('keeps the workspace trigger compact and truncates long names', () => {
|
||||
assert.match(homeSidebarSource, /<WorkspaceSwitcher className="w-1\/2/);
|
||||
assert.match(workspaceSwitcherSource, /h-9/);
|
||||
assert.match(workspaceSwitcherSource, /w-64/);
|
||||
assert.doesNotMatch(workspaceSwitcherSource, /min-w-80/);
|
||||
assert.match(workspaceSwitcherSource, /max-w-\[7rem\][^>]*truncate/);
|
||||
});
|
||||
|
||||
test('uses an infrastructure-level Box health endpoint in monitoring', () => {
|
||||
const statusCardSource = readSource(
|
||||
'src/app/home/monitoring/components/overview-cards/SystemStatusCards.tsx',
|
||||
);
|
||||
const backendClientSource = readSource('src/app/infra/http/BackendClient.ts');
|
||||
assert.match(backendClientSource, /getBoxRuntimeStatus/);
|
||||
assert.match(statusCardSource, /getBoxRuntimeStatus/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user