Use seed instead of signup page for admin + basic local file uploading using TUS & Uppy
This commit is contained in:
@@ -7,10 +7,6 @@ import NavbarLayout from './layouts/NavbarLayout/NavbarLayout'
|
||||
const Routes = () => {
|
||||
return (
|
||||
<Router useAuth={useAuth}>
|
||||
<Set wrap={AccountbarLayout} title="Create Admin Account">
|
||||
<Route path="/create-admin" page={SignupPage} name="signup" />
|
||||
</Set>
|
||||
|
||||
<Set wrap={AccountbarLayout} title="Login">
|
||||
<Route path="/login" page={LoginPage} name="login" />
|
||||
</Set>
|
||||
|
||||
29
web/src/components/Uploader/Uploader.tsx
Normal file
29
web/src/components/Uploader/Uploader.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import Compressor from '@uppy/compressor'
|
||||
import Uppy from '@uppy/core'
|
||||
import ImageEditor from '@uppy/image-editor'
|
||||
import { Dashboard } from '@uppy/react'
|
||||
import Tus from '@uppy/tus'
|
||||
|
||||
import '@uppy/image-editor/dist/style.min.css'
|
||||
import '@uppy/core/dist/style.min.css'
|
||||
import '@uppy/dashboard/dist/style.min.css'
|
||||
|
||||
const Uploader = () => {
|
||||
const [uppy] = useState(() =>
|
||||
new Uppy({
|
||||
restrictions: {
|
||||
allowedFileTypes: ['image/*'],
|
||||
maxNumberOfFiles: 10,
|
||||
},
|
||||
})
|
||||
.use(Tus, { endpoint: 'http://localhost:8911/files' }) // TODO: check if env is production and change endpoint accordingly
|
||||
.use(ImageEditor)
|
||||
.use(Compressor)
|
||||
)
|
||||
|
||||
return <Dashboard uppy={uppy} proudlyDisplayPoweredByUppy={false} />
|
||||
}
|
||||
|
||||
export default Uploader
|
||||
@@ -1,53 +1,15 @@
|
||||
import { Metadata } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
import Uploader from 'src/components/Uploader/Uploader'
|
||||
|
||||
const HomePage = () => {
|
||||
const { isAuthenticated } = useAuth()
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Home" description="Home page" />
|
||||
|
||||
<button className="btn" onClick={() => toast.loading('Loading...')}>
|
||||
Infinite loading
|
||||
</button>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() =>
|
||||
toast.promise(
|
||||
new Promise<number>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
const x = Math.random()
|
||||
x >= 0.5 ? resolve(x) : reject(x)
|
||||
}, 750)
|
||||
}),
|
||||
{
|
||||
loading: 'Loading...',
|
||||
success: (res) => `Success: ${res.toFixed(4)}`,
|
||||
error: (err: number) => `Error: ${err.toFixed(4)}`,
|
||||
}
|
||||
)
|
||||
}
|
||||
>
|
||||
Loading -{'>'} Random result
|
||||
</button>
|
||||
<button className="btn" onClick={() => toast('Blank')}>
|
||||
Blank
|
||||
</button>
|
||||
<button className="btn" onClick={() => toast.error('Error')}>
|
||||
Error
|
||||
</button>
|
||||
<button className="btn" onClick={() => toast.success('Success')}>
|
||||
Success
|
||||
</button>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() =>
|
||||
toast(
|
||||
"I'm baby sriracha poutine hammock pour-over direct trade, bruh coloring book ascot gatekeep put a bird on it YOLO biodiesel. Lyft wayfarers cloud bread, la croix lo-fi pork belly synth williamsburg before they sold out."
|
||||
)
|
||||
}
|
||||
>
|
||||
A pretty big notification
|
||||
</button>
|
||||
{isAuthenticated ? <Uploader /> : <></>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { mdiEmail, mdiKey, mdiAccount } from '@mdi/js'
|
||||
import { Icon } from '@mdi/react'
|
||||
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
TextField,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { Metadata, useQuery } from '@redwoodjs/web'
|
||||
import { toast } from '@redwoodjs/web/toast'
|
||||
|
||||
import { useAuth } from 'src/auth'
|
||||
|
||||
const QUERY = gql`
|
||||
query UserCount {
|
||||
userCount
|
||||
}
|
||||
`
|
||||
|
||||
const SignupPage = () => {
|
||||
const { data, loading } = useQuery(QUERY)
|
||||
const { isAuthenticated, signUp } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data.userCount >= 1) {
|
||||
toast.error('Account already exists')
|
||||
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [data, loading])
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) navigate(routes.home())
|
||||
}, [isAuthenticated])
|
||||
|
||||
const emailRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
emailRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data: Record<string, string>) => {
|
||||
const response = await signUp({
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
email: data.email.toLowerCase(),
|
||||
})
|
||||
|
||||
if (response.message) toast(response.message)
|
||||
else if (response.error) toast.error(response.error)
|
||||
else toast.success('Welcome!')
|
||||
}
|
||||
|
||||
if (loading) return <span className="loading loading-spinner loading-lg" />
|
||||
|
||||
return (
|
||||
<>
|
||||
<Metadata title="Sign Up" />
|
||||
|
||||
<div className="mx-auto my-32 flex w-fit flex-wrap items-center justify-center rounded-xl bg-base-100 p-2">
|
||||
<Form onSubmit={onSubmit} className="space-y-2">
|
||||
<Label
|
||||
name="username"
|
||||
className="input input-bordered input-disabled flex items-center gap-2"
|
||||
errorClassName="input input-bordered input-disabled flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="username"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiAccount} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="grow"
|
||||
value="admin"
|
||||
readOnly
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Label
|
||||
name="email"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="email"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiEmail} />
|
||||
</Label>
|
||||
<TextField
|
||||
name="email"
|
||||
className="grow"
|
||||
placeholder="Email"
|
||||
ref={emailRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
pattern: {
|
||||
value: new RegExp(
|
||||
/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-]+)(\.[a-zA-Z]{2,5}){1,2}$/
|
||||
),
|
||||
message: 'Invalid email',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="email" className="text-sm text-error" />
|
||||
|
||||
<Label
|
||||
name="password"
|
||||
className="input input-bordered flex items-center gap-2"
|
||||
errorClassName="input input-bordered flex items-center gap-2 input-error"
|
||||
>
|
||||
<Label
|
||||
name="password"
|
||||
className="h-4 w-4 opacity-70"
|
||||
errorClassName="h-4 w-4 text-error"
|
||||
>
|
||||
<Icon path={mdiKey} />
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="grow"
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Required',
|
||||
},
|
||||
minLength: {
|
||||
value: 8,
|
||||
message: 'Must be 8 or more characters',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<FieldError name="password" className="text-sm text-error" />
|
||||
|
||||
<div className="flex w-full">
|
||||
<Submit className="btn btn-primary mx-auto">Create</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignupPage
|
||||
Reference in New Issue
Block a user