Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow admins to pick the default approval organization #4657

Merged
merged 1 commit into from
Feb 21, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 136 additions & 55 deletions client/src/pages/admin/Index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { gql } from "@apollo/client"
import { DEFAULT_PAGE_PROPS, DEFAULT_SEARCH_PROPS } from "actions"
import API from "api"
import { OrganizationOverlayRow } from "components/advancedSelectWidget/AdvancedSelectOverlayRow"
import AdvancedSingleSelect from "components/advancedSelectWidget/AdvancedSingleSelect"
import AppContext from "components/AppContext"
import * as FieldHelper from "components/FieldHelper"
import Fieldset from "components/Fieldset"
Expand All @@ -11,6 +13,8 @@ import {
GENERAL_BANNER_VISIBILITY
} from "components/GeneralBanner"
import Messages from "components/Messages"
import Model from "components/Model"
import NavigationWarning from "components/NavigationWarning"
import {
jumpToTop,
mapPageDispatchersToProps,
Expand All @@ -19,25 +23,16 @@ import {
usePageTitle
} from "components/Page"
import { Field, Form, Formik } from "formik"
import { Organization } from "models"
import moment from "moment"
import RecentActivityTable from "pages/admin/RecentActivityTable"
import React, { useContext, useState } from "react"
import { Button, Col, Container, FormSelect, Row } from "react-bootstrap"
import { connect } from "react-redux"
import { toast } from "react-toastify"
import ORGANIZATIONS_ICON from "resources/organizations.png"
import { v4 as uuidv4 } from "uuid"

const DROPDOWN_FIELDS = [
{
name: GENERAL_BANNER_LEVEL,
options: GENERAL_BANNER_LEVELS
},
{
name: GENERAL_BANNER_VISIBILITY,
options: GENERAL_BANNER_VISIBILITIES
}
]

const GQL_GET_ADMIN_SETTINGS = gql`
query {
adminSettings {
Expand Down Expand Up @@ -90,6 +85,99 @@ const RECENT_ACTIVITIES = gql`
}
}
`

function convertFormToValue(key, value) {
return SPECIAL_FIELDS[key]?.convertFormToValue
? SPECIAL_FIELDS[key].convertFormToValue(value)
: value
}

function convertValueToForm(key, value) {
return SPECIAL_FIELDS[key]?.convertValueToForm
? SPECIAL_FIELDS[key].convertValueToForm(value)
: value
}

const SPECIAL_FIELDS = {
DEFAULT_APPROVAL_ORGANIZATION: {
widget: key => (
<AdvancedSingleSelect
fieldName={key}
overlayColumns={["Name"]}
overlayRenderRow={OrganizationOverlayRow}
filterDefs={{
allAdvisorOrganizations: {
label: "All advisor organizations",
queryVars: {
status: Model.STATUS.ACTIVE,
type: Organization.TYPE.ADVISOR_ORG
}
}
}}
objectType={Organization}
fields={Organization.autocompleteQuery}
valueKey="uuid"
addon={ORGANIZATIONS_ICON}
keepSearchText
/>
),
convertValueToForm: value => ({
uuid: value
}),
convertFormToValue: value => value?.uuid,
onChange: (key, value, setFieldTouched, setFieldValue) => {
// Make sure we keep only the uuid
const plainValue = convertFormToValue(key, value)
const formValue = convertValueToForm(key, plainValue)
// validation will be done by setFieldValue
setFieldTouched(key, true, false) // onBlur doesn't work when selecting an option
setFieldValue(key, formValue)
}
},
[GENERAL_BANNER_LEVEL]: {
widget: () => (
<FormSelect className="form-control">
{Object.values(GENERAL_BANNER_LEVELS).map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</FormSelect>
)
},
[GENERAL_BANNER_VISIBILITY]: {
widget: () => (
<FormSelect className="form-control">
{Object.values(GENERAL_BANNER_VISIBILITIES).map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</FormSelect>
)
}
}

function getFieldProps(key, value, setFieldTouched, setFieldValue) {
let fieldProps = {
key,
name: key,
component: FieldHelper.InputField
}
if (SPECIAL_FIELDS[key]) {
fieldProps = {
...fieldProps,
component: FieldHelper.SpecialField,
widget: SPECIAL_FIELDS[key].widget(key, value),
...(SPECIAL_FIELDS[key]?.onChange && {
onChange: v =>
SPECIAL_FIELDS[key].onChange(key, v, setFieldTouched, setFieldValue)
})
}
}
return fieldProps
}

const AdminIndex = ({ pageDispatchers }) => {
const { loadAppData } = useContext(AppContext)
const [recentActivities, setRecentActivities] = useState(null)
Expand All @@ -114,7 +202,10 @@ const AdminIndex = ({ pageDispatchers }) => {
}

const settings = {}
data.adminSettings.forEach(setting => (settings[setting.key] = setting.value))
data.adminSettings.forEach(
setting =>
(settings[setting.key] = convertValueToForm(setting.key, setting.value))
)

const recentActivitiesActionButton = (
<Button
Expand All @@ -133,9 +224,15 @@ const AdminIndex = ({ pageDispatchers }) => {

return (
<div>
<Messages success={saveSuccess} error={saveError} />
<Formik enableReinitialize onSubmit={onSubmit} initialValues={settings}>
{({ values, isSubmitting, submitForm }) => {
{({
isSubmitting,
dirty,
setFieldValue,
setFieldTouched,
values,
submitForm
}) => {
const action = (
<Button
variant="primary"
Expand All @@ -146,47 +243,28 @@ const AdminIndex = ({ pageDispatchers }) => {
</Button>
)
return (
<Form className="form-horizontal" method="post">
<Fieldset title="Site settings" action={action} />
<Fieldset>
{Object.map(settings, (key, value) => {
const dropdownField = DROPDOWN_FIELDS.find(
field => field.name === key
)
if (dropdownField) {
return (
<Field
name={key}
key={key}
component={FieldHelper.SpecialField}
widget={
<FormSelect className="form-control">
{Object.values(dropdownField.options).map(
option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
)
)}
</FormSelect>
}
/>
<>
<NavigationWarning isBlocking={dirty && !isSubmitting} />
<Messages success={saveSuccess} error={saveError} />
<Form className="form-horizontal" method="post">
<Fieldset title="Site settings" action={action} />
<Fieldset>
{Object.map(values, (key, value) => {
const fieldProps = getFieldProps(
key,
value,
setFieldTouched,
setFieldValue
)
}
return (
<Field
key={key}
name={key}
component={FieldHelper.InputField}
/>
)
})}
</Fieldset>
<div className="submit-buttons">
<div />
<div>{action}</div>
</div>
</Form>
return <Field {...fieldProps} />
})}
</Fieldset>
<div className="submit-buttons">
<div />
<div>{action}</div>
</div>
</Form>
</>
)
}}
</Formik>
Expand Down Expand Up @@ -258,7 +336,10 @@ const AdminIndex = ({ pageDispatchers }) => {

function onSubmit(values, form) {
// settings as JSON
const settings = Object.map(values, (key, value) => ({ key, value }))
const settings = Object.map(values, (key, value) => ({
key,
value: convertFormToValue(key, value)
}))
return API.mutation(GQL_SAVE_ADMIN_SETTINGS, { settings })
.then(response => onSubmitSuccess(response, values, form))
.catch(error => {
Expand Down
Loading