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

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

Open
BrunoQuaresma wants to merge2 commits intomain
base:main
Choose a base branch
Loading
frombq/improve-external-auth-on-tasks
Open
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 73 additions & 56 deletionssite/src/pages/TasksPage/TasksPage.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 {
Expand DownExpand Up@@ -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";
Expand All@@ -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 { ExternalAuthButton } from "../CreateWorkspacePage/ExternalAuthButton";
import { type UserOption, UsersCombobox } from "./UsersCombobox";

type TasksFilter = {
user: UserOption | undefined;
};

const TasksPage: FC = () => {
const { user, permissions } = useAuthenticated();
const [filter, setFilter] = useState<TasksFilter>({
Expand DownExpand Up@@ -201,21 +201,20 @@ type TaskFormProps = {
const TaskForm: FC<TaskFormProps> = ({ templates }) => {
const { user } = useAuthenticated();
const queryClient = useQueryClient();

const [templateId, setTemplateId] = useState<string>(templates[0].id);
const {
externalAuth,
externalAuthPollingState,
startPollingExternalAuth,
isLoadingExternalAuth,
externalAuthError,
} = useExternalAuth(
templates.find((t) => t.id === templateId)?.active_version_id,
const [selectedTemplateId, setSelectedTemplateId] = useState<string>(
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) =>
Expand All@@ -235,10 +234,6 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
const prompt = formData.get("prompt") as string;
const templateID = formData.get("templateID") as string;

if (!prompt || !templateID) {
return;
}

try {
await createTaskMutation.mutateAsync({
prompt,
Expand All@@ -253,8 +248,12 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
};

return (
<Form onSubmit={onSubmit} aria-label="Create AI task">
{Boolean(externalAuthError) && <ErrorAlert error={externalAuthError} />}
<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"
Expand All@@ -274,7 +273,7 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
<div className="flex items-center justify-between pt-2">
<Select
name="templateID"
onValueChange={(value) =>setTemplateId(value)}
onValueChange={(value) =>setSelectedTemplateId(value)}
defaultValue={templates[0].id}
required
>
Expand All@@ -294,43 +293,61 @@ const TaskForm: FC<TaskFormProps> = ({ templates }) => {
</SelectContent>
</Select>

<Button
size="sm"
type="submit"
disabled={!hasAllRequiredExternalAuth}
>
<Spinner
loading={createTaskMutation.isPending || isLoadingExternalAuth}
>
<SendIcon />
</Spinner>
Run task
</Button>
<div className="flex items-center gap-2">
{missedExternalAuth && isMissingExternalAuth && (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

isMissingExternalAuth has no effect here, right? It is already implied bymissedExternalAuth and because the component only renders something ifmissedExternalAuth.length > 0.

<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>
);
};

{!hasAllRequiredExternalAuth &&
externalAuth &&
externalAuth.length > 0 && (
<FormSection
title="External Authentication"
description="This template uses external services for authentication."
>
<FormFields>
{externalAuth.map((auth) => (
<ExternalAuthButton
key={auth.id}
auth={auth}
isLoading={externalAuthPollingState === "polling"}
onStartPolling={startPollingExternalAuth}
displayRetry={externalAuthPollingState === "abandoned"}
/>
))}
</FormFields>
</FormSection>
)}
</Form>
type ExternalAuthButtonProps = {
template: Template;
missedExternalAuth: TemplateVersionExternalAuth[];
};

const ExternalAuthButtons: FC<ExternalAuthButtonProps> = ({
Copy link
Member

Choose a reason for hiding this comment

The 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}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

isLoadingExternalAuth will always be false here, I think. Also, before we were disabling the buttons while polling. Want to check if dropping that was intentional.

onClick={() => {
window.open(auth.authenticate_url, "_blank", "width=900,height=600");
startPollingExternalAuth();
}}
>
<Spinner loading={isLoadingExternalAuth}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Before, we were showing a spinner while polling (externalAuthPollingState === "polling"), but now we only show while loading. Just want to confirm if that was intentional. I do think it makes sense to have some visual indication when the polling is happening.

If it was intentional, we can drop the spinner altogether;isLoadingExternalAuth will always be false here right? Since if we are loading, we have no auth buttons to display.

<ExternalImage src={auth.display_icon} />
</Spinner>
Login with {auth.display_name}
Copy link
Member

Choose a reason for hiding this comment

The 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 theSlack Notify external auth,Connect Slack Notify makes more sense to me thanLogin with Slack Notify.

</Button>
);
});
};

type TasksFilterProps = {
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp