Skip to content

Commit 9f69f69

Browse files
committed
site: remove circ dependency
1 parent df28ff9 commit 9f69f69

File tree

4 files changed

+97
-99
lines changed

4 files changed

+97
-99
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { WorkspaceApp } from "api/typesGenerated";
2+
import type { Workspace, WorkspaceAgent } from "api/typesGenerated";
3+
import {
4+
DropdownMenu,
5+
DropdownMenuContent,
6+
DropdownMenuItem,
7+
DropdownMenuTrigger,
8+
} from "components/DropdownMenu/DropdownMenu";
9+
import { Folder } from "lucide-react";
10+
import type { FC } from "react";
11+
import { AgentButton } from "../AgentButton";
12+
import { AppLink } from "../AppLink/AppLink";
13+
14+
type AppsProps = {
15+
section: AppSection;
16+
agent: WorkspaceAgent;
17+
workspace: Workspace;
18+
};
19+
20+
export const Apps: FC<AppsProps> = ({ section, agent, workspace }) => {
21+
return section.group ? (
22+
<DropdownMenu>
23+
<DropdownMenuTrigger asChild>
24+
<AgentButton>
25+
<Folder />
26+
{section.group}
27+
</AgentButton>
28+
</DropdownMenuTrigger>
29+
<DropdownMenuContent align="start">
30+
{section.apps.map((app) => (
31+
<DropdownMenuItem key={app.slug}>
32+
<AppLink grouped app={app} agent={agent} workspace={workspace} />
33+
</DropdownMenuItem>
34+
))}
35+
</DropdownMenuContent>
36+
</DropdownMenu>
37+
) : (
38+
<>
39+
{section.apps.map((app) => (
40+
<AppLink key={app.slug} app={app} agent={agent} workspace={workspace} />
41+
))}
42+
</>
43+
);
44+
};
45+
46+
type AppSection = {
47+
/**
48+
* If there is no `group`, just render all of the apps inline. If there is a
49+
* group name, show them all in a dropdown.
50+
*/
51+
group?: string;
52+
53+
apps: WorkspaceApp[];
54+
};
55+
56+
/**
57+
* Groups apps by their `group` property. Apps with the same group are placed
58+
* in the same section. Apps without a group are placed in their own section.
59+
*
60+
* The algorithm assumes that apps are already sorted by group, meaning that
61+
* every ungrouped section is expected to have a group in between, to make the
62+
* algorithm a little simpler to implement.
63+
*/
64+
export function organizeAgentApps(apps: readonly WorkspaceApp[]): AppSection[] {
65+
let currentSection: AppSection | undefined = undefined;
66+
const appGroups: AppSection[] = [];
67+
const groupsByName = new Map<string, AppSection>();
68+
69+
for (const app of apps) {
70+
if (app.hidden) {
71+
continue;
72+
}
73+
74+
if (!currentSection || app.group !== currentSection.group) {
75+
const existingSection = groupsByName.get(app.group!);
76+
if (existingSection) {
77+
currentSection = existingSection;
78+
} else {
79+
currentSection = {
80+
group: app.group,
81+
apps: [],
82+
};
83+
appGroups.push(currentSection);
84+
if (app.group) {
85+
groupsByName.set(app.group, currentSection);
86+
}
87+
}
88+
}
89+
90+
currentSection.apps.push(app);
91+
}
92+
93+
return appGroups;
94+
}

site/src/modules/resources/AgentDevcontainerCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import { AppStatuses } from "pages/WorkspacePage/AppStatuses";
2323
import type { FC } from "react";
2424
import { useEffect, useState } from "react";
2525
import { portForwardURL } from "utils/portForward";
26+
import { Apps, organizeAgentApps } from "./AgentApps/AgentApps";
2627
import { AgentButton } from "./AgentButton";
2728
import { AgentLatency } from "./AgentLatency";
28-
import { Apps, organizeAgentApps } from "./AgentRow";
2929
import { SubAgentStatus } from "./AgentStatus";
3030
import { PortForwardButton } from "./PortForwardButton";
3131
import { AgentSSHButton } from "./SSHButton/SSHButton";

site/src/modules/resources/AgentRow.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MockWorkspaceApp } from "testHelpers/entities";
2-
import { organizeAgentApps } from "./AgentRow";
2+
import { organizeAgentApps } from "./AgentApps/AgentApps";
33

44
describe("organizeAgentApps", () => {
55
test("returns one ungrouped app", () => {

site/src/modules/resources/AgentRow.tsx

Lines changed: 1 addition & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,12 @@ import type {
88
Workspace,
99
WorkspaceAgent,
1010
WorkspaceAgentMetadata,
11-
WorkspaceApp,
1211
} from "api/typesGenerated";
1312
import { isAxiosError } from "axios";
1413
import { Button } from "components/Button/Button";
1514
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
16-
import {
17-
DropdownMenu,
18-
DropdownMenuContent,
19-
DropdownMenuItem,
20-
DropdownMenuTrigger,
21-
} from "components/DropdownMenu/DropdownMenu";
2215
import { Stack } from "components/Stack/Stack";
2316
import { useProxy } from "contexts/ProxyContext";
24-
import { Folder } from "lucide-react";
2517
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
2618
import { AppStatuses } from "pages/WorkspacePage/AppStatuses";
2719
import {
@@ -36,15 +28,14 @@ import {
3628
import { useQuery } from "react-query";
3729
import AutoSizer from "react-virtualized-auto-sizer";
3830
import type { FixedSizeList as List, ListOnScrollProps } from "react-window";
39-
import { AgentButton } from "./AgentButton";
31+
import { Apps, organizeAgentApps } from "./AgentApps/AgentApps";
4032
import { AgentDevcontainerCard } from "./AgentDevcontainerCard";
4133
import { AgentLatency } from "./AgentLatency";
4234
import { AGENT_LOG_LINE_HEIGHT } from "./AgentLogs/AgentLogLine";
4335
import { AgentLogs } from "./AgentLogs/AgentLogs";
4436
import { AgentMetadata } from "./AgentMetadata";
4537
import { AgentStatus } from "./AgentStatus";
4638
import { AgentVersion } from "./AgentVersion";
47-
import { AppLink } from "./AppLink/AppLink";
4839
import { DownloadAgentLogsButton } from "./DownloadAgentLogsButton";
4940
import { PortForwardButton } from "./PortForwardButton";
5041
import { AgentSSHButton } from "./SSHButton/SSHButton";
@@ -354,93 +345,6 @@ export const AgentRow: FC<AgentRowProps> = ({
354345
);
355346
};
356347

357-
export type AppSection = {
358-
/**
359-
* If there is no `group`, just render all of the apps inline. If there is a
360-
* group name, show them all in a dropdown.
361-
*/
362-
group?: string;
363-
364-
apps: WorkspaceApp[];
365-
};
366-
367-
/**
368-
* organizeAgentApps returns an ordering of agent apps that accounts for
369-
* grouping. When we receive the list of apps from the backend, they have
370-
* already been "ordered" by their `order` attribute, but we are not given that
371-
* value. We must be careful to preserve that ordering, while also properly
372-
* grouping together all apps of any given group.
373-
*
374-
* The position of the group overall is determined by the `order` position of
375-
* the first app in the group. There may be several sections returned without
376-
* a group name, to allow placing grouped apps in between non-grouped apps. Not
377-
* every ungrouped section is expected to have a group in between, to make the
378-
* algorithm a little simpler to implement.
379-
*/
380-
export function organizeAgentApps(apps: readonly WorkspaceApp[]): AppSection[] {
381-
let currentSection: AppSection | undefined = undefined;
382-
const appGroups: AppSection[] = [];
383-
const groupsByName = new Map<string, AppSection>();
384-
385-
for (const app of apps) {
386-
if (app.hidden) {
387-
continue;
388-
}
389-
390-
if (!currentSection || app.group !== currentSection.group) {
391-
const existingSection = groupsByName.get(app.group!);
392-
if (existingSection) {
393-
currentSection = existingSection;
394-
} else {
395-
currentSection = {
396-
group: app.group,
397-
apps: [],
398-
};
399-
appGroups.push(currentSection);
400-
if (app.group) {
401-
groupsByName.set(app.group, currentSection);
402-
}
403-
}
404-
}
405-
406-
currentSection.apps.push(app);
407-
}
408-
409-
return appGroups;
410-
}
411-
412-
export type AppsProps = {
413-
section: AppSection;
414-
agent: WorkspaceAgent;
415-
workspace: Workspace;
416-
};
417-
418-
export const Apps: FC<AppsProps> = ({ section, agent, workspace }) => {
419-
return section.group ? (
420-
<DropdownMenu>
421-
<DropdownMenuTrigger asChild>
422-
<AgentButton>
423-
<Folder />
424-
{section.group}
425-
</AgentButton>
426-
</DropdownMenuTrigger>
427-
<DropdownMenuContent align="start">
428-
{section.apps.map((app) => (
429-
<DropdownMenuItem key={app.slug}>
430-
<AppLink grouped app={app} agent={agent} workspace={workspace} />
431-
</DropdownMenuItem>
432-
))}
433-
</DropdownMenuContent>
434-
</DropdownMenu>
435-
) : (
436-
<>
437-
{section.apps.map((app) => (
438-
<AppLink key={app.slug} app={app} agent={agent} workspace={workspace} />
439-
))}
440-
</>
441-
);
442-
};
443-
444348
const styles = {
445349
agentRow: (theme) => ({
446350
fontSize: 14,

0 commit comments

Comments
 (0)