mirror of
https://github.com/supabase/supabase.git
synced 2026-10-19 16:19:10 +03:00
Refactor org general settings to use new form component
This commit is contained in:
1 parent
738dcb6e37
commit
fed82591c3
7 files changed
+142
-96
No files matched your search
@@ -1,75 +1,117 @@
|
||||
import { useContext } from 'react'
|
||||
import { toJS } from 'mobx'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { AutoField } from 'uniforms-bootstrap4'
|
||||
import { Form, Input } from '@supabase/ui'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { checkPermissions } from 'hooks/queries/usePermissionsQuery'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { patch } from 'lib/common/fetch'
|
||||
import { checkPermissions } from 'lib/common/permissions'
|
||||
import { pluckJsonSchemaFields, pluckObjectFields } from 'lib/helpers'
|
||||
import { organizations } from 'stores/jsonSchema'
|
||||
|
||||
import OrganizationDeletePanel from './OrganizationDeletePanel'
|
||||
import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel'
|
||||
import {
|
||||
FormActions,
|
||||
FormPanel,
|
||||
FormSection,
|
||||
FormSectionContent,
|
||||
FormSectionLabel,
|
||||
} from 'components/ui/Forms'
|
||||
|
||||
import { PageContext } from 'pages/org/[slug]/settings'
|
||||
|
||||
const GeneralSettings = observer(() => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
const { ui } = useStore()
|
||||
const PageState: any = useContext(PageContext)
|
||||
|
||||
const formId = 'org-general-settings'
|
||||
const initialValues = {
|
||||
name: PageState.organization.name,
|
||||
billing_email: PageState.organization?.billing_email ?? '',
|
||||
}
|
||||
|
||||
const canUpdateOrganization = checkPermissions(
|
||||
PermissionAction.SQL_UPDATE,
|
||||
'postgres.public.organizations'
|
||||
)
|
||||
|
||||
const formModel = toJS(PageState.organization)
|
||||
// remove warning null value for controlled input
|
||||
if (!formModel.billing_email) formModel.billing_email = ''
|
||||
const BASIC_FIELDS = ['name', 'billing_email']
|
||||
const onUpdateOrganization = async (values: any, { setSubmitting, resetForm }: any) => {
|
||||
if (!canUpdateOrganization) {
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message: 'You do not have sufficient permissions to update your organization settings',
|
||||
})
|
||||
}
|
||||
|
||||
const handleUpdateOrg = async (model: any) => {
|
||||
const response = await patch(`${API_URL}/organizations/${PageState.organization.slug}`, model)
|
||||
setSubmitting(true)
|
||||
const response = await patch(`${API_URL}/organizations/${PageState.organization.slug}`, values)
|
||||
if (response.error) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to update organization: ${response.error.message}`,
|
||||
})
|
||||
} else {
|
||||
const updatedOrg = response
|
||||
PageState.onOrgUpdated(updatedOrg)
|
||||
const { name, billing_email } = response
|
||||
resetForm({
|
||||
values: { name, billing_email },
|
||||
initialValues: { name, billing_email },
|
||||
})
|
||||
|
||||
PageState.onOrgUpdated(response)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: 'Successfully saved settings',
|
||||
})
|
||||
}
|
||||
setSubmitting(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="container my-4 max-w-4xl space-y-8">
|
||||
<SchemaFormPanel
|
||||
title="General"
|
||||
schema={pluckJsonSchemaFields(organizations, BASIC_FIELDS)}
|
||||
model={formModel}
|
||||
onSubmit={(model: any) => handleUpdateOrg(pluckObjectFields(model, BASIC_FIELDS))}
|
||||
>
|
||||
<AutoField
|
||||
className="auto-field"
|
||||
name="name"
|
||||
showInlineError
|
||||
errorMessage="Please enter an organization name"
|
||||
disabled={!canUpdateOrganization}
|
||||
/>
|
||||
<AutoField
|
||||
name="billing_email"
|
||||
showInlineError
|
||||
errorMessage="Please enter an email address"
|
||||
disabled={!canUpdateOrganization}
|
||||
/>
|
||||
</SchemaFormPanel>
|
||||
<div className="container my-4 max-w-4xl space-y-8">
|
||||
<Form id={formId} initialValues={initialValues} onSubmit={onUpdateOrganization}>
|
||||
{({ isSubmitting, handleReset, resetForm, values, initialValues }: any) => {
|
||||
const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues)
|
||||
return (
|
||||
<FormPanel
|
||||
disabled
|
||||
footer={
|
||||
<div className="flex justify-between py-4 px-8">
|
||||
{!canUpdateOrganization && (
|
||||
<p className="w-4/5 text-sm text-scale-1000">
|
||||
You need additional permissions to manage your organization's settings
|
||||
</p>
|
||||
)}
|
||||
<FormActions
|
||||
form={formId}
|
||||
isSubmitting={isSubmitting}
|
||||
hasChanges={hasChanges}
|
||||
handleReset={handleReset}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<FormSection header={<FormSectionLabel>General settings</FormSectionLabel>}>
|
||||
<FormSectionContent loading={false}>
|
||||
<Input
|
||||
id="name"
|
||||
size="small"
|
||||
label="Organization name"
|
||||
disabled={!canUpdateOrganization}
|
||||
/>
|
||||
<Input
|
||||
id="billing_email"
|
||||
size="small"
|
||||
label="Billing email"
|
||||
disabled={!canUpdateOrganization}
|
||||
/>
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
</FormPanel>
|
||||
)
|
||||
}}
|
||||
</Form>
|
||||
|
||||
<OrganizationDeletePanel />
|
||||
</article>
|
||||
{canUpdateOrganization && <OrganizationDeletePanel />}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -5,12 +5,7 @@ import { Alert } from '@supabase/ui'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import DeleteOrganizationButton from './DeleteOrganizationButton'
|
||||
|
||||
import { PageContext } from 'pages/org/[slug]/settings'
|
||||
|
||||
const OrganizationDeletePanel = observer(() => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
|
||||
if (!PageState.isOrgOwner) return null
|
||||
return (
|
||||
<Panel
|
||||
title={
|
||||
|
||||
@@ -7,7 +7,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { Member } from 'types'
|
||||
import { useStore, useOrganizationDetail, useOrganizationRoles } from 'hooks'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { checkPermissions } from 'lib/common/permissions'
|
||||
import { checkPermissions } from 'hooks/queries/usePermissionsQuery'
|
||||
import { isInviteExpired, getUserDisplayName } from '../Organization.utils'
|
||||
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
@@ -86,6 +86,8 @@ const MembersView = () => {
|
||||
const role = (roles || []).find((role) => role.id === memberRoleId)
|
||||
const memberIsUser = x.primary_email == PageState.user.primary_email
|
||||
|
||||
console.log(x.gotrue_id, x.primary_email, x.role_ids)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table.tr key={i}>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useProfile, useStore } from 'hooks'
|
||||
import { ComponentType, useEffect } from 'react'
|
||||
import { NextRouter, useRouter } from 'next/router'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { usePermissions2 } from 'hooks/queries/usePermissionsQuery'
|
||||
import { usePermissions } from 'hooks/queries/usePermissionsQuery'
|
||||
|
||||
const PLATFORM_ONLY_PAGES = ['storage', 'reports', 'settings']
|
||||
|
||||
@@ -27,7 +27,7 @@ export function withAuth<T>(
|
||||
const returning =
|
||||
app.projects.isInitialized && app.organizations.isInitialized ? 'minimal' : undefined
|
||||
const { profile, isLoading } = useProfile(returning)
|
||||
const { permissions, isLoading: isPermissionLoading } = usePermissions2(returning)
|
||||
const { permissions, isLoading: isPermissionLoading } = usePermissions(returning)
|
||||
|
||||
const isAccessingBlockedPage = !IS_PLATFORM && PLATFORM_ONLY_PAGES.includes(page)
|
||||
const isRedirecting =
|
||||
|
||||
@@ -5,6 +5,8 @@ import { find } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Organization, Project } from 'types'
|
||||
|
||||
// [JOSHEN TODO] TO BE DEPRECATED
|
||||
|
||||
export function usePermissions(action: string, resource: string, data?: object) {
|
||||
if (!IS_PLATFORM) return true
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import useSWR from 'swr'
|
||||
import jsonLogic from 'json-logic-js'
|
||||
import { find } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export function usePermissions2(returning?: 'minimal') {
|
||||
import { useStore } from 'hooks'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL, IS_PLATFORM } from 'lib/constants'
|
||||
import { Organization, Project } from 'types'
|
||||
import { toJS } from 'mobx'
|
||||
|
||||
// [JOSHEN TODO] USE THIS INSTEAD RENAME TO usePermissions
|
||||
|
||||
export function usePermissions(returning?: 'minimal') {
|
||||
let url = `${API_URL}/profile/permissions`
|
||||
|
||||
if (returning) {
|
||||
@@ -21,3 +30,46 @@ export function usePermissions2(returning?: 'minimal') {
|
||||
isError: !!anyError,
|
||||
}
|
||||
}
|
||||
|
||||
export function checkPermissions(action: string, resource: string, data?: object) {
|
||||
if (!IS_PLATFORM) return true
|
||||
|
||||
const { app, ui } = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
let organization_id: number | undefined
|
||||
const { ref, slug } = router.query
|
||||
if (ref) {
|
||||
const project = find(app.projects.list(), { ref }) as Project | undefined
|
||||
organization_id = project?.organization_id
|
||||
} else if (slug) {
|
||||
const organization = find(app.projects.list(), { ref }) as Organization | undefined
|
||||
organization_id = organization?.id
|
||||
}
|
||||
|
||||
// console.log('CheckPermissions', ui.permissions)
|
||||
// console.log(
|
||||
// toJS(ui.permissions).filter((p) => p.resources.indexOf('postgres.public.organizations') >= 0)
|
||||
// )
|
||||
|
||||
return (ui?.permissions ?? [])
|
||||
.filter(
|
||||
(permission: {
|
||||
actions: string[]
|
||||
condition: jsonLogic.RulesLogic
|
||||
organization_id: number
|
||||
resources: string[]
|
||||
}) =>
|
||||
permission.actions.some((act) =>
|
||||
action ? action.match(act.replace('.', '.').replace('%', '.*')) : null
|
||||
) &&
|
||||
permission.resources.some((res) =>
|
||||
resource.match(res.replace('.', '.').replace('%', '.*'))
|
||||
) &&
|
||||
permission.organization_id === ui?.selectedOrganization?.id
|
||||
)
|
||||
.some(
|
||||
({ condition }: { condition: jsonLogic.RulesLogic }) =>
|
||||
condition === null || jsonLogic.apply(condition, data)
|
||||
)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import jsonLogic from 'json-logic-js'
|
||||
import { find } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { Organization, Project } from 'types'
|
||||
|
||||
// [Joshen] Should we be using a hook here?
|
||||
|
||||
export function checkPermissions(action: string, resource: string, data?: object) {
|
||||
if (!IS_PLATFORM) return true
|
||||
|
||||
const { app, ui } = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
let organization_id: number | undefined
|
||||
const { ref, slug } = router.query
|
||||
if (ref) {
|
||||
const project = find(app.projects.list(), { ref }) as Project | undefined
|
||||
organization_id = project?.organization_id
|
||||
} else if (slug) {
|
||||
const organization = find(app.projects.list(), { ref }) as Organization | undefined
|
||||
organization_id = organization?.id
|
||||
}
|
||||
|
||||
return (ui?.permissions ?? [])
|
||||
.filter(
|
||||
(permission: {
|
||||
actions: string[]
|
||||
condition: jsonLogic.RulesLogic
|
||||
organization_id: number
|
||||
resources: string[]
|
||||
}) =>
|
||||
permission.actions.some((act) =>
|
||||
action ? action.match(act.replace('.', '.').replace('%', '.*')) : null
|
||||
) &&
|
||||
permission.resources.some((res) =>
|
||||
resource.match(res.replace('.', '.').replace('%', '.*'))
|
||||
) &&
|
||||
permission.organization_id === ui?.selectedOrganization?.id
|
||||
)
|
||||
.some(
|
||||
({ condition }: { condition: jsonLogic.RulesLogic }) =>
|
||||
condition === null || jsonLogic.apply(condition, data)
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user