@ -9,6 +9,8 @@ import Button from '../Common/Button';
import { defineMessages , useIntl } from 'react-intl' ;
import { useUser , Permission } from '../../hooks/useUser' ;
import { useToasts } from 'react-toast-notifications' ;
import { messages as permissionMessages } from '../UserEdit' ;
import { hasPermission } from '../../../server/lib/permissions' ;
const messages = defineMessages ( {
generalsettings : 'General Settings' ,
@ -22,11 +24,19 @@ const messages = defineMessages({
toastApiKeyFailure : 'Something went wrong generating a new API Key.' ,
toastSettingsSuccess : 'Settings saved.' ,
toastSettingsFailure : 'Something went wrong saving settings.' ,
defaultPermissions : 'Default User Permissions' ,
} ) ;
interface PermissionOption {
id : string ;
name : string ;
description : string ;
permission : Permission ;
}
const SettingsMain : React.FC = ( ) = > {
const { addToast } = useToasts ( ) ;
const { hasPermission } = useUser ( ) ;
const { hasPermission : userHasPermission } = useUser ( ) ;
const intl = useIntl ( ) ;
const { data , error , revalidate } = useSWR < MainSettings > (
'/api/v1/settings/main'
@ -53,13 +63,62 @@ const SettingsMain: React.FC = () => {
return < LoadingSpinner / > ;
}
const permissionList : PermissionOption [ ] = [
{
id : 'admin' ,
name : intl.formatMessage ( permissionMessages . admin ) ,
description : intl.formatMessage ( permissionMessages . adminDescription ) ,
permission : Permission.ADMIN ,
} ,
{
id : 'settings' ,
name : intl.formatMessage ( permissionMessages . settings ) ,
description : intl.formatMessage ( permissionMessages . settingsDescription ) ,
permission : Permission.MANAGE_SETTINGS ,
} ,
{
id : 'users' ,
name : intl.formatMessage ( permissionMessages . users ) ,
description : intl.formatMessage ( permissionMessages . usersDescription ) ,
permission : Permission.MANAGE_USERS ,
} ,
{
id : 'managerequest' ,
name : intl.formatMessage ( permissionMessages . managerequests ) ,
description : intl.formatMessage (
permissionMessages . managerequestsDescription
) ,
permission : Permission.MANAGE_REQUESTS ,
} ,
{
id : 'request' ,
name : intl.formatMessage ( permissionMessages . request ) ,
description : intl.formatMessage ( permissionMessages . requestDescription ) ,
permission : Permission.REQUEST ,
} ,
{
id : 'vote' ,
name : intl.formatMessage ( permissionMessages . vote ) ,
description : intl.formatMessage ( permissionMessages . voteDescription ) ,
permission : Permission.VOTE ,
} ,
{
id : 'autoapprove' ,
name : intl.formatMessage ( permissionMessages . autoapprove ) ,
description : intl.formatMessage (
permissionMessages . autoapproveDescription
) ,
permission : Permission.AUTO_APPROVE ,
} ,
] ;
return (
< >
< div >
< h3 className = "text-lg leading-6 font-medium text-gray-200" >
< h3 className = "text-lg font-medium leading-6 text-gray-200">
{ intl . formatMessage ( messages . generalsettings ) }
< / h3 >
< p className = "mt-1 max-w-2xl text-sm leading-5 text-gray-500" >
< p className = "m ax-w-2xl mt-1 text-sm leading-5 text-gray-500">
{ intl . formatMessage ( messages . generalsettingsDescription ) }
< / p >
< / div >
@ -67,11 +126,14 @@ const SettingsMain: React.FC = () => {
< Formik
initialValues = { {
applicationUrl : data?.applicationUrl ,
defaultPermissions : data?.defaultPermissions ? ? 0 ,
} }
enableReinitialize
onSubmit = { async ( values ) = > {
try {
await axios . post ( '/api/v1/settings/main' , {
applicationUrl : values.applicationUrl ,
defaultPermissions : values.defaultPermissions ,
} ) ;
addToast ( intl . formatMessage ( messages . toastSettingsSuccess ) , {
@ -88,10 +150,10 @@ const SettingsMain: React.FC = () => {
}
} }
>
{ ( { isSubmitting } ) = > {
{ ( { isSubmitting , values , setFieldValue } ) = > {
return (
< Form >
{ h asPermission( Permission . ADMIN ) && (
{ userH asPermission( Permission . ADMIN ) && (
< div className = "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5" >
< label
htmlFor = "username"
@ -100,11 +162,11 @@ const SettingsMain: React.FC = () => {
{ intl . formatMessage ( messages . apikey ) }
< / label >
< div className = "mt-1 sm:mt-0 sm:col-span-2" >
< div className = " max-w-lg flex rounded-md shadow-sm">
< div className = " flex max-w-lg rounded-md shadow-sm">
< input
type = "text"
id = "apiKey"
className = "flex-1 form-input block w-full min-w-0 rounded-none rounded-l-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
className = "flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-none form-input rounded-l-md sm:text-sm sm:leading-5 "
value = { data ? . apiKey }
readOnly
/ >
@ -117,7 +179,7 @@ const SettingsMain: React.FC = () => {
e . preventDefault ( ) ;
regenerate ( ) ;
} }
className = " -ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-15 0"
className = " relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium leading-5 text-white transition duration-150 ease-in-out bg-indigo-500 border border-gray-500 rounded-r-md hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700"
>
< svg
className = "w-5 h-5"
@ -144,20 +206,98 @@ const SettingsMain: React.FC = () => {
{ intl . formatMessage ( messages . applicationurl ) }
< / label >
< div className = "mt-1 sm:mt-0 sm:col-span-2" >
< div className = " max-w-lg flex rounded-md shadow-sm">
< div className = " flex max-w-lg rounded-md shadow-sm">
< Field
id = "applicationUrl"
name = "applicationUrl"
type = "text"
placeholder = "https://os.example.com"
className = "flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
className = "flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5 "
/ >
< / div >
< / div >
< / div >
< div className = "mt-8 border-t border-gray-700 pt-5" >
< div className = "mt-6" >
< div role = "group" aria - labelledby = "label-permissions" >
< div className = "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline" >
< div >
< div
className = "text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
id = "label-permissions"
>
{ intl . formatMessage ( messages . defaultPermissions ) }
< / div >
< / div >
< div className = "mt-4 sm:mt-0 sm:col-span-2" >
< div className = "max-w-lg" >
{ permissionList . map ( ( permissionOption ) = > (
< div
className = { ` relative flex items-start first:mt-0 mt-4 ${
permissionOption . permission !==
Permission . ADMIN &&
hasPermission (
Permission . ADMIN ,
values . defaultPermissions
)
? 'opacity-50'
: ''
} ` }
key = { ` permission-option- ${ permissionOption . id } ` }
>
< div className = "flex items-center h-5" >
< input
id = { permissionOption . id }
name = "permissions"
type = "checkbox"
className = "w-4 h-4 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
disabled = {
permissionOption . permission !==
Permission . ADMIN &&
hasPermission (
Permission . ADMIN ,
values . defaultPermissions
)
}
onClick = { ( ) = > {
setFieldValue (
'defaultPermissions' ,
hasPermission (
permissionOption . permission ,
values . defaultPermissions
)
? values . defaultPermissions -
permissionOption . permission
: values . defaultPermissions +
permissionOption . permission
) ;
} }
checked = { hasPermission (
permissionOption . permission ,
values . defaultPermissions
) }
/ >
< / div >
< div className = "ml-3 text-sm leading-5" >
< label
htmlFor = { permissionOption . id }
className = "font-medium"
>
{ permissionOption . name }
< / label >
< p className = "text-gray-500" >
{ permissionOption . description }
< / p >
< / div >
< / div >
) ) }
< / div >
< / div >
< / div >
< / div >
< / div >
< div className = "pt-5 mt-8 border-t border-gray-700" >
< div className = "flex justify-end" >
< span className = "ml-3 inline-flex rounded-md shadow-sm" >
< span className = " inline-flex ml-3 rounded-md shadow-sm">
< Button
buttonType = "primary"
type = "submit"