({
user,
onClose,
onCreated,
onUpdated,
}: Omit<CreateUserSheetProps, "open">)
| 493 | } |
| 494 | |
| 495 | function UserForm({ |
| 496 | user, |
| 497 | onClose, |
| 498 | onCreated, |
| 499 | onUpdated, |
| 500 | }: Omit<CreateUserSheetProps, "open">) { |
| 501 | const { t } = useTranslation(); |
| 502 | const createUser = useAppStore((state) => state.createUser); |
| 503 | const updateUser = useAppStore((state) => state.updateUser); |
| 504 | const patchWorkspaceIamPolicy = useAppStore( |
| 505 | (state) => state.patchWorkspaceIamPolicy |
| 506 | ); |
| 507 | const passwordRestriction = useAppStore( |
| 508 | (s) => s.getWorkspaceProfile().passwordRestriction |
| 509 | ); |
| 510 | const enforceIdentityDomain = useAppStore( |
| 511 | (s) => s.getWorkspaceProfile().enforceIdentityDomain |
| 512 | ); |
| 513 | const workspaceDomains = useAppStore((s) => s.getWorkspaceProfile().domains); |
| 514 | |
| 515 | const isEditMode = |
| 516 | !!user && user.name !== "" && !user.name.endsWith("/unknown"); |
| 517 | |
| 518 | const allowUpdate = |
| 519 | !isEditMode || hasWorkspacePermissionV2("bb.users.update"); |
| 520 | |
| 521 | // Capture initial values on mount. Because the parent keys this component |
| 522 | // by user, it remounts fresh every time a different user is edited, so |
| 523 | // these initial values always reflect the latest `user` prop. |
| 524 | // |
| 525 | // The empty deps array is intentional: we want the initial baseline |
| 526 | // frozen at mount so dirty tracking compares against it. If we added |
| 527 | // `userMapToRoles` to deps, a Pinia store refresh mid-edit would move |
| 528 | // the baseline and incorrectly classify untouched fields as dirty |
| 529 | // (or vice versa). Since the outer CreateUserSheet wrapper remounts |
| 530 | // this component whenever the edited user changes, "mount-only" is the |
| 531 | // right scope for the baseline. |
| 532 | const initialRoles = useMemo(() => { |
| 533 | if (!user || !isEditMode) { |
| 534 | return [PresetRoleType.WORKSPACE_MEMBER]; |
| 535 | } |
| 536 | // Read a one-shot snapshot of the workspace role map at mount. The parent |
| 537 | // page loads the workspace IAM policy, so it is populated by the time an |
| 538 | // edit sheet opens; reading via getState keeps this baseline mount-only. |
| 539 | const roles = useAppStore |
| 540 | .getState() |
| 541 | .workspaceUserMapToRoles() |
| 542 | .get(getUserFullNameByType(user)); |
| 543 | return roles ? [...roles] : []; |
| 544 | }, []); |
| 545 | const initialTitle = user?.title ?? ""; |
| 546 | const initialEmail = user?.email ?? ""; |
| 547 | const initialPhone = user?.phone ?? ""; |
| 548 | |
| 549 | const [title, setTitle] = useState(initialTitle); |
| 550 | const [email, setEmail] = useState(initialEmail); |
| 551 | const [phone, setPhone] = useState(initialPhone); |
| 552 | const [password, setPassword] = useState(""); |
nothing calls this directly
no test coverage detected