606 lines
33 KiB
TypeScript
606 lines
33 KiB
TypeScript
"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 (
|
||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-purple-50 to-pink-50 p-3 sm:p-6">
|
||
<div className="max-w-7xl mx-auto space-y-4 sm:space-y-6">
|
||
{/* Header */}
|
||
<div className="flex flex-col space-y-4 sm:flex-row sm:items-center sm:justify-between sm:space-y-0">
|
||
<div className="flex flex-col sm:flex-row sm:items-center space-y-2 sm:space-y-0 sm:space-x-4">
|
||
<img
|
||
src="data:image/png;base64,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"
|
||
alt="公司 Logo"
|
||
className="h-10 w-auto sm:h-12 md:hidden"
|
||
/>
|
||
<div>
|
||
<h1 className="text-xl sm:text-2xl lg:text-3xl font-bold bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
|
||
系統管理中心
|
||
</h1>
|
||
<p className="text-sm sm:text-base text-gray-600 mt-1">用戶、模板與系統設定管理</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex space-x-2">
|
||
<Button
|
||
variant="outline"
|
||
className="bg-white/80 backdrop-blur-sm"
|
||
onClick={() => handleOpenDialog("backup")}
|
||
>
|
||
<Database className="h-4 w-4 mr-2" />
|
||
備份資料
|
||
</Button>
|
||
<Button
|
||
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white shadow-lg"
|
||
onClick={() => handleOpenDialog("user")}
|
||
>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
新增用戶
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stats Cards */}
|
||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3 sm:gap-4">
|
||
<Card className="shadow-lg bg-gradient-to-br from-blue-50 to-cyan-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-blue-700">總用戶</CardTitle>
|
||
<Users className="h-4 w-4 text-blue-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-blue-800">{adminStats.totalUsers}</div>
|
||
</CardContent>
|
||
</Card>
|
||
<Card className="shadow-lg bg-gradient-to-br from-green-50 to-emerald-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-green-700">活躍用戶</CardTitle>
|
||
<Shield className="h-4 w-4 text-green-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-green-800">{adminStats.activeUsers}</div>
|
||
</CardContent>
|
||
</Card>
|
||
<Card className="shadow-lg bg-gradient-to-br from-purple-50 to-violet-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-purple-700">KPI 模板</CardTitle>
|
||
<Target className="h-4 w-4 text-purple-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-purple-800">{adminStats.totalKPIs}</div>
|
||
</CardContent>
|
||
</Card>
|
||
<Card className="shadow-lg bg-gradient-to-br from-orange-50 to-amber-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-orange-700">啟用 KPI</CardTitle>
|
||
<Settings className="h-4 w-4 text-orange-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-orange-800">{adminStats.activeKPIs}</div>
|
||
</CardContent>
|
||
</Card>
|
||
<Card className="shadow-lg bg-gradient-to-br from-pink-50 to-rose-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-pink-700">審查問題</CardTitle>
|
||
<MessageSquare className="h-4 w-4 text-pink-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-pink-800">{adminStats.totalQuestions}</div>
|
||
</CardContent>
|
||
</Card>
|
||
<Card className="shadow-lg bg-gradient-to-br from-indigo-50 to-blue-100 border-0">
|
||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||
<CardTitle className="text-sm font-medium text-indigo-700">必填問題</CardTitle>
|
||
<Bell className="h-4 w-4 text-indigo-600" />
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-2xl font-bold text-indigo-800">{adminStats.requiredQuestions}</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
|
||
{/* Main Content */}
|
||
<Card className="shadow-lg bg-white/80 backdrop-blur-sm">
|
||
<CardHeader>
|
||
<CardTitle className="text-lg font-bold bg-gradient-to-r from-gray-800 to-gray-600 bg-clip-text text-transparent">
|
||
系統管理
|
||
</CardTitle>
|
||
<CardDescription>管理用戶、模板和系統設定</CardDescription>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||
<TabsList className="grid w-full grid-cols-4">
|
||
<TabsTrigger value="users">用戶管理</TabsTrigger>
|
||
<TabsTrigger value="kpi-templates">KPI 模板</TabsTrigger>
|
||
<TabsTrigger value="questions">審查問題</TabsTrigger>
|
||
<TabsTrigger value="settings">系統設定</TabsTrigger>
|
||
</TabsList>
|
||
|
||
{/* Users Tab */}
|
||
<TabsContent value="users" className="space-y-4">
|
||
<div className="flex justify-between items-center">
|
||
<h3 className="text-lg font-semibold">用戶管理</h3>
|
||
<Button onClick={() => handleOpenDialog("user")}>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
新增用戶
|
||
</Button>
|
||
</div>
|
||
<Table>
|
||
<TableHeader>
|
||
<TableRow>
|
||
<TableHead>用戶</TableHead>
|
||
<TableHead>部門/職位</TableHead>
|
||
<TableHead>角色</TableHead>
|
||
<TableHead>狀態</TableHead>
|
||
<TableHead>最後登入</TableHead>
|
||
<TableHead>操作</TableHead>
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
{users.map((user) => (
|
||
<TableRow key={user.id}>
|
||
<TableCell>
|
||
<div className="flex items-center space-x-3">
|
||
<Avatar className="h-8 w-8">
|
||
<AvatarFallback
|
||
className={`bg-gradient-to-br ${
|
||
user.color === "emerald"
|
||
? "from-emerald-400 to-green-500"
|
||
: user.color === "blue"
|
||
? "from-blue-400 to-cyan-500"
|
||
: user.color === "purple"
|
||
? "from-purple-400 to-violet-500"
|
||
: "from-orange-400 to-amber-500"
|
||
} text-white`}
|
||
>
|
||
{user.name.charAt(0)}
|
||
</AvatarFallback>
|
||
</Avatar>
|
||
<div>
|
||
<div className="font-medium">{user.name}</div>
|
||
<div className="text-sm text-gray-500">{user.email}</div>
|
||
</div>
|
||
</div>
|
||
</TableCell>
|
||
<TableCell>
|
||
<div>
|
||
<div className="font-medium">{user.department}</div>
|
||
<div className="text-sm text-gray-500">{user.position}</div>
|
||
</div>
|
||
</TableCell>
|
||
<TableCell>
|
||
<Badge variant={user.role === "executive" ? "default" : "secondary"}>
|
||
{user.role === "executive" ? "高階主管" : user.role === "manager" ? "經理" : "HR"}
|
||
</Badge>
|
||
</TableCell>
|
||
<TableCell>
|
||
<Badge variant={user.status === "active" ? "default" : "secondary"}>
|
||
{user.status === "active" ? "啟用" : "停用"}
|
||
</Badge>
|
||
</TableCell>
|
||
<TableCell className="text-sm text-gray-500">{user.lastLogin}</TableCell>
|
||
<TableCell>
|
||
<div className="flex items-center space-x-2">
|
||
<Button variant="ghost" size="sm">
|
||
<Edit className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="ghost" size="sm" className="text-red-500">
|
||
<Trash2 className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</TableCell>
|
||
</TableRow>
|
||
))}
|
||
</TableBody>
|
||
</Table>
|
||
</TabsContent>
|
||
|
||
{/* KPI Templates Tab */}
|
||
<TabsContent value="kpi-templates" className="space-y-4">
|
||
<div className="flex justify-between items-center">
|
||
<h3 className="text-lg font-semibold">KPI 模板管理</h3>
|
||
<Button onClick={() => handleOpenDialog("kpi")}>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
新增模板
|
||
</Button>
|
||
</div>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{kpiTemplates.map((template) => (
|
||
<Card
|
||
key={template.id}
|
||
className={`shadow-md hover:shadow-lg transition-shadow ${
|
||
template.color === "emerald"
|
||
? "bg-gradient-to-br from-emerald-50 to-green-100"
|
||
: template.color === "blue"
|
||
? "bg-gradient-to-br from-blue-50 to-cyan-100"
|
||
: template.color === "purple"
|
||
? "bg-gradient-to-br from-purple-50 to-violet-100"
|
||
: template.color === "orange"
|
||
? "bg-gradient-to-br from-orange-50 to-amber-100"
|
||
: template.color === "cyan"
|
||
? "bg-gradient-to-br from-cyan-50 to-teal-100"
|
||
: "bg-gradient-to-br from-red-50 to-pink-100"
|
||
}`}
|
||
>
|
||
<CardHeader className="pb-3">
|
||
<div className="flex items-center justify-between">
|
||
<CardTitle className="text-base">{template.name}</CardTitle>
|
||
<Badge variant={template.isActive ? "default" : "secondary"}>
|
||
{template.isActive ? "啟用" : "停用"}
|
||
</Badge>
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent className="space-y-3">
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-600">類別:</span>
|
||
<span className="font-medium">{template.category}</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-600">預設權重:</span>
|
||
<span className="font-medium">{template.defaultWeight}%</span>
|
||
</div>
|
||
<div className="flex justify-between text-sm">
|
||
<span className="text-gray-600">使用次數:</span>
|
||
<span className="font-medium">{template.usageCount}</span>
|
||
</div>
|
||
<div className="flex justify-between pt-2 border-t">
|
||
<Button variant="ghost" size="sm">
|
||
<Edit className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="ghost" size="sm" className="text-red-500">
|
||
<Trash2 className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
</TabsContent>
|
||
|
||
{/* Questions Tab */}
|
||
<TabsContent value="questions" className="space-y-4">
|
||
<div className="flex justify-between items-center">
|
||
<h3 className="text-lg font-semibold">審查問題管理</h3>
|
||
<Button onClick={() => handleOpenDialog("question")}>
|
||
<Plus className="h-4 w-4 mr-2" />
|
||
新增問題
|
||
</Button>
|
||
</div>
|
||
<div className="space-y-3">
|
||
{questionTemplates.map((question) => (
|
||
<Card key={question.id} className="shadow-md">
|
||
<CardContent className="p-4">
|
||
<div className="flex items-start justify-between">
|
||
<div className="flex-1">
|
||
<div className="flex items-center space-x-2 mb-2">
|
||
<Badge
|
||
variant="outline"
|
||
className={`${
|
||
question.color === "blue"
|
||
? "border-blue-300 text-blue-700 bg-blue-50"
|
||
: question.color === "emerald"
|
||
? "border-emerald-300 text-emerald-700 bg-emerald-50"
|
||
: question.color === "yellow"
|
||
? "border-yellow-300 text-yellow-700 bg-yellow-50"
|
||
: question.color === "purple"
|
||
? "border-purple-300 text-purple-700 bg-purple-50"
|
||
: "border-orange-300 text-orange-700 bg-orange-50"
|
||
}`}
|
||
>
|
||
{question.category}
|
||
</Badge>
|
||
<Badge variant={question.type === "評分" ? "default" : "secondary"}>
|
||
{question.type}
|
||
</Badge>
|
||
{question.isRequired && (
|
||
<Badge variant="destructive" className="text-xs">
|
||
必填
|
||
</Badge>
|
||
)}
|
||
</div>
|
||
<p className="text-gray-900 font-medium">{question.question}</p>
|
||
</div>
|
||
<div className="flex items-center space-x-2 ml-4">
|
||
<Button variant="ghost" size="sm">
|
||
<Edit className="h-4 w-4" />
|
||
</Button>
|
||
<Button variant="ghost" size="sm" className="text-red-500">
|
||
<Trash2 className="h-4 w-4" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
</TabsContent>
|
||
|
||
{/* Settings Tab */}
|
||
<TabsContent value="settings" className="space-y-4">
|
||
<h3 className="text-lg font-semibold">系統設定</h3>
|
||
<div className="space-y-4">
|
||
{systemSettings.map((setting) => (
|
||
<Card key={setting.id} className="shadow-md">
|
||
<CardContent className="p-4">
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex-1">
|
||
<div className="flex items-center space-x-2 mb-1">
|
||
<h4 className="font-medium text-gray-900">{setting.name}</h4>
|
||
<Badge
|
||
variant="outline"
|
||
className={
|
||
setting.category === "通知"
|
||
? "border-blue-300 text-blue-700 bg-blue-50"
|
||
: "border-red-300 text-red-700 bg-red-50"
|
||
}
|
||
>
|
||
{setting.category}
|
||
</Badge>
|
||
</div>
|
||
<p className="text-sm text-gray-600">{setting.description}</p>
|
||
</div>
|
||
<Switch checked={setting.enabled} />
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
</TabsContent>
|
||
</Tabs>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Dialog for various actions */}
|
||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||
<DialogContent className="max-w-md">
|
||
<DialogHeader>
|
||
<DialogTitle className="bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">
|
||
{dialogType === "user"
|
||
? "新增用戶"
|
||
: dialogType === "kpi"
|
||
? "新增 KPI 模板"
|
||
: dialogType === "question"
|
||
? "新增審查問題"
|
||
: "備份資料"}
|
||
</DialogTitle>
|
||
<DialogDescription>
|
||
{dialogType === "user"
|
||
? "建立新的系統用戶帳號"
|
||
: dialogType === "kpi"
|
||
? "建立新的 KPI 模板"
|
||
: dialogType === "question"
|
||
? "建立新的審查問題"
|
||
: "備份系統資料到安全位置"}
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
<div className="space-y-4">
|
||
{dialogType === "user" && (
|
||
<>
|
||
<div>
|
||
<Label htmlFor="userName">姓名</Label>
|
||
<Input id="userName" placeholder="輸入用戶姓名" />
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="userEmail">電子郵件</Label>
|
||
<Input id="userEmail" type="email" placeholder="輸入電子郵件" />
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="userRole">角色</Label>
|
||
<Select>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="選擇角色" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="executive">高階主管</SelectItem>
|
||
<SelectItem value="manager">經理</SelectItem>
|
||
<SelectItem value="hr">HR</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="department">部門</Label>
|
||
<Input id="department" placeholder="輸入部門名稱" />
|
||
</div>
|
||
</>
|
||
)}
|
||
{dialogType === "kpi" && (
|
||
<>
|
||
<div>
|
||
<Label htmlFor="kpiName">KPI 名稱</Label>
|
||
<Input id="kpiName" placeholder="輸入 KPI 名稱" />
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="kpiCategory">類別</Label>
|
||
<Select>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="選擇類別" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="財務">財務</SelectItem>
|
||
<SelectItem value="團隊">團隊</SelectItem>
|
||
<SelectItem value="營運">營運</SelectItem>
|
||
<SelectItem value="創新">創新</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="defaultWeight">預設權重 (%)</Label>
|
||
<Input id="defaultWeight" type="number" placeholder="0-100" />
|
||
</div>
|
||
</>
|
||
)}
|
||
{dialogType === "question" && (
|
||
<>
|
||
<div>
|
||
<Label htmlFor="questionText">問題內容</Label>
|
||
<Textarea id="questionText" placeholder="輸入審查問題..." />
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="questionType">問題類型</Label>
|
||
<Select>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="選擇類型" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="評分">評分</SelectItem>
|
||
<SelectItem value="文字">文字</SelectItem>
|
||
<SelectItem value="選擇">多選</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
<div>
|
||
<Label htmlFor="questionCategory">問題類別</Label>
|
||
<Select>
|
||
<SelectTrigger>
|
||
<SelectValue placeholder="選擇類別" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="績效">績效</SelectItem>
|
||
<SelectItem value="領導力">領導力</SelectItem>
|
||
<SelectItem value="目標">目標</SelectItem>
|
||
<SelectItem value="挑戰">挑戰</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
</>
|
||
)}
|
||
{dialogType === "backup" && (
|
||
<div className="text-center py-4">
|
||
<Database className="h-12 w-12 mx-auto text-gray-400 mb-4" />
|
||
<p className="text-gray-600">確定要執行系統資料備份嗎?</p>
|
||
<p className="text-sm text-gray-500 mt-2">備份過程可能需要幾分鐘時間</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<DialogFooter>
|
||
<Button
|
||
type="submit"
|
||
className="bg-gradient-to-r from-green-500 to-emerald-500 hover:from-green-600 hover:to-emerald-600 text-white"
|
||
>
|
||
{dialogType === "backup" ? "開始備份" : "建立"}
|
||
</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|