'use client'; import { useState, useEffect } from 'react'; import { useRouter, useParams } from 'next/navigation'; import Link from 'next/link'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import * as z from 'zod'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from '@/components/ui/form'; import { useUser } from '@/components/user-context'; import { toast } from 'sonner'; import { PERIOD_TYPES, DIRECTION_LABELS } from '@/lib/constants'; import type { Objective, Metric, Template } from '@/types/database'; import { ArrowLeft, Loader2 } from 'lucide-react'; import { getErrorMessage } from '@/lib/error-utils'; const formSchema = z.object({ title: z.string().min(1, 'KR标题不能为空'), objective_id: z.string().optional(), period_type: z.enum(['custom', '3d', '1w', '2w', 'monthly']), period_start: z.string().min(1, '开始日期不能为空'), period_end: z.string().min(1, '结束日期不能为空'), metric_name: z.string().min(1, '指标名称不能为空'), unit: z.string().min(1, '单位不能为空'), direction: z.enum(['increase', 'decrease']), target_value: z.number().positive('目标值必须大于0'), owner_user_id: z.string().optional(), note: z.string().optional(), }); type FormValues = z.infer; export default function NewKRPage() { const router = useRouter(); const params = useParams(); const projectId = params.id as string; const { currentUser, users } = useUser(); const [isSubmitting, setIsSubmitting] = useState(false); const [objectives, setObjectives] = useState([]); const [metrics, setMetrics] = useState([]); const [templates, setTemplates] = useState([]); const today = new Date().toISOString().split('T')[0]; const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { title: '', objective_id: '', period_type: 'custom', period_start: today, period_end: today, metric_name: '', unit: '', direction: 'increase', target_value: 0, owner_user_id: currentUser?.id || '', note: '', }, }); useEffect(() => { async function fetchData() { const [objRes, metricsRes, templatesRes] = await Promise.all([ fetch(`/api/objectives?project_id=${projectId}`), fetch('/api/metrics?active_only=true'), fetch('/api/templates?active_only=true'), ]); if (objRes.ok) { const data = await objRes.json(); setObjectives(data); } if (metricsRes.ok) { const data = await metricsRes.json(); setMetrics(data); } if (templatesRes.ok) { const data = await templatesRes.json(); setTemplates(data); } } fetchData(); }, [projectId]); const handlePeriodTypeChange = (value: string) => { form.setValue('period_type', value as FormValues['period_type']); const start = new Date(form.getValues('period_start')); let endDate: Date; switch (value) { case '3d': endDate = new Date(start); endDate.setDate(start.getDate() + 2); break; case '1w': endDate = new Date(start); endDate.setDate(start.getDate() + 6); break; case '2w': endDate = new Date(start); endDate.setDate(start.getDate() + 13); break; case 'monthly': endDate = new Date(start); endDate.setMonth(start.getMonth() + 1); endDate.setDate(endDate.getDate() - 1); break; default: return; } form.setValue('period_end', endDate.toISOString().split('T')[0]); }; const handleMetricSelect = (metricId: string) => { const metric = metrics.find(m => m.id === metricId); if (metric) { form.setValue('metric_name', metric.name); form.setValue('unit', metric.unit); form.setValue('direction', metric.direction); } }; const onSubmit = async (values: FormValues) => { if (new Date(values.period_end) < new Date(values.period_start)) { toast.error('结束日期不能早于开始日期'); return; } setIsSubmitting(true); try { const res = await fetch('/api/krs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...values, project_id: projectId, objective_id: values.objective_id || null, owner_user_id: values.owner_user_id || currentUser?.id, }), }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || '创建失败'); } const kr = await res.json(); toast.success('KR 创建成功'); router.push(`/krs/${kr.id}`); } catch (error) { toast.error(getErrorMessage(error)); } finally { setIsSubmitting(false); } }; return (
返回项目 新建 KR (Key Result)
( KR 标题 * )} /> {objectives.length > 0 && ( ( 关联目标 (Objective) )} /> )}
( 周期类型 )} /> ( 开始日期 * )} /> ( 结束日期 * )} />
{metrics.length > 0 && (
从指标库选择
)}
( 指标名称 * )} /> ( 单位 * )} />
( 方向 )} /> ( 目标值 * field.onChange(parseFloat(e.target.value) || 0)} /> )} />
( 负责人 每人每个周期最多 3 个 KR )} /> ( 备注