mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Follow up of #49637. Usages that impacted tests were fixed in the previous PR. This PR fixes the other usages so that label are correctly linked to their inputs. No visual changes ## How to test 1. Design system: [Form examples](https://design-system-git-fix-form-item-layout-usages-supabase.vercel.app/design-system/docs/ui-patterns/forms): moved `FormControl` around the `SelectTrigger` so that the label is linked to the button (It's actually done like this in the [Select Form example](https://design-system-git-fix-form-item-layout-usages-supabase.vercel.app/design-system/docs/components/select#form) and Radix recommend targeting the button too in their [documentation](https://www.radix-ui.com/primitives/docs/components/select#labelling)) 2. [Access tokens](https://studio-staging-463111oii-supabase.vercel.app/dashboard/account/tokens): updated usage to fallback on generated ids and fixed the select just like _1_ 3. [New TOTP factor](https://studio-staging-463111oii-supabase.vercel.app/dashboard/account/security): updated usage to fallback on generated ids 4. _Studio/Database/Extensions_ (`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/database/extensions`): updated the extension enabling modal to fallback on generated ids 5. _Studio/Integrations/Vault (`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/integrations/vault/secrets`): updated the secret edition modal to fallback on generated ids 6. _Studio/Observability(`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/observability`): updated the report creation and edition modals to fallback on generated ids 7. _Studio/SQL Editor(`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/sql/new`): updated the query renaming modal to fallback on generated ids 8. _Studio/Storage/Analytics(`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/storage/analytics`): updated the table creation sheet to fallback on generated ids (you must have a bucket first) 9. _Studio/Workers(`https://studio-staging-463111oii-supabase.vercel.app/dashboard/project/[PROJECT]/workers`): updated the worker creation modal to fallback on generated ids (you must have a bucket first) 10. Updated [Signup](https://studio-staging-463111oii-supabase.vercel.app/dashboard/sign-up?returnTo=%2Fnew), [Signin](https://studio-staging-463111oii-supabase.vercel.app/dashboard/sign-in) and [SSO Signin](https://studio-staging-463111oii-supabase.vercel.app/dashboard/sign-in-sso) forms to fallback on generated ids <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Improvements** - Standardized form field presentation across access tokens, authentication, reports, integrations, database extensions, SQL editor, storage, and worker deployment workflows. - Updated password fields and visibility toggles for more consistent input behavior. - Refined token expiration selection, verification code entry, and dropdown layouts. - Preserved existing labels, validation, options, and form functionality while simplifying the interface structure. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import { Lock } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { useRouter } from 'next/router'
|
|
import { useRef, useState } from 'react'
|
|
import { useForm, type SubmitHandler } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import { Button, Form, FormControl, FormField, Input } from 'ui'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import z from 'zod'
|
|
|
|
import { LastSignInWrapper } from './LastSignInWrapper'
|
|
import { useLastSignIn } from '@/hooks/misc/useLastSignIn'
|
|
import { BASE_PATH } from '@/lib/constants'
|
|
import { captureCriticalError } from '@/lib/error-reporting'
|
|
import { auth, buildPathWithParams } from '@/lib/gotrue'
|
|
|
|
const schema = z.object({
|
|
email: z.string().min(1, 'Email is required').email('Must be a valid email'),
|
|
})
|
|
|
|
const formId = 'sso-sign-in-form'
|
|
|
|
export const SignInSSOForm = () => {
|
|
const queryClient = useQueryClient()
|
|
const captchaRef = useRef<HCaptcha>(null)
|
|
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
|
|
const [_, setLastSignInUsed] = useLastSignIn()
|
|
const form = useForm<z.infer<typeof schema>>({
|
|
resolver: zodResolver(schema),
|
|
defaultValues: { email: '' },
|
|
})
|
|
const isSubmitting = form.formState.isSubmitting
|
|
|
|
const onSubmit: SubmitHandler<z.infer<typeof schema>> = async ({ email }) => {
|
|
const toastId = toast.loading('Signing in...')
|
|
|
|
let token = captchaToken
|
|
if (!token) {
|
|
const captchaResponse = await captchaRef.current?.execute({ async: true })
|
|
token = captchaResponse?.response ?? null
|
|
}
|
|
|
|
// redirects to /sign-in to check if the user has MFA setup (handled in SignInLayout.tsx)
|
|
const redirectTo = buildPathWithParams(
|
|
`${
|
|
process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
|
|
? location.origin
|
|
: process.env.NEXT_PUBLIC_SITE_URL
|
|
}${BASE_PATH}/sign-in-mfa?method=sso`
|
|
)
|
|
|
|
const { data, error } = await auth.signInWithSSO({
|
|
domain: email.split('@')[1],
|
|
options: {
|
|
captchaToken: token ?? undefined,
|
|
redirectTo,
|
|
},
|
|
})
|
|
|
|
if (!error) {
|
|
await queryClient.resetQueries()
|
|
setLastSignInUsed('sso')
|
|
if (data) {
|
|
// redirect to SSO identity provider page
|
|
window.location.href = data.url
|
|
}
|
|
} else {
|
|
setCaptchaToken(null)
|
|
captchaRef.current?.resetCaptcha()
|
|
toast.error(`Failed to sign in: ${error.message}`, { id: toastId })
|
|
captureCriticalError(error, 'sign in via SSO')
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Form {...form}>
|
|
<form
|
|
id={formId}
|
|
method="POST"
|
|
className="flex flex-col gap-4"
|
|
onSubmit={form.handleSubmit(onSubmit)}
|
|
>
|
|
<FormField
|
|
key="email"
|
|
name="email"
|
|
control={form.control}
|
|
render={({ field }) => (
|
|
<FormItemLayout label="Email">
|
|
<FormControl>
|
|
<Input type="email" autoComplete="email" {...field} placeholder="gavin@hooli.com" />
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
|
|
<div className="self-center">
|
|
<HCaptcha
|
|
ref={captchaRef}
|
|
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
|
|
size="invisible"
|
|
onVerify={(token) => {
|
|
setCaptchaToken(token)
|
|
}}
|
|
onExpire={() => {
|
|
setCaptchaToken(null)
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<Button block form={formId} type="submit" size="large" loading={isSubmitting}>
|
|
Sign in
|
|
</Button>
|
|
</form>
|
|
</Form>
|
|
)
|
|
}
|
|
|
|
export const SignInWithSSOButton = () => {
|
|
const router = useRouter()
|
|
|
|
return (
|
|
<LastSignInWrapper type="sso">
|
|
<Button asChild block size="large" variant="outline" icon={<Lock />}>
|
|
<Link href={{ pathname: '/sign-in-sso', query: router.query }}>Continue with SSO</Link>
|
|
</Button>
|
|
</LastSignInWrapper>
|
|
)
|
|
}
|