Files
model-platform/frontend/app/features/admin/UserFormDialog.tsx
T

236 lines
7.3 KiB
TypeScript

import type { Employee, Role, Workspace } from "../../services/api";
import {
AppFormDialog,
dialogPrimaryButtonClass,
dialogSecondaryButtonClass,
} from "~/components/common/AppFormDialog";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { CreateUserProjectField } from "./CreateUserProjectField";
import {
formFieldClass,
formHintClass,
formInputClass,
modalFormClass,
} from "../platform/modalUi";
export type UserFormState = {
username: string;
display_name: string;
email: string;
role_code: "admin" | "developer";
password: string;
status: "active" | "disabled" | "locked";
};
const FORM_INPUT_CLASS =
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
export function UserFormDialog({
open,
editing,
form,
saving,
currentUserId,
roles,
createRoleOptions,
createProjects,
createProjectsLoading,
selectedWorkspaceIds,
onChangeForm,
onToggleWorkspace,
onSubmit,
onClose,
onResetPassword,
}: {
open: boolean;
editing: Employee | null;
form: UserFormState;
saving: boolean;
currentUserId: string | undefined;
roles: Role[];
createRoleOptions: Role[];
createProjects: Workspace[];
createProjectsLoading: boolean;
selectedWorkspaceIds: string[];
onChangeForm: (next: UserFormState) => void;
onToggleWorkspace: (workspaceId: string) => void;
onSubmit: (event: React.FormEvent) => void;
onClose: () => void;
onResetPassword?: () => void;
}) {
return (
<AppFormDialog
open={open}
onOpenChange={(nextOpen) => {
if (!nextOpen) onClose();
}}
eyebrow="EMPLOYEE"
title={editing ? "编辑用户" : "添加用户"}
>
<form className={modalFormClass} onSubmit={onSubmit}>
<label className={formFieldClass}>
<span>
姓名<span className="text-danger">*</span>
</span>
<Input
className={FORM_INPUT_CLASS}
autoFocus={!editing}
value={form.display_name}
onChange={(event) => onChangeForm({ ...form, display_name: event.target.value })}
placeholder="请输入姓名"
/>
</label>
<label className={formFieldClass}>
<span>
登录账号<span className="text-danger">*</span>
</span>
<Input
className={FORM_INPUT_CLASS}
disabled={Boolean(editing)}
value={form.username}
onChange={(event) => onChangeForm({ ...form, username: event.target.value })}
placeholder="请输入登录账号"
autoComplete="username"
/>
</label>
{!editing && (
<label className={formFieldClass}>
<span>
密码<span className="text-danger">*</span>
</span>
<Input
className={FORM_INPUT_CLASS}
type="password"
autoComplete="new-password"
value={form.password}
onChange={(event) => onChangeForm({ ...form, password: event.target.value })}
placeholder="请输入密码(8~72 字符)"
/>
</label>
)}
<label className={formFieldClass}>
<span>邮箱</span>
<Input
className={FORM_INPUT_CLASS}
type="email"
autoComplete="email"
value={form.email}
onChange={(event) => onChangeForm({ ...form, email: event.target.value })}
placeholder="请输入邮箱(可选)"
/>
</label>
<label className={formFieldClass}>
<span>角色</span>
<select
className={formInputClass}
value={form.role_code}
disabled={
editing
? editing.user_id === currentUserId || roles.length === 0
: createRoleOptions.length === 0
}
onChange={(event) =>
onChangeForm({
...form,
role_code: event.target.value as UserFormState["role_code"],
})
}
>
{editing ? (
roles.length === 0 ? (
<option value="" disabled>
加载中…
</option>
) : (
roles.map((role) => (
<option key={role.role_code} value={role.role_code}>
{role.role_name}
</option>
))
)
) : createRoleOptions.length === 0 ? (
<option value="" disabled>
加载中…
</option>
) : (
createRoleOptions.map((role) => (
<option key={role.role_code} value={role.role_code}>
{role.role_name}
</option>
))
)}
</select>
</label>
{!editing && (
<CreateUserProjectField
projects={createProjects}
loading={createProjectsLoading}
selectedIds={selectedWorkspaceIds}
onToggle={onToggleWorkspace}
/>
)}
{editing && (
<label className={formFieldClass}>
<span>状态</span>
<select
className={formInputClass}
value={form.status}
disabled={
editing.user_id === currentUserId || editing.role_code === "admin"
}
onChange={(event) =>
onChangeForm({
...form,
status: event.target.value as UserFormState["status"],
})
}
>
<option value="active">正常</option>
<option value="disabled">停用</option>
<option value="locked">锁定</option>
</select>
</label>
)}
{editing && onResetPassword ? (
<div className="mb-4 rounded-md border border-[#e6edf3] bg-[#f8fafb] px-3.5 py-3">
<div className="text-[12px] font-semibold text-[#3c4e62]">安全</div>
<p className={`${formHintClass} mt-1.5 mb-2.5 leading-relaxed`}>
该用户忘记密码时,可为其设置临时密码。对方需使用新密码重新登录。
</p>
<Button
type="button"
variant="outline"
size="sm"
className={dialogSecondaryButtonClass}
onClick={onResetPassword}
>
重置密码
</Button>
</div>
) : null}
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
type="button"
onClick={onClose}
className={dialogSecondaryButtonClass}
>
取消
</Button>
<Button
variant="default"
size="sm"
type="submit"
disabled={saving}
className={dialogPrimaryButtonClass}
>
{saving ? "保存中…" : "保存"}
</Button>
</div>
</form>
</AppFormDialog>
);
}