feat: add category-driven ticket workflows
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
import type { TicketStatus } from './types'
|
||||
@@ -26,7 +27,15 @@ type TicketTransitionDialogProps = {
|
||||
status: TicketStatus | null
|
||||
actionLabel?: string
|
||||
onOpenChange: (open: boolean) => void
|
||||
onSubmit: (status: TicketStatus, remark: string) => Promise<void>
|
||||
onSubmit: (
|
||||
status: TicketStatus,
|
||||
values: {
|
||||
remark: string
|
||||
resolution: string
|
||||
is_resolved: boolean
|
||||
replied_to_customer: boolean
|
||||
}
|
||||
) => Promise<void>
|
||||
}
|
||||
|
||||
export function TicketTransitionDialog({
|
||||
@@ -37,22 +46,33 @@ export function TicketTransitionDialog({
|
||||
onSubmit
|
||||
}: TicketTransitionDialogProps) {
|
||||
const [remark, setRemark] = React.useState('')
|
||||
const [resolution, setResolution] = React.useState('')
|
||||
const [repliedToCustomer, setRepliedToCustomer] = React.useState(true)
|
||||
const [submitting, setSubmitting] = React.useState(false)
|
||||
const actionLabel = customActionLabel ?? (status ? STATUS_ACTION_LABELS[status] ?? '调整状态' : '调整状态')
|
||||
const isResolveAction = status === 'resolved'
|
||||
const canSubmit = isResolveAction ? Boolean((resolution || remark).trim()) : Boolean(remark.trim())
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) {
|
||||
setRemark('')
|
||||
setResolution('')
|
||||
setRepliedToCustomer(true)
|
||||
setSubmitting(false)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
if (!status || !remark.trim()) return
|
||||
if (!status || !canSubmit) return
|
||||
setSubmitting(true)
|
||||
try {
|
||||
await onSubmit(status, remark.trim())
|
||||
await onSubmit(status, {
|
||||
remark: remark.trim(),
|
||||
resolution: resolution.trim(),
|
||||
is_resolved: status === 'resolved',
|
||||
replied_to_customer: repliedToCustomer
|
||||
})
|
||||
onOpenChange(false)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
@@ -65,23 +85,46 @@ export function TicketTransitionDialog({
|
||||
<form className="grid gap-4" onSubmit={handleSubmit}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{actionLabel}</DialogTitle>
|
||||
<DialogDescription>请说明本次流转的原因、处理动作或待确认事项。</DialogDescription>
|
||||
<DialogDescription>
|
||||
{isResolveAction ? '填写本次处理结论,系统会同步记录到工单详情和流转时间线。' : '请说明本次流转的原因、处理动作或待确认事项。'}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AppField label="处理说明" htmlFor="transition-remark" required>
|
||||
{isResolveAction ? (
|
||||
<AppField label="解决方案" htmlFor="transition-resolution" required>
|
||||
<Textarea
|
||||
id="transition-resolution"
|
||||
required
|
||||
minLength={2}
|
||||
placeholder="例如:已查询采集进度,当前已完成 80%,预计今日完成。"
|
||||
value={resolution}
|
||||
onChange={(event) => setResolution(event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
) : null}
|
||||
<AppField label={isResolveAction ? '处理说明' : '处理说明'} htmlFor="transition-remark" required={!isResolveAction}>
|
||||
<Textarea
|
||||
id="transition-remark"
|
||||
required
|
||||
required={!isResolveAction}
|
||||
minLength={2}
|
||||
placeholder="例如:已完成日志排查,定位为配置变更导致,提交业务侧确认。"
|
||||
placeholder={isResolveAction ? '可补充内部排查说明。' : '例如:已完成日志排查,定位为配置变更导致,提交业务侧确认。'}
|
||||
value={remark}
|
||||
onChange={(event) => setRemark(event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
{isResolveAction ? (
|
||||
<div className="flex items-center justify-between rounded-md border border-border p-3">
|
||||
<div className="grid gap-1">
|
||||
<span className="text-sm font-medium text-foreground">已回复客户</span>
|
||||
<span className="text-xs text-muted-foreground">开启后会在工单结果中记录为已回复客户。</span>
|
||||
</div>
|
||||
<Switch checked={repliedToCustomer} onCheckedChange={setRepliedToCustomer} />
|
||||
</div>
|
||||
) : null}
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="submit" disabled={!remark.trim() || submitting}>
|
||||
<Button type="submit" disabled={!canSubmit || submitting}>
|
||||
{submitting ? '提交中...' : '确认提交'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user