---
tags: Design Review
---
# 1101 Design Review
## Wireframe / Mockup
{%figma https://www.figma.com/file/u9h7OTqQH5A6xKIJBsfFBz/Dokodemo-Design?node-id=0%3A1 %}
https://www.figma.com/file/u9h7OTqQH5A6xKIJBsfFBz/Dokodemo-Design?node-id=0%3A1
## Architecture

### build image to docker container
- [name=frontend]
1. `docker build -t frontend:v0.0.0 .`
-> frontend 是image name冒號後面為加上tag的樣子(建議手打比較快,最後的句點要記得打!)
2. 到gitlab container registry 找到 CLI commands
3. copy P an 1mage指令到vs code上執行
`docker push registry.gitlab.com/utaipei-edu/day-off-backend` -> 複製完會長這樣
- [name=backend]
1. `docker build -t backend:v0.0.0 .`
-> backend 是image name冒號後面為加上tag的樣子(建議手打比較快,最後的句點要記得打!)
2. 到gitlab container registry 找到 CLI commands
3. Push an image指令到vs code上執行
`docker push registry.gitlab.com/utaipei-edu/day-off-frontend` -> 複製完會長這樣
### combine frontend and backend 的 image
```bash!
docker-compose -f docker-compospp.y1l up -d
```
##### ->pull frontend and backend的image
##### ->build container和幫container取名字和tag
```bash!
docker-compose -f docker-compose-db.yml up -d
```
```bash!
docker-compose -f docker-compose-migrate.yml up -d
```
- [name=backend] 要更改mySQL內部東西的時候會用到
```bash!
docker-compose -f docker-compose-nginx.yml up -d
```
##### ->設定反向代理
## Spec/Interface
## Flow (with Sequence diagram)
lino fi1e path
or https://docs.gitlab.com/ee/administration/integration/plaml.h1ml
```plantuml
@startuml
participant Frontend order 10
participant Backend order 20
participant DB order 25
participant SchoolAPI order 30
==POST Login API==
Frontend->Backend:student_id, password
Backend->SchoolAPI:student_id, password
alt succes
SchoolAPI->Backend:Login Success token, ...
opt if user not in User table
Backend->DB:add user to User table
end
Backend->DB:write login record
Backend->Frontend:Login Success: token, studentId, name, code, msg
else failed
SchoolAPI->Backend:http 401 | 403 | 504 | 500
Backend->Frontend:msg, detail
end
==when server is shutdown==
Frontend->Backend:student_id, password
Backend->x SchoolAPI:student_id, password
Backend->Frontend:http 500
@enduml
```
```plantuml
@startuml
participant Frontend order 10
participant Backend order 20
participant DB order 25
participant SchoolAPI order 30
participant Redis order 40
==POST get record API==
Frontend->Backend:token
Backend->SchoolAPI:check user
alt valid
SchoolAPI->Backend: user valid
DB->Backend: 去 DB 抓所有的資料
alt Redis有資料
Redis->Backend: 去 Redis 抓課程查詢區間
else Redis無資料且DB也沒有資料
Backend->Backend: 找不到課程查詢區間,DB也沒有資料,用預設值
else Redis無資料但DB有資料
Backend->Backend: 找不到課程查詢區間,從DB拿出來的資料找
end
Backend->SchoolAPI:get record by token
SchoolAPI->Backend:record
Backend->Backend: 比對資料,更新請假審核狀態
Backend->DB: 將更新後的請假審核狀態寫入DB
DB->Backend:再去DB抓一次資料,資料查詢時已經分頁且過濾
Backend->Frontend:record, code, msg
else invalid
SchoolAPI->Backend: 資料驗證錯誤
Backend->Frontend:http 401
end
==when server is shutdown==
Frontend->Backend:請假紀錄
Backend-x SchoolAPI:check user
Backend->Frontend:http 500
@enduml
```
```plantuml
@startuml
participant Frontend order 10
participant Backend order 20
participant DB order 25
participant SchoolAPI order 30
participant Redis order 40
==POST Leave application API==
Frontend->Backend:請假紀錄
Backend->SchoolAPI:check user
alt valid
SchoolAPI->Backend: user valid
DB->Backend:get user and course data
Backend->Backend:check payload data
Backend->SchoolAPI:POST token, 請假紀錄
SchoolAPI->Backend:http 200 Success: data, ...
Backend->Redis:更新課程查詢區間
Backend->DB:write 請假紀錄
alt write to DB success
Backend->Frontend:code, msg
else failed
Backend->Backend:create background task for retry
Backend->Frontend:http 500
end
else invalid
SchoolAPI->Backend: 資料驗證錯誤
Backend->Frontend:http 401
end
==when server is shutdown==
Frontend->Backend:請假紀錄
Backend-x SchoolAPI:check user
Backend->Frontend:http 500
@enduml
```
```plantuml
participant Frontend order 10
participant Backend order 20
participant DB order 25
participant SchoolAPI order 30
@startuml
==GET getCourse API==
Frontend->Backend:token, search_date
Backend->SchoolAPI:check user
alt valid
SchoolAPI->Backend: user valid
Backend->SchoolAPI:GET token, search_date
SchoolAPI->Backend:http 200: courses, ...
Backend->DB:write courses to DB
Backend->Backend:移除請假過的節次
Backend->Frontend:courses, code, msg
else invalid
SchoolAPI->Backend: 資料驗證錯誤
Backend->Frontend:http 401
end
==when server is shutdown==
Frontend->Backend:token, search_date
Backend-x SchoolAPI:check user
Backend->Frontend:http 500
@enduml
```
```plantuml
participant Frontend order 10
participant Backend order 20
participant DB order 25
participant SchoolAPI order 30
@startuml
==POST logout API==
Frontend->Backend:token, search_date
Backend->SchoolAPI:check user
alt valid
SchoolAPI->Backend: user valid
Backend->DB: remove user's token from DB
Backend->SchoolAPI:POST school logout API
Backend->Frontend:code, msg
else invalid
SchoolAPI->Backend: 資料驗證錯誤
Backend->Frontend:http 401
end
==when server is shutdown==
Frontend->Backend:token, search_date
Backend-x SchoolAPI:check user
Backend->Frontend:http 500
@enduml
```
```plantuml
@startuml
==GET leave-type==
Frontend->Backend:GET /api/v1/leave-type
Backend->Backend:load all selectable leave-type
Backend->Frontend:code, msg, leave_types
@enduml
```
```plantuml
@startuml
==POST frontend-log==
Frontend->Backend:POST log_level, log_message
Backend->Backend:output frontend log in backend
Backend->Frontend:code, msg
@enduml
```
### Frontend
```plantuml
@startuml
participant Frontend order 20
participant Backend order 30
participant User order 10
==Login Page==
User->Frontend:輸入帳號密碼
Frontend->Frontend: 按下登入按鈕:加密帳號密碼
Frontend->Backend: post送出已加密的帳號密碼
Backend->Frontend: check user password correct or not
alt 帳號密碼錯誤
Backend->Frontend: 帳號密碼錯誤:msg detail
Frontend->User: 顯示在螢幕上:帳號密碼錯誤
else 帳號密碼正確
Backend->Frontend: token, userName, StudentId
Frontend->User: save token, change path to record page
end
==Record Page==
Frontend->Backend: get送出 page:1, token, record status(all, pending, approved, refused)
Backend->Frontend: data
alt 沒有資料
Frontend->User: no data page
else loading
Frontend->User: loading page
else 成功拿到資料
Frontend->Frontend: 記錄current page number
Frontend->User: 顯示請假紀錄
User->Frontend: User往下滑
Frontend->Backend: get送出 page:2, token, record status(all, pending, approved, refused)
end
@enduml
==Application Page==
User->Frontend: 選擇假別
Frontend->Backend: get leave type
Backend->Frontend: all leave type
Frontend->User:彈出modal給user選擇假別
User->Frontend:選擇日期
Frontend->Backend: get course
Backend->Frontend: all course of the user of the date
Frontend->User:使用者螢幕出現選擇課堂的選項
User->Frontend:點按課堂
Frontend->User:彈出modal給user選擇課程
User->Frontend:選擇課堂
Frontend->User:顯示節次選項
User->Frontend:選擇節次
Frontend->User:預覽請假的按鈕亮起
alt 填寫緣由
User->Frontend:填寫緣由
User->Frontend:按下預覽按鈕
else
User->Frontend:未填寫緣由(資料為null)
User->Frontend:按下預覽按鈕
end
Frontend->Backend:post送出請假資料
Backend->Frontend:請假是否成功
@enduml
```
```plantuml
@startuml
participant Form order 0
participant Modal order 10
participant ComponentInsideModal order 20
Form->Form:當使用者點選表單
Form->Modal:data, modalType, title
Modal->Modal:判斷modalType對應的元件
Modal->ComponentInsideModal:呼叫對應的元件(calendar, list, preview leave record)
Modal->ComponentInsideModal:data
ComponentInsideModal->ComponentInsideModal:顯示資料
ComponentInsideModal->Modal:當使用者選擇選項,回傳data
Modal->Modal:setState讓按鈕亮起來
Modal->Modal:使用者點選確定,關閉表單
Modal->Form:將使用者選擇的data回傳
@enduml
```
### Backend
#### API1 - 取得請假紀錄
- [name=brian]
Path: `/api/v1/record`
Request:
| Method | Header | query |
|:------:|:----------------------------------------------------------------------------------------------- |:------------------ |
| GET | Content-Type: application/json <br> x-trace-id: "\<uuid4\>" </br> token: Bearer "\<JWT TOKEN\>" | page: start from 1 |
Response:
- Status code 200
```json!
{
"code":0,
"msg":"SUCCESS",
"data":{
"hasNextPage":true,
"records":[
{
"uuid":"3c4ea096-e04f-40f1-8911-6846f1084979",
"name":"資料庫",
"code":"1234",
"date":"2022/10/18",
"weekday":4,
"sessions":[
5,
6,
7
],
"teacher":"大衛",
"leaveType":"病假",
"status":"approved",
"sendTime":"2022/10/17 12:30"
}
]
}
}
```
#### API2 - 判斷登入 for frontend
- [name=yaya]
Path: `/api/v1/login`
Request:
| Method | Header |request body |
|:------:|:-----------------------:|:---------------------------------------------------------------------------------------------|
| POST | Content-Type: application/json <br> x-trace-id: "\<uuid4\>" </br> |```{"studentId": "test1","password": "1"} ```|
Response:
- Status code 200
```json!
{
"code": 0,
"msg": "SUCCESS",
"data": [
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZDRlOGQ5YTA2MWMxYTJjMDIxY2JlMTgiLCJpYXQiOjE1NjU4NTczMjAsImV4cCI6MTU2NTk0MzcyMH0.GQVyQJLmwXd2jQZsjZ8n6cAWD0HQGjvlp2Mk8kAsGy8",
"userName": "金宣妘",
"studentId": "U10816014",
}
]
}
```
- Status code 401
```json!
{
"code": 2,
"msg": "帳號或密碼錯誤,請重新輸入",
"data": []
}
{
"code": 2, // 2 or 6?
"msg": "帳號或密碼為空,請重新輸入",
"data": []
}
```
- Status code 403
```json!
{
"code": 3,
"msg": "Suspended account",
"data": []
}
```
- Status code 504
```json!
{
"code": 4,
"msg": "Timeout",
"data": []
}
```
- Status code 500
```json!
{
"code": 5,
"msg": "Other",
"data": []
}
```
### API3 - Mock Get Leave History API
Path: /api/v1/mock/record
Request:
| Method | Header | Parameter | Note |
| :----: | :------------------------------------ | :--------- | :--- |
| GET | Content-Type: application/json | student_id: string ||
| | x-trace-id: "\<uuid4\>" | from_date: date(ISO8601 - UTC+8) | e.g. 2022-10-19 |
| | Authorization: Bearer "\<JWT TOKEN\>" | to_date: date(ISO8601 - UTC+8) | e.g. 2023-01-19 |
Response:
- Status code 200
```json!
{
"code": 0,
"msg": "Success",
"data": [
{
"name": "<name>",
"code": "<code>",
"date": "<date:ISO8601 - UTC+8>",
"weekday": "<weekday:integer, 1-7>",
"sessions": [<session:integer, 1-12>],
"teacher": "<teacher_fullname>",
"leave_type": <leave_type code>
}, ...
]
}
```
1. `weekday` in range 1-7 stands for Mon. to Sun.
2. `sessions` array, items are integer in range 1-12 stands for 節次 in a day
3. `leave_type` should have latest leave status in each query, format can be decided by original system
- Status code 400
```json!
{
"msg": "Invalid query param: to_date",
"code": 1100400
}
```
## References
leave_type code mapping:
| 假別代碼 | 假別名稱 | 假別簡稱 |
|:-------- | -------- |:-------- |
| 0 | | |
| 11 | 早退 | 退 |
| 21 | 事假 | 事 |
| 22 | 病假 | 病 |
| 23 | 公假 | 公 |
| 24 | 喪假 | 喪 |
| 25 | 婚假 | 婚 |
| 26 | 孕(產)假 | 產 |
| 27 | 哺育假 | 哺 |
| 28 | 防疫假 | 疫 |
| 29 | 公傷假 | 傷 |
| 30 | 生理假 | 生 |
| 51 | 請假 | 假 |
| 52 | 疫苗假 | 苗 |
## DB Schema
---
tags: backend
---
# DB schema
### user
| attribute | Type | Example | Note |
|:--------------- |:--------------------------- |:------------------------------------ |:-------------------------------- |
| user_id | VARCHAR(36)</br>**Primary Key** | 842f638c-4f58-11ed-8453-0242ac1b0003 | uuid |
| student_id | VARCHAR(256) | u11016025 | Index1 |
| name | VARCHAR(256) | Brian | Index2 |
| email | VARCHAR(256) | u11016025@go.utaipei.edu.tw | Index3 |
| enabled | TINYINT(1) | 1 |0: 停權, 1: 授權 |
| last_updated_at | TIMESTAMP | 2022-10-29 02:48:31 | Default: current time</br>onupdate: current time |
| last_updated_by | VARCHAR(256) | Brian | |
| created_at | TIMESTAMP | 2022-10-19 00:00:00 | Default: current time |
| token | VARCHAR(512) | (token) | |
### leave_record
| attribute | Type | Example | Note |
|:--------------- |:------------------------------- |:------------------------------------ |:------------------------------------------------ |
| record_id | VARCHAR(36)</br>**Primary Key** | 9a4e7638-e47f-41e7-b09b-ef6aad43276e | uuid |
| user_id | VARCHAR(36)</br>**Foreign Key** | 842f638c-4f58-11ed-8453-0242ac1b0003 | |
| date | TIMESTAMP | | |
| weekday | SMALLINT | 1 | |
| sessions | VARCHAR(256) | 1, 2,3 | |
| leave_type | SMALLINT | 22 | |
| status | SMALLINT | 1 | 0:審核, 1:通過, 拒絕 |
| last_updated_at | TIMESTAMP | 2022-10-29 02:48:31 | Default: current time</br>onupdate: current time |
| send_time | TIMESTAMP | 2022-10-19 00:00:00 | Default: current time |
| course_id | VARCHAR(256)<br>**foreign key** | 9a4e7638-e47f-41e7-b09b-ef6aad43276e | |
| last_check_at | TIMESTAMP | 2022-10-30 00:00:00 | 上一次與校務系統核對時間 |
### course
| attribute | Type | Example | Note |
|:--------------- |:--------------------------- |:------------------------------------ |:------------------------------------------------ |
| course_id | VARCHAR(36)</br>**Primary Key** | 9a4e7638-e47f-41e7-b09b-ef6aad43276e | uuid |
| code | VARCHAR(256) | 1234 | Index1 |
| name | VARCHAR(256) | | Index2 |
| weekday | SMALLINT | 1 | |
| sessions | VARCHAR(256) | 1, 2, 3 | |
| teacher | VARCHAR(256) | | |
| last_updated_at | TIMESTAMP | 2022-10-19 02:48:31 | Default: current time</br>onupdate: current time |
### login_record
| attribute | Type | Example | Note |
|:--------------- |:--------------------------- |:------------------------------------ |:------------------------------------------------ |
| login_record_id | VARCHAR(36)</br>**Primary Key** | 9a4e7638-e47f-41e7-b09b-ef6aad43276e | uuid |
| user_id | VARCHAR(256)</br>**Foreign Key** | 842f638c-4f58-11ed-8453-0242ac1b0003 | uuid |
| ip | VARCHAR(256) | | Index2 |
| login_time | TIMESTAMP | | |
| status | SMALLINT | 2 | error code |
| user_agent | VARCHAR(512) | | |

## Prerequisites
- 校務系統快生出來ˋˊ
# Frontend 元件階層
- <font color="yellow">黃色</font>是網頁的頁面(eg: "localhost:3000/record")
- <font color="pink">粉紅色</font>是在 "/src/pages" 底下的component
- <font color="lightblue">淺藍色</font>是在 "/src/componenets"底下的component
- <font color="lightgreen">綠色</font>是個別檔案底下的小元件或是render function
```plantuml
@startuml
hide empty description
state login #yellow{
state loginPage{
loginPage: path=src/pages/login/loginPage.js
}
}
@enduml
```
```plantuml
@startuml
state record #yellow{
state menuLayout#pink{
menuLayout: path= src/pages/record/menuLayout.js
menuLayout: rwd的menu,電腦menu會在頁面左半邊,手機menu會在floatingButton
}
state pageContent#lightgreen{
pageContent: path=src/componenets/record/categoryMenu.js
}
state categoryMenu#lightblue{
categoryMenu: path=src/componenets/record/categoryMenu.js
}
state floatingButton#lightblue{
floatingButton: path=src/componenets/record/floatingButton.js
}
state desktopMenu#lightblue{
desktopMenu: path=src/componenets/record/desktopMenu.js
}
state emptyComponent#lightblue{
emptyComponent: path=src/componenets/record/emptyComponent.js
}
state loadingAnimation#lightblue{
loadingAnimation: path=src/componenets/record/loadingAnimation.js
}
state recordCard#lightblue{
recordCard: path=src/componenets/record/recordCard.js
}
state recordDetail#lightblue{
recordDetail:path=src/componenets/record/recordCardDetail.js
}
state errorRecord#lightgreen{
errorRecord: path=/src/componenets/record/categoryMenu.js
}
state modal_from_application#lightblue{
modal_from_application: path=src/componenets/form/modal.js
}
state isLoading <<choice>>
state hasData <<choice>>
}
menuLayout --> desktopMenu
menuLayout --> categoryMenu
categoryMenu --> floatingButton
categoryMenu --> pageContent
pageContent --> isLoading
isLoading --> loadingAnimation:[isLoading: true]
isLoading --> hasData :[isLoading:false]
hasData -->emptyComponent:[no record data]
hasData --> recordCard : [has record data]
hasData --> errorRecord : [get record data error]
recordCard --> modal_from_application
modal_from_application --> recordDetail
@enduml
```
```plantuml
@startuml
state application #yellow{
state applicationPage #pink{
applicationPage: path=src/pages/form/applicationPage.js
}
state calendar #lightblue{
calendar: path=src/componenets/form/calendar.js
}
state form #lightblue{
form: path=src/componenets/form/form.js
}
state list #lightblue{
list: path=src/componenets/form/list.js
}
state loadingSpinner#lightblue{
loadingSpinner:src/componenets/form/loadingSpinner.js
}
state modal#lightblue{
modal:path=src/componenets/form/modal.js
}
state previewLeaveRecord#lightblue{
previewLeaveRecord: path=src/componenets/form/previewLeaveRecord.js
}
}
applicationPage --> form
form --> modal
form --> loadingSpinner
modal --> calendar
modal --> list
modal --> previewLeaveRecord
@enduml
```
```plantuml
@startuml
state result #yellow{
state failType <<choice>>
state resultPage#pink{
resultPage: path=src/pages/result/resultPage.js
}
state resultExpire#lightblue{
resultExpire:path=src/componenets/result/resultExpire.js
}
state resultNetworkFail#lightblue{
resultNetworkFail:path=src/componenets/result/resultNetworkFail.js
}
state resultSuccess#lightblue{
resultSuccess:path=src/componenets/result/resultSuccess.js
}
state resultSystemFail#lightblue{
resultSystemFail:path=src/componenets/result/resultSystemFail.js
}
}
resultPage --> failType
failType --> resultSuccess :[請假成功:成功送出請假]
failType --> resultExpire :[請假失敗:老師已送出點名單]
failType --> resultNetworkFail:[請假失敗:基於使用者網路連線導致的錯誤]
failType --> resultSystemFail :[請假失敗:因為校務系統或各種其他錯誤導致請假失敗]
@enduml
```