feat(frontend): initial Settings design

This commit is contained in:
sct
2020-10-30 07:30:38 +00:00
parent 6754003b62
commit 8742da0ebb
8 changed files with 216 additions and 1 deletions

View File

@@ -0,0 +1,38 @@
import React, { useEffect } from 'react';
import useClipboard from 'react-use-clipboard';
import { useToasts } from 'react-toast-notifications';
const CopyButton: React.FC<{ textToCopy: string }> = ({ textToCopy }) => {
const [isCopied, setCopied] = useClipboard(textToCopy, {
successDuration: 1000,
});
const { addToast } = useToasts();
useEffect(() => {
if (isCopied) {
addToast('Copied API key to clipboard', {
appearance: 'info',
autoDismiss: true,
});
}
}, [isCopied, addToast]);
return (
<button
onClick={setCopied}
className="-ml-px relative inline-flex items-center px-4 py-2 border border-cool-gray-500 text-sm leading-5 font-medium text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"
>
<svg
className="w-5 h-5 text-white"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M8 2a1 1 0 000 2h2a1 1 0 100-2H8z" />
<path d="M3 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v6h-4.586l1.293-1.293a1 1 0 00-1.414-1.414l-3 3a1 1 0 000 1.414l3 3a1 1 0 001.414-1.414L10.414 13H15v3a2 2 0 01-2 2H5a2 2 0 01-2-2V5zM15 11h2a1 1 0 110 2h-2v-2z" />
</svg>
</button>
);
};
export default CopyButton;

View File

@@ -0,0 +1,66 @@
import React from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
const SettingsLayout: React.FC = ({ children }) => {
const router = useRouter();
const activeLinkColor =
'border-indigo-600 text-indigo-500 focus:outline-none focus:text-indigo-500 focus:border-indigo-500';
const inactiveLinkColor =
'border-transparent text-cool-gray-500 hover:text-cool-gray-400 hover:border-cool-gray-300 focus:outline-none focus:text-cool-gray-4700 focus:border-cool-gray-300';
const settingsLink = ({
text,
route,
regex,
}: {
text: string;
route: string;
regex: RegExp;
}) => {
return (
<Link href={route}>
<a
className={`whitespace-no-wrap pb-4 px-1 border-b-2 font-medium text-sm leading-5 ${
!!router.pathname.match(regex) ? activeLinkColor : inactiveLinkColor
}`}
aria-current="page"
>
{text}
</a>
</Link>
);
};
return (
<>
<div className="border-b border-cool-gray-600 mt-10">
<div className="space-y-4 sm:flex sm:items-baseline sm:space-y-0 sm:space-x-10">
<h3 className="text-lg leading-6 font-medium text-white">Settings</h3>
<div>
<nav className="-mb-px flex space-x-8">
{settingsLink({
text: 'General Settings',
route: '/settings/main',
regex: /^\/settings(\/main)?$/,
})}
{settingsLink({
text: 'Plex Settings',
route: '/settings/plex',
regex: /^\/settings\/plex/,
})}
{settingsLink({
text: 'Services',
route: '/settings/services',
regex: /^\/settings\/services/,
})}
</nav>
</div>
</div>
</div>
<div className="mt-10 text-white">{children}</div>
</>
);
};
export default SettingsLayout;

View File

@@ -0,0 +1,63 @@
import React from 'react';
import useSWR from 'swr';
import LoadingSpinner from '../Common/LoadingSpinner';
import type { MainSettings } from '../../../server/lib/settings';
import CopyButton from './CopyButton';
const SettingsMain: React.FC = () => {
const { data, error } = useSWR<MainSettings>('/api/v1/settings/main');
if (!data && !error) {
return <LoadingSpinner />;
}
return (
<>
<div>
<h3 className="text-lg leading-6 font-medium text-cool-gray-200">
General Settings
</h3>
<p className="mt-1 max-w-2xl text-sm leading-5 text-cool-gray-500">
These are settings related to general Overseerr configuration.
</p>
</div>
<div className="mt-6 sm:mt-5">
<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"
className="block text-sm font-medium leading-5 text-cool-gray-400 sm:mt-px sm:pt-2"
>
API Key
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg flex rounded-md shadow-sm">
<input
id="username"
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-cool-gray-700 border border-cool-gray-500"
value={data?.apiKey}
readOnly
/>
<CopyButton textToCopy={data?.apiKey ?? ''} />
<button className="-ml-px relative inline-flex items-center px-4 py-2 border border-cool-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
</>
);
};
export default SettingsMain;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { NextPage } from 'next';
import SettingsLayout from '../../components/Settings/SettingsLayout';
import SettingsMain from '../../components/Settings/SettingsMain';
const SettingsPage: NextPage = () => {
return (
<SettingsLayout>
<SettingsMain />
</SettingsLayout>
);
};
export default SettingsPage;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { NextPage } from 'next';
import SettingsLayout from '../../components/Settings/SettingsLayout';
import SettingsMain from '../../components/Settings/SettingsMain';
const SettingsMainPage: NextPage = () => {
return (
<SettingsLayout>
<SettingsMain />
</SettingsLayout>
);
};
export default SettingsMainPage;