Skip to content

Commit 51c93a4

Browse files
committed
site: remove circ dependency
1 parent 53880ab commit 51c93a4

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy