import dayjs from 'dayjs' import Link from 'next/link' import { useMemo, type ReactNode } from 'react' import type { UseFormReturn } from 'react-hook-form' import { Alert, AlertDescription, AlertTitle, Button, Card, CardContent, CardFooter, CardHeader, Form, FormControl, FormField, FormItem, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, WarningIcon, } from 'ui' import { ShimmeringLoader } from 'ui-patterns' import { FormLayout } from 'ui-patterns/form/Layout/FormLayout' import type { ApprovalState, IApprovalFormSchema } from './ApiAuthorization.Schema' import { AuthorizeRequesterDetails } from '@/components/interfaces/Organization/OAuthApps/AuthorizeRequesterDetails' import type { ApiAuthorizationResponse } from '@/data/api-authorization/api-authorization-query' import { BASE_PATH } from '@/lib/constants' import type { Organization, ResponseError } from '@/types' type OrganizationsState_Loading = { _tag: 'loading' } type OrganizationsState_Error = { _tag: 'error' error: ResponseError | null } type OrganizationsState_Empty = { _tag: 'empty' } type OrganizationsState_NotMember = { _tag: 'not_member' } type OrganizationsState_Success = { _tag: 'success' organizations: Array } type OrganizationsState = | OrganizationsState_Loading | OrganizationsState_Error | OrganizationsState_Empty | OrganizationsState_NotMember | OrganizationsState_Success export interface ApiAuthorizationMainViewProps { approvalState: ApprovalState form: UseFormReturn requester: ApiAuthorizationResponse organizations: OrganizationsState requestedOrganizationSlug: string | undefined onApprove: () => void onDecline: () => void } export function ApiAuthorizationMainView({ approvalState, form, requester, organizations, requestedOrganizationSlug, onApprove, onDecline, }: ApiAuthorizationMainViewProps): ReactNode { const isMcpClient = requester.registration_type === 'dynamic' const isExpired = dayjs().isAfter(dayjs(requester.expires_at)) return ( {isMcpClient && } {isExpired && } {organizations._tag === 'loading' && } {organizations._tag === 'error' && } {organizations._tag === 'empty' && } {organizations._tag === 'not_member' && } {organizations._tag === 'success' && ( )} ) } interface FormShellProps { title: string children: ReactNode } function FormShell({ title, children }: FormShellProps): ReactNode { return ( {title} {children} ) } function McpNotice(): ReactNode { return ( MCP Client Connection This is an MCP (Model Context Protocol) client designed to connect with AI applications. Please ensure you trust this application before granting access to your organization's data. ) } function ExpiredNotice(): ReactNode { return ( This authorization request is expired Please retry your authorization request from the requesting app ) } function OrganizationsLoader(): ReactNode { return (
) } interface OrganizationsErrorNoticeProps { error: ResponseError | null } function OrganizationsErrorNotice({ error }: OrganizationsErrorNoticeProps): ReactNode { return ( There was an error loading your organizations Please try again. If the problem persists, contact support. {error &&

Error: {error.message}

}
) } function OrganizationsEmptyState(): ReactNode { return ( Organization is needed for installing an integration Your account isn't associated with any organizations. To use this integration, it must be installed within an organization. You'll be redirected to create an organization first. ) } function NotMemberOfOrganizationNotice(): ReactNode { return ( Organization is needed for installing an integration Your account is not a member of the pre-selected organization. To use this integration, it must be installed within an organization your account is associated with. ) } interface OrganizationSelectorProps { form: UseFormReturn requester: ApiAuthorizationResponse requestedOrganizationSlug: string | undefined organizations: Array disabled?: boolean } function OrganizationSelector({ form, requester, requestedOrganizationSlug, organizations, disabled = false, }: OrganizationSelectorProps): ReactNode { return (
( )} /> ) } interface FormFooterProps { disabled?: boolean approvalState: ApprovalState requester: ApiAuthorizationResponse organizations: OrganizationsState onDecline: () => void onApprove: () => void } function FormFooter({ disabled = false, approvalState, requester, organizations, onDecline, onApprove, }: FormFooterProps): ReactNode { const showApprovalButton = organizations._tag === 'success' || organizations._tag === 'not_member' return ( {organizations._tag === 'loading' && ( Authorize {requester.name} )} {organizations._tag === 'empty' && } {showApprovalButton && ( )} ) } interface LoadingApprovalButtonProps { children: ReactNode } function LoadingApprovalButton({ children }: LoadingApprovalButtonProps): ReactNode { return } function createReturnToSearchParam(): string | null { if (typeof window === 'undefined') { return null } const basePath = BASE_PATH let pathname = basePath ? location.pathname.replace(basePath, '') : location.pathname if (location.search) { pathname += location.search } return pathname } function CreateOrganizationLink(): ReactNode { const searchParamString = useMemo(function createSearchParams() { const searchParams = new URLSearchParams() const returnTo = createReturnToSearchParam() if (returnTo) { searchParams.set('returnTo', returnTo) } return searchParams.toString() }, []) return ( ) } interface ApprovalButtonProps { disabled?: boolean approvalState: ApprovalState requester: ApiAuthorizationResponse onApprove: () => void } function ApprovalButton({ disabled, approvalState, requester, onApprove, }: ApprovalButtonProps): ReactNode { return ( ) }