mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 19:05:06 +03:00
Scoped PAT: apply flex-row-reverse form layout
This commit is contained in:
2 files changed
+127
-104
No files matched your search
+124
-101
@@ -9,6 +9,8 @@ import {
|
||||
Label,
|
||||
RadioGroupCard,
|
||||
RadioGroupCardItem,
|
||||
RadioGroupStacked,
|
||||
RadioGroupStackedItem,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
@@ -109,57 +111,123 @@ export const ResourceAccessStep = ({ control, setValue, error }: ResourceAccessS
|
||||
|
||||
return (
|
||||
<section className="space-y-4 px-5 sm:px-6 py-6">
|
||||
<div>
|
||||
<h3 className="text-sm text-foreground">Resource access</h3>
|
||||
<p className="text-xs text-foreground-light">Choose what this token can reach.</p>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={control}
|
||||
name="resourceAccess"
|
||||
render={({ field }) => (
|
||||
<RadioGroupCard
|
||||
className={cn('grid-cols-2', {
|
||||
'pointer-events-none': isAccount,
|
||||
})}
|
||||
value={isAccount ? undefined : resourceAccess}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value)
|
||||
// Reset dependent selections when switching modes.
|
||||
setValue('projectRefs', [])
|
||||
if (value !== 'account') setValue('accountConfirmed', false)
|
||||
}}
|
||||
disabled={isAccount}
|
||||
>
|
||||
{CARD_OPTIONS.map((option) => (
|
||||
<RadioGroupCardItem
|
||||
key={option.value}
|
||||
id={option.value}
|
||||
value={option.value}
|
||||
className={cn('w-full', isAccount && 'opacity-50')}
|
||||
disabled={isAccount}
|
||||
label={
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-foreground">{option.name}</span>
|
||||
{option.recommended && <Badge variant="success">Recommended</Badge>}
|
||||
</div>
|
||||
<span className="text-foreground-light">{option.description}</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RadioGroupCard>
|
||||
)}
|
||||
/>
|
||||
{isAccount ? (
|
||||
<>
|
||||
<h3 className="text-sm text-foreground">Resource access</h3>
|
||||
<Admonition
|
||||
type="warning"
|
||||
title="Account-level access is broad."
|
||||
description={
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
This token can reach every organization and project you have access to. Prefer a
|
||||
single project or organization unless you specifically need account-wide access.
|
||||
</p>
|
||||
<div className="flex items-start gap-2">
|
||||
<FormField
|
||||
control={control}
|
||||
name="accountConfirmed"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Checkbox
|
||||
id="accountConfirmed"
|
||||
checked={accountConfirmed ?? false}
|
||||
onCheckedChange={(checked) => field.onChange(checked)}
|
||||
/>
|
||||
<Label htmlFor="accountConfirmed" className="text-xs text-foreground-light">
|
||||
I understand this token is not limited to one project or organization.
|
||||
</Label>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs text-foreground-light underline hover:text-foreground transition-colors"
|
||||
onClick={switchBackToSingleProject}
|
||||
tabIndex={0}
|
||||
>
|
||||
Switch back
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<FormField
|
||||
control={control}
|
||||
name="resourceAccess"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label="Resource access"
|
||||
description={
|
||||
!isAccount ? (
|
||||
<p className="text-xs text-foreground-lighter">
|
||||
Need access to every organization and project?{' '}
|
||||
<button
|
||||
type="button"
|
||||
className="text-foreground-light underline hover:text-foreground transition-colors"
|
||||
onClick={enableAccountLevel}
|
||||
tabIndex={0}
|
||||
>
|
||||
Advanced options
|
||||
</button>
|
||||
</p>
|
||||
) : null
|
||||
}
|
||||
id="resourceAccess"
|
||||
>
|
||||
<FormControl>
|
||||
<RadioGroupStacked
|
||||
className={cn({
|
||||
'pointer-events-none': isAccount,
|
||||
})}
|
||||
value={isAccount ? undefined : resourceAccess}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value)
|
||||
// Reset dependent selections when switching modes.
|
||||
setValue('projectRefs', [])
|
||||
if (value !== 'account') setValue('accountConfirmed', false)
|
||||
}}
|
||||
disabled={isAccount}
|
||||
>
|
||||
{CARD_OPTIONS.map((option) => (
|
||||
<RadioGroupStackedItem
|
||||
key={option.value}
|
||||
id={option.value}
|
||||
value={option.value}
|
||||
className={cn('w-full', isAccount && 'opacity-50')}
|
||||
disabled={isAccount}
|
||||
label={
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-foreground">{option.name}</span>
|
||||
{option.recommended && <Badge variant="success">Recommended</Badge>}
|
||||
</div>
|
||||
<span className="text-foreground-light">{option.description}</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RadioGroupStacked>
|
||||
</FormControl>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isAccount && resourceAccess === 'project' ? (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<>
|
||||
<FormField
|
||||
control={control}
|
||||
name="organizationSlugs"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout layout="vertical" label="Organization" id="organizationSlugs">
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label={<span className="sr-only">Organization</span>}
|
||||
id="organizationSlugs"
|
||||
>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value.length > 0 ? field.value[0] : ''}
|
||||
@@ -189,7 +257,11 @@ export const ResourceAccessStep = ({ control, setValue, error }: ResourceAccessS
|
||||
control={control}
|
||||
name="projectRefs"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout layout="vertical" label="Projects" id="projectRefs">
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label={<span className="sr-only">Projects</span>}
|
||||
id="projectRefs"
|
||||
>
|
||||
<MultiSelector
|
||||
onValuesChange={field.onChange}
|
||||
values={field.value}
|
||||
@@ -224,14 +296,18 @@ export const ResourceAccessStep = ({ control, setValue, error }: ResourceAccessS
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{!isAccount && resourceAccess === 'organization' ? (
|
||||
<FormField
|
||||
control={control}
|
||||
name="organizationSlugs"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout layout="vertical" label="Organizations" id="organizationSlugs">
|
||||
<FormItemLayout
|
||||
layout="flex-row-reverse"
|
||||
label={<span className="sr-only">Organization</span>}
|
||||
id="organizationSlugs"
|
||||
>
|
||||
<MultiSelector onValuesChange={field.onChange} values={field.value}>
|
||||
<MultiSelectorTrigger
|
||||
id="organizationSlugs"
|
||||
@@ -261,59 +337,6 @@ export const ResourceAccessStep = ({ control, setValue, error }: ResourceAccessS
|
||||
) : null}
|
||||
|
||||
{error && <p className="text-xs text-destructive">{error}</p>}
|
||||
|
||||
{!isAccount ? (
|
||||
<p className="text-xs text-foreground-lighter">
|
||||
Need access to every organization and project?{' '}
|
||||
<button
|
||||
type="button"
|
||||
className="text-foreground-light underline hover:text-foreground transition-colors"
|
||||
onClick={enableAccountLevel}
|
||||
tabIndex={0}
|
||||
>
|
||||
Advanced options
|
||||
</button>
|
||||
</p>
|
||||
) : (
|
||||
<Admonition
|
||||
type="warning"
|
||||
title="Account-level access is broad."
|
||||
description={
|
||||
<div className="space-y-3">
|
||||
<p>
|
||||
This token can reach every organization and project you have access to. Prefer a
|
||||
single project or organization unless you specifically need account-wide access.
|
||||
</p>
|
||||
<div className="flex items-start gap-2">
|
||||
<FormField
|
||||
control={control}
|
||||
name="accountConfirmed"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Checkbox
|
||||
id="accountConfirmed"
|
||||
checked={accountConfirmed ?? false}
|
||||
onCheckedChange={(checked) => field.onChange(checked)}
|
||||
/>
|
||||
<Label htmlFor="accountConfirmed" className="text-xs text-foreground-light">
|
||||
I understand this token is not limited to one project or organization.
|
||||
</Label>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs text-foreground-light underline hover:text-foreground transition-colors"
|
||||
onClick={switchBackToSingleProject}
|
||||
tabIndex={0}
|
||||
>
|
||||
Switch back
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -50,14 +50,14 @@ export const TokenDetails = ({ control, setValue }: TokenDetailsProps) => {
|
||||
|
||||
return (
|
||||
<section className="space-y-4 px-5 sm:px-6 py-6">
|
||||
<h3 className="text-sm text-foreground">Token details</h3>
|
||||
<h3 className="text-sm text-foreground sr-only">Token details</h3>
|
||||
|
||||
<FormField
|
||||
key="tokenName"
|
||||
name="tokenName"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormItemLayout name="tokenName" label="Name">
|
||||
<FormItemLayout name="tokenName" label="Name" layout="flex-row-reverse">
|
||||
<FormControl>
|
||||
<Input id="tokenName" {...field} placeholder="e.g. CI deploy token" />
|
||||
</FormControl>
|
||||
@@ -70,7 +70,7 @@ export const TokenDetails = ({ control, setValue }: TokenDetailsProps) => {
|
||||
name="expiresAt"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormItemLayout name="expiresAt" label="Expires in">
|
||||
<FormItemLayout name="expiresAt" label="Expires in" layout="flex-row-reverse">
|
||||
<div className="flex gap-2">
|
||||
<FormControl className="grow">
|
||||
<Select value={field.value} onValueChange={handleExpiryChange}>
|
||||
|
||||
Reference in new issue
Block a user