Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6fdaa27

Browse files
fix(site): fix render crash when no embedded apps are defined for task
1 parent82d5a20 commit6fdaa27

File tree

2 files changed

+230
-62
lines changed

2 files changed

+230
-62
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
importtype{WorkspaceApp}from"api/typesGenerated";
3+
import{
4+
MockTasks,
5+
MockWorkspace,
6+
MockWorkspaceAgent,
7+
MockWorkspaceApp,
8+
}from"testHelpers/entities";
9+
import{TaskApps}from"./TaskApps";
10+
import{withProxyProvider}from"testHelpers/storybook";
11+
12+
constmeta:Meta<typeofTaskApps>={
13+
title:"pages/TaskPage/TaskApps",
14+
component:TaskApps,
15+
decorators:[withProxyProvider()],
16+
parameters:{
17+
layout:"fullscreen",
18+
},
19+
};
20+
21+
exportdefaultmeta;
22+
typeStory=StoryObj<typeofTaskApps>;
23+
24+
constmockAgentNoApps={
25+
...MockWorkspaceAgent,
26+
apps:[],
27+
};
28+
29+
constmockExternalApp:WorkspaceApp={
30+
...MockWorkspaceApp,
31+
external:true,
32+
};
33+
34+
constmockEmbeddedApp:WorkspaceApp={
35+
...MockWorkspaceApp,
36+
external:false,
37+
};
38+
39+
consttaskWithNoApps={
40+
...MockTasks[0],
41+
workspace:{
42+
...MockWorkspace,
43+
latest_build:{
44+
...MockWorkspace.latest_build,
45+
resources:[
46+
{
47+
...MockWorkspace.latest_build.resources[0],
48+
agents:[mockAgentNoApps],
49+
},
50+
],
51+
},
52+
},
53+
};
54+
55+
exportconstNoEmbeddedApps:Story={
56+
args:{
57+
task:taskWithNoApps,
58+
},
59+
};
60+
61+
exportconstWithExternalAppsOnly:Story={
62+
args:{
63+
task:{
64+
...MockTasks[0],
65+
workspace:{
66+
...MockWorkspace,
67+
latest_build:{
68+
...MockWorkspace.latest_build,
69+
resources:[
70+
{
71+
...MockWorkspace.latest_build.resources[0],
72+
agents:[
73+
{
74+
...MockWorkspaceAgent,
75+
apps:[mockExternalApp],
76+
},
77+
],
78+
},
79+
],
80+
},
81+
},
82+
},
83+
},
84+
};
85+
86+
exportconstWithEmbeddedApps:Story={
87+
args:{
88+
task:{
89+
...MockTasks[0],
90+
workspace:{
91+
...MockWorkspace,
92+
latest_build:{
93+
...MockWorkspace.latest_build,
94+
resources:[
95+
{
96+
...MockWorkspace.latest_build.resources[0],
97+
agents:[
98+
{
99+
...MockWorkspaceAgent,
100+
apps:[mockEmbeddedApp],
101+
},
102+
],
103+
},
104+
],
105+
},
106+
},
107+
},
108+
},
109+
};
110+
111+
exportconstWithMixedApps:Story={
112+
args:{
113+
task:{
114+
...MockTasks[0],
115+
workspace:{
116+
...MockWorkspace,
117+
latest_build:{
118+
...MockWorkspace.latest_build,
119+
resources:[
120+
{
121+
...MockWorkspace.latest_build.resources[0],
122+
agents:[
123+
{
124+
...MockWorkspaceAgent,
125+
apps:[mockEmbeddedApp,mockExternalApp],
126+
},
127+
],
128+
},
129+
],
130+
},
131+
},
132+
},
133+
},
134+
};

‎site/src/pages/TaskPage/TaskApps.tsx‎

Lines changed: 96 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
importtype{WorkspaceApp}from"api/typesGenerated";
1+
importtype{WorkspaceAgent,WorkspaceApp}from"api/typesGenerated";
22
import{Button}from"components/Button/Button";
33
import{
44
DropdownMenu,
@@ -16,6 +16,8 @@ import { type FC, useState } from "react";
1616
import{LinkasRouterLink}from"react-router-dom";
1717
import{cn}from"utils/cn";
1818
import{TaskAppIFrame}from"./TaskAppIframe";
19+
import{Link}from"components/Link/Link";
20+
import{docs}from"utils/docs";
1921

2022
typeTaskAppsProps={
2123
task:Task;
@@ -37,25 +39,9 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
3739
constembeddedApps=apps.filter((app)=>!app.external);
3840
constexternalApps=apps.filter((app)=>app.external);
3941

40-
const[activeAppId,setActiveAppId]=useState<string>(()=>{
41-
constappId=embeddedApps[0]?.id;
42-
if(!appId){
43-
thrownewError("No apps found in task");
44-
}
45-
returnappId;
46-
});
47-
48-
constactiveApp=apps.find((app)=>app.id===activeAppId);
49-
if(!activeApp){
50-
thrownewError(`Active app with ID${activeAppId} not found in task`);
51-
}
52-
53-
constagent=agents.find((a)=>
54-
a.apps.some((app)=>app.id===activeAppId),
42+
const[activeAppId,setActiveAppId]=useState<string|undefined>(
43+
embeddedApps[0]?.id,
5544
);
56-
if(!agent){
57-
thrownewError(`Agent for app${activeAppId} not found in task workspace`);
58-
}
5945

6046
return(
6147
<mainclassName="flex flex-col">
@@ -76,56 +62,104 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
7662
</div>
7763

7864
{externalApps.length>0&&(
79-
<divclassName="ml-auto">
80-
<DropdownMenu>
81-
<DropdownMenuTriggerasChild>
82-
<Buttonsize="sm"variant="subtle">
83-
Open locally
84-
<ChevronDownIcon/>
85-
</Button>
86-
</DropdownMenuTrigger>
87-
<DropdownMenuContent>
88-
{externalApps.map((app)=>{
89-
constlink=useAppLink(app,{
90-
agent,
91-
workspace:task.workspace,
92-
});
93-
94-
return(
95-
<DropdownMenuItemkey={app.id}asChild>
96-
<RouterLinkto={link.href}>
97-
{app.icon ?(
98-
<ExternalImagesrc={app.icon}/>
99-
) :(
100-
<LayoutGridIcon/>
101-
)}
102-
{link.label}
103-
</RouterLink>
104-
</DropdownMenuItem>
105-
);
106-
})}
107-
</DropdownMenuContent>
108-
</DropdownMenu>
109-
</div>
65+
<TaskExternalAppsDropdown
66+
task={task}
67+
agents={agents}
68+
externalApps={externalApps}
69+
/>
11070
)}
11171
</div>
11272

113-
<divclassName="flex-1">
114-
{embeddedApps.map((app)=>{
115-
return(
116-
<TaskAppIFrame
117-
key={app.id}
118-
active={activeAppId===app.id}
119-
app={app}
120-
task={task}
121-
/>
122-
);
123-
})}
124-
</div>
73+
{embeddedApps.length>0 ?(
74+
<divclassName="flex-1">
75+
{embeddedApps.map((app)=>{
76+
return(
77+
<TaskAppIFrame
78+
key={app.id}
79+
active={activeAppId===app.id}
80+
app={app}
81+
task={task}
82+
/>
83+
);
84+
})}
85+
</div>
86+
) :(
87+
<divclassName="mx-auto my-auto flex flex-col items-center">
88+
<h3className="font-medium text-content-primary text-base">
89+
No embedded apps found.
90+
</h3>
91+
92+
<spanclassName="text-content-secondary text-sm">
93+
<Link
94+
href={docs("/ai-coder/tasks")}
95+
target="_blank"
96+
rel="noreferrer"
97+
>
98+
Learn how to configure apps
99+
</Link>{" "}
100+
for your tasks.
101+
</span>
102+
</div>
103+
)}
125104
</main>
126105
);
127106
};
128107

108+
typeTaskExternalAppsDropdownProps={
109+
task:Task;
110+
agents:WorkspaceAgent[];
111+
externalApps:WorkspaceApp[];
112+
};
113+
114+
constTaskExternalAppsDropdown:FC<TaskExternalAppsDropdownProps>=({
115+
task,
116+
agents,
117+
externalApps,
118+
})=>{
119+
return(
120+
<divclassName="ml-auto">
121+
<DropdownMenu>
122+
<DropdownMenuTriggerasChild>
123+
<Buttonsize="sm"variant="subtle">
124+
Open locally
125+
<ChevronDownIcon/>
126+
</Button>
127+
</DropdownMenuTrigger>
128+
<DropdownMenuContent>
129+
{externalApps.map((app)=>{
130+
constagent=agents.find((agent)=>
131+
agent.apps.some((a)=>a.id===app.id),
132+
);
133+
if(!agent){
134+
thrownewError(
135+
`Agent for app${app.id} not found in task workspace`,
136+
);
137+
}
138+
139+
constlink=useAppLink(app,{
140+
agent,
141+
workspace:task.workspace,
142+
});
143+
144+
return(
145+
<DropdownMenuItemkey={app.id}asChild>
146+
<RouterLinkto={link.href}>
147+
{app.icon ?(
148+
<ExternalImagesrc={app.icon}/>
149+
) :(
150+
<LayoutGridIcon/>
151+
)}
152+
{link.label}
153+
</RouterLink>
154+
</DropdownMenuItem>
155+
);
156+
})}
157+
</DropdownMenuContent>
158+
</DropdownMenu>
159+
</div>
160+
);
161+
};
162+
129163
typeTaskAppTabProps={
130164
task:Task;
131165
app:WorkspaceApp;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp