iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Modern Web

30 天製作工作室 SaaS 產品 (前端篇)系列 第 3

Day3:初始化前端專案與開發環境設定

  • 分享至 

  • xImage
  •  

為什麼要專門設計 OTP 管理介面?

昨天我們設計了前端整體架構,今天要開始實作第一個具體應用:Kyo-Dashboard OTP 管理介面。在接案過程中,客戶經常需要:

  • 查看簡訊發送記錄:了解發送狀況和失敗原因
  • 管理簡訊模板:不同場景使用不同的訊息內容
  • 監控發送統計:掌握使用量和成功率
  • 測試 OTP 功能:在正式環境測試簡訊發送

Kyo-Dashboard 要解決這些需求,提供完整的 OTP 服務管理功能。

Kyo-Dashboard 功能需求分析

核心功能模組

  • OTP 發送模組:手動發送測試、批次發送
  • 驗證記錄模組:查看所有發送和驗證記錄
  • 模板管理模組:建立、編輯、預覽簡訊模板
  • 統計分析模組:發送成功率、時間分布圖表

使用者體驗目標

  • 直觀的操作介面,非技術人員也能輕易使用
  • 即時的狀態回饋,清楚顯示操作結果
  • 響應式設計,支援各種裝置

建立前端應用基礎結構

1. 初始化 React + Vite 專案

cd apps
# 使用 Vite 建立 React + TypeScript 專案
pnpm create vite@latest kyo-dashboard --template react-ts

cd kyo-dashboard
# 安裝相依套件
pnpm install

2. 更新 package.json 配置

{
  "name": "@kyong/kyo-dashboard",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "typescript": "^5.2.2",
    "vite": "^5.2.0"
  }
}

3. 安裝必要的前端套件

# UI 框架和工具
pnpm add @mantine/core @mantine/hooks @mantine/form @mantine/notifications

# 狀態管理
pnpm add zustand

# 路由
pnpm add react-router-dom
pnpm add -D @types/react-router-dom

# 型別共享
pnpm add @kyong/kyo-types

# 開發工具
pnpm add -D @types/node

設定 TypeScript 配置

4. 建立 TypeScript 配置

// tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/pages/*": ["./src/pages/*"],
      "@/stores/*": ["./src/stores/*"],
      "@/services/*": ["./src/services/*"],
      "@/utils/*": ["./src/utils/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

5. Node.js 環境配置

// tsconfig.node.json
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

設定 Vite 建置工具

6. 配置 Vite

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@/components': path.resolve(__dirname, './src/components'),
      '@/pages': path.resolve(__dirname, './src/pages'),
      '@/stores': path.resolve(__dirname, './src/stores'),
      '@/services': path.resolve(__dirname, './src/services'),
      '@/utils': path.resolve(__dirname, './src/utils')
    }
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        changeOrigin: true
      }
    }
  },
  build: {
    outDir: 'dist',
    sourcemap: true
  }
})

建立專案目錄結構

7. 建立標準目錄

# 建立專案結構
mkdir -p src/components src/pages src/stores src/services src/utils src/types src/assets

# 建立基礎檔案
touch src/components/index.ts
touch src/pages/index.ts
touch src/stores/index.ts
touch src/services/index.ts
touch src/utils/index.ts

8. 建立基礎文件結構

src/
├── components/          # 共用組件
│   ├── Layout/         # 佈局組件
│   ├── OtpForm/        # OTP 表單組件
│   └── index.ts        # 組件匯出
├── pages/              # 頁面組件
│   ├── OtpSendPage.tsx
│   ├── OtpVerifyPage.tsx
│   └── index.ts
├── stores/             # Zustand 狀態管理
│   ├── otpStore.ts
│   ├── uiStore.ts
│   └── index.ts
├── services/           # API 服務層
│   ├── api.ts
│   ├── orpc.ts
│   └── index.ts
├── utils/              # 工具函數
│   ├── validators.ts
│   ├── formatters.ts
│   └── index.ts
├── assets/             # 靜態資源
├── App.tsx
├── main.tsx
└── index.css

設定 Mantine UI 框架

9. Mantine 基礎設定

// src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { MantineProvider } from '@mantine/core'
import { Notifications } from '@mantine/notifications'
import App from './App.tsx'

// Mantine CSS
import '@mantine/core/styles.css'
import '@mantine/notifications/styles.css'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <MantineProvider>
      <Notifications />
      <App />
    </MantineProvider>
  </React.StrictMode>,
)

10. 建立主要 App 組件

// src/App.tsx
import { AppShell, Container, Title } from '@mantine/core'

function App() {
  return (
    <AppShell
      header={{ height: 64 }}
      padding="md"
    >
      <AppShell.Header>
        <Container size="xl" h="100%" p="md">
          <Title order={2}>🔐 Kyo-Dashboard</Title>
        </Container>
      </AppShell.Header>

      <AppShell.Main>
        <Container size="md">
          <Title order={1} ta="center" mt="xl">
            歡迎使用 Kyo-System 管理台
          </Title>
          <p style={{ textAlign: 'center', marginTop: '1rem' }}>
            OTP 驗證服務管理介面
          </p>
        </Container>
      </AppShell.Main>
    </AppShell>
  )
}

export default App

設定開發工具

11. ESLint 配置

// .eslintrc.cjs
module.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    '@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': [
      'warn',
      { allowConstantExport: true },
    ],
  },
}

12. 環境變數設定

# .env.example
VITE_API_URL=http://localhost:8000
VITE_ORPC_ENDPOINT=/api/orpc
VITE_APP_NAME=Kyo-Dashboard
VITE_APP_VERSION=0.1.0
# .env.local
VITE_API_URL=http://localhost:8000
VITE_ORPC_ENDPOINT=/api/orpc
VITE_APP_NAME=Kyo-Dashboard
VITE_APP_VERSION=0.1.0

驗證專案設定

13. 測試開發伺服器

# 啟動開發伺服器
pnpm run dev

# 在另一個終端機測試建置
pnpm run build

# 檢查 TypeScript 編譯
pnpm exec tsc --noEmit

# 執行 linting
pnpm run lint

14. 檢查 Monorepo 整合

# 回到根目錄
cd ../../

# 測試 Turborepo 建置
pnpm run build

# 確認前端專案包含在 workspace 中
pnpm -r list --depth=0

建立基礎樣式

15. 全域樣式設定

/* src/index.css */
:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}

a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

#root {
  width: 100%;
  margin: 0 auto;
  text-align: center;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }

  a:hover {
    color: #747bff;
  }
}

今日成果

✅ React + Vite + TypeScript 專案初始化完成
✅ Mantine UI 框架整合與配置
✅ Monorepo workspace 整合測試
✅ TypeScript 路徑別名與型別配置
✅ ESLint 與開發工具設定
✅ 環境變數與建置配置
✅ 基礎專案結構建立

下一步規劃

Day4 我們會開始實作第一個核心組件:OTP 發送表單,整合 @kyong/kyo-types 型別定義,並建立 Zustand 狀態管理來處理 API 呼叫。


上一篇
Day2:Kyo-System 前端架構設計與技術選型
系列文
30 天製作工作室 SaaS 產品 (前端篇)3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言