cleanup, api, databuddy
This commit is contained in:
parent
a8869e5fdd
commit
8df1fed14c
20202
frontend/package-lock.json
generated
20202
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dank-inc/data-buddy": "^0.1.4",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
|
|
@ -1,27 +1,53 @@
|
||||||
import { message } from 'antd'
|
import Axios, { AxiosInstance } from 'axios'
|
||||||
import axios from 'axios'
|
|
||||||
import { User } from '../types'
|
import { User } from '../types'
|
||||||
import { mockUser } from './data'
|
import { JWT, setJWT, wipeJWT } from '../utils/jwt'
|
||||||
import { JWT, getJWT, setHeaders, 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) => {
|
export interface Api {
|
||||||
// if (dev) return mockUser
|
mock?: boolean
|
||||||
|
users: DataBuddy<User>
|
||||||
|
axios: AxiosInstance
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
export class Api {
|
||||||
const { data: jwt } = await axios.post<JWT>(`/api/dj-rest-auth/login/`, {
|
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,
|
username,
|
||||||
password,
|
password,
|
||||||
})
|
})
|
||||||
|
|
||||||
setJWT(jwt)
|
setJWT(data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
const user = await getLoggedInUser()
|
logout = () => {
|
||||||
return user
|
if (this.mock) return true
|
||||||
} catch (err) {
|
wipeJWT()
|
||||||
console.log(err)
|
}
|
||||||
message.error('Error logging in!')
|
|
||||||
|
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()
|
wipeJWT()
|
||||||
// axios -> delete session?
|
// 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
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,20 +3,14 @@ import { useUserContext } from '../contexts/UserContext'
|
||||||
import { Login } from './pages/Login'
|
import { Login } from './pages/Login'
|
||||||
import { Route, Switch } from 'react-router'
|
import { Route, Switch } from 'react-router'
|
||||||
import { Dashboard } from './pages/Dashboard'
|
import { Dashboard } from './pages/Dashboard'
|
||||||
import { NavRoute, AppHeader } from './layout/AppHeader'
|
import { AppHeader } from './layout/AppHeader'
|
||||||
import { Profile } from './pages/Profile'
|
import { Profile } from './pages/Profile'
|
||||||
import { NewUser } from './forms/NewUser'
|
import { NewUser } from './forms/NewUser'
|
||||||
|
import { ForgotPassword } from './pages/ForgotPassword'
|
||||||
|
|
||||||
export const CoreLayout = () => {
|
export const CoreLayout = () => {
|
||||||
const { user } = useUserContext()
|
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)
|
if (!user)
|
||||||
return (
|
return (
|
||||||
<Layout className="layout">
|
<Layout className="layout">
|
||||||
|
@ -28,14 +22,16 @@ export const CoreLayout = () => {
|
||||||
// header, sidebar, avatar?
|
// header, sidebar, avatar?
|
||||||
return (
|
return (
|
||||||
<Layout className="layout">
|
<Layout className="layout">
|
||||||
<AppHeader user={user} routes={routes} />
|
<AppHeader user={user} />
|
||||||
<Switch>
|
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
{routes.map(({ exact, path, component }) => (
|
<Switch>
|
||||||
<Route exact={exact} key={path} path={path} component={component} />
|
<Route path="/forgot-password" component={ForgotPassword} />
|
||||||
))}
|
<Route path="/login" component={Login} />
|
||||||
</Layout.Content>
|
<Route path="/new/user" component={NewUser} />
|
||||||
|
<Route path="/profile" component={Profile} />
|
||||||
|
<Route exact path="/" component={Dashboard} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</Layout.Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react'
|
import { Avatar, Button, Dropdown, Menu } from 'antd'
|
||||||
import { Avatar, Button, Typography } from 'antd'
|
|
||||||
import { Header } from 'antd/lib/layout/layout'
|
import { Header } from 'antd/lib/layout/layout'
|
||||||
import { User } from '../../types'
|
import { User } from '../../types'
|
||||||
import { Link, useHistory } from 'react-router-dom'
|
import { Link, useHistory } from 'react-router-dom'
|
||||||
|
import { useUserContext } from '../../contexts/UserContext'
|
||||||
|
|
||||||
export type NavRoute = {
|
export type NavRoute = {
|
||||||
path: string
|
path: string
|
||||||
|
@ -13,16 +13,16 @@ export type NavRoute = {
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: User | null
|
user: User | null
|
||||||
routes?: NavRoute[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppHeader = ({ user, routes }: Props) => {
|
export const AppHeader = ({ user }: Props) => {
|
||||||
|
const { handleLogout } = useUserContext()
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
// Unauthed Header
|
// Unauthed Header
|
||||||
if (!user)
|
if (!user)
|
||||||
return (
|
return (
|
||||||
<Header className="app-header">
|
<Header className="app-header">
|
||||||
<Typography.Title level={3}>MVP Django React! 🤠</Typography.Title>
|
<h3>MVP Django React! 🤠</h3>
|
||||||
</Header>
|
</Header>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,19 +30,29 @@ export const AppHeader = ({ user, routes }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Header className="app-header">
|
<Header className="app-header">
|
||||||
<div className="header-left">
|
<div className="header-left">
|
||||||
<Typography.Title level={3}>MVP Django React! 🤠</Typography.Title>
|
<Link to="/">
|
||||||
{routes?.map((route) => (
|
<h1>MVP Django React! 🤠</h1>
|
||||||
<Link key={`${route.path}-${route.label}`} to={route.path}>
|
|
||||||
{route.label}
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={() => history.push('/new/user')}>New User</Button>
|
<Button onClick={() => history.push('/new/user')}>New User</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="header-right">
|
<div className="header-right">
|
||||||
<Typography.Paragraph>Welcome, {user.username}!!</Typography.Paragraph>
|
<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>
|
<Avatar>{user.username[0]}</Avatar>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</Header>
|
</Header>
|
||||||
)
|
)
|
||||||
|
|
22
frontend/src/app/pages/ForgotPassword.tsx
Normal file
22
frontend/src/app/pages/ForgotPassword.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
19363
frontend/yarn.lock
19363
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
13
node_modules/.package-lock.json
generated
vendored
Normal file
13
node_modules/.package-lock.json
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "mvp-django-react",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@dank-inc/data-buddy": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dank-inc/data-buddy/-/data-buddy-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-GreH0gs1Wf/8thCt53FrzX0ngMuIhrTBZJWa6tlqLIST4EgKgJN6IPei1o7bOqKPFmLIMS/DG6o1b/QAC57oXw==",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
node_modules/@dank-inc/data-buddy/README.md
generated
vendored
Normal file
7
node_modules/@dank-inc/data-buddy/README.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Data Buddy
|
||||||
|
|
||||||
|
Need a little mock data thingy?
|
||||||
|
|
||||||
|
# Look no further!
|
||||||
|
|
||||||
|
I will document this someday
|
13
node_modules/@dank-inc/data-buddy/lib/index.d.ts
generated
vendored
Normal file
13
node_modules/@dank-inc/data-buddy/lib/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
declare type DataRecord = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
export declare type DataBuddyParams<T extends DataRecord> = T[];
|
||||||
|
export declare class DataBuddy<T extends DataRecord> {
|
||||||
|
data: T[];
|
||||||
|
constructor(records: T[]);
|
||||||
|
get: () => T[];
|
||||||
|
getOne: (id: string) => T | null;
|
||||||
|
update: (id: string, params: Partial<T>) => T | false;
|
||||||
|
delete: (id: string) => boolean;
|
||||||
|
}
|
||||||
|
export {};
|
43
node_modules/@dank-inc/data-buddy/lib/index.js
generated
vendored
Normal file
43
node_modules/@dank-inc/data-buddy/lib/index.js
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
"use strict";
|
||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.DataBuddy = void 0;
|
||||||
|
var DataBuddy = (function () {
|
||||||
|
function DataBuddy(records) {
|
||||||
|
var _this = this;
|
||||||
|
this.get = function () {
|
||||||
|
return _this.data;
|
||||||
|
};
|
||||||
|
this.getOne = function (id) {
|
||||||
|
return _this.data.find(function (record) { return record.id === id; }) || null;
|
||||||
|
};
|
||||||
|
this.update = function (id, params) {
|
||||||
|
var index = _this.data.findIndex(function (record) { return record.id === id; });
|
||||||
|
if (!index)
|
||||||
|
return false;
|
||||||
|
var record = _this.data[index];
|
||||||
|
_this.data[index] = __assign(__assign({}, record), params);
|
||||||
|
return _this.data[index];
|
||||||
|
};
|
||||||
|
this.delete = function (id) {
|
||||||
|
var index = _this.data.findIndex(function (record) { return record.id === id; });
|
||||||
|
if (!index)
|
||||||
|
return false;
|
||||||
|
_this.data.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
this.data = records;
|
||||||
|
}
|
||||||
|
return DataBuddy;
|
||||||
|
}());
|
||||||
|
exports.DataBuddy = DataBuddy;
|
24
node_modules/@dank-inc/data-buddy/package.json
generated
vendored
Normal file
24
node_modules/@dank-inc/data-buddy/package.json
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "@dank-inc/data-buddy",
|
||||||
|
"version": "0.1.3",
|
||||||
|
"author": "Elijah Lucian",
|
||||||
|
"license": "MIT",
|
||||||
|
"description": "Need a little mock api data state buddy?",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dank-inc/data-buddy"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "tsc --noEmit",
|
||||||
|
"compile": "rm -rf lib && tsc",
|
||||||
|
"deploy": "npm run compile && npm publish",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.2.4"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user