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

Commit59a6541

Browse files
refactor: move required external auth buttons to the submit side (#18586)
**Before:**![Screenshot 2025-06-25 at 14 4016](https://github.com/user-attachments/assets/cbc558f5-6eee-4133-afc9-2474f04a8a67)**After:**![Screenshot 2025-06-25 at 14 5353](https://github.com/user-attachments/assets/3a638f60-d1e4-40a4-a066-8d69fe96c198)
1 parent2d44add commit59a6541

File tree

3 files changed

+116
-56
lines changed

3 files changed

+116
-56
lines changed

‎site/src/hooks/useExternalAuth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ export const useExternalAuth = (versionId: string | undefined) => {
5050
externalAuthPollingState,
5151
isLoadingExternalAuth,
5252
externalAuthError:error,
53+
isPollingExternalAuth:externalAuthPollingState==="polling",
5354
};
5455
};

‎site/src/pages/TasksPage/TasksPage.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ export const MissingExternalAuth: Story = {
245245
});
246246

247247
awaitstep("Renders external authentication",async()=>{
248-
awaitcanvas.findByRole("button",{name:/loginwithgithub/i});
248+
awaitcanvas.findByRole("button",{name:/connecttogithub/i});
249249
});
250250
},
251251
};

‎site/src/pages/TasksPage/TasksPage.tsx

Lines changed: 114 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import Skeleton from "@mui/material/Skeleton";
22
import{API}from"api/api";
33
import{getErrorDetail,getErrorMessage}from"api/errors";
44
import{disabledRefetchOptions}from"api/queries/util";
5-
importtype{Template}from"api/typesGenerated";
5+
importtype{Template,TemplateVersionExternalAuth}from"api/typesGenerated";
66
import{ErrorAlert}from"components/Alert/ErrorAlert";
77
import{Avatar}from"components/Avatar/Avatar";
88
import{AvatarData}from"components/Avatar/AvatarData";
99
import{AvatarDataSkeleton}from"components/Avatar/AvatarDataSkeleton";
1010
import{Button}from"components/Button/Button";
11-
import{Form,FormFields,FormSection}from"components/Form/Form";
1211
import{displayError}from"components/GlobalSnackbar/utils";
1312
import{Margins}from"components/Margins/Margins";
1413
import{
@@ -37,9 +36,16 @@ import {
3736
TableRowSkeleton,
3837
}from"components/TableLoader/TableLoader";
3938

39+
import{ExternalImage}from"components/ExternalImage/ExternalImage";
40+
import{
41+
Tooltip,
42+
TooltipContent,
43+
TooltipProvider,
44+
TooltipTrigger,
45+
}from"components/Tooltip/Tooltip";
4046
import{useAuthenticated}from"hooks";
4147
import{useExternalAuth}from"hooks/useExternalAuth";
42-
import{RotateCcwIcon,SendIcon}from"lucide-react";
48+
import{RedoIcon,RotateCcwIcon,SendIcon}from"lucide-react";
4349
import{AI_PROMPT_PARAMETER_NAME,typeTask}from"modules/tasks/tasks";
4450
import{WorkspaceAppStatus}from"modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus";
4551
import{generateWorkspaceName}from"modules/workspaces/generateWorkspaceName";
@@ -50,12 +56,12 @@ import { Link as RouterLink } from "react-router-dom";
5056
importTextareaAutosizefrom"react-textarea-autosize";
5157
import{pageTitle}from"utils/page";
5258
import{relativeTime}from"utils/time";
53-
import{ExternalAuthButton}from"../CreateWorkspacePage/ExternalAuthButton";
5459
import{typeUserOption,UsersCombobox}from"./UsersCombobox";
5560

5661
typeTasksFilter={
5762
user:UserOption|undefined;
5863
};
64+
5965
constTasksPage:FC=()=>{
6066
const{ user, permissions}=useAuthenticated();
6167
const[filter,setFilter]=useState<TasksFilter>({
@@ -201,21 +207,24 @@ type TaskFormProps = {
201207
constTaskForm:FC<TaskFormProps>=({ templates})=>{
202208
const{ user}=useAuthenticated();
203209
constqueryClient=useQueryClient();
204-
205-
const[templateId,setTemplateId]=useState<string>(templates[0].id);
210+
const[selectedTemplateId,setSelectedTemplateId]=useState<string>(
211+
templates[0].id,
212+
);
213+
constselectedTemplate=templates.find(
214+
(t)=>t.id===selectedTemplateId,
215+
)asTemplate;
206216
const{
207217
externalAuth,
208-
externalAuthPollingState,
209-
startPollingExternalAuth,
210-
isLoadingExternalAuth,
211218
externalAuthError,
212-
}=useExternalAuth(
213-
templates.find((t)=>t.id===templateId)?.active_version_id,
214-
);
215-
216-
consthasAllRequiredExternalAuth=externalAuth?.every(
217-
(auth)=>auth.optional||auth.authenticated,
219+
isPollingExternalAuth,
220+
isLoadingExternalAuth,
221+
}=useExternalAuth(selectedTemplate.active_version_id);
222+
constmissedExternalAuth=externalAuth?.filter(
223+
(auth)=>!auth.optional&&!auth.authenticated,
218224
);
225+
constisMissingExternalAuth=missedExternalAuth
226+
?missedExternalAuth.length>0
227+
:true;
219228

220229
constcreateTaskMutation=useMutation({
221230
mutationFn:async({ prompt, templateId}:CreateTaskMutationFnProps)=>
@@ -235,10 +244,6 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
235244
constprompt=formData.get("prompt")asstring;
236245
consttemplateID=formData.get("templateID")asstring;
237246

238-
if(!prompt||!templateID){
239-
return;
240-
}
241-
242247
try{
243248
awaitcreateTaskMutation.mutateAsync({
244249
prompt,
@@ -253,8 +258,12 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
253258
};
254259

255260
return(
256-
<FormonSubmit={onSubmit}aria-label="Create AI task">
257-
{Boolean(externalAuthError)&&<ErrorAlerterror={externalAuthError}/>}
261+
<form
262+
onSubmit={onSubmit}
263+
aria-label="Create AI task"
264+
className="flex flex-col gap-4"
265+
>
266+
{externalAuthError&&<ErrorAlerterror={externalAuthError}/>}
258267

259268
<fieldset
260269
className="border border-border border-solid rounded-lg p-4"
@@ -274,7 +283,7 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
274283
<divclassName="flex items-center justify-between pt-2">
275284
<Select
276285
name="templateID"
277-
onValueChange={(value)=>setTemplateId(value)}
286+
onValueChange={(value)=>setSelectedTemplateId(value)}
278287
defaultValue={templates[0].id}
279288
required
280289
>
@@ -294,43 +303,93 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
294303
</SelectContent>
295304
</Select>
296305

297-
<Button
298-
size="sm"
299-
type="submit"
300-
disabled={!hasAllRequiredExternalAuth}
301-
>
302-
<Spinner
303-
loading={createTaskMutation.isPending||isLoadingExternalAuth}
304-
>
305-
<SendIcon/>
306-
</Spinner>
307-
Run task
308-
</Button>
306+
<divclassName="flex items-center gap-2">
307+
{missedExternalAuth&&(
308+
<ExternalAuthButtons
309+
template={selectedTemplate}
310+
missedExternalAuth={missedExternalAuth}
311+
/>
312+
)}
313+
314+
<Buttonsize="sm"type="submit"disabled={isMissingExternalAuth}>
315+
<Spinner
316+
loading={
317+
isLoadingExternalAuth||
318+
isPollingExternalAuth||
319+
createTaskMutation.isPending
320+
}
321+
>
322+
<SendIcon/>
323+
</Spinner>
324+
Run task
325+
</Button>
326+
</div>
309327
</div>
310328
</fieldset>
329+
</form>
330+
);
331+
};
311332

312-
{!hasAllRequiredExternalAuth&&
313-
externalAuth&&
314-
externalAuth.length>0&&(
315-
<FormSection
316-
title="External Authentication"
317-
description="This template uses external services for authentication."
318-
>
319-
<FormFields>
320-
{externalAuth.map((auth)=>(
321-
<ExternalAuthButton
322-
key={auth.id}
323-
auth={auth}
324-
isLoading={externalAuthPollingState==="polling"}
325-
onStartPolling={startPollingExternalAuth}
326-
displayRetry={externalAuthPollingState==="abandoned"}
327-
/>
328-
))}
329-
</FormFields>
330-
</FormSection>
333+
typeExternalAuthButtonProps={
334+
template:Template;
335+
missedExternalAuth:TemplateVersionExternalAuth[];
336+
};
337+
338+
constExternalAuthButtons:FC<ExternalAuthButtonProps>=({
339+
template,
340+
missedExternalAuth,
341+
})=>{
342+
const{
343+
startPollingExternalAuth,
344+
isPollingExternalAuth,
345+
externalAuthPollingState,
346+
}=useExternalAuth(template.active_version_id);
347+
constshouldRetry=externalAuthPollingState==="abandoned";
348+
349+
returnmissedExternalAuth.map((auth)=>{
350+
return(
351+
<divclassName="flex items-center gap-2"key={auth.id}>
352+
<Button
353+
variant="outline"
354+
size="sm"
355+
disabled={isPollingExternalAuth||auth.authenticated}
356+
onClick={()=>{
357+
window.open(
358+
auth.authenticate_url,
359+
"_blank",
360+
"width=900,height=600",
361+
);
362+
startPollingExternalAuth();
363+
}}
364+
>
365+
<Spinnerloading={isPollingExternalAuth}>
366+
<ExternalImagesrc={auth.display_icon}/>
367+
</Spinner>
368+
Connect to{auth.display_name}
369+
</Button>
370+
371+
{shouldRetry&&!auth.authenticated&&(
372+
<TooltipProvider>
373+
<TooltipdelayDuration={100}>
374+
<TooltipTriggerasChild>
375+
<Button
376+
variant="outline"
377+
size="icon"
378+
onClick={startPollingExternalAuth}
379+
>
380+
<RedoIcon/>
381+
<spanclassName="sr-only">Refresh external auth</span>
382+
</Button>
383+
</TooltipTrigger>
384+
<TooltipContent>
385+
Retry connecting to{auth.display_name}
386+
</TooltipContent>
387+
</Tooltip>
388+
</TooltipProvider>
331389
)}
332-
</Form>
333-
);
390+
</div>
391+
);
392+
});
334393
};
335394

336395
typeTasksFilterProps={

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp