136 lines
4.7 KiB
TypeScript
136 lines
4.7 KiB
TypeScript
import * as React from 'react'
|
|
|
|
import { AppField } from '@/components/app/AppField'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle
|
|
} from '@/components/ui/dialog'
|
|
import { Switch } from '@/components/ui/switch'
|
|
import { Textarea } from '@/components/ui/textarea'
|
|
|
|
import type { TicketStatus } from './types'
|
|
|
|
const STATUS_ACTION_LABELS: Partial<Record<TicketStatus, string>> = {
|
|
in_progress: '开始处理',
|
|
pending_confirm: '提交确认',
|
|
resolved: '标记解决',
|
|
closed: '关闭工单'
|
|
}
|
|
|
|
type TicketTransitionDialogProps = {
|
|
open: boolean
|
|
status: TicketStatus | null
|
|
actionLabel?: string
|
|
onOpenChange: (open: boolean) => void
|
|
onSubmit: (
|
|
status: TicketStatus,
|
|
values: {
|
|
remark: string
|
|
resolution: string
|
|
is_resolved: boolean
|
|
replied_to_customer: boolean
|
|
}
|
|
) => Promise<void>
|
|
}
|
|
|
|
export function TicketTransitionDialog({
|
|
open,
|
|
status,
|
|
actionLabel: customActionLabel,
|
|
onOpenChange,
|
|
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 || !canSubmit) return
|
|
setSubmitting(true)
|
|
try {
|
|
await onSubmit(status, {
|
|
remark: remark.trim(),
|
|
resolution: resolution.trim(),
|
|
is_resolved: status === 'resolved',
|
|
replied_to_customer: repliedToCustomer
|
|
})
|
|
onOpenChange(false)
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-lg">
|
|
<form className="grid gap-4" onSubmit={handleSubmit}>
|
|
<DialogHeader>
|
|
<DialogTitle>{actionLabel}</DialogTitle>
|
|
<DialogDescription>
|
|
{isResolveAction ? '填写本次处理结论,系统会同步记录到工单详情和流转时间线。' : '请说明本次流转的原因、处理动作或待确认事项。'}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
{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={!isResolveAction}
|
|
minLength={2}
|
|
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={!canSubmit || submitting}>
|
|
{submitting ? '提交中...' : '确认提交'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|