---
# System prepended metadata

title: 1101 Design Review
tags: [Design Review]

---

---
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
![](https://i.imgur.com/0FLanHv.jpg)
### 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)                |                                      |                                                   |


![](https://i.imgur.com/PPZLREO.png)

## 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
```
