mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
## Context Resolves FE-2985 As per PR title - Add project region info in project settings page for convenience <img width="722" height="375" alt="image" src="https://github.com/user-attachments/assets/b32e80ed-42bd-4b12-b9b4-a3e696646335" /> - Add project region info in vector buckets empty state <img width="1110" height="215" alt="image" src="https://github.com/user-attachments/assets/60bfde97-c3e3-4c10-8b86-98ecd0437ad5" /> - Make DB region copyable by clicking in instance config chart on home page <img width="419" height="298" alt="image" src="https://github.com/user-attachments/assets/269b9517-d0eb-42b9-9648-386c59d53842" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Project region is now shown as a read-only field with a descriptive region label in Settings. * Region identifiers are clickable to copy to clipboard, with a “Click to copy” tooltip and success toast. * Storage/empty-state messaging updated to show clearer, region-specific text and tooltip details. * Replica creation time now uses an enhanced timestamp display. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
194 lines
6.5 KiB
TypeScript
194 lines
6.5 KiB
TypeScript
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
|
import { useForm } from 'react-hook-form'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
CardFooter,
|
|
Form,
|
|
FormControl,
|
|
FormField,
|
|
Input_Shadcn_,
|
|
} from 'ui'
|
|
import { Admonition } from 'ui-patterns'
|
|
import { Input } from 'ui-patterns/DataInputs/Input'
|
|
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
|
import {
|
|
PageSection,
|
|
PageSectionContent,
|
|
PageSectionMeta,
|
|
PageSectionSummary,
|
|
PageSectionTitle,
|
|
} from 'ui-patterns/PageSection'
|
|
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
|
import * as z from 'zod'
|
|
|
|
import { AVAILABLE_REPLICA_REGIONS } from '../Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants'
|
|
import { ProjectAccessSection } from './ProjectAccessSection'
|
|
import { InlineLink } from '@/components/ui/InlineLink'
|
|
import { useProjectUpdateMutation } from '@/data/projects/project-update-mutation'
|
|
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
|
|
export const General = () => {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const isBranch = Boolean(project?.parent_project_ref)
|
|
|
|
const { can: canUpdateProject } = useAsyncCheckPermissions(PermissionAction.UPDATE, 'projects', {
|
|
resource: {
|
|
project_id: project?.id,
|
|
},
|
|
})
|
|
|
|
const { mutate: updateProject, isPending: isUpdating } = useProjectUpdateMutation()
|
|
|
|
const formSchema = z.object({
|
|
name: z.string().trim().min(3, 'Project name must be at least 3 characters long'),
|
|
})
|
|
|
|
const defaultValues = { name: project?.name ?? '' }
|
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues,
|
|
values: defaultValues,
|
|
mode: 'onSubmit',
|
|
reValidateMode: 'onBlur',
|
|
})
|
|
|
|
const regionLabel = AVAILABLE_REPLICA_REGIONS.find((region) =>
|
|
project?.region?.includes(region.region)
|
|
)
|
|
|
|
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
|
if (!project?.ref) return console.error('Ref is required')
|
|
|
|
updateProject(
|
|
{ ref: project.ref, name: values.name.trim() },
|
|
{
|
|
onSuccess: ({ name }) => {
|
|
form.reset({ name })
|
|
toast.success('Successfully saved settings')
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<PageSection>
|
|
<PageSectionMeta>
|
|
<PageSectionSummary>
|
|
<PageSectionTitle>General settings</PageSectionTitle>
|
|
</PageSectionSummary>
|
|
</PageSectionMeta>
|
|
<PageSectionContent>
|
|
{isBranch && (
|
|
<Admonition
|
|
type="default"
|
|
className="mb-4"
|
|
title="You are currently on a preview branch of your project"
|
|
>
|
|
Certain settings are not available while you're on a preview branch. To adjust your
|
|
project settings, you may return to your{' '}
|
|
<InlineLink href={`/project/${project?.parent_project_ref}/settings/general`}>
|
|
main branch
|
|
</InlineLink>
|
|
.
|
|
</Admonition>
|
|
)}
|
|
|
|
{project === undefined ? (
|
|
<Card>
|
|
<CardContent>
|
|
<GenericSkeletonLoader />
|
|
</CardContent>
|
|
</Card>
|
|
) : (
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
<Card>
|
|
<CardContent>
|
|
<FormField
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItemLayout
|
|
layout="flex-row-reverse"
|
|
label="Project name"
|
|
description="Displayed throughout the dashboard."
|
|
className="[&>div]:md:w-1/2"
|
|
>
|
|
<FormControl>
|
|
<Input_Shadcn_
|
|
{...field}
|
|
disabled={isBranch || !canUpdateProject}
|
|
autoComplete="off"
|
|
/>
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
)}
|
|
/>
|
|
</CardContent>
|
|
|
|
<CardContent>
|
|
<FormItemLayout
|
|
layout="flex-row-reverse"
|
|
label="Project ID"
|
|
description="Reference used in APIs and URLs."
|
|
className="[&>div]:md:w-1/2 [&>div>div]:md:w-full"
|
|
>
|
|
<FormControl>
|
|
<Input copy readOnly size="small" value={project.ref} />
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
</CardContent>
|
|
|
|
<CardContent>
|
|
<FormItemLayout
|
|
layout="flex-row-reverse"
|
|
label="Project region"
|
|
description={regionLabel?.name}
|
|
className="[&>div]:md:w-1/2 [&>div>div]:md:w-full"
|
|
>
|
|
<FormControl>
|
|
<Input copy readOnly size="small" value={project.region} />
|
|
</FormControl>
|
|
</FormItemLayout>
|
|
</CardContent>
|
|
|
|
<CardFooter className="justify-end space-x-2">
|
|
{form.formState.isDirty && (
|
|
<Button
|
|
type="default"
|
|
htmlType="button"
|
|
disabled={isUpdating}
|
|
onClick={() => form.reset({ name: project?.name ?? '' })}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
)}
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
disabled={
|
|
!form.formState.isDirty || isUpdating || !canUpdateProject || isBranch
|
|
}
|
|
loading={isUpdating}
|
|
>
|
|
Save changes
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</form>
|
|
</Form>
|
|
)}
|
|
</PageSectionContent>
|
|
</PageSection>
|
|
|
|
<ProjectAccessSection />
|
|
</>
|
|
)
|
|
}
|