- Notifications
You must be signed in to change notification settings - Fork926
refactor: move required external auth buttons to the submit side#18586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, 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 { displayError } from "components/GlobalSnackbar/utils"; | ||
import { Margins } from "components/Margins/Margins"; | ||
import { | ||
@@ -37,6 +36,7 @@ import { | ||
TableRowSkeleton, | ||
} from "components/TableLoader/TableLoader"; | ||
import { ExternalImage } from "components/ExternalImage/ExternalImage"; | ||
import { useAuthenticated } from "hooks"; | ||
import { useExternalAuth } from "hooks/useExternalAuth"; | ||
import { RotateCcwIcon, SendIcon } from "lucide-react"; | ||
@@ -50,12 +50,12 @@ 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 { type UserOption, UsersCombobox } from "./UsersCombobox"; | ||
type TasksFilter = { | ||
user: UserOption | undefined; | ||
}; | ||
const TasksPage: FC = () => { | ||
const { user, permissions } = useAuthenticated(); | ||
const [filter, setFilter] = useState<TasksFilter>({ | ||
@@ -201,21 +201,20 @@ type TaskFormProps = { | ||
const TaskForm: FC<TaskFormProps> = ({ templates }) => { | ||
const { user } = useAuthenticated(); | ||
const queryClient = useQueryClient(); | ||
const [selectedTemplateId, setSelectedTemplateId] = useState<string>( | ||
templates[0].id, | ||
); | ||
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 +234,6 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => { | ||
const prompt = formData.get("prompt") as string; | ||
const templateID = formData.get("templateID") as string; | ||
try { | ||
await createTaskMutation.mutateAsync({ | ||
prompt, | ||
@@ -253,8 +248,12 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => { | ||
}; | ||
return ( | ||
<form | ||
onSubmit={onSubmit} | ||
aria-label="Create AI task" | ||
className="flex flex-col gap-4" | ||
> | ||
{externalAuthError && <ErrorAlert error={externalAuthError} />} | ||
<fieldset | ||
className="border border-border border-solid rounded-lg p-4" | ||
@@ -274,7 +273,7 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => { | ||
<div className="flex items-center justify-between pt-2"> | ||
<Select | ||
name="templateID" | ||
onValueChange={(value) =>setSelectedTemplateId(value)} | ||
defaultValue={templates[0].id} | ||
required | ||
> | ||
@@ -294,43 +293,61 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => { | ||
</SelectContent> | ||
</Select> | ||
<div className="flex items-center gap-2"> | ||
{missedExternalAuth && isMissingExternalAuth && ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more.
| ||
<ExternalAuthButtons | ||
template={selectedTemplate} | ||
missedExternalAuth={missedExternalAuth} | ||
/> | ||
)} | ||
<Button size="sm" type="submit" disabled={isMissingExternalAuth}> | ||
<Spinner | ||
loading={createTaskMutation.isPending || isLoadingExternalAuth} | ||
> | ||
<SendIcon /> | ||
</Spinner> | ||
Run task | ||
</Button> | ||
</div> | ||
</div> | ||
</fieldset> | ||
</form> | ||
); | ||
}; | ||
type ExternalAuthButtonProps = { | ||
template: Template; | ||
missedExternalAuth: TemplateVersionExternalAuth[]; | ||
}; | ||
const ExternalAuthButtons: FC<ExternalAuthButtonProps> = ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This does not handle the abandoned polling state like the previous component does. It seems important to me otherwise you could be waiting for it to connect but nothing ever happens, if the polling was abandoned, with no visual indication as to why or a way to retry (aside from refreshing the browser). Would it make sense to modify the other component to have a variant so we can reuse it here? Or would it make more sense to duplicate the retry code maybe? | ||
template, | ||
missedExternalAuth, | ||
}) => { | ||
const { startPollingExternalAuth, isLoadingExternalAuth } = useExternalAuth( | ||
template.active_version_id, | ||
); | ||
return missedExternalAuth.map((auth) => { | ||
return ( | ||
<Button | ||
variant="outline" | ||
key={auth.id} | ||
size="sm" | ||
disabled={isLoadingExternalAuth || auth.authenticated} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more.
| ||
onClick={() => { | ||
window.open(auth.authenticate_url, "_blank", "width=900,height=600"); | ||
startPollingExternalAuth(); | ||
}} | ||
> | ||
<Spinner loading={isLoadingExternalAuth}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Before, we were showing a spinner while polling ( If it was intentional, we can drop the spinner altogether; | ||
<ExternalImage src={auth.display_icon} /> | ||
</Spinner> | ||
Login with {auth.display_name} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I still find the "Login with $name" buttons confusing without context, if it was "Connect $name" I think it would be clearer, but that could just be me. What do you think? That would also makes better sense with the | ||
</Button> | ||
); | ||
}); | ||
}; | ||
type TasksFilterProps = { | ||
Uh oh!
There was an error while loading.Please reload this page.