返回 AiToEarn
1 /**
2 * CreatePlanModal - 创建/编辑草稿弹窗
3 * 仅包含名称输入
4 */
5
6 'use client'
7
8 import { Loader2 } from 'lucide-react'
9 import { useCallback, useEffect, useState } from 'react'
10 import { useShallow } from 'zustand/react/shallow'
11 import { useTransClient } from '@/app/i18n/client'
12 import { Button } from '@/components/ui/button'
13 import {
14 Dialog,
15 DialogContent,
16 DialogFooter,
17 DialogHeader,
18 DialogTitle,
19 } from '@/components/ui/dialog'
20 import { Input } from '@/components/ui/input'
21 import { Label } from '@/components/ui/label'
22 import { useBrandPromotionStore } from '@/store/draft-box/brandPromotionStore'
23 import { toast } from '@/utils/ui/toast'
24
25 function CreatePlanModal() {
26 const { t } = useTransClient('brandPromotion')
27
28 const {
29 createPlanModalOpen,
30 editingPlan,
31 isSubmitting,
32 closePlanModal,
33 createPlan,
34 updatePlan,
35 } = useBrandPromotionStore(
36 useShallow(state => ({
37 createPlanModalOpen: state.createPlanModalOpen,
38 editingPlan: state.editingPlan,
39 isSubmitting: state.isSubmitting,
40 closePlanModal: state.closePlanModal,
41 createPlan: state.createPlan,
42 updatePlan: state.updatePlan,
43 })),
44 )
45
46 const isEdit = editingPlan !== null
47
48 // 表单状态
49 const [name, setName] = useState('')
50
51 // 初始化/重置表单
52 useEffect(() => {
53 if (createPlanModalOpen) {
54 if (editingPlan) {
55 setName(editingPlan.name || editingPlan.title || '')
56 }
57 else {
58 setName('')
59 }
60 }
61 }, [createPlanModalOpen, editingPlan])
62
63 const handleSubmit = useCallback(async () => {
64 if (!name.trim())
65 return
66
67 if (isEdit && editingPlan) {
68 const success = await updatePlan(editingPlan.id, {
69 name: name.trim(),
70 })
71 if (success) {
72 toast.success(t('createPlan.updateSuccess'))
73 closePlanModal()
74 }
75 else {
76 toast.error(t('createPlan.updateFailed'))
77 }
78 }
79 else {
80 const success = await createPlan({
81 name: name.trim(),
82 })
83 if (success) {
84 toast.success(t('createPlan.createSuccess'))
85 closePlanModal()
86 }
87 else {
88 toast.error(t('createPlan.createFailed'))
89 }
90 }
91 }, [name, isEdit, editingPlan, createPlan, updatePlan, closePlanModal, t])
92
93 if (!createPlanModalOpen) {
94 return null
95 }
96
97 return (
98 <Dialog
99 open={createPlanModalOpen}
100 onOpenChange={(open) => {
101 if (!open)
102 closePlanModal()
103 }}
104 >
105 <DialogContent className="sm:w-[min(500px,95vw)]">
106 <DialogHeader>
107 <DialogTitle>
108 {isEdit ? t('createPlan.editTitle') : t('createPlan.title')}
109 </DialogTitle>
110 </DialogHeader>
111
112 <div className="space-y-4 py-2">
113 {/* 名称 */}
114 <div className="space-y-2">
115 <Label>{t('createPlan.name')}</Label>
116 <Input
117 value={name}
118 onChange={e => setName(e.target.value)}
119 placeholder={t('createPlan.namePlaceholder')}
120 maxLength={50}
121 />
122 </div>
123 </div>
124
125 <DialogFooter>
126 <Button
127 variant="outline"
128 onClick={closePlanModal}
129 disabled={isSubmitting}
130 className="cursor-pointer"
131 >
132 {t('common.cancel')}
133 </Button>
134 <Button
135 onClick={handleSubmit}
136 disabled={isSubmitting || !name.trim()}
137 className="cursor-pointer"
138 >
139 {isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
140 {isEdit ? t('common.save') : t('common.confirm')}
141 </Button>
142 </DialogFooter>
143 </DialogContent>
144 </Dialog>
145 )
146 }
147
148 export default CreatePlanModal
149
149 lines Plain Text