Refactor org general settings to use new form component

This commit is contained in:
Joshen Lim committed 2022-07-22 15:46:22 +08:00
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 -2
View File
@@ -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 =
+2
View File
@@ -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
+55 -3
View File
@@ -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)
)
}
-47
View File
@@ -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)
)
}