From da84dd6e47438c2b4c7640d397afb336ac81310e Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 25 Jun 2025 17:54:29 +0000 Subject: [PATCH 1/6] refactor: move required external auth buttons to the submit side --- site/src/pages/TasksPage/TasksPage.tsx | 130 ++++++++++++++----------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index f86979f8eae00..1abb4501c53fa 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -2,13 +2,12 @@ import Skeleton from "@mui/material/Skeleton"; import { API } from "api/api"; import { getErrorDetail, getErrorMessage } from "api/errors"; import { disabledRefetchOptions } from "api/queries/util"; -import type { Template } from "api/typesGenerated"; +import type { Template, TemplateVersionExternalAuth } from "api/typesGenerated"; import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Avatar } from "components/Avatar/Avatar"; import { AvatarData } from "components/Avatar/AvatarData"; import { AvatarDataSkeleton } from "components/Avatar/AvatarDataSkeleton"; import { Button } from "components/Button/Button"; -import { Form, FormFields, FormSection } from "components/Form/Form"; import { displayError } from "components/GlobalSnackbar/utils"; import { Margins } from "components/Margins/Margins"; import { @@ -39,7 +38,7 @@ import { import { useAuthenticated } from "hooks"; import { useExternalAuth } from "hooks/useExternalAuth"; -import { RotateCcwIcon, SendIcon } from "lucide-react"; +import { AlertTriangleIcon, RotateCcwIcon, SendIcon } from "lucide-react"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; @@ -52,10 +51,12 @@ import { pageTitle } from "utils/page"; import { relativeTime } from "utils/time"; import { ExternalAuthButton } from "../CreateWorkspacePage/ExternalAuthButton"; import { type UserOption, UsersCombobox } from "./UsersCombobox"; +import { ExternalImage } from "components/ExternalImage/ExternalImage"; type TasksFilter = { user: UserOption | undefined; }; + const TasksPage: FC = () => { const { user, permissions } = useAuthenticated(); const [filter, setFilter] = useState({ @@ -201,21 +202,20 @@ type TaskFormProps = { const TaskForm: FC = ({ templates }) => { const { user } = useAuthenticated(); const queryClient = useQueryClient(); - - const [templateId, setTemplateId] = useState(templates[0].id); - const { - externalAuth, - externalAuthPollingState, - startPollingExternalAuth, - isLoadingExternalAuth, - externalAuthError, - } = useExternalAuth( - templates.find((t) => t.id === templateId)?.active_version_id, + const [selectedTemplateId, setSelectedTemplateId] = useState( + templates[0].id, ); - - const hasAllRequiredExternalAuth = externalAuth?.every( - (auth) => auth.optional || auth.authenticated, + const selectedTemplate = templates.find( + (t) => t.id === selectedTemplateId, + ) as Template; + const { externalAuth, isLoadingExternalAuth, externalAuthError } = + useExternalAuth(selectedTemplate.active_version_id); + const missedExternalAuth = externalAuth?.filter( + (auth) => !auth.optional && !auth.authenticated, ); + const isMissingExternalAuth = missedExternalAuth + ? missedExternalAuth.length > 0 + : true; const createTaskMutation = useMutation({ mutationFn: async ({ prompt, templateId }: CreateTaskMutationFnProps) => @@ -235,10 +235,6 @@ const TaskForm: FC = ({ templates }) => { const prompt = formData.get("prompt") as string; const templateID = formData.get("templateID") as string; - if (!prompt || !templateID) { - return; - } - try { await createTaskMutation.mutateAsync({ prompt, @@ -253,8 +249,12 @@ const TaskForm: FC = ({ templates }) => { }; return ( -
- {Boolean(externalAuthError) && } + + {externalAuthError && }
= ({ templates }) => {
- +
+ {missedExternalAuth && isMissingExternalAuth && ( + + )} + + +
+ + ); +}; - {!hasAllRequiredExternalAuth && - externalAuth && - externalAuth.length > 0 && ( - - - {externalAuth.map((auth) => ( - - ))} - - - )} - +type ExternalAuthButtonProps = { + template: Template; + missedExternalAuth: TemplateVersionExternalAuth[]; +}; + +const ExternalAuthButtons: FC = ({ + template, + missedExternalAuth, +}) => { + const { startPollingExternalAuth, isLoadingExternalAuth } = useExternalAuth( + template.active_version_id, ); + + return missedExternalAuth.map((auth) => { + return ( + + ); + }); }; type TasksFilterProps = { From db8a91b57f1ebe0104a941dc352e0bfc894ad688 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 25 Jun 2025 18:10:08 +0000 Subject: [PATCH 2/6] fmt --- site/src/pages/TasksPage/TasksPage.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index 1abb4501c53fa..b9652b09a8819 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -36,9 +36,10 @@ import { TableRowSkeleton, } from "components/TableLoader/TableLoader"; +import { ExternalImage } from "components/ExternalImage/ExternalImage"; import { useAuthenticated } from "hooks"; import { useExternalAuth } from "hooks/useExternalAuth"; -import { AlertTriangleIcon, RotateCcwIcon, SendIcon } from "lucide-react"; +import { RotateCcwIcon, SendIcon } from "lucide-react"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; @@ -49,9 +50,7 @@ import { Link as RouterLink } from "react-router-dom"; import TextareaAutosize from "react-textarea-autosize"; import { pageTitle } from "utils/page"; import { relativeTime } from "utils/time"; -import { ExternalAuthButton } from "../CreateWorkspacePage/ExternalAuthButton"; import { type UserOption, UsersCombobox } from "./UsersCombobox"; -import { ExternalImage } from "components/ExternalImage/ExternalImage"; type TasksFilter = { user: UserOption | undefined; From 8a12f602fc8f87b18093d1560b0aed1af96ed77a Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 26 Jun 2025 19:52:38 +0000 Subject: [PATCH 3/6] Apply asher's review comments --- site/src/hooks/useExternalAuth.ts | 1 + site/src/pages/TasksPage/TasksPage.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/site/src/hooks/useExternalAuth.ts b/site/src/hooks/useExternalAuth.ts index 942ce25fa892e..04197235289d9 100644 --- a/site/src/hooks/useExternalAuth.ts +++ b/site/src/hooks/useExternalAuth.ts @@ -50,5 +50,6 @@ export const useExternalAuth = (versionId: string | undefined) => { externalAuthPollingState, isLoadingExternalAuth, externalAuthError: error, + isPollingExternalAuth: externalAuthPollingState === "polling", }; }; diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index b9652b09a8819..a1cdf39f78f0a 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -207,7 +207,7 @@ const TaskForm: FC = ({ templates }) => { const selectedTemplate = templates.find( (t) => t.id === selectedTemplateId, ) as Template; - const { externalAuth, isLoadingExternalAuth, externalAuthError } = + const { externalAuth, externalAuthError, isPollingExternalAuth } = useExternalAuth(selectedTemplate.active_version_id); const missedExternalAuth = externalAuth?.filter( (auth) => !auth.optional && !auth.authenticated, @@ -294,7 +294,7 @@ const TaskForm: FC = ({ templates }) => {
- {missedExternalAuth && isMissingExternalAuth && ( + {missedExternalAuth && ( = ({ templates }) => { ); }); From a6ff6850b5dbaf99218ae42419954ec893e97ff0 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 26 Jun 2025 20:16:20 +0000 Subject: [PATCH 4/6] Add retry --- site/src/pages/TasksPage/TasksPage.tsx | 72 +++++++++++++++++++------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index a1cdf39f78f0a..c47708d43225e 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -39,7 +39,7 @@ import { import { ExternalImage } from "components/ExternalImage/ExternalImage"; import { useAuthenticated } from "hooks"; import { useExternalAuth } from "hooks/useExternalAuth"; -import { RotateCcwIcon, SendIcon } from "lucide-react"; +import { RedoIcon, RotateCcwIcon, SendIcon } from "lucide-react"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus"; import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"; @@ -51,6 +51,12 @@ import TextareaAutosize from "react-textarea-autosize"; import { pageTitle } from "utils/page"; import { relativeTime } from "utils/time"; import { type UserOption, UsersCombobox } from "./UsersCombobox"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; type TasksFilter = { user: UserOption | undefined; @@ -325,27 +331,55 @@ const ExternalAuthButtons: FC = ({ template, missedExternalAuth, }) => { - const { startPollingExternalAuth, isPollingExternalAuth } = useExternalAuth( - template.active_version_id, - ); + const { + startPollingExternalAuth, + isPollingExternalAuth, + externalAuthPollingState, + } = useExternalAuth(template.active_version_id); + const shouldRetry = externalAuthPollingState === "abandoned"; return missedExternalAuth.map((auth) => { return ( - +
+ + + {shouldRetry && !auth.authenticated && ( + + + + + + + Retry connect to {auth.display_name} + + + + )} +
); }); }; From 2c2be532cc47cf23c71dec3015b6122c0520959b Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Thu, 26 Jun 2025 20:33:18 +0000 Subject: [PATCH 5/6] fmt --- site/src/pages/TasksPage/TasksPage.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index c47708d43225e..212d9bce493dc 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -37,6 +37,12 @@ import { } from "components/TableLoader/TableLoader"; import { ExternalImage } from "components/ExternalImage/ExternalImage"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "components/Tooltip/Tooltip"; import { useAuthenticated } from "hooks"; import { useExternalAuth } from "hooks/useExternalAuth"; import { RedoIcon, RotateCcwIcon, SendIcon } from "lucide-react"; @@ -51,12 +57,6 @@ import TextareaAutosize from "react-textarea-autosize"; import { pageTitle } from "utils/page"; import { relativeTime } from "utils/time"; import { type UserOption, UsersCombobox } from "./UsersCombobox"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "components/Tooltip/Tooltip"; type TasksFilter = { user: UserOption | undefined; From d294d9bf85270025dff6ec8f1b4feb9399e69905 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Fri, 27 Jun 2025 13:37:34 +0000 Subject: [PATCH 6/6] Apply Asher's comments --- site/src/pages/TasksPage/TasksPage.stories.tsx | 2 +- site/src/pages/TasksPage/TasksPage.tsx | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/site/src/pages/TasksPage/TasksPage.stories.tsx b/site/src/pages/TasksPage/TasksPage.stories.tsx index 287018cf5a2d7..9707532d82ded 100644 --- a/site/src/pages/TasksPage/TasksPage.stories.tsx +++ b/site/src/pages/TasksPage/TasksPage.stories.tsx @@ -245,7 +245,7 @@ export const MissingExternalAuth: Story = { }); await step("Renders external authentication", async () => { - await canvas.findByRole("button", { name: /login with github/i }); + await canvas.findByRole("button", { name: /connect to github/i }); }); }, }; diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index 212d9bce493dc..10781434e5358 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -213,8 +213,12 @@ const TaskForm: FC = ({ templates }) => { const selectedTemplate = templates.find( (t) => t.id === selectedTemplateId, ) as Template; - const { externalAuth, externalAuthError, isPollingExternalAuth } = - useExternalAuth(selectedTemplate.active_version_id); + const { + externalAuth, + externalAuthError, + isPollingExternalAuth, + isLoadingExternalAuth, + } = useExternalAuth(selectedTemplate.active_version_id); const missedExternalAuth = externalAuth?.filter( (auth) => !auth.optional && !auth.authenticated, ); @@ -309,7 +313,11 @@ const TaskForm: FC = ({ templates }) => { - Retry connect to {auth.display_name} + Retry connecting to {auth.display_name}