blob: 8c96cb795822478fadede318a56dde6b36676fa3 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { useCallback, useEffect, useState } from 'react';
import { css, t, SupersetClient, useTheme, styled } from '@superset-ui/core';
import SubMenu, { SubMenuProps } from 'src/features/home/SubMenu';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import { Descriptions } from 'src/components/Descriptions';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import {
UserInfoEditModal,
UserInfoResetPasswordModal,
} from 'src/features/userInfo/UserInfoModal';
import { Icons, Collapse } from '@superset-ui/core/components';
const StyledHeader = styled.div`
${({ theme }) => css`
font-weight: ${theme.fontWeightStrong};
text-align: left;
font-size: 18px;
padding: ${theme.sizeUnit * 3}px;
padding-left: ${theme.sizeUnit * 7}px;
display: inline-block;
line-height: ${theme.sizeUnit * 9}px;
width: 100%;
background-color: ${theme.colorBgContainer};
margin-bottom: ${theme.sizeUnit * 6}px;
`}
`;
const DescriptionsContainer = styled.div`
${({ theme }) => css`
margin: 0px ${theme.sizeUnit * 3}px ${theme.sizeUnit * 6}px
${theme.sizeUnit * 3}px;
background-color: ${theme.colorBgContainer};
`}
`;
const StyledLayout = styled.div`
${({ theme }) => css`
.ant-row {
margin: 0px ${theme.sizeUnit * 3}px ${theme.sizeUnit * 6}px
${theme.sizeUnit * 3}px;
}
&& .menu > .ant-menu {
padding: 0px;
}
&& .nav-right {
left: 0;
padding-left: ${theme.sizeUnit * 4}px;
position: relative;
height: ${theme.sizeUnit * 15}px;
}
`}
`;
const DescriptionTitle = styled.span`
font-weight: ${({ theme }) => theme.fontWeightStrong};
`;
enum ModalType {
ResetPassword = 'resetPassword',
Edit = 'edit',
}
export function UserInfo({ user }: { user: UserWithPermissionsAndRoles }) {
const theme = useTheme();
const [modalState, setModalState] = useState({
resetPassword: false,
edit: false,
});
const openModal = (type: ModalType) =>
setModalState(prev => ({ ...prev, [type]: true }));
const closeModal = (type: ModalType) =>
setModalState(prev => ({ ...prev, [type]: false }));
const { addDangerToast } = useToasts();
const [userDetails, setUserDetails] = useState(user);
useEffect(() => {
getUserDetails();
}, []);
const getUserDetails = useCallback(() => {
SupersetClient.get({ endpoint: '/api/v1/me/' })
.then(({ json }) => {
const transformedUser = {
...json.result,
firstName: json.result.first_name,
lastName: json.result.last_name,
};
setUserDetails(transformedUser);
})
.catch(error => {
addDangerToast('Failed to fetch user info:', error);
});
}, [userDetails]);
const SubMenuButtons: SubMenuProps['buttons'] = [
{
name: (
<>
<Icons.LockOutlined
iconColor={theme.colorPrimary}
iconSize="m"
css={css`
margin: auto ${theme.sizeUnit * 2}px auto 0;
vertical-align: text-top;
`}
/>
{t('Reset my password')}
</>
),
buttonStyle: 'secondary',
onClick: () => {
openModal(ModalType.ResetPassword);
},
'data-test': 'reset-password-button',
},
{
name: (
<>
<Icons.FormOutlined
iconSize="m"
css={css`
margin: auto ${theme.sizeUnit * 2}px auto 0;
vertical-align: text-top;
`}
/>
{t('Edit user')}
</>
),
buttonStyle: 'primary',
onClick: () => {
openModal(ModalType.Edit);
},
'data-test': 'edit-user-button',
},
];
return (
<StyledLayout>
<StyledHeader>Your user information</StyledHeader>
<DescriptionsContainer>
<Collapse defaultActiveKey={['userInfo', 'personalInfo']} ghost>
<Collapse.Panel
header={<DescriptionTitle>User info</DescriptionTitle>}
key="userInfo"
>
<Descriptions
bordered
size="small"
column={1}
labelStyle={{ width: '120px' }}
>
<Descriptions.Item label="User Name">
{user.username}
</Descriptions.Item>
<Descriptions.Item label="Is Active?">
{user.isActive ? 'Yes' : 'No'}
</Descriptions.Item>
<Descriptions.Item label="Role">
{user.roles ? Object.keys(user.roles).join(', ') : 'None'}
</Descriptions.Item>
<Descriptions.Item label="Login count">
{user.loginCount}
</Descriptions.Item>
</Descriptions>
</Collapse.Panel>
<Collapse.Panel
header={<DescriptionTitle>Personal info</DescriptionTitle>}
key="personalInfo"
>
<Descriptions
bordered
size="small"
column={1}
labelStyle={{ width: '120px' }}
>
<Descriptions.Item label="First Name">
{userDetails.firstName}
</Descriptions.Item>
<Descriptions.Item label="Last Name">
{userDetails.lastName}
</Descriptions.Item>
<Descriptions.Item label="Email">{user.email}</Descriptions.Item>
</Descriptions>
</Collapse.Panel>
</Collapse>
</DescriptionsContainer>
{modalState.resetPassword && (
<UserInfoResetPasswordModal
onHide={() => closeModal(ModalType.ResetPassword)}
show={modalState.resetPassword}
onSave={() => {
closeModal(ModalType.ResetPassword);
}}
/>
)}
{modalState.edit && (
<UserInfoEditModal
onHide={() => closeModal(ModalType.Edit)}
show={modalState.edit}
onSave={() => {
closeModal(ModalType.Edit);
getUserDetails();
}}
user={userDetails}
/>
)}
<SubMenu buttons={SubMenuButtons} />
</StyledLayout>
);
}
export default UserInfo;