diff --git a/app/submit/page.tsx b/app/submit/page.tsx index 6761b2c..1aafbd7 100644 --- a/app/submit/page.tsx +++ b/app/submit/page.tsx @@ -11,7 +11,9 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { Checkbox } from "@/components/ui/checkbox" -import { Sparkles, ArrowLeft, Send, BarChart3, Eye, EyeOff, Shield, Info, Mail, ImageIcon } from "lucide-react" +import { Alert, AlertDescription } from "@/components/ui/alert" +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog" +import { Sparkles, ArrowLeft, Send, BarChart3, Eye, EyeOff, Shield, Info, Mail, ImageIcon, Lightbulb } from "lucide-react" import { useToast } from "@/hooks/use-toast" import { soundManager } from "@/lib/sound-effects" import HeaderMusicControl from "@/components/header-music-control" @@ -20,6 +22,7 @@ import ContentModerationFeedback from "@/components/content-moderation-feedback" import ImageUpload from "@/components/image-upload" import type { ImageFile } from "@/lib/image-utils" import { WishService } from "@/lib/supabase-service" +import { categorizeWish, type Wish } from "@/lib/categorization" export default function SubmitPage() { const [formData, setFormData] = useState({ @@ -36,6 +39,9 @@ export default function SubmitPage() { const router = useRouter() const [moderationResult, setModerationResult] = useState(null) const [showModerationFeedback, setShowModerationFeedback] = useState(false) + const [currentCategory, setCurrentCategory] = useState(null) + const [showCategoryHint, setShowCategoryHint] = useState(false) + const [showConfirmDialog, setShowConfirmDialog] = useState(false) // 初始化音效系統 useEffect(() => { @@ -50,24 +56,43 @@ export default function SubmitPage() { initSound() }, []) - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() + // 實時檢測分類並顯示提示 + useEffect(() => { + // 只有當用戶輸入了一定內容才進行分類檢測 + const hasMinimumContent = + formData.title.trim().length > 3 || + formData.currentPain.trim().length > 10 - // 先進行內容審核 - const moderation = moderateWishForm(formData) - setModerationResult(moderation) + if (hasMinimumContent) { + // 創建模擬的 Wish 物件進行分類 + const mockWish: Wish = { + id: 0, + title: formData.title, + currentPain: formData.currentPain, + expectedSolution: formData.expectedSolution, + expectedEffect: formData.expectedEffect, + createdAt: new Date().toISOString() + } - if (!moderation.isAppropriate) { - setShowModerationFeedback(true) - await soundManager.play("click") // 播放提示音效 - toast({ - title: "內容需要修改", - description: "請根據建議修改內容後再次提交", - variant: "destructive", - }) - return + const category = categorizeWish(mockWish) + setCurrentCategory(category.name) + + // 如果分類為"其他問題"且內容不夠詳細,顯示提示(只對公開分享) + const isOtherCategory = category.name === "其他問題" + const contentLength = + formData.title.trim().length + + formData.currentPain.trim().length + + formData.expectedSolution.trim().length + + setShowCategoryHint(isOtherCategory && contentLength < 50 && formData.isPublic) + } else { + setCurrentCategory(null) + setShowCategoryHint(false) } + }, [formData.title, formData.currentPain, formData.expectedSolution, formData.expectedEffect, formData.isPublic]) + // 实际的提交逻辑 + const performSubmit = async () => { setIsSubmitting(true) setShowModerationFeedback(false) @@ -95,6 +120,25 @@ export default function SubmitPage() { ? "正在為你準備專業的回饋,其他人也能看到你的分享..." : "正在為你準備專業的回饋,你的分享將保持私密...", }) + + // 重置表单 + setFormData({ + title: "", + currentPain: "", + expectedSolution: "", + expectedEffect: "", + isPublic: true, + email: "", + }) + setImages([]) + setIsSubmitting(false) + setModerationResult(null) + + // 跳轉到感謝頁面 + setTimeout(() => { + router.push("/thank-you") + }, 1000) + } catch (error) { console.error("提交困擾失敗:", error) @@ -108,25 +152,42 @@ export default function SubmitPage() { }) setIsSubmitting(false) + } + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + + // 先進行內容審核 + const moderation = moderateWishForm(formData) + setModerationResult(moderation) + + if (!moderation.isAppropriate) { + setShowModerationFeedback(true) + await soundManager.play("click") // 播放提示音效 + toast({ + title: "內容需要修改", + description: "請根據建議修改內容後再次提交", + variant: "destructive", + }) return } - setFormData({ - title: "", - currentPain: "", - expectedSolution: "", - expectedEffect: "", - isPublic: true, - email: "", - }) - setImages([]) - setIsSubmitting(false) - setModerationResult(null) + // 检查是否为"其他问题"分类且内容较少,需要确认 + if (currentCategory === "其他問題" && formData.isPublic) { + const contentLength = + formData.title.trim().length + + formData.currentPain.trim().length + + formData.expectedSolution.trim().length - // 跳轉到感謝頁面 - setTimeout(() => { - router.push("/thank-you") - }, 1000) + if (contentLength < 50) { + setShowConfirmDialog(true) + return + } + } + + // 直接提交 + await performSubmit() } const handleChange = (field: string, value: string | boolean) => { @@ -463,6 +524,26 @@ export default function SubmitPage() { /> )} + {/* 分類提示 - 當被歸類為其他問題時的友好提示 */} + {showCategoryHint && currentCategory === "其他問題" && ( + + + + + 為了讓管理者更好地理解和解決您的問題,建議您提供更具體的資訊: +
• 問題發生在什麼情況下? +
• 目前使用什麼系統或工具? +
• 這個問題對工作造成什麼影響? +
• 您理想中的解決方式是什麼? +
+ + 詳細的描述能幫助我們提供更精準的解決方案! + +
+
+
+ )} + {/* 隱私設定區塊 */}
@@ -565,6 +646,45 @@ export default function SubmitPage() { + + {/* 确认对话框 */} + + + + + + 建議補充更多資訊 + + + 我們發現您的描述可能還不夠詳細。為了讓管理者更好地理解和解決您的問題,建議您補充以下資訊: +

+ • 問題發生在什麼情況下?
+ • 目前使用什麼系統或工具?
+ • 這個問題對工作造成什麼影響?
+ • 您理想中的解決方式是什麼? +

+ 您現在想要補充更多資訊,還是直接提交? +
+
+ + setShowConfirmDialog(false)} + > + 讓我補充資訊 + + { + setShowConfirmDialog(false) + await performSubmit() + }} + > + 直接提交 + + +
+
diff --git a/components/radar-chart.tsx b/components/radar-chart.tsx index 876d7b9..d19897d 100644 --- a/components/radar-chart.tsx +++ b/components/radar-chart.tsx @@ -41,7 +41,8 @@ export default function RadarChart({ data }: RadarChartProps) { const centerX = size / 2 const centerY = size / 2 - const radius = Math.min(centerX, centerY) - 60 + // 调整边距到合理范围,既保证文字显示又不让图表太小 + const radius = Math.min(centerX, centerY) - 80 // 清除畫布 ctx.clearRect(0, 0, size, size) @@ -124,31 +125,44 @@ export default function RadarChart({ data }: RadarChartProps) { ctx.stroke() }) - // 繪製標籤 - 響應式字體大小 + // 繪製標籤 - 调整为合适的文字大小 ctx.fillStyle = "#E2E8F0" - const fontSize = Math.max(10, Math.min(14, size / 30)) // 響應式字體大小 + const fontSize = Math.max(10, Math.min(14, size / 32)) // 适当增大字体提高可读性 ctx.font = `${fontSize}px sans-serif` - ctx.textAlign = "center" activeData.forEach((item, index) => { const angle = index * angleStep - Math.PI / 2 - const labelRadius = radius + 30 + // 增加标签距离确保文字不被遮住 + const labelRadius = radius + 40 const x = centerX + Math.cos(angle) * labelRadius const y = centerY + Math.sin(angle) * labelRadius - // 調整文字對齊 - if (Math.cos(angle) < -0.1) { + // 更精確的文字對齊 + const cosAngle = Math.cos(angle) + const sinAngle = Math.sin(angle) + + if (cosAngle < -0.3) { ctx.textAlign = "right" - } else if (Math.cos(angle) > 0.1) { + } else if (cosAngle > 0.3) { ctx.textAlign = "left" } else { ctx.textAlign = "center" } + // 垂直對齊調整 + let textY = y + if (sinAngle < -0.3) { + // 上方文字,向下偏移一點 + textY = y + fontSize / 3 + } else if (sinAngle > 0.3) { + // 下方文字,向上偏移一點 + textY = y - fontSize / 3 + } + // 繪製分類名稱 - ctx.fillText(item.name, x, y) - // 繪製數量 - ctx.fillText(`${item.count}`, x, y + fontSize + 2) + ctx.fillText(item.name, x, textY) + // 繪製數量,減少行間距 + ctx.fillText(`${item.count}`, x, textY + fontSize + 3) }) }, [data]) @@ -168,7 +182,7 @@ export default function RadarChart({ data }: RadarChartProps) { }, []) return ( -
+