[Design/Routes] Welcome Screen / Initial Setup (#42)

* feat(new component): welcome screen and initial setup component

* feat(frontend): setup with login, settings, radarr/sonarr

* feat(frontend): add login functionality to login step for setup

Co-authored-by: Alexander Zoitos <azoitos1@gmail.com>
pull/175/head
Jeff Bentley 4 years ago committed by GitHub
parent 06dc606bcf
commit 366074c12a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,8 +18,8 @@ const PlexLoginButton: React.FC<PlexLoginButtonProps> = ({
setLoading(true);
try {
const authToken = await plexOAuth.login();
onAuthToken(authToken);
setLoading(false);
onAuthToken(authToken);
} catch (e) {
if (onError) {
onError(e.message);

@ -48,7 +48,11 @@ interface SyncStatus {
libraries: Library[];
}
const SettingsPlex: React.FC = () => {
interface SettingsPlexProps {
onComplete?: () => void;
}
const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
const intl = useIntl();
const { data, error, revalidate } = useSWR<PlexSettings>(
'/api/v1/settings/plex'
@ -78,6 +82,9 @@ const SettingsPlex: React.FC = () => {
} as PlexSettings);
revalidate();
if (onComplete) {
onComplete();
}
} catch (e) {
setSubmitError(e.response.data.message);
} finally {

@ -0,0 +1,54 @@
import React, { useEffect, useState } from 'react';
import { useUser } from '../../hooks/useUser';
import PlexLoginButton from '../PlexLoginButton';
import axios from 'axios';
interface LoginWithPlexProps {
onComplete: () => void;
}
const LoginWithPlex: React.FC<LoginWithPlexProps> = ({ onComplete }) => {
const [authToken, setAuthToken] = useState<string | undefined>(undefined);
const { user, revalidate } = useUser();
// Effect that is triggered when the `authToken` comes back from the Plex OAuth
// We take the token and attempt to login. If we get a success message, we will
// ask swr to revalidate the user which _shouid_ come back with a valid user.
useEffect(() => {
const login = async () => {
const response = await axios.post('/api/v1/auth/login', { authToken });
if (response.data?.email) {
revalidate();
}
};
if (authToken) {
login();
}
}, [authToken, revalidate]);
// Effect that is triggered whenever `useUser`'s user changes. If we get a new
// valid user, we call onComplete which will take us to the next step in Setup.
useEffect(() => {
if (user) {
onComplete();
}
}, [user, onComplete]);
return (
<form>
<div className="flex justify-center font-bold text-xl mb-2">
Welcome to Overseerr
</div>
<div className="flex justify-center text-sm pb-6 mb-2">
Get started by logging in with your Plex account
</div>
<div className="flex items-center justify-center">
<PlexLoginButton onAuthToken={(authToken) => setAuthToken(authToken)} />
</div>
</form>
);
};
export default LoginWithPlex;

@ -0,0 +1,76 @@
import React from 'react';
interface CurrentStep {
stepNumber: number;
description: string;
active?: boolean;
completed?: boolean;
isLastStep?: boolean;
}
const SetupSteps: React.FC<CurrentStep> = ({
stepNumber,
description,
active = false,
completed = false,
isLastStep = false,
}) => {
return (
<li className="relative md:flex-1 md:flex">
<div className="px-6 py-4 flex items-center text-sm leading-5 font-medium space-x-4">
<div
className={`flex-shrink-0 w-10 h-10 flex items-center justify-center border-2
${active ? 'border-indigo-600 ' : 'border-white '}
${completed ? 'bg-indigo-600 border-indigo-600 ' : ''} rounded-full`}
>
{completed && (
<svg
className="w-6 h-6 text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip="evenodd"
/>
</svg>
)}
{!completed && (
<p className={active ? 'text-white' : 'text-indigo-200'}>
{stepNumber}
</p>
)}
</div>
<p
className={`text-sm leading-5 font-medium ${
active ? 'text-white' : 'text-indigo-200'
}`}
>
{description}
</p>
</div>
{!isLastStep && (
<div className="hidden md:block absolute top-0 right-0 h-full w-5">
<svg
className="h-full w-full text-cool-gray-600"
viewBox="0 0 22 80"
fill="none"
preserveAspectRatio="none"
>
<path
d="M0 -2L20 40L0 82"
vectorEffect="non-scaling-stroke"
stroke="currentcolor"
strokeLinejoin="round"
/>
</svg>
</div>
)}
</li>
);
};
export default SetupSteps;

@ -0,0 +1,101 @@
import { useRouter } from 'next/router';
import React, { useState } from 'react';
import Button from '../Common/Button';
import ImageFader from '../Common/ImageFader';
import SettingsPlex from '../Settings/SettingsPlex';
import SettingsServices from '../Settings/SettingsServices';
import LoginWithPlex from './LoginWithPlex';
import SetupSteps from './SetupSteps';
const Setup: React.FC = () => {
const [currentStep, setCurrentStep] = useState(1);
const [plexSettingsComplete, setPlexSettingsComplete] = useState(false);
const router = useRouter();
return (
<div className="min-h-screen bg-cool-gray-900 flex flex-col justify-center py-12 sm:px-6 lg:px-8 relative">
<ImageFader
backgroundImages={[
'/images/rotate1.jpg',
'/images/rotate2.jpg',
'/images/rotate3.jpg',
'/images/rotate4.jpg',
]}
/>
<div className="px-4 sm:px-2 md:px-0 sm:mx-auto sm:w-full sm:max-w-2xl relative z-50">
<img
src="/logo.png"
className="mx-auto max-h-32 w-auto mb-10"
alt="Overseerr Logo"
/>
<nav className="relative z-50">
<ul
className=" bg-cool-gray-800 bg-opacity-50 border border-cool-gray-600 rounded-md divide-y divide-cool-gray-600 md:flex md:divide-y-0"
style={{ backdropFilter: 'blur(5px)' }}
>
<SetupSteps
stepNumber={1}
description={'Login with Plex'}
active={currentStep === 1}
completed={currentStep > 1}
/>
<SetupSteps
stepNumber={2}
description={'Configure Plex'}
active={currentStep === 2}
completed={currentStep > 2}
/>
<SetupSteps
stepNumber={3}
description={'Configure Services'}
active={currentStep === 3}
isLastStep
/>
</ul>
</nav>
<div className="w-full mt-10 p-4 text-white bg-cool-gray-800 bg-opacity-50 border border-cool-gray-600 rounded-md">
{currentStep === 1 && (
<LoginWithPlex onComplete={() => setCurrentStep(2)} />
)}
{currentStep === 2 && (
<div>
<SettingsPlex onComplete={() => setPlexSettingsComplete(true)} />
<div className="mt-8 border-t border-cool-gray-700 pt-5">
<div className="flex justify-end">
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button
buttonType="primary"
disabled={!plexSettingsComplete}
onClick={() => setCurrentStep(3)}
>
Continue
</Button>
</span>
</div>
</div>
</div>
)}
{currentStep === 3 && (
<div>
<SettingsServices />
<div className="mt-8 border-t border-cool-gray-700 pt-5">
<div className="flex justify-end">
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button
buttonType="primary"
onClick={() => router.push('/')}
>
Finish Setup
</Button>
</span>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default Setup;

@ -0,0 +1,9 @@
import React from 'react';
import { NextPage } from 'next';
import Setup from '../components/Setup';
const SetupPage: NextPage = () => {
return <Setup />;
};
export default SetupPage;
Loading…
Cancel
Save