建立檔案

This commit is contained in:
2025-08-05 08:22:44 +08:00
commit 042d03aff7
122 changed files with 34763 additions and 0 deletions

View File

@@ -0,0 +1,668 @@
"use client"
import type React from "react"
import { useState } from "react"
import { useAuth } from "@/contexts/auth-context"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
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 { Switch } from "@/components/ui/switch"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import {
Upload,
FileText,
Link,
CheckCircle,
Clock,
Info,
Lightbulb,
Target,
Zap,
AlertTriangle,
FileVideo,
X,
} from "lucide-react"
interface AppSubmissionDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
}
export function AppSubmissionDialog({ open, onOpenChange }: AppSubmissionDialogProps) {
const { user, canSubmitApp } = useAuth()
const [step, setStep] = useState(1)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isSubmitted, setIsSubmitted] = useState(false)
const [formData, setFormData] = useState({
name: "",
type: "文字處理",
description: "",
appUrl: "",
demoFile: null as File | null,
sourceCodeUrl: "",
documentation: "",
features: "",
technicalDetails: "",
requestFeatured: false,
agreeTerms: false,
})
// 檢查用戶權限
if (!user || !canSubmitApp()) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center space-x-2">
<AlertTriangle className="w-5 h-5 text-orange-600" />
<span></span>
</DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="text-center py-6">
<div className="w-16 h-16 bg-orange-100 rounded-full flex items-center justify-center mx-auto mb-4">
<AlertTriangle className="w-8 h-8 text-orange-600" />
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2"></h3>
<p className="text-gray-600 mb-4">
AI {user?.role === "admin" ? "管理員" : "一般用戶"}
</p>
<div className="bg-blue-50 rounded-lg p-4 mb-4">
<div className="flex items-start space-x-3">
<Info className="w-5 h-5 text-blue-600 mt-0.5" />
<div className="text-left">
<p className="text-sm font-medium text-blue-900"></p>
<ul className="text-sm text-blue-700 mt-1 space-y-1">
<li> </li>
<li> </li>
<li> 調</li>
</ul>
</div>
</div>
</div>
<Button onClick={() => onOpenChange(false)} className="w-full">
</Button>
</div>
</DialogContent>
</Dialog>
)
}
const handleInputChange = (field: string, value: string | boolean | File | null) => {
setFormData((prev) => ({ ...prev, [field]: value }))
}
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0] || null
handleInputChange("demoFile", file)
}
const removeFile = () => {
handleInputChange("demoFile", null)
}
const handleNext = () => {
if (step < 3) {
setStep(step + 1)
}
}
const handlePrevious = () => {
if (step > 1) {
setStep(step - 1)
}
}
const handleSubmit = async () => {
setIsSubmitting(true)
// 模擬提交過程
await new Promise((resolve) => setTimeout(resolve, 2000))
setIsSubmitting(false)
setIsSubmitted(true)
// 3秒後關閉對話框
setTimeout(() => {
onOpenChange(false)
setIsSubmitted(false)
setStep(1)
setFormData({
name: "",
type: "文字處理",
description: "",
appUrl: "",
demoFile: null,
sourceCodeUrl: "",
documentation: "",
features: "",
technicalDetails: "",
requestFeatured: false,
agreeTerms: false,
})
}, 3000)
}
const isStep1Valid = formData.name && formData.description && formData.appUrl
const isStep2Valid = formData.features
const isStep3Valid = formData.agreeTerms
if (isSubmitted) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-md">
<div className="text-center py-8">
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<CheckCircle className="w-8 h-8 text-green-600" />
</div>
<h3 className="text-xl font-semibold text-gray-900 mb-2"></h3>
<p className="text-gray-600 mb-4"> 1-3 </p>
<div className="bg-blue-50 rounded-lg p-4 mb-4">
<div className="flex items-start space-x-3">
<Info className="w-5 h-5 text-blue-600 mt-0.5" />
<div className="text-left">
<p className="text-sm font-medium text-blue-900"></p>
<ul className="text-sm text-blue-700 mt-1 space-y-1">
<li> </li>
<li> </li>
<li> </li>
</ul>
</div>
</div>
</div>
<p className="text-sm text-gray-500">...</p>
</div>
</DialogContent>
</Dialog>
)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center space-x-2">
<Upload className="w-5 h-5 text-blue-600" />
<span> AI </span>
</DialogTitle>
<DialogDescription> AI </DialogDescription>
</DialogHeader>
{/* Progress Indicator */}
<div className="flex items-center justify-center space-x-4 mb-6">
{[1, 2, 3].map((stepNumber) => (
<div key={stepNumber} className="flex items-center">
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${
step >= stepNumber ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-600"
}`}
>
{stepNumber}
</div>
{stepNumber < 3 && (
<div className={`w-12 h-0.5 mx-2 ${step > stepNumber ? "bg-blue-600" : "bg-gray-200"}`} />
)}
</div>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Form */}
<div className="lg:col-span-2">
{step === 1 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<FileText className="w-5 h-5" />
<span></span>
</CardTitle>
<CardDescription> AI </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="name"> *</Label>
<Input
id="name"
value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)}
placeholder="輸入您的應用名稱"
/>
</div>
<div className="space-y-2">
<Label htmlFor="type"> *</Label>
<Select value={formData.type} onValueChange={(value) => handleInputChange("type", value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="文字處理"></SelectItem>
<SelectItem value="圖像生成"></SelectItem>
<SelectItem value="語音辨識"></SelectItem>
<SelectItem value="推薦系統"></SelectItem>
<SelectItem value="數據分析"></SelectItem>
<SelectItem value="其他"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="description"> *</Label>
<Textarea
id="description"
value={formData.description}
onChange={(e) => handleInputChange("description", e.target.value)}
placeholder="詳細描述您的應用功能、特色和創新點..."
rows={4}
/>
<p className="text-xs text-gray-500"> 100-500 </p>
</div>
<div className="space-y-2">
<Label htmlFor="appUrl"> *</Label>
<div className="relative">
<Link className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
id="appUrl"
value={formData.appUrl}
onChange={(e) => handleInputChange("appUrl", e.target.value)}
placeholder="https://your-app.example.com"
className="pl-10"
/>
</div>
<p className="text-xs text-gray-500"></p>
</div>
<div className="space-y-2">
<Label htmlFor="demoFile"></Label>
<div className="space-y-3">
{!formData.demoFile ? (
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-blue-400 hover:bg-blue-50 transition-colors">
<input
type="file"
id="demoFile"
accept="video/*,.mp4,.avi,.mov,.wmv,.flv,.webm"
onChange={handleFileChange}
className="hidden"
/>
<label htmlFor="demoFile" className="cursor-pointer">
<div className="flex flex-col items-center space-y-2">
<div className="w-12 h-12 bg-gray-100 rounded-full flex items-center justify-center">
<FileVideo className="w-6 h-6 text-gray-400" />
</div>
<div>
<p className="text-sm font-medium text-gray-900"></p>
<p className="text-xs text-gray-500"> MP4, AVI, MOV, WMV 100MB</p>
</div>
</div>
</label>
</div>
) : (
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="flex items-center space-x-3">
<FileVideo className="w-5 h-5 text-blue-600" />
<div>
<p className="text-sm font-medium text-blue-900">{formData.demoFile.name}</p>
<p className="text-xs text-blue-700">
{(formData.demoFile.size / 1024 / 1024).toFixed(2)} MB
</p>
</div>
</div>
<Button
type="button"
variant="ghost"
size="sm"
onClick={removeFile}
className="text-red-600 hover:text-red-700 hover:bg-red-50"
>
<X className="w-4 h-4" />
</Button>
</div>
)}
</div>
<p className="text-xs text-gray-500"></p>
</div>
</CardContent>
</Card>
)}
{step === 2 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Zap className="w-5 h-5" />
<span></span>
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="features"> *</Label>
<Textarea
id="features"
value={formData.features}
onChange={(e) => handleInputChange("features", e.target.value)}
placeholder="列出應用的主要功能和特色,例如:&#10;• 支援多語言對話&#10;• 上下文理解能力&#10;• 個性化回應..."
rows={4}
/>
</div>
<div className="space-y-2">
<Label htmlFor="technicalDetails"></Label>
<Textarea
id="technicalDetails"
value={formData.technicalDetails}
onChange={(e) => handleInputChange("technicalDetails", e.target.value)}
placeholder="技術架構、使用的 AI 模型、開發框架等..."
rows={3}
/>
</div>
<div className="space-y-2">
<Label htmlFor="sourceCodeUrl"></Label>
<div className="relative">
<Link className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
id="sourceCodeUrl"
value={formData.sourceCodeUrl}
onChange={(e) => handleInputChange("sourceCodeUrl", e.target.value)}
placeholder="https://github.com/username/repo"
className="pl-10"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="documentation"></Label>
<div className="relative">
<Link className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
<Input
id="documentation"
value={formData.documentation}
onChange={(e) => handleInputChange("documentation", e.target.value)}
placeholder="https://docs.your-app.com"
className="pl-10"
/>
</div>
</div>
</CardContent>
</Card>
)}
{step === 3 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CheckCircle className="w-5 h-5" />
<span></span>
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Application Summary */}
<div className="bg-gray-50 rounded-lg p-4">
<h4 className="font-medium mb-3"></h4>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-500"></span>
<span className="font-medium">{formData.name}</span>
</div>
<div>
<span className="text-gray-500"></span>
<Badge variant="outline">{formData.type}</Badge>
</div>
<div className="col-span-2">
<span className="text-gray-500"></span>
<span className="font-medium">
{user?.name} ({user?.department})
</span>
</div>
{formData.demoFile && (
<div className="col-span-2">
<span className="text-gray-500"></span>
<span className="font-medium">{formData.demoFile.name}</span>
</div>
)}
</div>
</div>
{/* Featured Request */}
<div className="flex items-center space-x-3 p-4 bg-yellow-50 rounded-lg">
<Switch
id="requestFeatured"
checked={formData.requestFeatured}
onCheckedChange={(checked) => handleInputChange("requestFeatured", checked)}
/>
<div className="flex-1">
<Label htmlFor="requestFeatured" className="font-medium">
</Label>
<p className="text-sm text-gray-600">
</p>
</div>
</div>
{/* Terms Agreement */}
<div className="space-y-4">
<div className="flex items-start space-x-3 p-4 border rounded-lg">
<Switch
id="agreeTerms"
checked={formData.agreeTerms}
onCheckedChange={(checked) => handleInputChange("agreeTerms", checked)}
/>
<div className="flex-1">
<Label htmlFor="agreeTerms" className="font-medium">
*
</Label>
<div className="text-sm text-gray-600 mt-2 space-y-1">
<p> </p>
<p> </p>
<p> 1-3 </p>
<p> 使</p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)}
</div>
{/* Sidebar */}
<div className="space-y-4">
{/* Progress Card */}
<Card>
<CardHeader>
<CardTitle className="text-lg"></CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<div className={`flex items-center space-x-3 ${step >= 1 ? "text-blue-600" : "text-gray-400"}`}>
<div
className={`w-6 h-6 rounded-full flex items-center justify-center text-xs ${
step >= 1 ? "bg-blue-600 text-white" : "bg-gray-200"
}`}
>
1
</div>
<span className="text-sm"></span>
</div>
<div className={`flex items-center space-x-3 ${step >= 2 ? "text-blue-600" : "text-gray-400"}`}>
<div
className={`w-6 h-6 rounded-full flex items-center justify-center text-xs ${
step >= 2 ? "bg-blue-600 text-white" : "bg-gray-200"
}`}
>
2
</div>
<span className="text-sm"></span>
</div>
<div className={`flex items-center space-x-3 ${step >= 3 ? "text-blue-600" : "text-gray-400"}`}>
<div
className={`w-6 h-6 rounded-full flex items-center justify-center text-xs ${
step >= 3 ? "bg-blue-600 text-white" : "bg-gray-200"
}`}
>
3
</div>
<span className="text-sm"></span>
</div>
</CardContent>
</Card>
{/* Tips Card */}
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center space-x-2">
<Lightbulb className="w-4 h-4" />
<span></span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-sm">
{step === 1 && (
<>
<div className="flex items-start space-x-2">
<Target className="w-4 h-4 text-blue-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<Target className="w-4 h-4 text-blue-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<Target className="w-4 h-4 text-blue-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<FileVideo className="w-4 h-4 text-blue-500 mt-0.5" />
<p></p>
</div>
</>
)}
{step === 2 && (
<>
<div className="flex items-start space-x-2">
<Zap className="w-4 h-4 text-green-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<Zap className="w-4 h-4 text-green-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<Zap className="w-4 h-4 text-green-500 mt-0.5" />
<p></p>
</div>
</>
)}
{step === 3 && (
<>
<div className="flex items-start space-x-2">
<CheckCircle className="w-4 h-4 text-purple-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<CheckCircle className="w-4 h-4 text-purple-500 mt-0.5" />
<p></p>
</div>
<div className="flex items-start space-x-2">
<CheckCircle className="w-4 h-4 text-purple-500 mt-0.5" />
<p></p>
</div>
</>
)}
</CardContent>
</Card>
{/* Review Process Card */}
<Card>
<CardHeader>
<CardTitle className="text-lg flex items-center space-x-2">
<Clock className="w-4 h-4" />
<span></span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-sm">
<div className="flex items-center space-x-3">
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<Upload className="w-3 h-3 text-blue-600" />
</div>
<span></span>
</div>
<div className="flex items-center space-x-3">
<div className="w-6 h-6 bg-yellow-100 rounded-full flex items-center justify-center">
<Clock className="w-3 h-3 text-yellow-600" />
</div>
<span></span>
</div>
<div className="flex items-center space-x-3">
<div className="w-6 h-6 bg-green-100 rounded-full flex items-center justify-center">
<CheckCircle className="w-3 h-3 text-green-600" />
</div>
<span></span>
</div>
<Separator />
<div className="bg-blue-50 p-3 rounded-lg">
<p className="text-xs text-blue-700">
<strong></strong>1-3
<br />
<strong></strong>
</p>
</div>
</CardContent>
</Card>
</div>
</div>
{/* Action Buttons */}
<div className="flex justify-between pt-6 border-t">
<Button variant="outline" onClick={handlePrevious} disabled={step === 1}>
</Button>
<div className="flex space-x-3">
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
{step < 3 ? (
<Button
onClick={handleNext}
disabled={(step === 1 && !isStep1Valid) || (step === 2 && !isStep2Valid)}
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700"
>
</Button>
) : (
<Button
onClick={handleSubmit}
disabled={!isStep3Valid || isSubmitting}
className="bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700"
>
{isSubmitting ? (
<>
<Clock className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
<>
<Upload className="w-4 h-4 mr-2" />
</>
)}
</Button>
)}
</div>
</div>
</DialogContent>
</Dialog>
)
}