From de685f28f5a203e9b726d1af9d52f3bc1162efa2 Mon Sep 17 00:00:00 2001
From: viktorstrate <viktorstrate@gmail.com>
Date: Sat, 17 Jul 2021 17:14:35 +0200
Subject: [PATCH] Redesign for initial setup wizard

---
 ui/src/Pages/LoginPage/InitialSetupPage.tsx | 180 +++++++++++---------
 ui/src/Pages/LoginPage/LoginPage.tsx        |  10 +-
 ui/src/Pages/LoginPage/loginUtilities.tsx   |   6 +-
 ui/src/primitives/form/Input.tsx            |  10 +-
 4 files changed, 115 insertions(+), 91 deletions(-)

diff --git a/ui/src/Pages/LoginPage/InitialSetupPage.tsx b/ui/src/Pages/LoginPage/InitialSetupPage.tsx
index 6f879d2..852527d 100644
--- a/ui/src/Pages/LoginPage/InitialSetupPage.tsx
+++ b/ui/src/Pages/LoginPage/InitialSetupPage.tsx
@@ -1,13 +1,15 @@
-import React, { useState } from 'react'
+import React from 'react'
 import { gql, useQuery, useMutation } from '@apollo/client'
 import { Redirect } from 'react-router-dom'
-import { Button, Form, Message, Header } from 'semantic-ui-react'
 import { Container } from './loginUtilities'
 
 import { checkInitialSetupQuery, login } from './loginUtilities'
 import { authToken } from '../../helpers/authentication'
 import { useTranslation } from 'react-i18next'
 import { CheckInitialSetup } from './__generated__/CheckInitialSetup'
+import { useForm } from 'react-hook-form'
+import { Submit, TextField } from '../../primitives/form/Input'
+import MessageBox from '../../primitives/form/MessageBox'
 
 const initialSetupMutation = gql`
   mutation InitialSetup(
@@ -27,14 +29,26 @@ const initialSetupMutation = gql`
   }
 `
 
+type InitialSetupFormData = {
+  username: string
+  password: string
+  rootPath: string
+}
+
 const InitialSetupPage = () => {
   const { t } = useTranslation()
 
-  const [state, setState] = useState({
-    username: '',
-    password: '',
-    rootPath: '',
-  })
+  const {
+    register,
+    handleSubmit,
+    formState: { errors: formErrors },
+  } = useForm<InitialSetupFormData>()
+
+  // const [state, setState] = useState({
+  //   username: '',
+  //   password: '',
+  //   rootPath: '',
+  // })
 
   if (authToken()) {
     return <Redirect to="/" />
@@ -48,41 +62,37 @@ const InitialSetupPage = () => {
     <Redirect to="/" />
   )
 
-  const [
-    authorize,
-    { loading: authorizeLoading, data: authorizationData },
-  ] = useMutation(initialSetupMutation, {
-    onCompleted: data => {
-      const { success, token } = data.initialSetupWizard
+  const [authorize, { loading: authorizeLoading, data: authorizationData }] =
+    useMutation(initialSetupMutation, {
+      onCompleted: data => {
+        const { success, token } = data.initialSetupWizard
 
-      if (success) {
-        login(token)
-      }
-    },
-  })
-
-  const handleChange = (
-    event: React.ChangeEvent<HTMLInputElement>,
-    key: string
-  ) => {
-    const value = event.target.value
-    setState(prevState => ({
-      ...prevState,
-      [key]: value,
-    }))
-  }
-
-  const signIn = (event: React.FormEvent<HTMLFormElement>) => {
-    event.preventDefault()
-
-    authorize({
-      variables: {
-        username: state.username,
-        password: state.password,
-        rootPath: state.rootPath,
+        if (success) {
+          login(token)
+        }
       },
     })
-  }
+
+  // const handleChange = (
+  //   event: React.ChangeEvent<HTMLInputElement>,
+  //   key: string
+  // ) => {
+  //   const value = event.target.value
+  //   setState(prevState => ({
+  //     ...prevState,
+  //     [key]: value,
+  //   }))
+  // }
+
+  const signIn = handleSubmit(data => {
+    authorize({
+      variables: {
+        username: data.username,
+        password: data.password,
+        rootPath: data.rootPath,
+      },
+    })
+  })
 
   let errorMessage = null
   if (authorizationData && !authorizationData.initialSetupWizard.success) {
@@ -93,49 +103,59 @@ const InitialSetupPage = () => {
     <div>
       {initialSetupRedirect}
       <Container>
-        <Header as="h1" textAlign="center">
+        <h1 className="text-center text-xl">
           {t('login_page.initial_setup.title', 'Initial Setup')}
-        </Header>
-        <Form
-          style={{ width: 500, margin: 'auto' }}
-          error={!!errorMessage}
-          onSubmit={signIn}
-          loading={
-            authorizeLoading || authorizationData?.initialSetupWizard?.success
-          }
-        >
-          <Form.Field>
-            <label>{t('login_page.field.username', 'Username')}</label>
-            <input onChange={e => handleChange(e, 'username')} />
-          </Form.Field>
-          <Form.Field>
-            <label>{t('login_page.field.password', 'Password')}</label>
-            <input
-              type="password"
-              onChange={e => handleChange(e, 'password')}
-            />
-          </Form.Field>
-          <Form.Field>
-            <label>
-              {t(
-                'login_page.initial_setup.field.photo_path.label',
-                'Photo path'
-              )}
-            </label>
-            <input
-              placeholder={t(
-                'login_page.initial_setup.field.photo_path.placeholder',
-                '/path/to/photos'
-              )}
-              type="text"
-              onChange={e => handleChange(e, 'rootPath')}
-            />
-          </Form.Field>
-          <Message error content={errorMessage} />
-          <Button type="submit">
+        </h1>
+        <form onSubmit={signIn} className="max-w-[500px] mx-auto">
+          <TextField
+            wrapperClassName="my-4"
+            fullWidth
+            {...register('username', { required: true })}
+            label={t('login_page.field.username', 'Username')}
+            error={
+              formErrors.username?.type == 'required'
+                ? 'Please enter a username'
+                : undefined
+            }
+          />
+          <TextField
+            wrapperClassName="my-4"
+            fullWidth
+            {...register('password', { required: true })}
+            label={t('login_page.field.password', 'Password')}
+            error={
+              formErrors.password?.type == 'required'
+                ? 'Please enter a password'
+                : undefined
+            }
+          />
+          <TextField
+            wrapperClassName="my-4"
+            fullWidth
+            {...register('rootPath', { required: true })}
+            label={t(
+              'login_page.initial_setup.field.photo_path.label',
+              'Photo path'
+            )}
+            placeholder={t(
+              'login_page.initial_setup.field.photo_path.placeholder',
+              '/path/to/photos'
+            )}
+            error={
+              formErrors.password?.type == 'required'
+                ? 'Please enter a photo path'
+                : undefined
+            }
+          />
+          <MessageBox
+            type="negative"
+            message={errorMessage}
+            show={errorMessage}
+          />
+          <Submit className="mt-2" disabled={authorizeLoading}>
             {t('login_page.initial_setup.field.submit', 'Setup Photoview')}
-          </Button>
-        </Form>
+          </Submit>
+        </form>
       </Container>
     </div>
   )
diff --git a/ui/src/Pages/LoginPage/LoginPage.tsx b/ui/src/Pages/LoginPage/LoginPage.tsx
index 7f4a9b4..a06968a 100644
--- a/ui/src/Pages/LoginPage/LoginPage.tsx
+++ b/ui/src/Pages/LoginPage/LoginPage.tsx
@@ -27,7 +27,7 @@ const LogoHeader = () => {
   const { t } = useTranslation()
 
   return (
-    <div className="flex justify-center flex-col pb-20 pt-12">
+    <div className="flex justify-center flex-col mb-14 mt-20">
       <img className="h-24" src={logoPath} alt="photoview logo" />
       <h1 className="text-3xl text-center mt-4">
         {t('login_page.welcome', 'Welcome to Photoview')}
@@ -73,13 +73,13 @@ const LoginForm = () => {
 
   return (
     <form
-      className="mx-auto max-w-[450px] px-4"
+      className="mx-auto max-w-[500px] px-4"
       onSubmit={handleSubmit(onSubmit)}
       // loading={loading || (data && data.authorizeUser.success)}
     >
       <TextField
         sizeVariant="big"
-        wrapperClassName="my-4"
+        wrapperClassName="my-6"
         className="w-full"
         label={t('login_page.field.username', 'Username')}
         {...register('username', { required: true })}
@@ -91,7 +91,7 @@ const LoginForm = () => {
       />
       <TextField
         sizeVariant="big"
-        wrapperClassName="my-4"
+        wrapperClassName="my-6"
         className="w-full"
         type="password"
         label={t('login_page.field.password', 'Password')}
@@ -106,7 +106,7 @@ const LoginForm = () => {
         type="submit"
         disabled={loading}
         value={t('login_page.field.submit', 'Sign in') as string}
-        className="rounded-md px-8 py-2 mt-1 focus:outline-none cursor-pointer bg-gradient-to-bl from-[#FF8246] to-[#D6264D] text-white font-semibold focus:ring-2 focus:ring-red-200 disabled:cursor-default disabled:opacity-80"
+        className="rounded-md px-8 py-2 mt-2 focus:outline-none cursor-pointer bg-gradient-to-bl from-[#FF8246] to-[#D6264D] text-white font-semibold focus:ring-2 focus:ring-red-200 disabled:cursor-default disabled:opacity-80"
       />
       <MessageBox
         message={errorMessage}
diff --git a/ui/src/Pages/LoginPage/loginUtilities.tsx b/ui/src/Pages/LoginPage/loginUtilities.tsx
index 06eb8ab..7cd1b0c 100644
--- a/ui/src/Pages/LoginPage/loginUtilities.tsx
+++ b/ui/src/Pages/LoginPage/loginUtilities.tsx
@@ -1,7 +1,7 @@
 import { gql } from '@apollo/client'
 import { saveTokenCookie } from '../../helpers/authentication'
 import styled from 'styled-components'
-import { Container as SemanticContainer } from 'semantic-ui-react'
+// import { Container as SemanticContainer } from 'semantic-ui-react'
 
 export const checkInitialSetupQuery = gql`
   query CheckInitialSetup {
@@ -16,6 +16,4 @@ export function login(token: string) {
   window.location.href = '/'
 }
 
-export const Container = styled(SemanticContainer)`
-  margin-top: 80px;
-`
+export const Container = styled.div.attrs({ className: 'mt-20' })``
diff --git a/ui/src/primitives/form/Input.tsx b/ui/src/primitives/form/Input.tsx
index 9de1c3e..9669cca 100644
--- a/ui/src/primitives/form/Input.tsx
+++ b/ui/src/primitives/form/Input.tsx
@@ -37,7 +37,7 @@ export const TextField = forwardRef(
     let variant = 'bg-white border-gray-200 focus:border-blue-400'
     if (error)
       variant =
-        'bg-red-50 border-red-200 focus:border-red-400 focus:ring-red-100'
+        'bg-red-50 border-red-200 focus:border-red-400 focus:ring-red-100 placeholder-red-300'
 
     if (disabled) variant = 'bg-gray-100'
 
@@ -148,15 +148,21 @@ const buttonStyles = ({ variant, background }: ButtonProps) =>
     background == 'white' ? 'bg-white' : 'bg-gray-50'
   )
 
+type SubmitProps = ButtonProps & {
+  children: string
+}
+
 export const Submit = ({
   className,
   variant,
   background,
+  children,
   ...props
-}: ButtonProps & React.ButtonHTMLAttributes<HTMLInputElement>) => (
+}: SubmitProps & React.ButtonHTMLAttributes<HTMLInputElement>) => (
   <input
     className={classNames(buttonStyles({ variant, background }), className)}
     type="submit"
+    value={children}
     {...props}
   />
 )
