People.Client: added Invite dialog component

This commit is contained in:
Daniil Senkiv 2019-09-17 09:16:42 +03:00
parent 67007af5d5
commit 84c2f3faed
9 changed files with 414 additions and 42 deletions

View File

@ -7,12 +7,20 @@ import {
DropDownItem,
toastr
} from "asc-web-components";
import InviteDialog from './../../dialogs/Invite';
import { isAdmin } from '../../../store/auth/selectors';
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from '../i18n';
import { typeUser, typeGuest, department } from './../../../helpers/customNames';
class PureArticleMainButtonContent extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogVisible: false,
}
}
onDropDownItemClick = (link) => {
this.props.history.push(link);
};
@ -21,48 +29,57 @@ class PureArticleMainButtonContent extends React.Component {
toastr.success(text);
};
toggleDialogVisible = () => this.setState({ dialogVisible: !this.state.dialogVisible });
render() {
console.log("People ArticleMainButtonContent render");
const { isAdmin, settings, t } = this.props;
return (
isAdmin ?
<MainButton
isDisabled={false}
isDropdown={true}
text={t('Actions')}
>
<DropDownItem
icon="CatalogEmployeeIcon"
label={t('CustomNewEmployee', { typeUser })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/create/user`)}
<>
<MainButton
isDisabled={false}
isDropdown={true}
text={t('Actions')}
>
<DropDownItem
icon="CatalogEmployeeIcon"
label={t('CustomNewEmployee', { typeUser })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/create/user`)}
/>
<DropDownItem
icon="CatalogGuestIcon"
label={t('CustomNewGuest', { typeGuest })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/create/guest`)}
/>
<DropDownItem
icon="CatalogDepartmentsIcon"
label={t('CustomNewDepartment', { department })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/group/create`)}
/>
<DropDownItem isSeparator />
<DropDownItem
icon="InvitationLinkIcon"
label={t('InviteLinkTitle')}
onClick={this.toggleDialogVisible}
/>
<DropDownItem
icon="PlaneIcon"
label={t('LblInviteAgain')}
onClick={this.onNotImplementedClick.bind(this, "Invite again action")}
/>
<DropDownItem
icon="ImportIcon"
label={t('ImportPeople')}
onClick={this.onNotImplementedClick.bind(this, "Import people action")}
/>
</MainButton>
<InviteDialog
visible={this.state.dialogVisible}
onClose={this.toggleDialogVisible}
onCloseButton={this.toggleDialogVisible}
/>
<DropDownItem
icon="CatalogGuestIcon"
label={t('CustomNewGuest', { typeGuest })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/create/guest`)}
/>
<DropDownItem
icon="CatalogDepartmentsIcon"
label={t('CustomNewDepartment', { department })}
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/group/create`)}
/>
<DropDownItem isSeparator />
<DropDownItem
icon="InvitationLinkIcon"
label={t('InviteLinkTitle')}
onClick={this.onNotImplementedClick.bind(this, "Invitation link action")}
/>
<DropDownItem
icon="PlaneIcon"
label={t('LblInviteAgain')}
onClick={this.onNotImplementedClick.bind(this, "Invite again action")}
/>
<DropDownItem
icon="ImportIcon"
label={t('ImportPeople')}
onClick={this.onNotImplementedClick.bind(this, "Import people action")}
/>
</MainButton>
</>
:
<></>
);

View File

@ -0,0 +1,57 @@
import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import config from "../../../../package.json";
const newInstance = i18n.createInstance();
if (process.env.NODE_ENV === "production") {
newInstance
.use(Backend)
.init({
lng: 'en',
fallbackLng: "en",
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
useSuspense: true
},
backend: {
loadPath: `${config.homepage}/locales/Invite/{{lng}}/{{ns}}.json`
}
});
} else if (process.env.NODE_ENV === "development") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
}
};
newInstance.init({
resources: resources,
lng: 'en',
fallbackLng: "en",
debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === 'lowercase') return value.toLowerCase();
return value;
}
},
react: {
useSuspense: true
}
});
}
export default newInstance;

View File

@ -0,0 +1,224 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import {
toastr,
ModalDialog,
Link,
Checkbox,
Button,
Textarea,
Text
} from "asc-web-components";
import { getInvitationLink, getShortenedLink } from '../../../store/profile/actions';
import { withTranslation, I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import { typeGuests } from './../../../helpers/customNames';
import styled from 'styled-components'
const ModalDialogContainer = styled.div`
.margin-text {
margin: 12px 0;
}
.margin-link {
margin-right: 12px;
}
.margin-textarea {
margin-top: 12px;
}
.flex{
display: flex;
justify-content: space-between;
}
`;
const textAreaName = 'link-textarea';
class PureInviteDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
isGuest: false,
peopleInvitationLink: '',
guestInvitationLink: '',
isLoading: true,
isLinkShort: false
}
}
onCopyLinkToClipboard = () => {
const { t } = this.props;
const link = document.getElementsByName(textAreaName)[0];
link.select();
document.execCommand('copy');
toastr.success(t('LinkCopySuccess'));
window.getSelection().removeAllRanges();
link.blur();
};
onCheckedGuest = () => this.setState({ isGuest: !this.state.isGuest });
onGetShortenedLink = () => {
this.setState({ isLoading: true });
const { getShortenedLink } = this.props;
getShortenedLink(this.state.peopleInvitationLink)
.then((res) => {
// console.log("getShortInvitationLinkPeople success", res.data.response);
this.setState({ peopleInvitationLink: res.data.response });
})
.catch(e => {
console.error("getShortInvitationLink error", e);
this.setState({ isLoading: false });
});
getShortenedLink(this.state.guestInvitationLink)
.then((res) => {
// console.log("getShortInvitationLinkGuest success", res.data.response);
this.setState({
guestInvitationLink: res.data.response,
isLoading: false,
isLinkShort: true
});
})
.catch(e => {
console.error("getShortInvitationLink error", e);
});
};
componentDidUpdate(prevProps, prevState) {
if (!prevProps.visible && !prevState.peopleInvitationLink && !prevState.guestInvitationLink) {
// console.log('INVITE DIALOG DidUpdate');
const { getInvitationLink } = this.props;
const isGuest = true;
getInvitationLink()
.then((res) => {
// console.log("getInvitationLinkPeople success", res.data.response);
this.setState({
peopleInvitationLink: res.data.response,
isLoading: false
});
this.onCopyLinkToClipboard();
})
.catch(e => {
console.error("getInvitationLinkPeople error", e);
this.setState({ isLoading: false });
});
getInvitationLink(isGuest)
.then((res) => {
// console.log("getInvitationLinkGuest success", res.data.response);
this.setState({ guestInvitationLink: res.data.response });
})
.catch(e => {
console.error("getInvitationLinkGuest error", e);
this.setState({ isLoading: false });
});
};
}
onClickToCloseButton = () => this.props.onCloseButton && this.props.onCloseButton();
render() {
console.log("InviteDialog render");
const { t, visible, onClose } = this.props;
const fakeSettings = { hasShortenService: false };
return (
<ModalDialogContainer>
<ModalDialog
visible={visible}
onClose={() => onClose && onClose()}
headerContent={t('InviteLinkTitle')}
bodyContent={(
<>
<Text.Body
className='margin-text'
as='p'>
{t('HelpAnswerLinkInviteSettings')}
</Text.Body>
<Text.Body
className='margin-text'
as='p'>
{t('InviteLinkValidInterval', { count: 7 })}
</Text.Body>
<div className='flex'>
<div>
<Link
className='margin-link'
type='action'
isHovered={true}
onClick={this.onCopyLinkToClipboard}
>
{t('CopyToClipboard')}
</Link>
{
fakeSettings.hasShortenService && !this.state.isLinkShort &&
<Link type='action'
isHovered={true}
onClick={this.onGetShortenedLink}
>
{t('GetShortenLink')}
</Link>
}
</div>
<Checkbox
label={t('InviteUsersAsCollaborators', { typeGuests })}
isChecked={this.state.isGuest}
onChange={this.onCheckedGuest}
/>
</div>
<Textarea
className='margin-textarea'
isReadOnly={true}
isDisabled={this.state.isLoading}
name={textAreaName}
value={this.state.isGuest ? this.state.guestInvitationLink : this.state.peopleInvitationLink}
/>
</>
)}
footerContent={(
<>
<Button
key="CloseBtn"
label={this.state.isLoading ? t('LoadingProcessing') : t('CloseButton')}
size="medium"
primary={true}
onClick={this.onClickToCloseButton}
isLoading={this.state.isLoading}
/>
</>
)}
/>
</ModalDialogContainer>
);
};
};
const mapStateToProps = (state) => {
return {
settings: state.auth.settings
}
}
const InviteDialogContainer = withTranslation()(PureInviteDialog);
const InviteDialog = (props) => <I18nextProvider i18n={i18n}><InviteDialogContainer {...props} /></I18nextProvider>;
InviteDialog.propTypes = {
visible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
onCloseButton: PropTypes.func.isRequired
};
export default connect(mapStateToProps, { getInvitationLink, getShortenedLink })(withRouter(InviteDialog));

View File

@ -0,0 +1,13 @@
{
"InviteLinkTitle": "Invitation link",
"HelpAnswerLinkInviteSettings": "Share the link to invite your colleagues to your portal.",
"InviteLinkValidInterval": "This link is valid for {{ count }} day only.",
"CopyToClipboard": "Copy the link",
"CloseButton": "Close",
"LinkCopySuccess": "Link has been copied to the clipboard",
"GetShortenLink": "Get shortened link",
"InviteUsersAsCollaborators": "Add users as {{typeGuests, lowercase}}",
"LoadingProcessing": "Loading...",
"InviteLinkValidInterval_plural": "This link is valid for {{ count }} days only."
}

View File

@ -3,5 +3,6 @@ export const department = 'Department';
export const position = 'Position';
export const employedSinceDate = 'Employed since';
export const typeGuest = 'Guest';
export const typeGuests = 'Guests';
export const typeUser = 'Employee';
export const headOfDepartment = 'Head of Department';

View File

@ -9,6 +9,22 @@
"ImportPeople"
]
},
"dialogs": {
"Invite": {
"Resource": [
"HelpAnswerLinkInviteSettings",
"CopyToClipboard",
"CloseButton",
"GetShortenLink",
"LoadingProcessing",
"InviteUsersAsCollaborators",
"InviteLinkTitle"
],
"ResourceJS": [
"LinkCopySuccess"
]
}
},
"pages": {
"Profile": {
"Resource": [

View File

@ -31,7 +31,7 @@ export function employeeWrapperToMemberModel(profile) {
const department = profile.groups ? profile.groups.map(group => group.id) : [];
const worksFrom = profile.workFrom;
return {...profile, comment, department, worksFrom};
return { ...profile, comment, department, worksFrom };
}
export function fetchProfile(userName) {
@ -56,8 +56,8 @@ export function fetchProfile(userName) {
export function createProfile(profile) {
return (dispatch, getState) => {
const {people} = getState();
const {filter} = people;
const { people } = getState();
const { filter } = people;
const member = employeeWrapperToMemberModel(profile);
let result;
@ -75,8 +75,8 @@ export function createProfile(profile) {
export function updateProfile(profile) {
return (dispatch, getState) => {
const {people} = getState();
const {filter} = people;
const { people } = getState();
const { filter } = people;
const member = employeeWrapperToMemberModel(profile);
let result;
@ -112,4 +112,24 @@ export function updateAvatar(profileId, images) {
});
}
};
};
};
export function getInvitationLink(isGuest = false) {
return dispatch => {
return api.getInvitationLink(isGuest)
.then(res => {
checkResponseError(res);
return Promise.resolve(res);
});
}
}
export function getShortenedLink(link) {
return dispatch => {
return api.getShortenedLink(link)
.then(res => {
checkResponseError(res);
return Promise.resolve(res);
});
}
}

View File

@ -152,6 +152,20 @@ export function getGroup(groupId) {
: axios.get(`${API_URL}/group/${groupId}.json`);
}
export function getInvitationLink(isGuest) {
return IS_FAKE
? fakeApi.getInvitationLink(isGuest)
: isGuest
? axios.get(`${API_URL}/portal/users/invite/2.json`)
: axios.get(`${API_URL}/portal/users/invite/1.json`);
}
export function getShortenedLink(link) {
return IS_FAKE
? fakeApi.getShortenedLink(link)
: axios.put(`${API_URL}/portal/getshortenlink.json`, link);
}
function CheckError(res) {
if (res.data && res.data.error) {
const error = res.data.error.message || "Unknown error has happened";

View File

@ -558,3 +558,13 @@ export function getGroup(groupId) {
]
});
}
export function getInvitationLink(isGuest) {
return fakeResponse(isGuest
? "guest invitation link"
: "user invitation link");
}
export function getShortenedLink(link) {
return fakeResponse("SHORT LINK: " + link);
}