Submit
Path:
~
/
/
usr
/
share
/
grafana
/
public
/
app
/
features
/
admin
/
File Content:
UserProfile.tsx
import { css, cx } from '@emotion/css'; import React, { PureComponent, useRef, useState } from 'react'; import { Button, ConfirmButton, ConfirmModal, Input, LegacyInputStatus, Stack } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; import { AccessControlAction, UserDTO } from 'app/types'; interface Props { user: UserDTO; onUserUpdate: (user: UserDTO) => void; onUserDelete: (userId: number) => void; onUserDisable: (userId: number) => void; onUserEnable: (userId: number) => void; onPasswordChange(password: string): void; } export function UserProfile({ user, onUserUpdate, onUserDelete, onUserDisable, onUserEnable, onPasswordChange, }: Props) { const [showDeleteModal, setShowDeleteModal] = useState(false); const [showDisableModal, setShowDisableModal] = useState(false); const deleteUserRef = useRef<HTMLButtonElement | null>(null); const showDeleteUserModal = (show: boolean) => () => { setShowDeleteModal(show); if (!show && deleteUserRef.current) { deleteUserRef.current.focus(); } }; const disableUserRef = useRef<HTMLButtonElement | null>(null); const showDisableUserModal = (show: boolean) => () => { setShowDisableModal(show); if (!show && disableUserRef.current) { disableUserRef.current.focus(); } }; const handleUserDelete = () => onUserDelete(user.id); const handleUserDisable = () => onUserDisable(user.id); const handleUserEnable = () => onUserEnable(user.id); const onUserNameChange = (newValue: string) => { onUserUpdate({ ...user, name: newValue, }); }; const onUserEmailChange = (newValue: string) => { onUserUpdate({ ...user, email: newValue, }); }; const onUserLoginChange = (newValue: string) => { onUserUpdate({ ...user, login: newValue, }); }; const authSource = user.authLabels?.length && user.authLabels[0]; const lockMessage = authSource ? `Synced via ${authSource}` : ''; const editLocked = user.isExternal || !contextSrv.hasPermissionInMetadata(AccessControlAction.UsersWrite, user); const passwordChangeLocked = user.isExternal || !contextSrv.hasPermissionInMetadata(AccessControlAction.UsersPasswordUpdate, user); const canDelete = contextSrv.hasPermissionInMetadata(AccessControlAction.UsersDelete, user); const canDisable = contextSrv.hasPermissionInMetadata(AccessControlAction.UsersDisable, user); const canEnable = contextSrv.hasPermissionInMetadata(AccessControlAction.UsersEnable, user); return ( <div> <h3 className="page-heading">User information</h3> <Stack direction="column" gap={1.5}> <div> <table className="filter-table form-inline"> <tbody> <UserProfileRow label="Name" value={user.name} locked={editLocked} lockMessage={lockMessage} onChange={onUserNameChange} /> <UserProfileRow label="Email" value={user.email} locked={editLocked} lockMessage={lockMessage} onChange={onUserEmailChange} /> <UserProfileRow label="Username" value={user.login} locked={editLocked} lockMessage={lockMessage} onChange={onUserLoginChange} /> <UserProfileRow label="Password" value="********" inputType="password" locked={passwordChangeLocked} lockMessage={lockMessage} onChange={onPasswordChange} /> </tbody> </table> </div> <Stack gap={2}> {canDelete && ( <> <Button variant="destructive" onClick={showDeleteUserModal(true)} ref={deleteUserRef}> Delete user </Button> <ConfirmModal isOpen={showDeleteModal} title="Delete user" body="Are you sure you want to delete this user?" confirmText="Delete user" onConfirm={handleUserDelete} onDismiss={showDeleteUserModal(false)} /> </> )} {user.isDisabled && canEnable && ( <Button variant="secondary" onClick={handleUserEnable}> Enable user </Button> )} {!user.isDisabled && canDisable && ( <> <Button variant="secondary" onClick={showDisableUserModal(true)} ref={disableUserRef}> Disable user </Button> <ConfirmModal isOpen={showDisableModal} title="Disable user" body="Are you sure you want to disable this user?" confirmText="Disable user" onConfirm={handleUserDisable} onDismiss={showDisableUserModal(false)} /> </> )} </Stack> </Stack> </div> ); } interface UserProfileRowProps { label: string; value?: string; locked?: boolean; lockMessage?: string; inputType?: string; onChange?: (value: string) => void; } interface UserProfileRowState { value: string; editing: boolean; } export class UserProfileRow extends PureComponent<UserProfileRowProps, UserProfileRowState> { inputElem?: HTMLInputElement; static defaultProps: Partial<UserProfileRowProps> = { value: '', locked: false, lockMessage: '', inputType: 'text', }; state = { editing: false, value: this.props.value || '', }; setInputElem = (elem: HTMLInputElement) => { this.inputElem = elem; }; onEditClick = () => { if (this.props.inputType === 'password') { // Reset value for password field this.setState({ editing: true, value: '' }, this.focusInput); } else { this.setState({ editing: true }, this.focusInput); } }; onCancelClick = () => { this.setState({ editing: false, value: this.props.value || '' }); }; onInputChange = (event: React.ChangeEvent<HTMLInputElement>, status?: LegacyInputStatus) => { if (status === LegacyInputStatus.Invalid) { return; } this.setState({ value: event.target.value, }); }; onInputBlur = (event: React.FocusEvent<HTMLInputElement>, status?: LegacyInputStatus) => { if (status === LegacyInputStatus.Invalid) { return; } this.setState({ value: event.target.value, }); }; focusInput = () => { if (this.inputElem && this.inputElem.focus) { this.inputElem.focus(); } }; onSave = () => { if (this.props.onChange) { this.props.onChange(this.state.value); } }; render() { const { label, locked, lockMessage, inputType } = this.props; const { value } = this.state; const labelClass = cx( 'width-16', css` font-weight: 500; ` ); if (locked) { return <LockedRow label={label} value={value} lockMessage={lockMessage} />; } const inputId = `${label}-input`; return ( <tr> <td className={labelClass}> <label htmlFor={inputId}>{label}</label> </td> <td className="width-25" colSpan={2}> {this.state.editing ? ( <Input id={inputId} type={inputType} defaultValue={value} onBlur={this.onInputBlur} onChange={this.onInputChange} ref={this.setInputElem} width={30} /> ) : ( <span>{this.props.value}</span> )} </td> <td> <ConfirmButton confirmText="Save" onClick={this.onEditClick} onConfirm={this.onSave} onCancel={this.onCancelClick} > Edit </ConfirmButton> </td> </tr> ); } } interface LockedRowProps { label: string; value?: string; lockMessage?: string; } export const LockedRow = ({ label, value, lockMessage }: LockedRowProps) => { const lockMessageClass = css` font-style: italic; margin-right: 0.6rem; `; const labelClass = cx( 'width-16', css` font-weight: 500; ` ); return ( <tr> <td className={labelClass}>{label}</td> <td className="width-25" colSpan={2}> {value} </td> <td> <span className={lockMessageClass}>{lockMessage}</span> </td> </tr> ); };
Submit
FILE
FOLDER
INFO
Name
Size
Permission
Action
UserListPublicDashboardPage
---
0755
Users
---
0755
ldap
---
0755
partials
---
0755
state
---
0755
AdminEditOrgPage.tsx
4158 bytes
0644
AdminFeatureTogglesAPI.test.ts
2528 bytes
0644
AdminFeatureTogglesAPI.ts
1973 bytes
0644
AdminFeatureTogglesPage.tsx
2586 bytes
0644
AdminFeatureTogglesTable.tsx
6014 bytes
0644
AdminListOrgsPage.tsx
1610 bytes
0644
AdminOrgsTable.tsx
3152 bytes
0644
AdminSettings.tsx
975 bytes
0644
AdminSettingsTable.tsx
2399 bytes
0644
LicenseChrome.tsx
2360 bytes
0644
OrgRolePicker.tsx
838 bytes
0644
ServerStats.test.tsx
1964 bytes
0644
ServerStats.tsx
4890 bytes
0644
ServerStatsCard.tsx
1911 bytes
0644
UpgradePage.tsx
6220 bytes
0644
UserAdminPage.tsx
6093 bytes
0644
UserCreatePage.tsx
2260 bytes
0644
UserLdapSyncInfo.tsx
2613 bytes
0644
UserListAdminPage.tsx
4214 bytes
0644
UserListAnonymousPage.tsx
3615 bytes
0644
UserListPage.test.tsx
7373 bytes
0644
UserListPage.tsx
4130 bytes
0644
UserOrgs.tsx
15051 bytes
0644
UserPermissions.tsx
2910 bytes
0644
UserProfile.tsx
8668 bytes
0644
UserSessions.tsx
3661 bytes
0644
api.ts
1477 bytes
0644
utils.ts
509 bytes
0644
N4ST4R_ID | Naxtarrr