mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 06:39:09 +03:00
* improve some studio mobile layouts * improve some studio mobile layouts * improve settings * assistant mobile * assistant mobile * assistant mobile * responsive formlayout and new project layout * improve dashboard pages headers * improve dashboard auth pages * mobile org settings * mobile billing fixes * adjust paused project container height * remove comments * triggers * leftovers * ai assistant * fix errors * remove 16px input size * fix test * merge access tokens settings page conflicsts * smol integrations here and there
141 lines
5.8 KiB
TypeScript
141 lines
5.8 KiB
TypeScript
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import { useEffect } from 'react'
|
|
import { toast } from 'sonner'
|
|
|
|
import { useParams } from 'common'
|
|
import { NoProjectsOnPaidOrgInfo } from 'components/interfaces/Billing/NoProjectsOnPaidOrgInfo'
|
|
import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold'
|
|
import { FormActions } from 'components/ui/Forms/FormActions'
|
|
import { FormPanel } from 'components/ui/Forms/FormPanel'
|
|
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
|
|
import { useOrganizationUpdateMutation } from 'data/organizations/organization-update-mutation'
|
|
import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query'
|
|
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
|
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
|
import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi'
|
|
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
|
import { OPT_IN_TAGS } from 'lib/constants'
|
|
import { Form, Input, Toggle } from 'ui'
|
|
import OptInToOpenAIToggle from './OptInToOpenAIToggle'
|
|
import OrganizationDeletePanel from './OrganizationDeletePanel'
|
|
|
|
const GeneralSettings = () => {
|
|
const { slug } = useParams()
|
|
const queryClient = useQueryClient()
|
|
const selectedOrganization = useSelectedOrganization()
|
|
const { name } = selectedOrganization ?? {}
|
|
|
|
const formId = 'org-general-settings'
|
|
const isOptedIntoAi = useOrgOptedIntoAi()
|
|
const initialValues = { name: name ?? '', isOptedIntoAi }
|
|
|
|
const organizationDeletionEnabled = useIsFeatureEnabled('organizations:delete')
|
|
|
|
const canUpdateOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
|
const canDeleteOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
|
const { mutate: updateOrganization, isLoading: isUpdating } = useOrganizationUpdateMutation()
|
|
|
|
const onUpdateOrganization = async (values: any, { resetForm }: any) => {
|
|
if (!canUpdateOrganization) {
|
|
return toast.error('You do not have the required permissions to update this organization')
|
|
}
|
|
|
|
if (!slug) return console.error('Slug is required')
|
|
|
|
const existingOptInTags = selectedOrganization?.opt_in_tags ?? []
|
|
const updatedOptInTags =
|
|
values.isOptedIntoAi && !existingOptInTags.includes(OPT_IN_TAGS.AI_SQL)
|
|
? existingOptInTags.concat([OPT_IN_TAGS.AI_SQL])
|
|
: !values.isOptedIntoAi && existingOptInTags.includes(OPT_IN_TAGS.AI_SQL)
|
|
? existingOptInTags.filter((x) => x !== OPT_IN_TAGS.AI_SQL)
|
|
: existingOptInTags
|
|
|
|
updateOrganization(
|
|
{ slug, name: values.name, opt_in_tags: updatedOptInTags },
|
|
{
|
|
onSuccess: () => {
|
|
resetForm({ values, initialValues: values })
|
|
invalidateOrganizationsQuery(queryClient)
|
|
toast.success('Successfully saved settings')
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
return (
|
|
<ScaffoldContainerLegacy>
|
|
<NoProjectsOnPaidOrgInfo organization={selectedOrganization} />
|
|
|
|
<Form id={formId} initialValues={initialValues} onSubmit={onUpdateOrganization}>
|
|
{({ handleReset, values, initialValues, resetForm }: any) => {
|
|
const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
|
|
|
|
// [Alaister] although this "technically" is breaking the rules of React hooks
|
|
// it won't error because the hooks are always rendered in the same order
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
useEffect(() => {
|
|
const values = { name: name ?? '', isOptedIntoAi }
|
|
resetForm({ values, initialValues: values })
|
|
}, [selectedOrganization?.slug])
|
|
|
|
return (
|
|
<FormPanel
|
|
footer={
|
|
<div className="flex p-4 md:px-8">
|
|
<FormActions
|
|
form={formId}
|
|
isSubmitting={isUpdating}
|
|
hasChanges={hasChanges}
|
|
handleReset={handleReset}
|
|
helper={
|
|
!canUpdateOrganization
|
|
? "You need additional permissions to manage this organization's settings"
|
|
: undefined
|
|
}
|
|
/>
|
|
</div>
|
|
}
|
|
>
|
|
<FormSection header={<FormSectionLabel>General settings</FormSectionLabel>}>
|
|
<FormSectionContent loading={false}>
|
|
<Input
|
|
id="name"
|
|
size="small"
|
|
label="Organization name"
|
|
disabled={!canUpdateOrganization}
|
|
/>
|
|
<Input
|
|
copy
|
|
disabled
|
|
id="slug"
|
|
size="small"
|
|
label="Organization slug"
|
|
value={selectedOrganization?.slug}
|
|
/>
|
|
<div className="mt-4">
|
|
<Toggle
|
|
id="isOptedIntoAi"
|
|
name="isOptedIntoAi"
|
|
disabled={!canUpdateOrganization}
|
|
size="small"
|
|
label="Opt-in to sending anonymous data to OpenAI"
|
|
descriptionText="By opting into sending anonymous data, Supabase AI can improve the answers it shows you. This is an organization-wide setting."
|
|
/>
|
|
|
|
<OptInToOpenAIToggle className="ml-16" />
|
|
</div>
|
|
</FormSectionContent>
|
|
</FormSection>
|
|
</FormPanel>
|
|
)
|
|
}}
|
|
</Form>
|
|
|
|
{organizationDeletionEnabled && canDeleteOrganization && <OrganizationDeletePanel />}
|
|
</ScaffoldContainerLegacy>
|
|
)
|
|
}
|
|
|
|
export default GeneralSettings
|