// ======================================== // Issue Hub Page // ======================================== // Unified page for issues, queue, and discovery with tab navigation import { useState, useCallback } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useIntl } from 'react-intl'; import { Plus, RefreshCw, Github, Loader2, } from 'lucide-react'; import { IssueHubHeader } from '@/components/issue/hub/IssueHubHeader'; import { IssueHubTabs, type IssueTab } from '@/components/issue/hub/IssueHubTabs'; import { IssuesPanel } from '@/components/issue/hub/IssuesPanel'; import { IssueBoardPanel } from '@/components/issue/hub/IssueBoardPanel'; import { QueuePanel } from '@/components/issue/hub/QueuePanel'; import { DiscoveryPanel } from '@/components/issue/hub/DiscoveryPanel'; import { ObservabilityPanel } from '@/components/issue/hub/ObservabilityPanel'; import { ExecutionPanel } from '@/components/issue/hub/ExecutionPanel'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Label } from '@/components/ui/Label'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/Dialog'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/Select'; import { useIssues, useIssueMutations, useIssueQueue } from '@/hooks'; import { pullIssuesFromGitHub, uploadAttachments } from '@/lib/api'; import type { Issue } from '@/lib/api'; import { cn } from '@/lib/utils'; // Issue types type IssueType = 'bug' | 'feature' | 'improvement' | 'other'; const ISSUE_TYPE_CONFIG: Record = { bug: { label: 'Bug', description: '功能异常或错误', color: 'bg-red-500' }, feature: { label: 'Feature', description: '新功能需求', color: 'bg-green-500' }, improvement: { label: 'Improvement', description: '现有功能改进', color: 'bg-blue-500' }, other: { label: 'Other', description: '其他类型', color: 'bg-gray-500' }, }; function NewIssueDialog({ open, onOpenChange, onSubmit, isCreating }: { open: boolean; onOpenChange: (open: boolean) => void; onSubmit: (data: { title: string; context?: string; priority?: Issue['priority']; type?: IssueType; attachments?: File[] }) => void; isCreating: boolean; }) { const { formatMessage } = useIntl(); const [title, setTitle] = useState(''); const [context, setContext] = useState(''); const [priority, setPriority] = useState('medium'); const [type, setType] = useState('other'); const [attachments, setAttachments] = useState([]); const [dragOver, setDragOver] = useState(false); const handleFileSelect = (files: FileList | null) => { if (!files) return; const validFiles = Array.from(files).filter(file => { const validTypes = ['image/', 'text/', 'application/pdf', '.md', '.txt', '.json']; return validTypes.some(t => file.type.includes(t) || file.name.endsWith(t)); }); setAttachments(prev => [...prev, ...validFiles]); }; const removeAttachment = (index: number) => { setAttachments(prev => prev.filter((_, i) => i !== index)); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (title.trim()) { onSubmit({ title: title.trim(), context: context.trim() || undefined, priority, type, attachments: attachments.length > 0 ? attachments : undefined }); // Reset setTitle(''); setContext(''); setPriority('medium'); setType('other'); setAttachments([]); onOpenChange(false); } }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); setDragOver(true); }; const handleDragLeave = () => { setDragOver(false); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); handleFileSelect(e.dataTransfer.files); }; return ( {formatMessage({ id: 'issues.createDialog.title' })}
{/* 标题 */}
setTitle(e.target.value)} placeholder={formatMessage({ id: 'issues.createDialog.placeholders.title' })} className="mt-1.5" required autoFocus />

{title.length}/200

{/* 描述 */}