Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
feat(logic): create basic logic for fetch and main UI
Browse files Browse the repository at this point in the history
hack: 독립적으로 E2E 테스트를 작성하는 방법을 아직 못 찾음..

TODO: 테스트 전/후로백엔드의 DB를 비우자
  • Loading branch information
mu-hun committed Nov 11, 2019
1 parent abfbed1 commit cc0c3d3
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 39 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
"dotenv": "^8.2.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0",
"simple-query-string": "^1.3.2",
"styled-components": "^4.4.0",
"typescript": "3.6.4"
"typescript": "3.6.4",
"use-async-effect": "^2.2.1"
},
"scripts": {
"developing": " yarn env:browser yarn develop",
Expand Down Expand Up @@ -46,6 +49,8 @@
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0",
"@types/dotenv": "^6.1.1",
"@types/react-router-dom": "^5.1.2",
"@types/simple-query-string": "^1.3.0",
"@types/styled-components": "^4.1.19",
"@typescript-eslint/eslint-plugin-tslint": "^2.5.0",
"@typescript-eslint/parser": "^2.5.0",
Expand Down
28 changes: 28 additions & 0 deletions src/@types/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export type TypeLoginRes = { mailid: string }

export interface TypeUserNoPw {
student_no: number
student_pw: string
}

export type TypeSemester = {
averagePoint: number // Float
totalCredit: number
isOutside: boolean
semester: 'FIRST' | 'SUMMER' | 'SECOND' | 'WINTER'
year: number
}

export type TypeUser = {
name: string
mailid: string
averagePoint: number // Float
semesters: TypeSemester[]
}

export enum enumSemester {
'1학기' = 'FIRST',
'하기계절' = 'SUMMER',
'2학기' = 'SECOND',
'동기계절' = 'WINTER',
}
9 changes: 9 additions & 0 deletions src/@types/state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { TypeUser } from './models'

export type TypeRes = {
status: number
}

export interface TypeLoad extends TypeRes {
data: TypeUser
}
64 changes: 33 additions & 31 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
import React, { FormEvent, useState, Fragment } from 'react'
import React from 'react'

import logo from './logo.svg'
import './App.css'

import { Input } from './view/Input'
import { submitID } from './control'
import Login from './routes/Login'
import Main from './routes/Main'
import Fetch from './routes/Fetch'
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from 'react-router-dom'

const App: React.FC = () => {
const [flag, setFlag] = useState<boolean>()
const onSubmit = async (e: FormEvent) => {
e.preventDefault()
// @ts-ignore
const { value }: { value: string } = e.target[0]
setFlag(await submitID(value))
}
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{typeof flag === 'undefined' ? (
<Fragment>
<p>제주대학교 메일 ID를 입력해주세요!</p>
<form onSubmit={onSubmit}>
<Input name="mailid" />
</form>
</Fragment>
) : flag ? (
<p id="result">mail was send</p>
) : (
<p id="result">not found username from mail server</p>
)}
</header>
</div>
)
}
const App: React.FC = () => (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Router>
<Switch>
<Route exact path="/">
<Redirect to="login" />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/main">
<Main />
</Route>
<Route path="/fetch">
<Fetch />
</Route>
</Switch>
</Router>
</header>
</div>
)

export default App
30 changes: 27 additions & 3 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
import axios, { AxiosResponse } from 'axios'
import { TypeUser, TypeUserNoPw } from './@types/models'
import { getToken } from './utils'

const baseURL = 'http://localhost:4000/'

const request = axios.create({
baseURL,
})

export const sendID = (id: string): Promise<AxiosResponse<string>> =>
request.post('login', id, {
headers: { 'Content-Type': 'text/plain' },
export const sendID = (
mailid: string
): Promise<AxiosResponse<string | undefined>> =>
request.post(
'login',
{ mailid },
{
headers: { 'Content-Type': 'application/json' },
}
)

export const fetchData = (
data: TypeUserNoPw
): Promise<AxiosResponse<TypeUser>> =>
request.post('fetch', data, {
headers: {
Authorization: `Bearer ${getToken()}`,
},
})

export const loadData = (): Promise<AxiosResponse<TypeUser>> =>
request.get('load', {
headers: {
Authorization: `Bearer ${getToken()}`,
},
})
31 changes: 27 additions & 4 deletions src/control.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import { sendID } from './api'
import { sendID, loadData } from './api'
import { TypeLoad } from './@types/state'
import { fetchData } from './api'
import { TypeUserNoPw } from './@types/models'

export const submitID = async (id: string) => {
export const submitID = async (mailid: string) => {
try {
return (await sendID(id)).status === 201
return (await sendID(mailid)).status === 201
} catch (e) {
return false
if (e.message.includes('401')) return false
throw e
}
}

export const resData = async (): Promise<TypeLoad> => {
const { status, data } = await loadData()
return {
status,
data,
}
}

export const fetchAndSet = async (data: TypeUserNoPw): Promise<number> => {
const { status } = await fetchData(data)
return status
}

type OptionalNumber = number | undefined

// FF: High Order Function
export const isCodeFF = (status: OptionalNumber) => (code: OptionalNumber) =>
status === code
60 changes: 60 additions & 0 deletions src/routes/Fetch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useState, FormEvent, useEffect, Fragment } from 'react'

import { fetchAndSet, isCodeFF } from '../control'
import { Input } from '../view/Input'
import { saveToken } from '../utils'
import { Redirect } from 'react-router-dom'

interface FormElements extends HTMLFormElement {
student_no: HTMLInputElement
student_pw: HTMLInputElement
}

interface FormTarget extends FormEvent<HTMLFormElement> {
target: FormElements
}

const App = () => {
useEffect(saveToken, [])

const [status, setStatus] = useState<number>()
const isCode = isCodeFF(status)

const onSubmit = async (event: FormTarget) => {
event.preventDefault()
setStatus(
await fetchAndSet({
student_no: parseInt(event.target.student_no.value),
student_pw: event.target.student_pw.value,
})
)
}

const Form = () => (
<div id="form">
<p>학번 및 비밀번호를 입력해주세요!</p>
<form onSubmit={onSubmit}>
<Input name="student_no" type="username" />
<Input name="student_pw" type="password" />
<button type="submit">Submit</button>
</form>
</div>
)

if (isCode(undefined)) return <Form />
return (
<Fragment>
~~
{isCode(201) && <Redirect to="/main" />}
{isCode(401) && (
<div id="invalid">
<p>입력한 정보가 유효하지 않습니다</p>
<Form />
</div>
)}
{isCode(500) && <p id="error">500 오류</p>}
</Fragment>
)
}

export default App
32 changes: 32 additions & 0 deletions src/routes/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { FormEvent, useState, Fragment } from 'react'

import { Input } from '../view/Input'
import { submitID } from '../control'

const App: React.FC = () => {
const [flag, setFlag] = useState<boolean>()
const onSubmit = async (event: FormEvent) => {
event.preventDefault()
// @ts-ignore
const { value }: { value: string } = event.target[0]
setFlag(await submitID(value))
}
return (
<Fragment>
{typeof flag === 'undefined' ? (
<Fragment>
<p>제주대학교 메일 ID를 입력해주세요!</p>
<form onSubmit={onSubmit}>
<Input name="mailid" />
</form>
</Fragment>
) : flag ? (
<p id="result">mail was send</p>
) : (
<p id="result">not found username from mail server</p>
)}
</Fragment>
)
}

export default App
42 changes: 42 additions & 0 deletions src/routes/Main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useState, Fragment } from 'react'
import useAsyncEffect from 'use-async-effect'

import { resData, isCodeFF } from '../control'
import { Redirect, Link } from 'react-router-dom'
import { TypeUser } from '../@types/models'

const App = () => {
const [status, setStatus] = useState<number>()
const [userData, setUserData] = useState<TypeUser>()

useAsyncEffect(async () => {
const { status: resStatus, data } = await resData()
setStatus(resStatus)
setUserData(data)
}, [])

const isCode = isCodeFF(status)

const Go2Login = () => (
<div id="error">
{isCode(401) && <p>서버에서 사용자 정보를 찾을 수 없어요 :/</p>}
{isCode(500) && <p>500 오류 :/</p>}
<Link to="/login">다시 로그인하러 가기</Link>
</div>
)

if (isCode(undefined)) return <p id="loading">불러오는 중..</p>
return (
<Fragment>
{isCode(200) && (
<p>
<span id="result">캐싱 된 데이터:</span> {console.log(userData)}
</p>
)}
{isCode(204) && <Redirect to="/fetch" />}
{(isCode(401) || isCode(500)) && <Go2Login />}
</Fragment>
)
}

export default App
9 changes: 9 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import qs from 'querystring'

export const saveToken = () => {
localStorage.setItem('token', qs.parse(window.location.search)[
'?token'
] as string)
}

export const getToken = () => localStorage.getItem('token')

0 comments on commit cc0c3d3

Please sign in to comment.