mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
@@ -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="max-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>
|
||||
{hasPermission(Permission.ADMIN) && (
|
||||
{userHasPermission(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-150"
|
||||
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"
|
||||
|
Reference in New Issue
Block a user