Files
hair-keeper/src/app/(main)/users/dept-admin/components/DeptAdminCreateDialog.tsx

187 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { trpc } from '@/lib/trpc'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Dialog, DialogTrigger } from '@/components/ui/dialog'
import { Plus } from 'lucide-react'
import { toast } from 'sonner'
import { FormDialog, FormActionBar, FormGridContent, FormCancelAction, FormSubmitAction, type FormFieldConfig } from '@/components/common/form-dialog'
import {
AdvancedSelect,
SelectPopover,
SelectTrigger,
SelectContent,
SelectInput,
SelectItemList,
SelectedName
} from '@/components/common/advanced-select'
import { useSmartSelectOptions } from '@/hooks/use-smart-select-options'
import { Textarea } from '@/components/ui/textarea'
// 创建院系管理员的 schema
const createDeptAdminSchema = z.object({
uid: z.string().min(1, '用户ID不能为空'),
deptCode: z.string().min(1, '院系代码不能为空'),
adminEmail: z.string().email('邮箱格式不正确').optional().or(z.literal('')),
adminLinePhone: z.string().optional(),
adminMobilePhone: z.string().optional(),
note: z.string().optional(),
})
type CreateDeptAdminInput = z.input<typeof createDeptAdminSchema>
const createDeptAdminDefaultValues: CreateDeptAdminInput = {
uid: '',
deptCode: '',
adminEmail: '',
adminLinePhone: '',
adminMobilePhone: '',
note: '',
}
interface DeptAdminCreateDialogProps {
onDeptAdminCreated: () => void
}
export function DeptAdminCreateDialog({ onDeptAdminCreated }: DeptAdminCreateDialogProps) {
// 表单 dialog 控制
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
// react-hook-form 管理创建表单
const createForm = useForm<CreateDeptAdminInput>({
resolver: zodResolver(createDeptAdminSchema),
defaultValues: createDeptAdminDefaultValues,
})
// 获取院系列表
const { data: depts } = trpc.common.getDepts.useQuery()
const deptOptions = depts?.map(dept => ({ id: dept.code, name: dept.fullName, shortName: dept.name })) || []
const { sortedOptions: sortedDeptOptions, logSelection: logDeptSelection } = useSmartSelectOptions({
options: deptOptions,
context: 'deptAdmin.create.dept',
scope: 'personal',
})
// 创建院系管理员 mutation
const createDeptAdminMutation = trpc.deptAdmin.create.useMutation({
onSuccess: () => {
setIsCreateDialogOpen(false)
createForm.reset(createDeptAdminDefaultValues)
toast.success('院系管理员创建成功')
onDeptAdminCreated()
},
onError: (error) => {
toast.error(error.message || '创建院系管理员失败')
},
})
// 定义字段配置
const formFields: FormFieldConfig[] = React.useMemo(() => [
{
name: 'uid',
label: '用户ID',
required: true,
render: ({ field }) => (
<Input {...field} placeholder="请输入用户ID职工号" />
),
},
{
name: 'deptCode',
label: '院系',
required: true,
render: ({ field }) => (
<AdvancedSelect
{...field}
options={sortedDeptOptions}
onChange={(value) => { logDeptSelection(value); field.onChange(value) }}
filterFunction={(option, searchValue) => {
const search = searchValue.toLowerCase()
return option.id.includes(search) || option.name.toLowerCase().includes(search) ||
(option.shortName && option.shortName.toLowerCase().includes(search))
}}
>
<SelectPopover>
<SelectTrigger placeholder="请选择院系">
<SelectedName />
</SelectTrigger>
<SelectContent>
<SelectInput placeholder="搜索院系名称/代码" />
<SelectItemList />
</SelectContent>
</SelectPopover>
</AdvancedSelect>
),
},
{
name: 'adminEmail',
label: '邮箱',
render: ({ field }) => (
<Input {...field} type="email" placeholder="请输入邮箱" />
),
},
{
name: 'adminLinePhone',
label: '座机',
render: ({ field }) => (
<Input {...field} placeholder="请输入座机号码" />
),
},
{
name: 'adminMobilePhone',
label: '手机',
render: ({ field }) => (
<Input {...field} placeholder="请输入手机号码" />
),
},
{
name: 'note',
label: '备注',
render: ({ field }) => (
<Textarea {...field} placeholder="请输入备注信息" className="min-h-[80px]" />
),
},
], [sortedDeptOptions, logDeptSelection])
const handleSubmit = async (data: CreateDeptAdminInput) => {
createDeptAdminMutation.mutate(data)
}
const handleClose = () => {
setIsCreateDialogOpen(false)
}
return (
<>
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
<DialogTrigger asChild>
<Button className="flex items-center gap-2">
<Plus className="h-4 w-4" />
</Button>
</DialogTrigger>
</Dialog>
<FormDialog
isOpen={isCreateDialogOpen}
title="创建院系管理员"
description="请填写院系管理员信息"
form={createForm}
fields={formFields}
onClose={handleClose}
>
<FormGridContent />
<FormActionBar>
<FormCancelAction />
<FormSubmitAction onSubmit={handleSubmit} isSubmitting={createDeptAdminMutation.isPending}></FormSubmitAction>
</FormActionBar>
</FormDialog>
</>
)
}