---
# System prepended metadata

title: TWDH 驗證、帳戶管理、以及檔案與專案 (前端)

---

> [name=Hao-Cheng Lo]
> [time=Feb 8, 2023]

本文件旨在提供我在TWDH專案中擔任前端期間，負責的主要任務和項目的詳細概述。目的是讓接替我的工程師可以更輕鬆地理解和接手我的工作。主要的技術涵蓋 `React` 、 `Next` 、 `Redux` 、 `TS` 、 `Tailwind` 、以及 `AntD` 。我主要負責註冊、登入、自動化登入、驗證與頁面保護、個人帳號管理、重設與忘記密碼、檔案管理、專案管理、首頁設計等功能的開發和維護。

:::success
本文件中的代碼截圖，僅展示主要功能以及其目的。詳細代碼內容，請參考前端REPO。
:::


## 代碼庫結構

我們的代碼庫主要業務邏輯在於五個部分：`components`，`pages`，`models`，`api`，和`hooks`。每一個部分都有其特定的用途：

* `components`: 這裡包含所有我們應用中使用的React組件以及`HOC`。例如，`/Auth` 文件夾包含與用戶認證相關的所有組件。
* `pages`: 這裡包含我們的所有頁面，如`/login`，`/signup`等。每個頁面都對應一個具體的路由。
* `models`: 這裡包含我們的redux store和與其相關的所有操作，例如關於驗證的狀態管理如`/auth/authActions.ts`和`/auth/authReducer.ts`。
* `api`：這裡主要撰寫與其他服務器溝通的接口。實際接口的路由位置是在`util/apiConfig.ts`。
* `hooks`: 這裡包含我們的自定義Hooks，例如`/useFetchFilesProjects.ts`和`/useFetchUserData.ts`。

:::warning
目前本文件中的功能的api，皆是存於各類組件、hook、models中，未來可以獨立出來放置於`api`中，以增加可維護性。
:::

## 主要功能

在我負責的部分中，我主要管理並開發以下的功能，其中路由可以對應瀏覽器路由以及`pages`底下的頁面路徑：

0. **首頁 (`/`)**: 首頁展示各類此專案的目的與各類資訊。
1. **註冊 (`/signup`)**: 允許新用戶註冊一個帳號。完成註冊後，後端會發送一封帳號驗證信件。
2. **驗證結果頁面 (`/auth/[authToken]`)**: 新用戶點擊驗證信件中的連結會來到此頁面通知帳號驗證成功。
3. **登入 (`/login`)**: 允許已註冊並已驗證的用戶進行登入。
4. **自動化登入**: 在用戶登入後，如果他們再次訪問我們的應用，該功能將自動登入用戶。
5. **驗證與頁面保護**: 這部分使用高階組件（HOC）`withAuth`來保護需要驗證的頁面，只有已登入的用戶才能訪問。
6. **個人帳號管理 (`[user]/setting`)**: 讓用戶能夠管理他們的個人資訊。
7. **重設與忘記密碼 (`/pwreset/request` 與 `/pwreset/[resetToken]`)**: 如果用戶忘記了他們的密碼，他們可以要求重設。一旦請求被接受，後端會發送一封包含重設連結的郵件。
8. **檔案管理 (`[user]/sources`)**: 提供用戶一個可以上傳和管理他們的`csv`檔案的界面，並且可以創建專案。
9. **專案管理 (`[user]/projects`)**: 讓用戶能夠管理他們的專案。


在實現這些功能的過程中，我使用了Redux來管理應用的狀態（主要包含`auth`，`file`和`project`等），並創建了自定義Hooks（例如`useFetchFilesProjects`和`useFetchUserData`）來抓取和管理後端的數據。

上述功能的具體組件位於`components`文件夾下，並由對應的頁面在`pages`文件夾中調用。

## Redux Model

以下是Redux中的`auth`，`file`，和`project`模型的行為解釋：

1. **`auth`**：這個模型主要用於管理與使用者驗證機制相關的數據和行為。以下是它的主要功能：
    - 簡單行為：
      - `SIGN_UP_SUCCESS` 和 `SIGN_UP_FAILURE`：這兩種行為用於表示註冊成功或失敗。
      - `SET_TOKEN` 和 `REMOVE_TOKEN`：用於設置和移除用戶的認證令牌。
      - `SET_USER` 和 `REMOVE_USER`：用於設置和移除當前的用戶數據。
    - 複雜行為：
      - `initApp`：這個行為在應用啟動時執行，設置認證令牌、用戶數據和axios的請求頭。
      - `signUp`：這個行為讓新用戶可以註冊。
      - `logIn`：這個行為讓已註冊的用戶可以登入。
      - `logOut`：這個行為讓已登入的用戶可以登出，同時移除認證令牌、用戶數據和axios的請求頭。
2. **`file`**：這個模型主要用於管理與檔案處理相關的數據和行為。主要功能如下：
    - `ADD_UPLOADED`、`REMOVE_UPLOADED`、`CLEAR_UPLOADED` 和 `SET_UPLOADED`：這些行為用於管理已上傳檔案的列表。具體來說，它們分別用於新增已上傳檔案、移除已上傳檔案、清空已上傳檔案的列表和設置已上傳檔案的列表。
3. **`project`**：這個模型主要用於管理與專案處理相關的數據和行為。主要功能如下：
    - `ADD_PROJECT`、`REMOVE_PROJECT` 和 `SET_PROJECTS`：這些行為用於管理專案列表。具體來說，它們分別用於新增專案、移除專案和設置專案列表。
    - `SET_WORKING_PROJECT` 和 `UPDATE_WORKING_PROJECT`：這兩種行為用於設置和更新**當前的**工作專案。
    - `UPDATE_PROJECT_KEY_VALUE`：這個行為用於更新專案的特定鍵值。


:::warning
1. 目前僅列出我負責的功能，其他功能可以持續於文件中進行補充。
2. 按照redux原則，牽扯副作用的複雜原則（例如打API），可以撰寫於`middleware.ts`文件中。
3. 各個redux model中，`reducer.ts`文件中，定義了資料的存儲態，有需要者請前往查看。
:::


## 自定義 Hooks


當然，以下是本文件中涉及的自定義Hooks的說明：

1. **`useFetchFilesProjects`**：這個自定義Hook的主要目的是從後端API獲取檔案和專案數據並將其分派到Redux Store中。它是在本專案中需要抓取這些數據的地方會被調用的。主要邏輯如下：
    - 當調用這個Hook時，它會創建兩個函數`fetchProjects`和`fetchFiles`，這兩個函數分別用來從後端抓取專案和檔案數據。
    - 在每個函數中，它會嘗試向對應的API endpoint發送GET請求。然後，它會等待請求的回應，並將回應的數據處理成應用所需要的格式。
    - 然後，它會分派對應的Redux行為（`setProjects`和`setUploaded`）來更新Store中的數據。
    - 最後，它會在Hook的`useEffect`內調用這兩個函數，以確保在組件掛載時抓取數據。
    - 這個Hook返回一個包含這兩個函數的數組，以便在其他地方手動抓取數據。

==相關代碼片段==

![](https://hackmd.io/_uploads/r1Ivi1_Y3.png)


2. **`useFetchUserData`**：這個自定義Hook的主要目的是在應用啟動時或自動化登入時抓取並設置用戶數據。主要邏輯如下：
    - 當調用這個Hook時，它會創建一個`fetchUserData`函數。
    - 在這個函數中，它會首先檢查本地儲存是否有認證令牌。如果有，它會設置axios的請求頭，然後嘗試向後端的GetUser API發送一個POST請求來獲取用戶數據。
    - 然後，它會處理API的回應，提取新的認證令牌和用戶數據，並分派`initApp`行為來更新Store中的數據。
    - 最後，這個Hook會在`useEffect`中調用`fetchUserData`函數，以確保在應用啟動時抓取數據。它會檢查當前的路由，並只在需要的路由中進行這個操作。

==相關代碼片段==

![](https://hackmd.io/_uploads/r1BPhyOK2.png)

> 這兩個Hooks都適用於以異步方式從後端API抓取數據的情況，並且他們都利用了Redux來管理應用的狀態。它們提供了一種乾淨且組織良好的方式來處理這種常見的業務邏輯。

:::warning
1. **`useFetchFilesProjects`**：該Hook可能會在組件重新渲染時引發不必要的API請求，因為它在`useEffect`中無條件地調用了`fetchProjects`和`fetchFiles`。如果這些函數中的數據並未變化，這將導致不必要的網絡流量和效能消耗。未來可以考慮添加檢查機制，以避免在數據沒有變化的情況下進行不必要的數據請求。例如，可以在Redux Store中存儲一個標記來記錄何時需要重新抓取數據，並將此標記作為`useEffect`的依賴。
2. **`useFetchUserData`**：該Hook硬編碼了路由條件，可能限制了它的重用性。如果在其他路由下需要進行相同的邏輯，則可修改此Hook或者複製相同的邏輯。考慮將需要執行這種邏輯的路由作為參數傳入，使得該Hook更具重用性。另外，還可以考慮將`fetchUserData`函數抽取為一個獨立的函數，並在其他地方重用。
:::

## 頁面與其相關組件

### 首頁以及佈局 `/`

首頁主要展示本專案的相關最新資訊圖示以及各類主要路由的入口。

1. **頁面`pages/index.tsx`**：這是專案的主頁面，主要包含一些專案的介紹和入口。一開始，你會看到一個提示資訊讓使用者使用Chrome瀏覽器進入本系統。接著，有一個Hero區塊，這部分包含平台的目的說明以及兩個主要操作按鈕：開始匯入檔案和編輯既有資料庫。然後，你會看到四個主要的資料庫入口，以圖像的形式展示。最後，有一個輪播區塊，其中包含四個資料庫的詳細介紹。
2. **組件`components/organisms/Layout/FourDataBases.tsx`**：這個組件展示四個主要的史料庫縮圖和相關連結。每個史料庫的圖片上都附有點擊事件，當點擊時會新開一個頁面跳轉到該史料庫的網站。這個組件也實現了一種特殊的動畫效果，當滾動頁面時，每個史料庫的圖片會依序浮現出來。
3. **組件`components/organisms/Layout/AvatarDropdown.tsx`**：這是一個下拉選單組件，主要展示使用者的頭像和一些使用者相關的選項。當使用者點擊自己的頭像時，會出現一個下拉選單，其中包括修改個人資料和顯示使用者名稱兩個選項。使用者可以點擊修改個人資料來跳轉到個人資訊的設定頁面。
4. **佈局`components/organisms/Layout/Layout.tsx`**：這是一個佈局組件，主要負責網站的主要佈局，包括頂部的導覽列、主要內容區域以及頁尾。導覽列的部分，會根據使用者是否登入來顯示不同的內容。若使用者已經登入，則顯示使用者的名稱、頭像以及一些主要的功能按鈕。若使用者未登入，則顯示登入和註冊的按鈕。主要內容區域則是用來放置各個頁面的內容。頁尾包含網站的版權資訊，以及一些其他的連結。

==相關頁面==

**導覽列與Hero**

![](https://hackmd.io/_uploads/S1syuAcYh.png)

**台灣史料資料庫與史料庫介紹**

![](https://hackmd.io/_uploads/SJUYd09K3.png)

**Footer**

![](https://hackmd.io/_uploads/BJXs_AcYh.png)

### 註冊 `/signup`

註冊功能主要讓新用戶能創建一個帳號並進行驗證，接著才能進行登入。以下是相關的頁面和組件：

1. **頁面：`/pages/signup/index.tsx`**

這個頁面根據redux store中的`email`狀態來決定顯示哪個組件。若`email`不存在，則顯示`SignUpForm`組件，讓用戶填寫註冊資料。若`email`存在（也就是註冊成功），則顯示`EmailVerificationMessage`組件，提示用戶前往電子信箱進行驗證。為了防止透過路由讓使用者直接進入到寄發驗證信頁面，這邊使用`SPA`。

==頁面展示==

![](https://hackmd.io/_uploads/ByBFDPFth.png)


2. **組件：`/components/Auth/SignUpForm.tsx`**

這個組件顯示註冊表單並處理用戶提交的資料。當用戶提交表單時，`onFinish`函數會被觸發，這個函數會取出表單中的各項資料，並呼叫redux的`signUp`行為。當用戶成功註冊後，表單會被清空，並自動跳轉到寄送驗證信的頁面。值得注意的是，在`Form`中有對信箱、密碼做驗證校正確認。

==相關代碼==

![](https://hackmd.io/_uploads/BkmIlOYF2.png)

3. **組件：`/components/Auth/EmailVerificationMessage.tsx`**

這個組件顯示一個訊息給用戶，提示他們已經發送了驗證郵件到他們的信箱，並需要前往信箱進行驗證。這個訊息包含了一個標題、用戶的電子郵件、和一段說明文字。



:::warning
1. 未來可以嘗試結合第三方登入以及仿偽機制。
2. 在`EmailVerificationMessage.tsx`中，目前只是顯示一個靜態的訊息。可能未來可以讓這個訊息更動態一些，例如添加一個按鈕讓用戶可以重新發送驗證郵件等等。
:::





### 驗證結果頁面 `/auth/[authToken]`

驗證結果頁面的主要目的是根據用戶點擊驗證郵件中的鏈接，確認他們的電子郵件地址。以下是相關的頁面：

1. **頁面：`/pages/auth/[authToken]/index.tsx`**

這個頁面會根據URL中的驗證token向後端發送一個POST請求，來進行驗證。這個POST請求的回應將決定要顯示的訊息。如果驗證成功，則顯示“帳號驗證成功！”的訊息，並在畫面上顯示“帳號驗證成功！”；如果驗證失敗，則顯示“帳號驗證失敗！”的錯誤訊息，並導向首頁。

==相關代碼==

![](https://hackmd.io/_uploads/S1yrmOtKh.png)


:::warning
1. 目前，頁面會在驗證失敗時導向首頁，但不會在驗證成功時進行跳轉。考慮在驗證成功後，自動導向用戶到登入頁面，或者直接登入用戶並導向首頁。
2. 此外，當驗證正在進行時，畫面會顯示“帳號驗證中...”。可能會想要添加一個loading spinner或者其他視覺效果，來提供更好的用戶反饋。
:::



### 登入 `/login`

登入功能的主要目的是讓用戶能夠在我們的平台上登入和使用我們的服務。這個功能涉及到以下的頁面和組件：

1. **頁面：`/pages/login/index.tsx`**

該頁面負責顯示登入表單。它從`components/Auth/LogInForm.tsx`中導入`LogInForm`組件，然後在一個中心化的容器中渲染它。這個頁面的主要目的是提供一個讓用戶可以輸入他們的登入資訊的介面。

==頁面展示==

![](https://hackmd.io/_uploads/B1gMGldtn.png)

![](https://hackmd.io/_uploads/SyE7-_FFn.png)

==相關代碼==

![](https://hackmd.io/_uploads/HkpEZuKKn.png)

2. **組件：`/components/Auth/LogInForm.tsx`**

這個組件負責實現登入表單。在這個組件中，我們定義了一個`onFinish`函數，該函數在表單提交時被調用。這個函數會從表單中獲取用戶輸入的電子郵件(或是使用者名稱)和密碼，然後調用Redux中的`logIn`行為來嘗試登入用戶。如果登入成功，則將用戶導向首頁；如果登入失敗，則重新啟用表單讓用戶可以再次嘗試。此外，這個組件還定義了一個`requestResetPassword`函數，該函數在用戶點擊「忘記密碼？」的鏈接時被調用，將用戶導向密碼重設頁面。

==相關代碼==

![](https://hackmd.io/_uploads/HJfJzlOYn.png)


:::warning
1. 未來可以嘗試結合第三方登入以及仿偽機制。
:::

### 自動化登入功能與登出功能



在我們的系統中，我們實現了自動化登入的功能。這個功能在使用者打開網頁時，如果檢測到在localStorage中有JWT token，系統將會自動將使用者登入。這個功能是由`hooks/useFetchUserData.ts`完成的。同時，在使用者登入之後，我們會在導航欄上顯示出相關的資訊。另外，在登入後，登出按鍵即出現在導航欄之上，使用者可以透過其按鍵進行登出。

1. **頁面：`Layout`**

這個頁面是我們導航欄的主要組件。在這個組件中，我們會根據Redux store中的`token`和`user`的狀態來決定導航欄的內容。當使用者被驗證時，導航欄會顯示使用者的名字、用戶頭像，並提供檔案和專案的連結，以及一個用於登出的按鈕。當使用者未被驗證時，導航欄上會提供連結至登入和註冊頁面的按鈕。

==頁面展示==

登入前：

![](https://hackmd.io/_uploads/ByLtT_KK3.png)

登入後：

![](https://hackmd.io/_uploads/rkljpdtY2.png)

==相關代碼==

![](https://hackmd.io/_uploads/ryqZ0_Fth.png)

:::warning
1. 當前的導航欄在用戶狀態改變時（如登入或登出）會重新渲染，這可能會導致不必要的重繪和性能下降。一種可能的改善方式是使用React的memoization功能，如`React.memo`，來避免不必要的重繪。
:::


### 驗證與頁面保護

在我們的專案中，我們使用了一個叫做`withAuth`的高階組件(Higher-Order Component, HOC)來保護我們的路由。這個組件的目的是為了確保只有已驗證的使用者可以訪問特定的路由，未驗證的使用者會被導向主頁。

以下是`withAuth` HOC的詳細介紹：

1. **檔案位置：** `components/Auth/withAuth.tsx`
2. **目的：** 這個組件的目的是確保只有已驗證的使用者可以訪問特定的頁面。如果使用者未驗證，他們會被重定向到主頁。（請參考下方的代碼）。
3. **使用方法：** 要使用這個HOC，你只需要將需要保護的組件作為參數傳遞給`withAuth`函數。如：`export default withAuth(MyComponent);`
4. **邏輯：** 這個HOC使用了React的hooks。我們使用了`useEffect`來監聽`user`和`loggedInUsername`的變化。如果使用者名稱與登入的使用者名稱不一致，我們則將用戶重定向到主頁。我們也使用了`loading`狀態來在驗證過程中顯示一個載入畫面。

==相關代碼==

![](https://hackmd.io/_uploads/S1_EW3cYh.png)

:::warning
1. 思考更可靠的驗證方式，而非使用者名稱。
:::


### 個人帳號管理 `[user]/setting`

此頁面負責用戶的個人資料管理、上傳、以及重設密碼。

1. **頁面：`pages/[user]/setting/index.tsx`**

這個頁面基本上只是為了載入並呈現`SettingForm`元件。

==相關頁面==

修改表單資料

![](https://hackmd.io/_uploads/ByYkdhqK3.png)


修改頭貼

![](https://hackmd.io/_uploads/Hy7_t3cK2.png)




2. **組件：`components/Setting/SettingForm.tsx`**

在`SettingForm`元件中，我們首先從Redux的store中取得目前用戶的資料，這些資料主要用於初始化表單的初始值（用戶名稱與電子郵件是無法被修改的）。

在`onFinish`函數中，我們會在表單驗證通過後，把表單中的值取出並用來組成一個FormData物件。我們也將上傳的用戶頭像（如果有的話）添加到這個FormData物件中。

然後，我們使用axios對API進行PATCH請求，以便更新用戶設定。如果此請求成功，我們將從回應中取得更新後的用戶數據，並將它們儲存到Redux的store中。否則，我們將向用戶顯示錯誤訊息。

最後，我們將表單的disabled狀態設回false，以便用戶可以再次編輯和提交表單。

在表單的佈局上，我們主要使用了antd的`Form.Item`元件來建立表單欄位，並使用了antd的`Input`和`Input.Password`元件來取得用戶的輸入。

==相關代碼==

![](https://hackmd.io/_uploads/BksTL3cKh.png)

3. **組件：`components/Setting/AvatarUpload.tsx`**

`AvatarUpload`元件主要負責處理用戶頭像的上傳和預覽。在此元件中，我們首先從Redux的store中取得目前用戶的頭像URL，並將它設定為預設的圖像陣列（如果沒有的話，即使用前端固定頭像`public/images/user7.jpg`）。

在`onChange`函數中，我們會在文件列表改變時更新本地的圖像陣列狀態和上傳的圖片。為了只允許一個圖片，我們在添加新的圖片時會移除舊的圖片。

在`onPreview`函數中，我們會在用戶點擊預覽按鈕時打開一個新的視窗來顯示圖片。

在`onRemove`函數中，我們會在用戶點擊移除按鈕時清空本地的文件列表。

這個元件使用了antd的`Upload`元件來處理文件的上傳，並使用了`ImgCrop`元件來提供圖片的裁剪功能。上傳按鈕的文字會根據文件列表的長度來改變，如果文件列表是空的，那麼按鈕的文字會是"上傳頭像"，否則，它會是"修改頭像"。

==相關代碼==

![](https://hackmd.io/_uploads/rkgWw2cth.png)



### 重設與忘記密碼 `/pwreset/request` & `/pwreset/[resetToken]`

這部分包含了重設密碼和請求重設密碼的流程。可以由登入頁面中的忘記密碼進入，亦可以由個人帳戶管理中的重設密碼進入。

#### 發送重設密碼信件

1. **頁面`pages/pwreset/request/index.tsx` 和組件`components/Auth/RequestReset.tsx`**

`pages/pwreset/request/index.tsx`是請求重設密碼的主頁面，它載入並顯示`RequestReset`元件。

在`RequestReset`元件中，用戶輸入他們的電子郵件地址並提交。然後，程式碼將輸入的電子郵件地址作為POST請求的內容，發送到API的`RequestPasswordReset`端點。

如果此請求成功，將顯示一條成功的消息，**告知用戶已發送重設密碼的郵件**。如果此請求失敗，將顯示一條錯誤消息。

==相關頁面==

![](https://hackmd.io/_uploads/ryAiJp5Y2.png)

#### 重設密碼

1. **頁面`pages/pwreset/[resetToken]/index.tsx` 和組件 `components/Auth/ResetPassword.tsx`**

`pages/pwreset/[resetToken]/index.tsx`是重設密碼的主頁面，它載入並顯示`ResetPassword`元件。

在`ResetPassword`元件中，用戶輸入新密碼並提交。然後，程式碼將輸入的新密碼、確認密碼，以及從URL中提取的重設令牌一起作為POST請求的內容，發送到API的`ConfirmPasswordReset`端點。

如果此請求成功，將顯示一條成功的消息，並導向登入頁面，以便用戶可以用新的密碼登入。如果此請求失敗，將顯示一條錯誤消息。

在每個表單元件中，我們都用到了antd的`Form`和`Form.Item`元件來創建和驗證表單。另外，我們還使用了antd的`Input`和`Input.Password`元件來獲取用戶的輸入，並使用了antd的`Button`元件來創建提交按鈕。

==相關頁面==

![](https://hackmd.io/_uploads/HJERk6cYh.png)

### 檔案管理 `[user]/sources`

這裡是關於使用者上傳與管理他們的資料源（CSV檔案）之頁面以其功能。==由於相關代碼過多，故以下說明主要邏輯與相關變數與函數，詳細代碼請至相關檔案位置查看。==


1. **頁面`pages/[user]/sources/index.tsx`**

此頁面引入`FileList`組件，該組件將負責渲染和管理使用者的所有CSV檔案。


2. **組件`components/File/FileList.tsx`**

`FileList` 組件主要用於呈現一個用戶已上傳的 CSV 檔案列表，以及一些相關操作如新增、修改檔案，建立專案等功能。

1. 首先，我們從 Redux store 中取得當前的用戶名(`uploader`)和已上傳的 CSV 檔案(`uploadedCsv`)。
2. 然後我們創建了一些 `useState` 變數來管理模態窗口的狀態 (`open`, `openProject`, `openInfo`)，當前選擇的檔案 (`currentFile`)，是否正在加載 (`loading`)，搜索文字 (`searchText`)，以及選定的表格行鍵 (`selectedRowKeys`)。
3. `updateCsvToBackend` 函數負責**更新檔案名稱**並將其儲存到後端。首先從 `currentFile` 中取出檔案的 id，然後利用 `axios` 發送 PATCH 請求到後端服務以更新對應的檔案名稱。一旦收到服務器的響應，就會將新的檔案資訊符合要求格式並保存在 `formatedUpdatedFile` 中。然後，這個新的檔案資訊會被分派到 Redux store，以便更新應用程式的狀態。若過程中出現錯誤，則捕捉錯誤並顯示錯誤訊息。
4. `onSelectChange` 函數則用於處理表格行的選擇變化。當使用者選擇或取消選擇表格行時，這個函數會被調用，並將新的行鍵設置到 `selectedRowKeys` 的狀態中。以便後續做專案建立使用。
5. 組件還定義了一些其他的函數，例如 `showModal`, `handleInfoCancel`, `handleCompleteRenewTable`, `showProjectModal`, `handleProjectClose`, `handleInfoOnClick` 等，這些函數主要用於處理模態窗口的開啟和關閉，以及根據用戶的操作來更新組件的狀態或觸發其他操作。
6. 在 `useEffect` 中，我們重置表單欄位並設置當前檔案的名稱作為更新檔案名稱的初始值。
7. `columns` 是一個陣列，定義了表格的列，包括列標題、數據索引、渲染函數等。
8. 最後，在組件的返回部分，我們渲染了一個 `Table` 組件，並將 `uploadedCsv` 作為數據源傳遞給它，其中我們實現表格中的查詢與排序功能。我們也渲染了一些 `Modal` 組件，用於上傳檔案、建立專案和修改檔案資訊。每個 `Modal` 都有一個表單，用戶可以在這些表單中輸入資訊，如新的檔案名稱等。

這就是 `FileList` 組件的主要邏輯和功能。在使用中，這個組件會連接到 Redux store，並根據 store 的狀態以及用戶的操作來更新自己的狀態和渲染畫面。

==相關頁面==

檔案列表、查詢、排序、以及選擇

![](https://hackmd.io/_uploads/BJ2YXC9t2.png)

![](https://hackmd.io/_uploads/HJq0mCcY3.png)

更改檔案名稱

![](https://hackmd.io/_uploads/B1jeE0ct3.png)


3. **組件`components/File/FileUpload.tsx`**

`FileUpload` 組件主要負責上傳 CSV 檔案的功能。它提供了上傳、預覽和管理上傳檔案的功能。以下是它的主要邏輯和功能：

1. 組件接收三個來自父組件的參數：`open`（用於判斷上傳模態框是否打開）、`setOpen`（控制模態框的開關）、`handleCompleteRenewTable`（一個用於更新檔案列表的函數）。
2. 組件利用 `useState` 創建了兩個狀態變數 `fileList` 和 `existFiles` 分別用於存儲即將上傳的檔案列表和已存在的檔案列表。
3. 在組件的 `useEffect` 中，當 `fileList` 狀態更新時會檢查所有檔案是否已完成上傳，如果已完成則會在一段時間後清空檔案列表。
4. `handleBeforeUpload` 函數在檔案上傳之前被調用，該函數主要進行檔案的基本檢查，例如檢查是否為 CSV 檔案、是否已經上傳過、是否已經在上傳列表中等，如果檔案符合條件，則將檔案添加到 `fileList`。
5. `handleUpload` 函數用於將檔案上傳到服務器，該函數首先創建一個 `FormData` 對象，然後將檔案及其相關資訊添加到 `FormData` 對象中，最後使用 `axios` 發送 POST 請求將檔案及其相關資訊發送到服務器。
6. 組件還定義了一些其他的函數，例如 `handleClose`, `handleUploadAll`, `renderStatus`, `renderDropdown`, `renderPreviewImage`, `renderDescription`, `renderFileItem`, `renderHeader` 等，這些函數主要用於管理組件的狀態、渲染 UI 元素、處理用戶的操作等。
7. 在組件的返回部分，我們渲染了一個 `Upload.Dragger` 組件，用戶可以點擊或拖曳檔案到該組件上傳檔案。我們也渲染了一個 `List` 組件，用於顯示即將上傳的檔案列表，用戶可以在此查看檔案的上傳狀態，也可以取消上傳(由圖標UI展示)。

總的來說，這個組件提供了一個使用者友好的界面讓用戶能輕鬆上傳 CSV 檔案，並實時查看上傳進度和狀態。

==相關頁面==

![](https://hackmd.io/_uploads/SyeJHCcYn.png)

4. **組件`components/File/CreateProject.tsx`**

`CreateProject` 組件，它是負責在前端創建新專案的功能部分。組件接收了四個來自父組件的屬性：`open`（決定是否打開創建專案的界面）、`setOpen`（控制創建專案界面的開關）、`selectedRowKeys`（存儲用戶所選擇的要添加到新專案的 CSV 檔案）和 `setSelectedRowKeys`（更新用戶所選擇的 CSV 檔案）。

以下是該組件的主要功能和邏輯：

1. 使用 `useSelector` 從 Redux store 中獲取當前用戶的名稱以及上傳的 CSV 檔案。
2. 定義了 `createProjectToBackend` 函數，該函數用於將新專案的信息（包括專案名、描述、所有者以及要加入的 CSV 檔案等）發送到後端。
3. `onFinish` 函數在表單完成驗證並提交時被調用。該函數會調用 `createProjectToBackend` 函數創建新的專案，並更新相關的狀態，最後將頁面跳轉到專案列表頁面。
4. `useEffect` 用來檢測當沒有選擇任何檔案時，會重置表單並關閉創建專案的界面。
5. 組件還渲染了一個表單以及一個表格。表單用於輸入新專案的基本信息，包括專案名稱和描述。表格則用於顯示用戶選擇的要添加到新專案的 CSV 檔案。

總的來說，`CreateProject` 組件提供了一個界面讓用戶能夠輕鬆地創建新的專案並選擇要添加到新專案的 CSV 檔案。

==相關頁面==

![](https://hackmd.io/_uploads/Bkrzr09Y2.png)


### 專案管理 `[user]/projects`

這裡是關於用戶的專案列表以及每個專案的卡片展示。以下是各個部分的詳細解釋。

1. **頁面`pages/[user]/projects/index.tsx`**

這是用戶專案列表的主頁面，它將呈現所有用戶的專案。用戶可以看到每個專案的名稱、說明和關聯的CSV檔案，並且可以進行一些操作，如欄位對應、新增檔案、資料編輯和檢索頁面等。

此頁面使用Redux的`useSelector`來取得當前登入用戶的專案列表。專案列表是依據每個專案的建立時間或更新時間進行排序的。此頁面還使用了`useRef`來取得DOM元素的引用，並使用`useEffect`來處理滾輪滾動事件，使專案列表可以在水平方向與垂直方向進行滾動。

==相關頁面==

![](https://hackmd.io/_uploads/HyKdraqtn.png)

==相關代碼==

![](https://hackmd.io/_uploads/rkPe-a9t2.png)

2. **組件`components/Project/ProjectCard.tsx`**

此元件主要負責顯示單個專案的詳細資訊和提供一些用戶交互功能。以下是該元件的一些重要部分及其邏輯：

1. **專案詳情**：首先，元件將顯示專案的名稱和描述，這些資訊來自於傳入的`project`屬性。同時，還會根據`project.sourceCsvs`的值（即與專案相關聯的CSV檔案ID列表），從Redux狀態中找到相對應的檔案，並將它們的名稱顯示在專案卡片上。
2. **操作按鈕**：專案卡片提供了一系列操作按鈕，允許用戶對專案進行欄位對應、資料編輯，檢視檢索頁面，並新增CSV檔案。這些操作都有對應的圖標和點擊事件處理函數。與專案相關的操作，例如欄位對應和資料編輯，它們的可用性會依賴於專案的狀態（是否已經完成對應或是否已經生成檢索頁面）。
3. **新增CSV檔案**：專案卡片還提供了一個彈出式選單，用戶可以在其中選擇要新增到專案的CSV檔案。當用戶選擇一個檔案並點擊"新增"按鈕後，元件會發送一個PATCH請求到後端，將新的CSV檔案ID新增到專案的`sourceCsvs`欄位。如果請求成功，元件會更新Redux狀態中的專案資訊，並顯示一個成功的訊息。如果請求失敗，則會顯示一個錯誤訊息。
4. **刪除專案**：在專案卡片上還有一個"刪除專案"的按鈕，用戶可以點擊該按鈕來刪除專案。當用戶點擊該按鈕時，會彈出一個確認對話框詢問用戶是否確定要刪除該專案。如果用戶確定要刪除，元件會發送一個DELETE請求到後端以刪除該專案。如果請求成功，元件會從Redux狀態中移除該專案，並顯示一個成功的訊息。如果請求失敗，則會顯示一個錯誤訊息。

在整個元件的設計中，使用了React的Hooks（如`useState`、`useRef`、`useEffect`等）來管理元件的狀態和處理生命週期事件。同時，也使用了Redux來管理應用的全局狀態，並透過dispatch操作來更新狀態。此外，這個元件還利用axios庫來與後端API進行通訊，執行像是更新專案資訊或刪除專案這樣的操作。

==相關頁面==

專案卡片

![](https://hackmd.io/_uploads/r1JjHT9K3.png)

刪除專案

![](https://hackmd.io/_uploads/HyQnSpcK3.png)

==相關代碼==

邏輯

![](https://hackmd.io/_uploads/SyVl5TcK2.png)

Layout

![](https://hackmd.io/_uploads/Sy_OKpctn.png)




## 相關連結、參考、以及學習資料


#### 內部資訊

- Github Repo：https://github.com/DocuSkyGithub/DH-frontend
- API文件：https://hackmd.io/H3nhl0KnRZ-tUHuLmSfVdQ

#### 外部連結

1. React：https://react.dev/
2. Next：https://nextjs.org/
3. Tailwind：https://tailwindcss.com/
4. Ant-Design：https://ant.design/

#### 聯絡我

- Hao-Cheng Lo 駱皓正(austenpsy@gmail.com) 