cleanup, api, databuddy

This commit is contained in:
Elijah Lucian
2021-04-14 18:03:23 -06:00
parent a8869e5fdd
commit 8df1fed14c
12 changed files with 30813 additions and 9005 deletions

20202
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@dank-inc/data-buddy": "^0.1.4",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",

View File

@@ -1,27 +1,53 @@
import { message } from 'antd'
import axios from 'axios'
import Axios, { AxiosInstance } from 'axios'
import { User } from '../types'
import { mockUser } from './data'
import { JWT, getJWT, setHeaders, setJWT, wipeJWT } from '../utils/jwt'
import { JWT, setJWT, wipeJWT } from '../utils/jwt'
import { DataBuddy } from '@dank-inc/data-buddy'
const dev = process.env.NODE_ENV === 'development'
export type ApiParams = {
baseURL?: string
mock?: boolean
users: DataBuddy<User>
}
export const logIn = async (username: string, password: string) => {
// if (dev) return mockUser
export interface Api {
mock?: boolean
users: DataBuddy<User>
axios: AxiosInstance
}
try {
const { data: jwt } = await axios.post<JWT>(`/api/dj-rest-auth/login/`, {
export class Api {
constructor({ mock, users, baseURL }: ApiParams) {
this.mock = mock
this.users = users
this.axios = Axios.create({ baseURL })
}
login = async (username: string, password: string): Promise<JWT> => {
if (this.mock)
return {
id: 'mock-id',
token: 'token-token-token',
exp: +new Date(),
}
const { data } = await this.axios.post<JWT>(`/api/dj-rest-auth/login/`, {
username,
password,
})
setJWT(jwt)
setJWT(data)
return data
}
const user = await getLoggedInUser()
return user
} catch (err) {
console.log(err)
message.error('Error logging in!')
logout = () => {
if (this.mock) return true
wipeJWT()
}
getUser = async (id: string) => {
if (this.mock) return this.users.getOne(id)
const { data } = await this.axios.get<User>(`users/${id}`)
return data
}
}
@@ -29,13 +55,3 @@ export const logOut = () => {
wipeJWT()
// axios -> delete session?
}
export const getLoggedInUser = async () => {
if (dev) return mockUser
const jwt = getJWT()
if (!jwt) throw new Error('User not logged in') // TODO: decorator
const { data } = await axios.get<User>(`/users/${jwt.id}`, setHeaders())
return data
}

View File

@@ -3,20 +3,14 @@ import { useUserContext } from '../contexts/UserContext'
import { Login } from './pages/Login'
import { Route, Switch } from 'react-router'
import { Dashboard } from './pages/Dashboard'
import { NavRoute, AppHeader } from './layout/AppHeader'
import { AppHeader } from './layout/AppHeader'
import { Profile } from './pages/Profile'
import { NewUser } from './forms/NewUser'
import { ForgotPassword } from './pages/ForgotPassword'
export const CoreLayout = () => {
const { user } = useUserContext()
const routes: NavRoute[] = [
{ exact: true, path: '/', label: 'Dashboard', component: Dashboard },
{ path: '/profile', label: 'Profile', component: Profile },
{ path: '/login', label: 'Login', component: Login },
{ path: '/new/user', label: 'New User', component: NewUser },
]
if (!user)
return (
<Layout className="layout">
@@ -28,14 +22,16 @@ export const CoreLayout = () => {
// header, sidebar, avatar?
return (
<Layout className="layout">
<AppHeader user={user} routes={routes} />
<Switch>
<Layout.Content>
{routes.map(({ exact, path, component }) => (
<Route exact={exact} key={path} path={path} component={component} />
))}
</Layout.Content>
</Switch>
<AppHeader user={user} />
<Layout.Content>
<Switch>
<Route path="/forgot-password" component={ForgotPassword} />
<Route path="/login" component={Login} />
<Route path="/new/user" component={NewUser} />
<Route path="/profile" component={Profile} />
<Route exact path="/" component={Dashboard} />
</Switch>
</Layout.Content>
</Layout>
)
}

View File

@@ -1,8 +1,8 @@
import React from 'react'
import { Avatar, Button, Typography } from 'antd'
import { Avatar, Button, Dropdown, Menu } from 'antd'
import { Header } from 'antd/lib/layout/layout'
import { User } from '../../types'
import { Link, useHistory } from 'react-router-dom'
import { useUserContext } from '../../contexts/UserContext'
export type NavRoute = {
path: string
@@ -13,16 +13,16 @@ export type NavRoute = {
type Props = {
user: User | null
routes?: NavRoute[]
}
export const AppHeader = ({ user, routes }: Props) => {
export const AppHeader = ({ user }: Props) => {
const { handleLogout } = useUserContext()
const history = useHistory()
// Unauthed Header
if (!user)
return (
<Header className="app-header">
<Typography.Title level={3}>MVP Django React! 🤠</Typography.Title>
<h3>MVP Django React! 🤠</h3>
</Header>
)
@@ -30,19 +30,29 @@ export const AppHeader = ({ user, routes }: Props) => {
return (
<Header className="app-header">
<div className="header-left">
<Typography.Title level={3}>MVP Django React! 🤠</Typography.Title>
{routes?.map((route) => (
<Link key={`${route.path}-${route.label}`} to={route.path}>
{route.label}
</Link>
))}
<Link to="/">
<h1>MVP Django React! 🤠</h1>
</Link>
</div>
<div>
<Button onClick={() => history.push('/new/user')}>New User</Button>
</div>
<div className="header-right">
<Typography.Paragraph>Welcome, {user.username}!!</Typography.Paragraph>
<Avatar>{user.username[0]}</Avatar>
<p>Welcome, {user.username}!!</p>
<Dropdown
overlay={
<Menu style={{ display: 'flex', flexDirection: 'column' }}>
<Button onClick={() => history.push('/login')}>Login</Button>
<Button onClick={handleLogout}>Logout</Button>
<Button onClick={() => history.push('/forgot-password')}>
Forgot Password
</Button>
<Button onClick={() => history.push('/profile')}>Profile</Button>
</Menu>
}
>
<Avatar>{user.username[0]}</Avatar>
</Dropdown>
</div>
</Header>
)

View File

@@ -0,0 +1,22 @@
import { Button, Input, Layout } from 'antd'
import { useState } from 'react'
export const ForgotPassword = () => {
const [email, setEmail] = useState('')
const handleClick = () => {
console.log(email)
}
return (
<Layout>
<h1>Forgot Password</h1>
<Input
value={email}
onChange={(e) => setEmail(e.target.value)}
type="email"
/>
<Button onClick={handleClick}>Submit</Button>
</Layout>
)
}

File diff suppressed because it is too large Load Diff