From d96b0a511d542bff8630dec9b4db4528e17a7a50 Mon Sep 17 00:00:00 2001 From: aken1023 Date: Fri, 1 Aug 2025 00:55:05 +0800 Subject: [PATCH] init --- .gitignore | 27 + .vscode/settings.json | 18 + README.md | 219 ++ app/admin/page.tsx | 605 +++++ app/api/database/test/route.ts | 29 + app/api/kpi/[id]/progress/route.ts | 19 + app/api/kpi/[id]/route.ts | 31 + app/api/kpi/route.ts | 32 + app/api/reviews/route.ts | 34 + app/api/users/route.ts | 25 + app/globals.css | 90 + app/kpi/page.tsx | 739 ++++++ app/layout.tsx | 52 + app/page.tsx | 446 ++++ app/progress/page.tsx | 385 +++ app/reports/page.tsx | 455 ++++ app/reviews/page.tsx | 621 +++++ components.json | 21 + components/navigation.tsx | 69 + components/theme-provider.tsx | 11 + components/ui/accordion.tsx | 58 + components/ui/alert-dialog.tsx | 141 ++ components/ui/alert.tsx | 59 + components/ui/aspect-ratio.tsx | 7 + components/ui/avatar.tsx | 50 + components/ui/badge.tsx | 36 + components/ui/breadcrumb.tsx | 115 + components/ui/button.tsx | 56 + components/ui/calendar.tsx | 213 ++ components/ui/card.tsx | 79 + components/ui/carousel.tsx | 262 ++ components/ui/chart.tsx | 365 +++ components/ui/checkbox.tsx | 30 + components/ui/collapsible.tsx | 11 + components/ui/command.tsx | 153 ++ components/ui/context-menu.tsx | 200 ++ components/ui/dialog.tsx | 122 + components/ui/drawer.tsx | 118 + components/ui/dropdown-menu.tsx | 200 ++ components/ui/form.tsx | 178 ++ components/ui/hover-card.tsx | 29 + components/ui/input-otp.tsx | 71 + components/ui/input.tsx | 22 + components/ui/label.tsx | 26 + components/ui/menubar.tsx | 236 ++ components/ui/navigation-menu.tsx | 128 + components/ui/pagination.tsx | 117 + components/ui/popover.tsx | 31 + components/ui/progress.tsx | 28 + components/ui/radio-group.tsx | 44 + components/ui/resizable.tsx | 45 + components/ui/scroll-area.tsx | 48 + components/ui/select.tsx | 160 ++ components/ui/separator.tsx | 31 + components/ui/sheet.tsx | 140 ++ components/ui/sidebar.tsx | 763 ++++++ components/ui/skeleton.tsx | 15 + components/ui/slider.tsx | 28 + components/ui/sonner.tsx | 31 + components/ui/switch.tsx | 29 + components/ui/table.tsx | 117 + components/ui/tabs.tsx | 55 + components/ui/textarea.tsx | 22 + components/ui/toast.tsx | 129 + components/ui/toaster.tsx | 35 + components/ui/toggle-group.tsx | 61 + components/ui/toggle.tsx | 45 + components/ui/tooltip.tsx | 30 + components/ui/use-mobile.tsx | 19 + components/ui/use-toast.ts | 194 ++ hooks/use-mobile.tsx | 19 + hooks/use-toast.ts | 194 ++ lib/database.ts | 349 +++ lib/utils.ts | 6 + next.config.mjs | 14 + package.json | 75 + pnpm-lock.yaml | 3678 ++++++++++++++++++++++++++++ postcss.config.mjs | 8 + public/placeholder-logo.png | Bin 0 -> 568 bytes public/placeholder-logo.svg | 1 + public/placeholder-user.jpg | Bin 0 -> 1635 bytes public/placeholder.jpg | Bin 0 -> 1064 bytes public/placeholder.svg | 1 + scripts/check-db-structure.js | 97 + scripts/check-env.js | 21 + scripts/create-database-schema.sql | 171 ++ scripts/init-database.js | 275 +++ scripts/reset-database.js | 296 +++ scripts/setup-env.js | 27 + scripts/simple-db-check.js | 52 + scripts/test-api.js | 101 + scripts/test-db-connection.js | 51 + scripts/test-kpi-data.js | 64 + styles/globals.css | 90 + tailwind.config.ts | 98 + tsconfig.json | 27 + 96 files changed, 14825 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 app/admin/page.tsx create mode 100644 app/api/database/test/route.ts create mode 100644 app/api/kpi/[id]/progress/route.ts create mode 100644 app/api/kpi/[id]/route.ts create mode 100644 app/api/kpi/route.ts create mode 100644 app/api/reviews/route.ts create mode 100644 app/api/users/route.ts create mode 100644 app/globals.css create mode 100644 app/kpi/page.tsx create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 app/progress/page.tsx create mode 100644 app/reports/page.tsx create mode 100644 app/reviews/page.tsx create mode 100644 components.json create mode 100644 components/navigation.tsx create mode 100644 components/theme-provider.tsx create mode 100644 components/ui/accordion.tsx create mode 100644 components/ui/alert-dialog.tsx create mode 100644 components/ui/alert.tsx create mode 100644 components/ui/aspect-ratio.tsx create mode 100644 components/ui/avatar.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/carousel.tsx create mode 100644 components/ui/chart.tsx create mode 100644 components/ui/checkbox.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/command.tsx create mode 100644 components/ui/context-menu.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/drawer.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/hover-card.tsx create mode 100644 components/ui/input-otp.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/menubar.tsx create mode 100644 components/ui/navigation-menu.tsx create mode 100644 components/ui/pagination.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/progress.tsx create mode 100644 components/ui/radio-group.tsx create mode 100644 components/ui/resizable.tsx create mode 100644 components/ui/scroll-area.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/slider.tsx create mode 100644 components/ui/sonner.tsx create mode 100644 components/ui/switch.tsx create mode 100644 components/ui/table.tsx create mode 100644 components/ui/tabs.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/toast.tsx create mode 100644 components/ui/toaster.tsx create mode 100644 components/ui/toggle-group.tsx create mode 100644 components/ui/toggle.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 components/ui/use-mobile.tsx create mode 100644 components/ui/use-toast.ts create mode 100644 hooks/use-mobile.tsx create mode 100644 hooks/use-toast.ts create mode 100644 lib/database.ts create mode 100644 lib/utils.ts create mode 100644 next.config.mjs create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 postcss.config.mjs create mode 100644 public/placeholder-logo.png create mode 100644 public/placeholder-logo.svg create mode 100644 public/placeholder-user.jpg create mode 100644 public/placeholder.jpg create mode 100644 public/placeholder.svg create mode 100644 scripts/check-db-structure.js create mode 100644 scripts/check-env.js create mode 100644 scripts/create-database-schema.sql create mode 100644 scripts/init-database.js create mode 100644 scripts/reset-database.js create mode 100644 scripts/setup-env.js create mode 100644 scripts/simple-db-check.js create mode 100644 scripts/test-api.js create mode 100644 scripts/test-db-connection.js create mode 100644 scripts/test-kpi-data.js create mode 100644 styles/globals.css create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5f26951 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "sqltools.connections": [ + { + "mysqlOptions": { + "authProtocol": "default", + "enableSsl": "Disabled" + }, + "previewLimit": 50, + "server": "mysql.theaken.com", + "port": 33306, + "driver": "MySQL", + "name": "mysql.theaken.com", + "group": "mysql.theaken.com", + "database": "db_A022", + "username": "A022" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5cb306 --- /dev/null +++ b/README.md @@ -0,0 +1,219 @@ +# 執行管理績效儀表板 + +一個現代化的執行管理績效儀表板系統,使用 Next.js 和 MySQL 資料庫構建。 + +## 🚀 功能特色 + +- **📊 KPI 管理**: 完整的關鍵績效指標設定、追蹤和管理 +- **👥 用戶管理**: 多角色用戶系統(執行長、經理、HR) +- **📈 進度追蹤**: 即時進度更新和視覺化圖表 +- **📋 評審系統**: 績效評審安排和管理 +- **📊 報告生成**: 自動化報告和數據分析 +- **🎨 現代化 UI**: 響應式設計,支援深色/淺色主題 + +## 🛠 技術架構 + +- **前端**: Next.js 15, React 19, TypeScript +- **UI 框架**: Tailwind CSS, Radix UI +- **資料庫**: MySQL 8.0+ +- **圖表**: Recharts +- **表單**: React Hook Form + Zod +- **狀態管理**: React Hooks + +## 📋 系統需求 + +- Node.js 18+ +- MySQL 8.0+ +- pnpm (推薦) 或 npm + +## 🚀 快速開始 + +### 1. 克隆專案 + +```bash +git clone +cd executive-performance-dashboard +``` + +### 2. 安裝依賴 + +```bash +pnpm install +``` + +### 3. 環境設定 + +創建 `.env.local` 檔案: + +```env +# MySQL 資料庫設定 +DB_HOST=mysql.theaken.com +DB_PORT=33306 +DB_DATABASE=db_A022 +DB_USERNAME=A022 +DB_PASSWORD=NNyTXRWWtP4b + +# Next.js 設定 +NEXT_PUBLIC_APP_URL=http://localhost:3006 +``` + +### 4. 初始化資料庫 + +```bash +# 測試資料庫連接 +pnpm db:test + +# 初始化資料庫結構和測試數據 +pnpm db:init +``` + +### 5. 啟動開發伺服器 + +```bash +pnpm dev +``` + +訪問 [http://localhost:3006](http://localhost:3006) 查看應用程式。 + +## 📊 資料庫結構 + +### 主要資料表 + +- **users**: 用戶資訊和角色管理 +- **kpi**: 關鍵績效指標定義 +- **kpi_progress**: KPI 進度追蹤記錄 +- **reviews**: 績效評審安排 +- **reports**: 報告生成記錄 + +### 資料關係 + +``` +users (1) ←→ (N) kpi +kpi (1) ←→ (N) kpi_progress +users (1) ←→ (N) reviews (作為 reviewer) +users (1) ←→ (N) reviews (作為 reviewee) +``` + +## 🔧 API 端點 + +### KPI 管理 +- `GET /api/kpi` - 獲取 KPI 列表 +- `POST /api/kpi` - 創建新 KPI +- `PUT /api/kpi/[id]` - 更新 KPI +- `DELETE /api/kpi/[id]` - 刪除 KPI +- `POST /api/kpi/[id]/progress` - 更新 KPI 進度 + +### 用戶管理 +- `GET /api/users` - 獲取用戶列表 +- `POST /api/users` - 創建新用戶 + +### 評審管理 +- `GET /api/reviews` - 獲取評審列表 +- `POST /api/reviews` - 創建新評審 + +### 系統管理 +- `GET /api/database/test` - 測試資料庫連接 + +## 📁 專案結構 + +``` +executive-performance-dashboard/ +├── app/ # Next.js App Router +│ ├── api/ # API 路由 +│ ├── kpi/ # KPI 管理頁面 +│ ├── progress/ # 進度追蹤頁面 +│ ├── reports/ # 報告頁面 +│ ├── reviews/ # 評審頁面 +│ └── admin/ # 管理員頁面 +├── components/ # React 組件 +│ ├── ui/ # 基礎 UI 組件 +│ └── navigation.tsx # 導航組件 +├── lib/ # 工具函數 +│ ├── database.ts # 資料庫連接和服務 +│ └── utils.ts # 通用工具函數 +├── scripts/ # 腳本檔案 +│ ├── create-database-schema.sql # 資料庫結構 +│ ├── init-database.js # 初始化腳本 +│ └── test-db-connection.js # 連接測試 +└── public/ # 靜態資源 +``` + +## 🎯 使用指南 + +### 管理員功能 + +1. **用戶管理**: 在管理員頁面新增、編輯、刪除用戶 +2. **KPI 審核**: 審核和批准 KPI 設定 +3. **系統監控**: 查看系統使用統計和報告 + +### 經理功能 + +1. **KPI 設定**: 為團隊成員設定 KPI +2. **進度追蹤**: 定期更新 KPI 進度 +3. **評審安排**: 安排績效評審會議 + +### 員工功能 + +1. **進度更新**: 更新個人 KPI 進度 +2. **評審參與**: 參與績效評審 +3. **報告查看**: 查看個人績效報告 + +## 🔒 安全性 + +- 所有資料庫查詢使用參數化查詢防止 SQL 注入 +- 環境變數管理敏感資訊 +- API 端點包含錯誤處理和驗證 + +## 🧪 測試 + +```bash +# 測試資料庫連接 +pnpm db:test + +# 運行 lint 檢查 +pnpm lint + +# 建置專案 +pnpm build +``` + +## 📈 部署 + +### 生產環境部署 + +1. 設定生產環境變數 +2. 建置專案: `pnpm build` +3. 啟動生產伺服器: `pnpm start` + +### Docker 部署 + +```dockerfile +FROM node:18-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +RUN npm run build +EXPOSE 3006 +CMD ["npm", "start"] +``` + +## 🤝 貢獻 + +1. Fork 專案 +2. 創建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交變更 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 開啟 Pull Request + +## 📄 授權 + +本專案採用 MIT 授權 - 查看 [LICENSE](LICENSE) 檔案了解詳情。 + +## 📞 支援 + +如有問題或建議,請開啟 Issue 或聯繫開發團隊。 + +--- + +**開發團隊** - 執行管理績效儀表板專案組 \ No newline at end of file diff --git a/app/admin/page.tsx b/app/admin/page.tsx new file mode 100644 index 0000000..cbd6d91 --- /dev/null +++ b/app/admin/page.tsx @@ -0,0 +1,605 @@ +"use client" + +import { useState } from "react" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Textarea } from "@/components/ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Avatar, AvatarFallback } from "@/components/ui/avatar" +import { Switch } from "@/components/ui/switch" +import { Users, Settings, Target, MessageSquare, Plus, Edit, Trash2, Shield, Database, Bell } from "lucide-react" + +// Mock data with colors +const users = [ + { + id: 1, + name: "陳雅雯", + email: "sarah.chen@company.com", + role: "executive", + status: "active", + lastLogin: "2024-02-10", + department: "業務部", + position: "業務副總", + color: "emerald", + }, + { + id: 2, + name: "王志明", + email: "mike.wang@company.com", + role: "manager", + status: "active", + lastLogin: "2024-02-09", + department: "技術部", + position: "技術長", + color: "blue", + }, + { + id: 3, + name: "李美玲", + email: "lisa.lee@company.com", + role: "executive", + status: "inactive", + lastLogin: "2024-01-28", + department: "行銷部", + position: "行銷長", + color: "purple", + }, + { + id: 4, + name: "張建國", + email: "john.chang@company.com", + role: "manager", + status: "active", + lastLogin: "2024-02-08", + department: "研發部", + position: "研發總監", + color: "orange", + }, +] + +const kpiTemplates = [ + { id: 1, name: "營收成長率", category: "財務", defaultWeight: 30, isActive: true, usageCount: 15, color: "emerald" }, + { id: 2, name: "團隊滿意度", category: "團隊", defaultWeight: 25, isActive: true, usageCount: 12, color: "blue" }, + { id: 3, name: "市場佔有率", category: "營運", defaultWeight: 20, isActive: true, usageCount: 8, color: "purple" }, + { id: 4, name: "創新指數", category: "創新", defaultWeight: 25, isActive: false, usageCount: 3, color: "orange" }, + { id: 5, name: "客戶滿意度", category: "營運", defaultWeight: 20, isActive: true, usageCount: 10, color: "cyan" }, + { id: 6, name: "成本控制率", category: "財務", defaultWeight: 15, isActive: true, usageCount: 6, color: "red" }, +] + +const questionTemplates = [ + { id: 1, question: "您如何評價本季度的整體表現?", type: "評分", category: "績效", isRequired: true, color: "blue" }, + { id: 2, question: "本期間您的主要成就是什麼?", type: "文字", category: "績效", isRequired: true, color: "emerald" }, + { + id: 3, + question: "您面臨了哪些挑戰,如何克服?", + type: "文字", + category: "挑戰", + isRequired: false, + color: "yellow", + }, + { id: 4, question: "您如何有效領導團隊?", type: "評分", category: "領導力", isRequired: true, color: "purple" }, + { id: 5, question: "下一季度的目標是什麼?", type: "文字", category: "目標", isRequired: true, color: "orange" }, +] + +const systemSettings = [ + { id: 1, name: "自動提醒", description: "自動發送審查提醒通知", enabled: true, category: "通知" }, + { id: 2, name: "郵件通知", description: "透過郵件發送系統通知", enabled: true, category: "通知" }, + { id: 3, name: "資料備份", description: "每日自動備份系統資料", enabled: true, category: "安全" }, + { id: 4, name: "雙重驗證", description: "要求使用者啟用雙重驗證", enabled: false, category: "安全" }, + { id: 5, name: "審計日誌", description: "記錄所有系統操作日誌", enabled: true, category: "安全" }, +] + +export default function AdminPanel() { + const [activeTab, setActiveTab] = useState("users") + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [dialogType, setDialogType] = useState("") + + const handleOpenDialog = (type: string) => { + setDialogType(type) + setIsDialogOpen(true) + } + + const adminStats = { + totalUsers: users.length, + activeUsers: users.filter((u) => u.status === "active").length, + totalKPIs: kpiTemplates.length, + activeKPIs: kpiTemplates.filter((k) => k.isActive).length, + totalQuestions: questionTemplates.length, + requiredQuestions: questionTemplates.filter((q) => q.isRequired).length, + } + + return ( +
+
+ {/* Header */} +
+
+ 公司 Logo +
+

+ 系統管理中心 +

+

用戶、模板與系統設定管理

+
+
+
+ + +
+
+ + {/* Stats Cards */} +
+ + + 總用戶 + + + +
{adminStats.totalUsers}
+
+
+ + + 活躍用戶 + + + +
{adminStats.activeUsers}
+
+
+ + + KPI 模板 + + + +
{adminStats.totalKPIs}
+
+
+ + + 啟用 KPI + + + +
{adminStats.activeKPIs}
+
+
+ + + 審查問題 + + + +
{adminStats.totalQuestions}
+
+
+ + + 必填問題 + + + +
{adminStats.requiredQuestions}
+
+
+
+ + {/* Main Content */} + + + + 系統管理 + + 管理用戶、模板和系統設定 + + + + + 用戶管理 + KPI 模板 + 審查問題 + 系統設定 + + + {/* Users Tab */} + +
+

用戶管理

+ +
+ + + + 用戶 + 部門/職位 + 角色 + 狀態 + 最後登入 + 操作 + + + + {users.map((user) => ( + + +
+ + + {user.name.charAt(0)} + + +
+
{user.name}
+
{user.email}
+
+
+
+ +
+
{user.department}
+
{user.position}
+
+
+ + + {user.role === "executive" ? "高階主管" : user.role === "manager" ? "經理" : "HR"} + + + + + {user.status === "active" ? "啟用" : "停用"} + + + {user.lastLogin} + +
+ + +
+
+
+ ))} +
+
+
+ + {/* KPI Templates Tab */} + +
+

KPI 模板管理

+ +
+
+ {kpiTemplates.map((template) => ( + + +
+ {template.name} + + {template.isActive ? "啟用" : "停用"} + +
+
+ +
+ 類別: + {template.category} +
+
+ 預設權重: + {template.defaultWeight}% +
+
+ 使用次數: + {template.usageCount} +
+
+ + +
+
+
+ ))} +
+
+ + {/* Questions Tab */} + +
+

審查問題管理

+ +
+
+ {questionTemplates.map((question) => ( + + +
+
+
+ + {question.category} + + + {question.type} + + {question.isRequired && ( + + 必填 + + )} +
+

{question.question}

+
+
+ + +
+
+
+
+ ))} +
+
+ + {/* Settings Tab */} + +

系統設定

+
+ {systemSettings.map((setting) => ( + + +
+
+
+

{setting.name}

+ + {setting.category} + +
+

{setting.description}

+
+ +
+
+
+ ))} +
+
+
+
+
+ + {/* Dialog for various actions */} + + + + + {dialogType === "user" + ? "新增用戶" + : dialogType === "kpi" + ? "新增 KPI 模板" + : dialogType === "question" + ? "新增審查問題" + : "備份資料"} + + + {dialogType === "user" + ? "建立新的系統用戶帳號" + : dialogType === "kpi" + ? "建立新的 KPI 模板" + : dialogType === "question" + ? "建立新的審查問題" + : "備份系統資料到安全位置"} + + +
+ {dialogType === "user" && ( + <> +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + )} + {dialogType === "kpi" && ( + <> +
+ + +
+
+ + +
+
+ + +
+ + )} + {dialogType === "question" && ( + <> +
+ +