Continued cleaning
This commit is contained in:
@@ -3,9 +3,7 @@ export default {
|
||||
sentry_dsn: false,
|
||||
front_root_url: (document.location.protocol || "http") + '//' + document.location.host,
|
||||
api_root_url: (document.location.protocol || "http") + '//' + document.location.host,
|
||||
websocket_url: (document.location.protocol || "http").replace("http", "ws") + '//' + document.location.host,
|
||||
mobile_appstore: 'https://apps.apple.com/fr/app/tdrive/id1588764852?l=en',
|
||||
mobile_googleplay: 'https://play.google.com/store/apps/details?id=com.tdrive.tdrive&gl=FR'
|
||||
websocket_url: (document.location.protocol || "http").replace("http", "ws") + '//' + document.location.host
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -15,7 +13,4 @@ export default {
|
||||
api_root_url: 'https://api.tdrive.app',
|
||||
websocket_url: 'wss://api.tdrive.app'
|
||||
};
|
||||
|
||||
In this form:
|
||||
"export default { env_dev: false, front_root_url: 'https://web.tdrive.app', api_root_url: 'https://web.tdrive.app', websocket_url: 'wss://web.tdrive.app' };"
|
||||
*/
|
||||
*/
|
||||
@@ -80,7 +80,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
text: 'Copy public link',
|
||||
hide: !item.access_info.public?.level || item.access_info.public?.level === 'none',
|
||||
onClick: () => {
|
||||
copyToClipboard(getPublicLink(item));
|
||||
copyToClipboard(getPublicLink(item || parent?.item));
|
||||
ToasterService.success('Public link copied to clipboard');
|
||||
},
|
||||
},
|
||||
@@ -244,7 +244,7 @@ export const useOnBuildContextMenu = (children: DriveItem[], initialParentId?: s
|
||||
!parent?.item?.access_info?.public?.level ||
|
||||
parent?.item?.access_info?.public?.level === 'none',
|
||||
onClick: () => {
|
||||
copyToClipboard(getPublicLink(item));
|
||||
copyToClipboard(getPublicLink(item || parent?.item));
|
||||
ToasterService.success('Public link copied to clipboard');
|
||||
},
|
||||
},
|
||||
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
/* eslint-disable react/no-direct-mutation-state */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import WorkspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import GroupService from '@deprecated/workspaces/groups.js';
|
||||
import popupManager from '@deprecated/popupManager/popupManager.js';
|
||||
import Emojione from 'components/emojione/emojione';
|
||||
import ButtonWithTimeout from 'components/buttons/button-with-timeout.jsx';
|
||||
import Input from 'components/inputs/input.jsx';
|
||||
import AddUserByEmail from '@views/client/popup/AddUser/AddUserByEmail';
|
||||
import './CreateWorkspacePage.scss';
|
||||
|
||||
export default class CreateWorkspacePage extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
i18n: Languages,
|
||||
workspaces: WorkspaceService,
|
||||
page: 1,
|
||||
name: '',
|
||||
members: [],
|
||||
willClose: false,
|
||||
};
|
||||
Languages.addListener(this);
|
||||
WorkspaceService.addListener(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.input) {
|
||||
this.input.focus();
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
Languages.removeListener(this);
|
||||
WorkspaceService.removeListener(this);
|
||||
}
|
||||
displayStep() {
|
||||
if (this.state.page === 1) {
|
||||
return (
|
||||
<div className="">
|
||||
<div className="subtitle">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.createworkspacepage.add_subtitle',
|
||||
[],
|
||||
'Besoin de plus de place ? Un nouvel espace de travail et ce sera parfait !',
|
||||
)}{' '}
|
||||
<Emojione type=":grin:" />
|
||||
</div>
|
||||
<br />
|
||||
<Input
|
||||
refInput={ref => {
|
||||
this.input = ref;
|
||||
}}
|
||||
className="full_width"
|
||||
big
|
||||
onKeyDown={e => {
|
||||
if (e.keyCode === 13 && this.state.name.length > 0) {
|
||||
this.next();
|
||||
}
|
||||
}}
|
||||
placeholder={Languages.t(
|
||||
'scenes.app.popup.createworkspacepage.placeholder_name',
|
||||
[],
|
||||
"Nom de l'espace de travail",
|
||||
)}
|
||||
value={this.state.name}
|
||||
onChange={evt => this.setState({ name: evt.target.value })}
|
||||
/>
|
||||
<div className="bottom">
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a href="#" className="returnBtn blue_link" onClick={() => this.previous()}>
|
||||
{this.state.i18n.t('general.back')}
|
||||
</a>
|
||||
<ButtonWithTimeout
|
||||
className="medium"
|
||||
disabled={this.state.name.length <= 0 || this.state.workspaces.loading}
|
||||
onClick={() => this.next()}
|
||||
loading={this.state.workspaces.loading}
|
||||
value={this.state.i18n.t('general.continue')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.state.page === 2) {
|
||||
return (
|
||||
<AddUserByEmail
|
||||
inline
|
||||
onChange={members => {
|
||||
this.state.members = members;
|
||||
this.setState({ members });
|
||||
this.setState({});
|
||||
}}
|
||||
previous={() => this.previous()}
|
||||
finish={() => this.next()}
|
||||
loading={this.state.workspaces.loading}
|
||||
noMagicLink
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
previous() {
|
||||
if (this.state.page <= 1) {
|
||||
popupManager.close();
|
||||
} else {
|
||||
this.setState({ page: this.state.page - 1 });
|
||||
}
|
||||
}
|
||||
next() {
|
||||
if (this.state.page >= 2) {
|
||||
if (!this.did_create_workspace) {
|
||||
this.did_create_workspace = true;
|
||||
WorkspaceService.createWorkspace(
|
||||
this.state.name,
|
||||
this.state.members || [],
|
||||
GroupService.currentGroupId,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.setState({ page: this.state.page + 1 });
|
||||
}
|
||||
}
|
||||
close() {
|
||||
this.setState({ willClose: true });
|
||||
setTimeout(() => {
|
||||
WorkspaceService.closeCreateWorkspacePage();
|
||||
}, 200);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className={'createWorkspaceView'}>
|
||||
<div
|
||||
className={
|
||||
'center_box_container login_view ' + (this.state.willClose ? 'fade_out ' : 'fade_in ')
|
||||
}
|
||||
>
|
||||
<div className="center_box ">
|
||||
{/*<StepCounter total={1} current={this.state.page} />*/}
|
||||
<div className="title">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.createworkspacepage.create_new_workspace',
|
||||
[],
|
||||
'Créer un nouvel espace de travail',
|
||||
)}
|
||||
{/* {this.state.page}/1*/}
|
||||
</div>
|
||||
{this.displayStep()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
.createWorkspaceView {
|
||||
height: 100%;
|
||||
background: white;
|
||||
position: relative;
|
||||
|
||||
.close {
|
||||
background: #f2f2f2;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 13px;
|
||||
color: #666;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
right: 10vh;
|
||||
top: 10vh;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.center_box_container {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
.center_box {
|
||||
max-height: 80vh;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
height: auto;
|
||||
width: 400px;
|
||||
padding: 40px;
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.bottom {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.returnBtn {
|
||||
flex: 1;
|
||||
}
|
||||
.nextBtn {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
.select {
|
||||
width: 100%;
|
||||
border: solid 1px rgba(0, 0, 0, 0.3);
|
||||
height: 38px;
|
||||
border-radius: var(--border-radius-base);
|
||||
background: white;
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
margin: 10px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
|
||||
import WorkspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import userService from '@features/users/services/current-user-service';
|
||||
import loginService from '@features/auth/login-service';
|
||||
import AccountService from '@deprecated/login/account';
|
||||
import popupManager from '@deprecated/popupManager/popupManager.js';
|
||||
import ButtonWithTimeout from 'components/buttons/button-with-timeout.jsx';
|
||||
import Input from 'components/inputs/input.jsx';
|
||||
import './SecondMail.scss';
|
||||
|
||||
export default class SecondMail extends Component {
|
||||
/*
|
||||
props : {
|
||||
onReturn : function
|
||||
}
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
i18n: Languages,
|
||||
workspaceService: WorkspaceService,
|
||||
users_repository: Collections.get('users'),
|
||||
loginService: loginService,
|
||||
mail: '',
|
||||
code: '',
|
||||
page: 1,
|
||||
};
|
||||
Collections.get('users').addListener(this);
|
||||
Collections.get('users').listenOnly(this, [
|
||||
Collections.get('users').find(userService.getCurrentUserId()).front_id,
|
||||
]);
|
||||
loginService.addListener(this);
|
||||
Languages.addListener(this);
|
||||
WorkspaceService.addListener(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.input) {
|
||||
this.input.focus();
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
Collections.get('users').removeListener(this);
|
||||
loginService.removeListener(this);
|
||||
Languages.removeListener(this);
|
||||
WorkspaceService.removeListener(this);
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (prevState.page === 1 && this.state.page === 2 && this.input) {
|
||||
this.input.focus();
|
||||
}
|
||||
if (prevState.page === 2 && this.state.page === 1 && this.input) {
|
||||
this.input.focus();
|
||||
}
|
||||
}
|
||||
displayStep() {
|
||||
if (this.state.page === 1) {
|
||||
return (
|
||||
<div className="secondMail center_box">
|
||||
<div className="title">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.add_secondary_emails')}
|
||||
</div>
|
||||
<div className="subtitle">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.add_secondary_emails_comment')}
|
||||
</div>
|
||||
<div className="mails">
|
||||
<div className="mainMail mail">
|
||||
<div className="littleTittle">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.main_mail_title')}
|
||||
</div>
|
||||
<div className="mailAdress">
|
||||
{
|
||||
this.state.users_repository
|
||||
.find(userService.getCurrentUserId())
|
||||
.mails.find(function (element) {
|
||||
return element.main;
|
||||
}).email
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mainMail mail">
|
||||
<div className="littleTittle">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.other_mail_title')}
|
||||
</div>
|
||||
{this.state.users_repository.find(userService.getCurrentUserId()).mails.map(item => {
|
||||
if (item.main) {
|
||||
return '';
|
||||
}
|
||||
return <div className="mailAdress">{item.email}</div>;
|
||||
})}
|
||||
<Input
|
||||
className={
|
||||
'full_width ' +
|
||||
(this.state.loginService.error_secondary_mail_already ? 'error' : '')
|
||||
}
|
||||
refInput={ref => {
|
||||
this.input = ref;
|
||||
}}
|
||||
type="text"
|
||||
onKeyDown={e => {
|
||||
if (e.keyCode === 13 && this.state.mail.length > 0) {
|
||||
this.next();
|
||||
}
|
||||
}}
|
||||
placeholder={this.state.i18n.t('scenes.app.workspaces.welcome_page.new_email')}
|
||||
value={this.state.mail}
|
||||
onChange={evt => this.setState({ mail: evt.target.value })}
|
||||
/>
|
||||
{this.state.loginService.error_secondary_mail_already && (
|
||||
<span id="errorUsernameExist" className={'text error'}>
|
||||
{this.state.i18n.t('scenes.login.create_account.email_used')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<div className="return">
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a href="#" className="blue_link" onClick={() => this.previous()}>
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.done')}
|
||||
</a>
|
||||
</div>
|
||||
<ButtonWithTimeout
|
||||
className="medium"
|
||||
disabled={this.state.loginService.loading}
|
||||
onClick={() => this.next()}
|
||||
value={this.state.i18n.t('scenes.app.workspaces.welcome_page.add_new_email')}
|
||||
loading={this.state.loginService.loading}
|
||||
loadingTimeout={1500}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.state.page === 2) {
|
||||
return (
|
||||
<div className="secondMail center_box">
|
||||
<div className="title">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.add_secondary_emails')}
|
||||
</div>
|
||||
<div className="subtitle">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.we_sent_you_mail', [
|
||||
this.state.mail,
|
||||
])}
|
||||
</div>
|
||||
<div className="mails">
|
||||
<div className="code">
|
||||
<div className="littleTittle">
|
||||
{this.state.i18n.t('scenes.app.workspaces.welcome_page.code_verification')}
|
||||
</div>
|
||||
<Input
|
||||
refInput={ref => {
|
||||
this.input = ref;
|
||||
}}
|
||||
type="text"
|
||||
onKeyDown={e => {
|
||||
if (e.keyCode === 13 && this.state.code.length > 0) {
|
||||
this.next();
|
||||
}
|
||||
}}
|
||||
placeholder={'123-456-789'}
|
||||
onChange={evt => this.setState({ code: evt.target.value })}
|
||||
className={
|
||||
this.state.loginService.error_code || this.state.error_code ? 'error' : ''
|
||||
}
|
||||
style={{ maxWidth: '200px', textAlign: 'center' }}
|
||||
/>
|
||||
|
||||
{(this.state.loginService.error_code || this.state.error_code) && (
|
||||
<span id="errorUsernameExist" className={'text error'} style={{ display: 'block' }}>
|
||||
{this.state.i18n.t('scenes.apps.account.account.email_add_modal.invalid_code')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<div className="return">
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a href="#" className="blue_link" onClick={() => this.previous()}>
|
||||
{this.state.i18n.t('general.back')}
|
||||
</a>
|
||||
</div>
|
||||
<ButtonWithTimeout
|
||||
className="medium"
|
||||
disabled={this.state.loginService.loading}
|
||||
onClick={() => this.next()}
|
||||
value={this.state.i18n.t('general.confirm')}
|
||||
loading={this.state.loginService.loading}
|
||||
loadingTimeout={1500}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
previous() {
|
||||
if (this.state.page <= 1) {
|
||||
this.props.onReturn();
|
||||
popupManager.close();
|
||||
} else {
|
||||
this.setState({ page: this.state.page - 1 });
|
||||
}
|
||||
}
|
||||
next() {
|
||||
if (this.state.page === 2) {
|
||||
if (this.state.code) {
|
||||
AccountService.verifySecondMail(
|
||||
this.state.mail,
|
||||
this.state.code,
|
||||
thot => {
|
||||
thot.setState({ page: 1, mail: '', code: '' });
|
||||
},
|
||||
this,
|
||||
);
|
||||
}
|
||||
} else if (this.state.page === 1) {
|
||||
if (this.state.mail) {
|
||||
AccountService.addNewMail(this.state.mail, thot => thot.setState({ page: 2 }), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return this.displayStep();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
.secondMail {
|
||||
text-align: center;
|
||||
width: 600px;
|
||||
padding: 40px;
|
||||
margin: auto;
|
||||
|
||||
.title {
|
||||
font-size: 40px;
|
||||
.emojione {
|
||||
margin-right: 5px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
bottom: 8px;
|
||||
}
|
||||
}
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
line-height: 25px;
|
||||
margin: auto;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 40px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.mails {
|
||||
padding: 30px;
|
||||
text-align: left;
|
||||
padding: 0px 100px;
|
||||
.littleTittle {
|
||||
font-weight: 600;
|
||||
}
|
||||
.mail {
|
||||
margin-bottom: 20px;
|
||||
.littleTittle {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.mailAdress {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
.code {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
padding: 0px 100px;
|
||||
margin-top: 20px;
|
||||
line-height: 54px;
|
||||
display: flex;
|
||||
.return {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
.nextBtn {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
.company-application-popup {
|
||||
.ant-tabs-tab-btn {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Descriptions
|
||||
.ant-descriptions-bordered .ant-descriptions-item-label,
|
||||
.ant-descriptions-bordered .ant-descriptions-item-content {
|
||||
padding: 8px 24px;
|
||||
}
|
||||
|
||||
// Tabs btn
|
||||
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list,
|
||||
.ant-tabs > div > .ant-tabs-nav .ant-tabs-nav-list {
|
||||
padding-top: 22px;
|
||||
}
|
||||
|
||||
// Tabs Content
|
||||
.ant-tabs-left > .ant-tabs-content-holder > .ant-tabs-content > .ant-tabs-tabpane,
|
||||
.ant-tabs-left > div > .ant-tabs-content-holder > .ant-tabs-content > .ant-tabs-tabpane {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.ant-tabs-ink-bar .ant-tabs-ink-bar-animated,
|
||||
.ant-tabs-content-holder {
|
||||
display: block;
|
||||
min-height: 383px;
|
||||
}
|
||||
|
||||
.company-application-popup-install-btn {
|
||||
height: 24px;
|
||||
background: var(--success);
|
||||
color: var(--white);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover,
|
||||
&.ant-btn-loading {
|
||||
background: var(--success);
|
||||
opacity: 0.8;
|
||||
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
.ant-tabs-left > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
|
||||
.ant-tabs-right > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
|
||||
.ant-tabs-left > div > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
|
||||
.ant-tabs-right > div > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab {
|
||||
margin: 0;
|
||||
}
|
||||
.company-application-popup-installed-tag {
|
||||
height: 24px;
|
||||
|
||||
margin-right: 0;
|
||||
opacity: 0.5;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.applications-table-actions-btn {
|
||||
height: 22px !important;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint';
|
||||
import { Divider, Table, Typography, Row, Input, Col, Button } from 'antd';
|
||||
|
||||
import { Application } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useApplications } from '@features/applications/hooks/use-applications';
|
||||
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
|
||||
import { useCurrentCompany } from '@features/companies/hooks/use-companies';
|
||||
import { delayRequest } from '@features/global/utils/managedSearchRequest';
|
||||
import AvatarComponent from '@components/avatar/avatar';
|
||||
|
||||
import './ApplicationsStyles.scss';
|
||||
|
||||
type ColumnObjectType = { key: number } & Application;
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
export default () => {
|
||||
const { company } = useCurrentCompany();
|
||||
|
||||
if (!company?.id) return <></>;
|
||||
|
||||
const {
|
||||
applications: applicationsList,
|
||||
loading: isLoadingApplicationsList,
|
||||
search,
|
||||
} = useApplications();
|
||||
const { loading, applications, add } = useCompanyApplications(company.id);
|
||||
|
||||
const [data, _setData] = useState<ColumnObjectType[]>([]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { xs, sm } = useBreakpoint();
|
||||
|
||||
useEffect(() => {
|
||||
refreshApplications();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [applicationsList?.length]);
|
||||
|
||||
const refreshApplications = () => {
|
||||
applicationsList && setData(applicationsList);
|
||||
};
|
||||
|
||||
const setData = (list: Application[]) => {
|
||||
if (list) {
|
||||
_setData(
|
||||
list.map((app, key) => ({
|
||||
key: key + 1,
|
||||
name: { name: app.identity.name, icon: app.identity.icon },
|
||||
description: app.identity.description,
|
||||
installed: loading,
|
||||
...app,
|
||||
})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickButton = async (application: Application) => add(application.id);
|
||||
|
||||
const columns: ColumnsType<ColumnObjectType> = [
|
||||
{
|
||||
title: Languages.t('scenes.app.integrations_parameters.applications_table.name'),
|
||||
dataIndex: 'name',
|
||||
width: 550,
|
||||
render: (
|
||||
{ name, icon }: { name: string; icon: string },
|
||||
record: ColumnObjectType,
|
||||
index: number,
|
||||
) => (
|
||||
<Row key={index} wrap={false} align="middle" justify="start">
|
||||
<AvatarComponent url={icon} />
|
||||
<Typography.Text className="small-x-margin">{name}</Typography.Text>
|
||||
</Row>
|
||||
),
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
render: (_initialValue, record: ColumnObjectType) => {
|
||||
const { key, ...application } = record;
|
||||
return (
|
||||
<Button
|
||||
key={key}
|
||||
loading={loading}
|
||||
type="ghost"
|
||||
className="applications-table-actions-btn"
|
||||
onClick={() => onClickButton(application)}
|
||||
>
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.install_button')}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Divider />
|
||||
</Row>
|
||||
<Row justify="space-between" wrap={false} className="small-bottom-margin">
|
||||
<Col>
|
||||
<Typography.Title level={3} style={{ margin: 0 }}>
|
||||
{Languages.t('scenes.app.integrations_parameters.applications_table.title')}
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
<Col>
|
||||
<Input
|
||||
placeholder={Languages.t(
|
||||
'scenes.app.integrations_parameters.applications_table.search_placeholder',
|
||||
)}
|
||||
onChange={e => {
|
||||
const value = e.target?.value;
|
||||
delayRequest('application_search', async () => await search(value));
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
loading={isLoadingApplicationsList}
|
||||
dataSource={data.filter(app => !applications.map(a => a.id).includes(app.id))}
|
||||
size="small"
|
||||
pagination={{ pageSize: DEFAULT_PAGE_SIZE, simple: true }}
|
||||
scroll={{ x: xs || sm ? true : undefined }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import moment from 'moment';
|
||||
import { capitalize } from 'lodash';
|
||||
import { Check } from 'react-feather';
|
||||
import { Tabs, Button, Typography, Col, Tag, Descriptions, Row, Divider } from 'antd';
|
||||
|
||||
import { Application, ApplicationAccess } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import AvatarComponent from '@components/avatar/avatar';
|
||||
import ObjectModal from '@components/object-modal/object-modal';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
|
||||
|
||||
import './ApplicationsStyles.scss';
|
||||
|
||||
type PropsType = {
|
||||
application: Application;
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Text, Link, Title } = Typography;
|
||||
const { Item } = Descriptions;
|
||||
|
||||
const InformationsDescriptions = ({ application }: { application: Application }) => {
|
||||
const createdDate = moment(application.stats.created_at).format('L');
|
||||
|
||||
return (
|
||||
<Descriptions layout="vertical" bordered>
|
||||
<Item
|
||||
label={Languages.t(
|
||||
'scenes.app.integrations_parameters.company_application_popup.tab_info.descriptions.website_item',
|
||||
)}
|
||||
span={3}
|
||||
>
|
||||
<Link onClick={() => window.open(application.identity.website, 'blank')}>
|
||||
{application.identity.website}
|
||||
</Link>
|
||||
</Item>
|
||||
|
||||
<Item
|
||||
label={Languages.t(
|
||||
'scenes.app.integrations_parameters.company_application_popup.tab_info.descriptions.description_item',
|
||||
)}
|
||||
span={3}
|
||||
>
|
||||
<Text type="secondary">{application.identity.description}</Text>
|
||||
</Item>
|
||||
|
||||
<Item
|
||||
label={Languages.t(
|
||||
'scenes.app.integrations_parameters.company_application_popup.tab_info.descriptions.created_item',
|
||||
)}
|
||||
span={3}
|
||||
>
|
||||
<Text type="secondary">{createdDate}</Text>
|
||||
</Item>
|
||||
|
||||
<Item label="Compatible with Tdrive" span={3}>
|
||||
<Text type="secondary">
|
||||
{application.identity.compatibility.includes('tdrive')
|
||||
? 'Yes'
|
||||
: `No, this integration is compatible with ${application.identity.compatibility
|
||||
.map(v => capitalize(v))
|
||||
.join(', ')}.`}
|
||||
</Text>
|
||||
</Item>
|
||||
</Descriptions>
|
||||
);
|
||||
};
|
||||
|
||||
const AccessDescriptions = ({ application }: { application: Application }) => (
|
||||
<Descriptions layout="vertical" bordered>
|
||||
{Object.keys(application.access).map(key => {
|
||||
const values: string[] = (application.access as ApplicationAccess)[
|
||||
key as keyof ApplicationAccess
|
||||
];
|
||||
|
||||
return (
|
||||
<Item key={key} label={capitalize(key === 'hooks' ? 'listened events' : key)} span={3}>
|
||||
{values?.length > 0 ? (
|
||||
values.map(v => (
|
||||
<Tag key={v} color="var(--success)">
|
||||
{v}
|
||||
</Tag>
|
||||
))
|
||||
) : (
|
||||
<Text style={{ minHeight: 22 }} type="secondary">
|
||||
This integration doesn't have any {key} access
|
||||
</Text>
|
||||
)}
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</Descriptions>
|
||||
);
|
||||
|
||||
export default ({ application, companyId }: PropsType) => {
|
||||
const {
|
||||
add: addOneCompanyApplication,
|
||||
loading: isLoadingCompanyApplications,
|
||||
isInstalled: isApplicationInstalledInCompany,
|
||||
} = useCompanyApplications(companyId);
|
||||
|
||||
const onClickButton = async () => {
|
||||
if (!isApplicationInstalledInCompany(application.id)) {
|
||||
await addOneCompanyApplication(application.id);
|
||||
}
|
||||
|
||||
ModalManager.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<ObjectModal
|
||||
className="company-application-popup"
|
||||
headerStyle={{ height: 32, marginTop: 24 }}
|
||||
footerDividerStyle={{ marginTop: 0 }}
|
||||
closable
|
||||
footer={
|
||||
isApplicationInstalledInCompany(application.id) ? (
|
||||
<Tag color="var(--success)" className="company-application-popup-installed-tag">
|
||||
<Check size={16} />{' '}
|
||||
{Languages.t('scenes.app.integrations_parameters.company_application_popup.tag')}
|
||||
</Tag>
|
||||
) : (
|
||||
<Button
|
||||
type="ghost"
|
||||
loading={isLoadingCompanyApplications}
|
||||
onClick={onClickButton}
|
||||
className="company-application-popup-install-btn"
|
||||
>
|
||||
{Languages.t('scenes.app.integrations_parameters.company_application_popup.btn')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
title={
|
||||
<Row align="middle" justify="start">
|
||||
<Col>
|
||||
<AvatarComponent url={application.identity.icon} />
|
||||
</Col>
|
||||
<Col className="small-x-margin">
|
||||
<Title level={3} style={{ margin: 0 }}>
|
||||
{application.identity.name}
|
||||
</Title>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
>
|
||||
<Divider style={{ margin: '16px 0 0 0' }} />
|
||||
|
||||
<Tabs defaultActiveKey="1" tabPosition="left">
|
||||
<TabPane
|
||||
tab={Languages.t(
|
||||
'scenes.app.integrations_parameters.company_application_popup.tab_btn_info',
|
||||
)}
|
||||
key="1"
|
||||
>
|
||||
<InformationsDescriptions application={application} />
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="App access" key="2">
|
||||
<AccessDescriptions application={application} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</ObjectModal>
|
||||
);
|
||||
};
|
||||
-190
@@ -1,190 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { MoreHorizontal } from 'react-feather';
|
||||
import { Divider, Table, Typography, Row, Col, Button, Grid } from 'antd';
|
||||
|
||||
import {
|
||||
useCompanyApplications,
|
||||
useCompanyApplicationsRealtime,
|
||||
} from '@features/applications/hooks/use-company-applications';
|
||||
import { Application } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import AvatarComponent from '@components/avatar/avatar';
|
||||
import CompanyApplicationPopup from './CompanyApplicationPopup';
|
||||
import Menu from 'components/menus/menu';
|
||||
import WorkspacesApps from '@deprecated/workspaces/workspaces_apps.jsx';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import ApplicationEditor from '../../../application-parameters/pages/application-editor';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
|
||||
import './ApplicationsStyles.scss';
|
||||
|
||||
type ColumnObjectType = { key: number } & Application;
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
const { useBreakpoint } = Grid;
|
||||
export default () => {
|
||||
const companyId = useRouterCompany();
|
||||
|
||||
if (!companyId) return <></>;
|
||||
|
||||
const {
|
||||
applications: companyApplications,
|
||||
loading: isLoadingCompanyApplications,
|
||||
remove: deleteOneCompanyApplication,
|
||||
} = useCompanyApplications(companyId);
|
||||
useCompanyApplicationsRealtime();
|
||||
|
||||
const [data, _setData] = useState<ColumnObjectType[]>([]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { xs, sm, md, lg, xl } = useBreakpoint();
|
||||
|
||||
useEffect(() => {
|
||||
refreshCompanyApplications();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [companyApplications.length]);
|
||||
|
||||
const refreshCompanyApplications = () => {
|
||||
companyApplications && setData(companyApplications);
|
||||
};
|
||||
|
||||
const buildMenu = (application: Application) => {
|
||||
return [
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t(
|
||||
'scenes.app.integrations_parameters.company_applications_table.more_menu.show_application',
|
||||
),
|
||||
onClick: () =>
|
||||
ModalManager.open(
|
||||
<CompanyApplicationPopup application={application} companyId={companyId} />,
|
||||
{
|
||||
position: 'center',
|
||||
size: { width: '600px' },
|
||||
},
|
||||
),
|
||||
},
|
||||
application.company_id === companyId &&
|
||||
AccessRightsService.hasCompanyLevel(companyId, 'admin') && {
|
||||
type: 'menu',
|
||||
text: Languages.t(
|
||||
'scenes.app.integrations_parameters.company_applications_table.more_menu.developer_settings',
|
||||
),
|
||||
onClick: () =>
|
||||
ModalManager.open(
|
||||
<ApplicationEditor application={application} companyId={companyId} />,
|
||||
{
|
||||
position: 'center',
|
||||
size: { width: 700 },
|
||||
},
|
||||
),
|
||||
},
|
||||
application.company_id !== companyId && {
|
||||
type: 'menu',
|
||||
className: 'error',
|
||||
text: Languages.t(
|
||||
'scenes.app.integrations_parameters.company_applications_table.more_menu.remove_application',
|
||||
),
|
||||
onClick: () => AlertManager.confirm(() => deleteOneCompanyApplication(application.id)),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const setData = (list: Application[]) => {
|
||||
if (list) {
|
||||
_setData(
|
||||
list.map((app, key) => ({
|
||||
key: key + 1,
|
||||
name: { name: app.identity.name, icon: app.identity.icon },
|
||||
description: app.identity.description,
|
||||
...app,
|
||||
})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ColumnsType<ColumnObjectType> = [
|
||||
{
|
||||
title: Languages.t('scenes.app.integrations_parameters.company_applications_table.name'),
|
||||
dataIndex: 'name',
|
||||
width: 550,
|
||||
render: (
|
||||
{ name, icon }: { name: string; icon: string },
|
||||
record: ColumnObjectType,
|
||||
index: number,
|
||||
) => {
|
||||
return (
|
||||
<Row key={index} align="middle">
|
||||
<AvatarComponent
|
||||
url={icon?.length ? icon : undefined}
|
||||
fallback={`${process.env.PUBLIC_URL}/public/img/hexagon.png`}
|
||||
/>
|
||||
<Typography.Text className="small-left-margin">{name}</Typography.Text>
|
||||
</Row>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
width: 200,
|
||||
|
||||
render: (_initialValue, record: ColumnObjectType) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { key, ...application } = record;
|
||||
return (
|
||||
<div style={{ float: 'right' }}>
|
||||
{!!(application.display?.tdrive?.configuration || []).includes('global') && (
|
||||
<Button
|
||||
type="ghost"
|
||||
style={{ marginRight: 8 }}
|
||||
onClick={() => {
|
||||
WorkspacesApps.notifyApp(application.id, 'configuration', 'workspace', {});
|
||||
}}
|
||||
>
|
||||
{/* TODO: Translation here Configure -> Preferences */}
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.configure_button')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'inline-block' }}>
|
||||
<Menu menu={buildMenu(application)}>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<MoreHorizontal size={18} />}
|
||||
className="applications-table-actions-btn"
|
||||
/>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bottom-margin">
|
||||
<Divider />
|
||||
<Row justify="start" wrap={false}>
|
||||
<Col>
|
||||
<Typography.Title level={3}>
|
||||
{Languages.t('scenes.app.integrations_parameters.company_applications_table.title')}
|
||||
</Typography.Title>
|
||||
</Col>
|
||||
</Row>
|
||||
<div>
|
||||
<Table
|
||||
loading={isLoadingCompanyApplications}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
size="small"
|
||||
pagination={{ pageSize: DEFAULT_PAGE_SIZE, simple: true }}
|
||||
scroll={{ x: xs || sm ? true : undefined }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
|
||||
import { getFilesTree } from '@components/uploads/file-tree-utils';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections';
|
||||
import groupService from '@deprecated/workspaces/groups';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { addApiUrlIfNeeded } from '@features/global/utils/URLUtils';
|
||||
import ButtonWithTimeout from 'components/buttons/button-with-timeout';
|
||||
import Input from 'components/inputs/input';
|
||||
import Attribute from 'components/parameters/attribute';
|
||||
|
||||
import './Pages.scss';
|
||||
|
||||
export default class CompanyIdendity extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
var group = Collections.get('groups').find(groupService.currentGroupId);
|
||||
this.state = {
|
||||
i18n: Languages,
|
||||
group: Collections.get('groups'),
|
||||
groupService: groupService,
|
||||
groupName: group.name ? group.name : '',
|
||||
groupLogo: group.logo ? group.logo : '',
|
||||
};
|
||||
groupService.addListener(this);
|
||||
Languages.addListener(this);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
groupService.removeListener(this);
|
||||
Languages.removeListener(this);
|
||||
}
|
||||
changeLogo(event) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
var that = this;
|
||||
event.preventDefault();
|
||||
getFilesTree(event, function (tree) {
|
||||
var first = tree[Object.keys(tree)[0]];
|
||||
if (first.constructor.name !== 'Object') {
|
||||
//A file
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
that.groupLogo.style.backgroundImage = "url('" + e.target.result + "')";
|
||||
};
|
||||
that.setState({ groupLogo: first });
|
||||
reader.readAsDataURL(first);
|
||||
}
|
||||
});
|
||||
}
|
||||
render() {
|
||||
var group = Collections.get('groups').find(groupService.currentGroupId);
|
||||
return (
|
||||
<div className="">
|
||||
<div className="title">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.company_identity_title',
|
||||
[],
|
||||
"Identité de l'entreprise",
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<div className="subtitle">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.displayed_preferencies_subtitle',
|
||||
[],
|
||||
"Préférences d'affichage",
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.company_name_label',
|
||||
[],
|
||||
"Nom de l'entreprise",
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.company_name_description',
|
||||
[],
|
||||
'Modifiez le nom de cette entreprise',
|
||||
)}
|
||||
focusOnOpen={this.inputWorkspaceName}
|
||||
>
|
||||
<div className="parameters_form">
|
||||
<Input
|
||||
medium
|
||||
refInput={node => (this.inputWorkspaceName = node)}
|
||||
type="text"
|
||||
value={this.state.groupName}
|
||||
onKeyDown={e => {
|
||||
if (e.keyCode === 13) {
|
||||
groupService.updateName(this.state.groupName);
|
||||
}
|
||||
}}
|
||||
onChange={ev => this.setState({ groupName: ev.target.value })}
|
||||
/>
|
||||
<ButtonWithTimeout
|
||||
className="small buttonValidation"
|
||||
disabled={this.state.groupService.loading}
|
||||
onClick={() => this.state.groupService.updateName(this.state.groupName)}
|
||||
loading={this.state.groupService.loading}
|
||||
value={this.state.i18n.t('general.update')}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.apps.parameters.workspace_sections.workspace.logo_company',
|
||||
[],
|
||||
"Logo de l'entreprise",
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.logo_company_modify_description',
|
||||
[],
|
||||
"Modifiez l'image de cet entreprise",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
onClick={() => {
|
||||
this.fileinput.click();
|
||||
}}
|
||||
>
|
||||
<input
|
||||
ref={node => (this.fileinput = node)}
|
||||
type="file"
|
||||
style={{ position: 'absolute', top: '-10000px', left: '-10000px', width: '100px' }}
|
||||
onChange={e => this.changeLogo(e)}
|
||||
/>
|
||||
<div
|
||||
ref={ref => (this.groupLogo = ref)}
|
||||
className={'image thumbnail ' + (this.state.groupLogo ? 'has_image ' : '')}
|
||||
style={{
|
||||
backgroundImage: addApiUrlIfNeeded(this.state.groupLogo, true),
|
||||
}}
|
||||
>
|
||||
{((group.mininame || group.name) + '-')[0].toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.weight_max_small_text',
|
||||
[],
|
||||
'Maximum weight 5 mo.',
|
||||
)}
|
||||
<br />
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a
|
||||
className="red"
|
||||
onClick={() => {
|
||||
this.setState({ groupLogo: null });
|
||||
}}
|
||||
>
|
||||
{Languages.t('general.remove')}
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<ButtonWithTimeout
|
||||
className="small buttonValidation"
|
||||
disabled={this.state.groupService.loading}
|
||||
onClick={() => this.state.groupService.updateLogo(this.state.groupLogo)}
|
||||
loading={this.state.groupService.loading}
|
||||
value={this.state.i18n.t('general.update')}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
import { Button, Row, Typography } from 'antd';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import ApplicationsTable from './Applications/ApplicationsTable';
|
||||
import CompanyApplicationsTable from './Applications/CompanyApplicationsTable';
|
||||
import ApplicationCreator from '../../application-parameters/pages/application-creator';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
|
||||
import './Pages.scss';
|
||||
|
||||
export default () => {
|
||||
const companyId = useRouterCompany();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row wrap={false} align="middle" justify="start">
|
||||
<Typography.Title level={1} style={{ marginBottom: 0, marginRight: 16 }}>
|
||||
{Languages.t('scenes.app.integrations_parameters.title')}
|
||||
</Typography.Title>
|
||||
|
||||
{AccessRightsService.hasCompanyLevel(companyId, 'admin') && (
|
||||
<Button
|
||||
type="ghost"
|
||||
onClick={() =>
|
||||
ModalManager.open(<ApplicationCreator />, {
|
||||
position: 'center',
|
||||
size: { width: '600px', minHeight: '329px' },
|
||||
})
|
||||
}
|
||||
>
|
||||
{Languages.t('scenes.app.integrations_parameters.add_application')}
|
||||
</Button>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Suspense fallback={<></>}>
|
||||
<CompanyApplicationsTable />
|
||||
<ApplicationsTable />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import Input from 'components/inputs/input.jsx';
|
||||
import Button from 'components/buttons/button.jsx';
|
||||
import MediumPopupManager from '@components/modal/modal-manager';
|
||||
import {
|
||||
ObjectModal,
|
||||
ObjectModalFormTitle,
|
||||
ObjectModalTitle,
|
||||
} from 'components/object-modal/deprecated-object-modal.jsx';
|
||||
import Api from '@features/global/framework/api-service';
|
||||
import Workspaces from '@deprecated/workspaces/workspaces.jsx';
|
||||
import './pages.scss';
|
||||
|
||||
export default class CreateCompanyAccount extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
password: '',
|
||||
fullname: '',
|
||||
};
|
||||
Languages.addListener(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Languages.removeListener(this);
|
||||
}
|
||||
|
||||
save() {
|
||||
if (!(this.state.password || '').trim() || (this.state.password || '').length < 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
MediumPopupManager.closeAll();
|
||||
|
||||
var data = {
|
||||
mail: this.props.email,
|
||||
fullname: this.state.fullname,
|
||||
password: this.state.password,
|
||||
language: Languages.language,
|
||||
workspace_id: Workspaces.currentWorkspaceId,
|
||||
};
|
||||
Api.post('/ajax/users/subscribe/company_subscribe', data, res => {
|
||||
if ((res || {}).data == 'success') {
|
||||
AlertManager.alert(() => {}, {
|
||||
title: 'Account created',
|
||||
text: (
|
||||
<div className="allow_selection">
|
||||
<b>Login:</b> {this.props.email}
|
||||
<br />
|
||||
<b>Password:</b> {this.state.password}
|
||||
<br />
|
||||
<span className="text">
|
||||
{Languages.t(
|
||||
'scenes.apps.account.account.send_info',
|
||||
[],
|
||||
'Send this information to your member to let him now its new credentials.',
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
AlertManager.alert(() => {}, {
|
||||
title: 'An error occurred',
|
||||
text: Languages.t('scenes.app.popup.createcompany.try_again', [], 'Please try again.'),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="">
|
||||
<ObjectModal
|
||||
className="create_company_account_modal "
|
||||
onClose={() => MediumPopupManager.closeAll()}
|
||||
footer={
|
||||
<div>
|
||||
<Button
|
||||
className="secondary-light small right-margin"
|
||||
style={{ width: 'auto' }}
|
||||
onClick={() => {
|
||||
MediumPopupManager.closeAll();
|
||||
}}
|
||||
>
|
||||
{Languages.t('general.cancel', [], 'Cancel')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="small primary"
|
||||
style={{ width: 'auto', float: 'right' }}
|
||||
onClick={() => {
|
||||
this.save();
|
||||
}}
|
||||
>
|
||||
{Languages.t('scenes.apps.account.account.save', [], 'Save account')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
title={
|
||||
<ObjectModalTitle>
|
||||
{this.props.edit
|
||||
? Languages.t('scenes.app.popup.workspace.edit_temp', [], 'Edit temporary account')
|
||||
: Languages.t(
|
||||
'scenes.app.popup.workspace.create_temp',
|
||||
[],
|
||||
'Create temporary account',
|
||||
)}
|
||||
</ObjectModalTitle>
|
||||
}
|
||||
>
|
||||
<span className="text">
|
||||
{Languages.t(
|
||||
'scenes.apps.account.message_temporary',
|
||||
[],
|
||||
'A temporary account works like a normal Tdrive account, but you generate its password and you will be able to reset it if necessary.',
|
||||
)}
|
||||
<br />
|
||||
{Languages.t(
|
||||
'scenes.apps.account.message_user_signin',
|
||||
[],
|
||||
'Your user can sign in at anytime using this same email and change its temporary account to a normal account.',
|
||||
)}
|
||||
</span>
|
||||
|
||||
{!this.props.edit && [
|
||||
<ObjectModalFormTitle
|
||||
icon="user"
|
||||
name={Languages.t('scenes.apps.account.account.fullname', [], 'Fullname')}
|
||||
/>,
|
||||
<Input
|
||||
className="full_width"
|
||||
placeholder="John Snow"
|
||||
onChange={e => this.setState({ fullname: e.target.value })}
|
||||
/>,
|
||||
]}
|
||||
|
||||
<ObjectModalFormTitle
|
||||
icon="envelope"
|
||||
name={Languages.t('login.email_login', [], 'Email / Login')}
|
||||
/>
|
||||
<Input className="full_width" disabled value={this.props.email} />
|
||||
|
||||
<ObjectModalFormTitle
|
||||
icon="lock"
|
||||
name={Languages.t('scenes.apps.account.account.password', [], 'Password')}
|
||||
/>
|
||||
<Input
|
||||
className="full_width"
|
||||
placeholder={Languages.t(
|
||||
'scenes.apps.account.account.password_for_user',
|
||||
[],
|
||||
'Password for your user',
|
||||
)}
|
||||
onChange={e => this.setState({ password: e.target.value })}
|
||||
/>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<span className="text">
|
||||
{Languages.t(
|
||||
'scenes.login.create_account.too_short_password',
|
||||
[],
|
||||
'Your password must contain at least 8 characters.',
|
||||
)}
|
||||
</span>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</ObjectModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
.workspaceParameter {
|
||||
.image {
|
||||
background-color: rgba(0, 0, 50, 0.25);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: #000000aa;
|
||||
font-size: 40px;
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
border-radius: var(--border-radius-large);
|
||||
margin: auto;
|
||||
margin-bottom: 8px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
padding: 10px 0px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.has_image {
|
||||
font-size: 0px;
|
||||
}
|
||||
.action {
|
||||
text-align: right;
|
||||
svg {
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
}
|
||||
svg:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.editLevel {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.close svg {
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
.confirm {
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
.addMemberBtn {
|
||||
text-align: right;
|
||||
}
|
||||
.addMember {
|
||||
.buttonWithTimeout_container {
|
||||
margin: auto;
|
||||
}
|
||||
.group_section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.user_image {
|
||||
vertical-align: top;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: var(--primary-background);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.apps,
|
||||
.app_editor,
|
||||
.application_search {
|
||||
.app_icon {
|
||||
vertical-align: top;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.application {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 45px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--border-radius-base);
|
||||
|
||||
&:hover {
|
||||
background: var(--grey-background);
|
||||
}
|
||||
|
||||
.market_app_image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
margin-right: 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.market_app_content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.market_app_title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.market_app_description {
|
||||
opacity: 0.5;
|
||||
font-size: 13px;
|
||||
margin-top: 3px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.market_app_buttons {
|
||||
input {
|
||||
margin: 4px 0;
|
||||
min-width: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app_editor {
|
||||
box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
|
||||
margin: 15px;
|
||||
margin-top: 0px;
|
||||
background: #555;
|
||||
border-radius: var(--border-radius-large);
|
||||
color: #fff;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.application_search,
|
||||
.app_editor {
|
||||
.app_icon {
|
||||
vertical-align: initial;
|
||||
margin-bottom: -4px;
|
||||
margin-right: 6px;
|
||||
margin-left: 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.buttonWithTimeout_container {
|
||||
text-align: right;
|
||||
|
||||
input {
|
||||
width: auto;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonGoBack {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.label.for_input {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { AlertTriangle } from 'react-feather';
|
||||
import { Alert, Button, Checkbox, Input, Row, Typography } from 'antd';
|
||||
|
||||
import Logger from '@features/global/framework/logger-service';
|
||||
import { WorkspaceType, WorkspaceUserType } from '@features/workspaces/types/workspace';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import ObjectModal from '@components/object-modal/object-modal';
|
||||
import WorkspaceAPIClient from '@features/workspaces/api/workspace-api-client';
|
||||
import RouterService from '@features/router/services/router-service';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import WorkspaceUserAPIClient from '@features/workspace-members/api/workspace-members-api-client';
|
||||
|
||||
import './styles.scss';
|
||||
|
||||
const { Text } = Typography;
|
||||
const logger = Logger.getLogger('DeleteWorkspacePopup');
|
||||
|
||||
export default () => {
|
||||
const { companyId, workspaceId } = RouterService.getStateFromRoute();
|
||||
const [workspace, setWorkspace] = useState<WorkspaceType>();
|
||||
const [checked, setChecked] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [workspaceUsers, setWorkspaceUsers] = useState<WorkspaceUserType[]>([]);
|
||||
const [workspaceName, setWorkspaceName] = useState<string | undefined>();
|
||||
|
||||
const handleWorkspaceChangesRef = useRef(async () => {
|
||||
if (companyId && workspaceId) {
|
||||
logger.log(`Proccessing request for workspace ${workspaceId}`);
|
||||
|
||||
try {
|
||||
const res = await WorkspaceAPIClient.get(companyId, workspaceId);
|
||||
|
||||
res && setWorkspace(res);
|
||||
} catch (e) {
|
||||
logger.error(`Error while trying to fetch workspace ${workspaceId}`, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handleWorkspaceUsersChangesRef = useRef(async () => {
|
||||
if (companyId && workspaceId) {
|
||||
logger.log(`Proccessing request for workspace users ${workspaceId}`);
|
||||
|
||||
try {
|
||||
const res = await WorkspaceUserAPIClient.list(companyId, workspaceId);
|
||||
|
||||
res && setWorkspaceUsers(res);
|
||||
} catch (e) {
|
||||
logger.error(`Error while trying to fetch workspace users in ${workspaceId}`, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
handleWorkspaceChangesRef.current();
|
||||
handleWorkspaceUsersChangesRef.current();
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
return workspace ? (
|
||||
<ObjectModal
|
||||
titleLevel={3}
|
||||
closable
|
||||
title={Languages.t('scenes.app.popup.workspaceparameter.pages.delete_workspace_popup.title', [
|
||||
workspace.name,
|
||||
])}
|
||||
contentStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'center' }}
|
||||
footer={
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
className={classNames('delete-workspace-button', {
|
||||
disabled:
|
||||
!isEqual(workspaceName, workspace.name) || !checked || workspaceUsers.length > 1,
|
||||
})}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
await WorkspaceAPIClient.delete(workspace.company_id, workspace.id);
|
||||
window.location.replace('/');
|
||||
setLoading(false);
|
||||
ModalManager.close();
|
||||
}}
|
||||
disabled={
|
||||
!isEqual(workspaceName, workspace.name) || !checked || workspaceUsers.length > 1
|
||||
}
|
||||
>
|
||||
{Languages.t('general.delete')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{workspaceUsers.length > 1 && (
|
||||
<Row className="x-margin">
|
||||
{!loading ? (
|
||||
<Alert
|
||||
className="alert-total-members"
|
||||
type="error"
|
||||
message=""
|
||||
description={
|
||||
<>
|
||||
<AlertTriangle color="var(--error)" size={16} className="small-right-margin" />
|
||||
<Text>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.delete_workspace_popup.alert_total_members',
|
||||
[workspaceUsers.length - 1, workspace.name],
|
||||
)}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
|
||||
<Row className="x-margin workspace-name-confirmation-input">
|
||||
<Input
|
||||
placeholder={`${Languages.t('scenes.app.popup.workspaceparameter.pages.enter')}${
|
||||
workspace.name
|
||||
}`}
|
||||
onChange={e => setWorkspaceName(e.target.value)}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row className="x-margin">
|
||||
<Checkbox onChange={() => setChecked(!checked)}>
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.delete_workspace_popup.checkbox')}
|
||||
</Checkbox>
|
||||
</Row>
|
||||
</ObjectModal>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
import React, { Suspense, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import { Avatar, Button, Col, Descriptions, Divider, Input, Row, Switch, Typography } from 'antd';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { useCurrentWorkspace } from '@features/workspaces/hooks/use-workspaces';
|
||||
import AvatarComponent from '@components/avatar/avatar';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import DeleteWorkspacePopup from './DeleteWorkspacePopup';
|
||||
import WorkspaceAPIClient, {
|
||||
WorkspaceUpdateResource,
|
||||
} from '@features/workspaces/api/workspace-api-client';
|
||||
import { ToasterService as Toaster } from '@features/global/services/toaster-service';
|
||||
import { addApiUrlIfNeeded } from '@features/global/utils/URLUtils';
|
||||
import { getBase64 } from '@features/global/utils/strings';
|
||||
|
||||
const { Item } = Descriptions;
|
||||
const { Text, Title, Link } = Typography;
|
||||
|
||||
const MAX_LOGO_FILE_SIZE = 5000000;
|
||||
const ALLOWED_LOGO_FORMATS = ['image/gif', 'image/jpeg', 'image/png'];
|
||||
|
||||
const WorkspaceIdentity = () => {
|
||||
const uploadInputRef = useRef<HTMLInputElement>(null);
|
||||
const { workspace, refresh } = useCurrentWorkspace();
|
||||
const [workspaceName, setWorkspaceName] = useState<string | undefined>(workspace?.name);
|
||||
|
||||
const onClickUpdateWorkspace = async (partials: WorkspaceUpdateResource) => {
|
||||
const updatedObject: WorkspaceUpdateResource = {
|
||||
...(partials.name && { name: partials.name }),
|
||||
...(partials.default && { default: partials.default }),
|
||||
...(partials.logo && { logo: partials.logo || '' }),
|
||||
...(partials.logo_b64 && { logo_b64: partials.logo_b64 }),
|
||||
};
|
||||
|
||||
if (workspace) {
|
||||
try {
|
||||
const res = await WorkspaceAPIClient.update(
|
||||
workspace.company_id,
|
||||
workspace.id,
|
||||
updatedObject,
|
||||
);
|
||||
|
||||
if (res) {
|
||||
res && refresh();
|
||||
res.name && setWorkspaceName(res.name);
|
||||
|
||||
Toaster.success(
|
||||
Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.toaster.success.update',
|
||||
),
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeWorkspaceLogo = async () => {
|
||||
if (!uploadInputRef?.current) return;
|
||||
const file = uploadInputRef.current.files?.[0];
|
||||
try {
|
||||
if (file && workspace) {
|
||||
if (file.size > MAX_LOGO_FILE_SIZE) {
|
||||
throw new Error(
|
||||
Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.toaster.error.max_size',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!ALLOWED_LOGO_FORMATS.includes(file.type)) {
|
||||
throw new Error(
|
||||
Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.toaster.error.bad_format',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const res = await onClickUpdateWorkspace({ logo_b64: await getBase64(file) });
|
||||
if (!res) {
|
||||
throw new Error(
|
||||
Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.toaster.error.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
Toaster.error(
|
||||
`${Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.toaster.error.prefix',
|
||||
)} - ${Languages.t(err as string)}`,
|
||||
);
|
||||
}
|
||||
uploadInputRef.current.value = '';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
uploadInputRef?.current && (uploadInputRef.current.onchange = onChangeWorkspaceLogo);
|
||||
}, [uploadInputRef, onChangeWorkspaceLogo]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title level={1}>{Languages.t('scenes.app.popup.workspaceparameter.pages.title')}</Title>
|
||||
<Suspense fallback={<></>}>
|
||||
<div>
|
||||
<Divider />
|
||||
|
||||
<Descriptions layout="vertical" bordered>
|
||||
<Item
|
||||
label={Languages.t('scenes.app.popup.workspaceparameter.pages.name_label')}
|
||||
span={3}
|
||||
>
|
||||
<Row align="middle" justify="start" className="small-bottom-margin">
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.name_description')}
|
||||
</Text>
|
||||
</Row>
|
||||
<Row align="middle" justify="space-between">
|
||||
<Col flex="auto" className="small-right-margin">
|
||||
<Input value={workspaceName} onChange={e => setWorkspaceName(e.target.value)} />
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={isEqual(workspaceName, workspace?.name)}
|
||||
onClick={() => workspaceName && onClickUpdateWorkspace({ name: workspaceName })}
|
||||
>
|
||||
{Languages.t('general.save')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Item>
|
||||
|
||||
<Item
|
||||
span={3}
|
||||
label={Languages.t('scenes.app.popup.workspaceparameter.pages.logo_subtitle')}
|
||||
>
|
||||
<Row align="middle" justify="start" className="small-bottom-margin">
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.logo_modify_description')}
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
<Row align="middle" justify="space-between">
|
||||
<Col
|
||||
className="workspace-logo-column"
|
||||
onClick={() => {
|
||||
uploadInputRef.current?.click();
|
||||
}}
|
||||
>
|
||||
{workspace?.logo && workspace?.logo.length > 0 ? (
|
||||
<AvatarComponent size={64} url={addApiUrlIfNeeded(workspace.logo)} />
|
||||
) : (
|
||||
<Avatar
|
||||
size={64}
|
||||
shape="square"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Title level={2} style={{ margin: 0 }} type="secondary">
|
||||
{workspace?.name.split('')[0].toUpperCase()}
|
||||
</Title>
|
||||
</Avatar>
|
||||
)}
|
||||
</Col>
|
||||
<Col
|
||||
flex="auto"
|
||||
className="small-left-margin"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.weight_max_small_text')}
|
||||
</Text>
|
||||
|
||||
{workspace?.logo && workspace?.logo.length > 0 && (
|
||||
<Link
|
||||
type="danger"
|
||||
className="small-top-margin"
|
||||
onClick={() => {
|
||||
// If file is not uploaded but in the inputNode value
|
||||
if (uploadInputRef.current?.value) {
|
||||
uploadInputRef.current.value = '';
|
||||
}
|
||||
|
||||
// Delete workspace logo
|
||||
onClickUpdateWorkspace({ logo: uploadInputRef.current?.value || 'none' });
|
||||
}}
|
||||
>
|
||||
{Languages.t('general.delete')}
|
||||
</Link>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Item>
|
||||
|
||||
<Item
|
||||
span={3}
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.default_workspace.label',
|
||||
)}
|
||||
>
|
||||
<Row wrap={false} align="middle" justify="space-between">
|
||||
<Col>
|
||||
<Text type="secondary">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_identity.default_workspace.description',
|
||||
)}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col>
|
||||
<Switch
|
||||
defaultChecked={workspace?.default}
|
||||
onChange={e => onClickUpdateWorkspace({ default: e })}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Item>
|
||||
<Item
|
||||
span={3}
|
||||
label={Languages.t('scenes.app.popup.workspaceparameter.pages.deleteworkspace')}
|
||||
>
|
||||
<Row wrap={false} align="middle" justify="space-between">
|
||||
<Col>
|
||||
<Text type="secondary">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.deleteworkspace_description',
|
||||
)}
|
||||
</Text>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() =>
|
||||
workspace &&
|
||||
ModalManager.open(<DeleteWorkspacePopup />, {
|
||||
position: 'center',
|
||||
size: { width: '600px' },
|
||||
})
|
||||
}
|
||||
style={{ color: 'var(--error)', border: '1px solid var(--error)' }}
|
||||
>
|
||||
{Languages.t('general.delete')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
<input ref={uploadInputRef} type="file" style={{ display: 'none' }} />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkspaceIdentity;
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
.alert-total-members {
|
||||
background: transparent;
|
||||
border-color: var(--error);
|
||||
padding: 8;
|
||||
}
|
||||
|
||||
.workspace-name-confirmation-input {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.workspace-logo-column {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-workspace-button {
|
||||
color: var(--white) !important;
|
||||
background: var(--error) !important;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Search } from 'react-feather';
|
||||
import { Row, Col, Button, Input, Typography, Divider } from 'antd';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
|
||||
import WorkspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import groupService from '@deprecated/workspaces/groups.js';
|
||||
import workspacesUsers from '@features/workspace-members/services/workspace-members-service';
|
||||
import Switch from 'components/inputs/switch';
|
||||
import Pending from '@views/client/popup/WorkspaceParameter/Pages/WorkspacePartnerTabs/Pending';
|
||||
import Members from '@views/client/popup/WorkspaceParameter/Pages/WorkspacePartnerTabs/Members';
|
||||
import LockedInviteAlert from '@components/locked-features-components/locked-invite-alert';
|
||||
import FeatureTogglesService, {
|
||||
FeatureNames,
|
||||
} from '@features/global/services/feature-toggles-service';
|
||||
import { useCurrentCompany } from '@features/companies/hooks/use-companies';
|
||||
|
||||
import './Pages.scss';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
|
||||
|
||||
type PropsType = {
|
||||
col: {
|
||||
user: Record<string, string>;
|
||||
level: string;
|
||||
};
|
||||
adminLevelId: string;
|
||||
onChange: () => void;
|
||||
};
|
||||
|
||||
export const AdminSwitch = (props: PropsType) => {
|
||||
workspacesUsers.useListener(useState as unknown as undefined);
|
||||
const loading = workspacesUsers.updateLevelUserLoading[props.col.user.id];
|
||||
const checked = props.col.level === props.adminLevelId;
|
||||
return (
|
||||
<div className="editLevel">
|
||||
<Switch
|
||||
loading={loading}
|
||||
label={Languages.t('scenes.app.popup.workspaceparameter.pages.moderator_status')}
|
||||
checked={checked}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
Collections.get('workspaces').useListener(useState);
|
||||
WorkspaceService.useListener();
|
||||
workspacesUsers.useListener();
|
||||
Languages.useListener();
|
||||
const { company } = useCurrentCompany();
|
||||
const workspaceId = useRouterWorkspace();
|
||||
|
||||
const usersInGroup = [];
|
||||
Object.keys(workspacesUsers.users_by_group[groupService.currentGroupId] || {}).map(
|
||||
// eslint-disable-next-line array-callback-return
|
||||
key => {
|
||||
const user = workspacesUsers.users_by_group[groupService.currentGroupId][key].user;
|
||||
if (
|
||||
!workspacesUsers.getUsersByWorkspace(WorkspaceService.currentWorkspaceId)[key] ||
|
||||
!workspacesUsers.getUsersByWorkspace(WorkspaceService.currentWorkspaceId)[key].user ||
|
||||
!workspacesUsers.getUsersByWorkspace(WorkspaceService.currentWorkspaceId)[key].user.id
|
||||
) {
|
||||
usersInGroup.push({
|
||||
id: user.id,
|
||||
user: user,
|
||||
externe: workspacesUsers.users_by_group[groupService.currentGroupId][key].externe,
|
||||
groupLevel:
|
||||
workspacesUsers.users_by_group[WorkspaceService.currentGroupId][key].groupLevel,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="workspaceParameter">
|
||||
<Typography.Title level={1}>
|
||||
{Languages.t('scenes.app.popup.workspaceparameter.pages.collaborateurs')}
|
||||
</Typography.Title>
|
||||
|
||||
{workspacesUsers.errorOnInvitation && (
|
||||
<div className="blocError">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.invitation_error',
|
||||
[],
|
||||
'An error occurred while inviting the following users: ',
|
||||
)}
|
||||
<br />
|
||||
<span className="text">
|
||||
{workspacesUsers.errorUsersInvitation.filter(item => item).join(', ')}
|
||||
</span>
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.invited_guest_check_message',
|
||||
[],
|
||||
'Check that the username or e-mail used is valid.',
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
{!FeatureTogglesService.isActiveFeatureName(FeatureNames.COMPANY_INVITE_MEMBER) ? (
|
||||
<LockedInviteAlert company={company} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<Row className="small-y-margin" justify="space-between" align="middle">
|
||||
<Col>
|
||||
<Input
|
||||
placeholder={Languages.t('components.listmanager.filter')}
|
||||
prefix={<Search size={16} color="var(--grey-dark)" />}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Pending filter={searchValue} />
|
||||
|
||||
<Members filter={searchValue} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import { Col, Row, Typography } from 'antd';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { ChevronsUp, ChevronUp } from 'react-feather';
|
||||
const { Text } = Typography;
|
||||
|
||||
const RoleComponent = ({ text, icon }: { text: string; icon?: JSX.Element }): JSX.Element => (
|
||||
<Row align="middle">
|
||||
{!!icon && (
|
||||
<Col pull={1} style={{ height: 16 }}>
|
||||
{icon}
|
||||
</Col>
|
||||
)}
|
||||
<Col>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{text}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
export default ({ companyRole, workspaceRole }: { companyRole: string; workspaceRole: string }) => {
|
||||
// Company
|
||||
switch (companyRole) {
|
||||
case 'owner':
|
||||
return (
|
||||
<RoleComponent
|
||||
text={Languages.t('scenes.app.popup.appsparameters.pages.company_label')}
|
||||
icon={<ChevronsUp size={16} style={{ padding: 0 }} />}
|
||||
/>
|
||||
);
|
||||
case 'admin':
|
||||
return (
|
||||
<RoleComponent
|
||||
text={Languages.t('general.user.role.company.admin')}
|
||||
icon={<ChevronsUp size={16} style={{ padding: 0 }} />}
|
||||
/>
|
||||
);
|
||||
case 'guest':
|
||||
return <RoleComponent text={Languages.t('general.user.role.company.guest')} />;
|
||||
}
|
||||
|
||||
// Workspace
|
||||
if (workspaceRole === 'moderator') {
|
||||
return (
|
||||
<RoleComponent
|
||||
text={Languages.t('scenes.app.popup.workspaceparameter.pages.moderator_status')}
|
||||
icon={<ChevronUp size={16} style={{ padding: 0 }} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
-275
@@ -1,275 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import _ from 'lodash';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { Table, Row, Typography } from 'antd';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import EditIcon from '@material-ui/icons/MoreHorizOutlined';
|
||||
import Menu from 'components/menus/menu.jsx';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import UserService from '@features/users/services/current-user-service';
|
||||
import workspacesUsers from '@features/workspace-members/services/workspace-members-service';
|
||||
import workspaceUserRightsService from '@features/workspaces/services/workspace-user-rights-service';
|
||||
import InitService from '@features/global/services/init-service';
|
||||
import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint';
|
||||
import Api from '@features/global/framework/api-service';
|
||||
import ConsoleService from '@features/console/services/console-service';
|
||||
import WorkspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import { delayRequest } from '@features/global/utils/managedSearchRequest';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
|
||||
import Icon from '@components/icon/icon';
|
||||
import { WorkspaceUserType } from '@features/workspaces/types/workspace';
|
||||
import { useSetUserList } from '@features/users/hooks/use-user-list';
|
||||
import { useSearchUsers } from '@features/users/hooks/use-search-user-list';
|
||||
import MemberGrade from './MemberGrade';
|
||||
|
||||
type ColumnObjectType = { [key: string]: any };
|
||||
|
||||
const { Link, Text, Title } = Typography;
|
||||
|
||||
export default ({ filter }: { filter: string }) => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, setData] = useState<ColumnObjectType[]>([]);
|
||||
const [pageToken, setPageToken] = useState<string | null>(null);
|
||||
const [serverSearchedData, setServerSearchedData] = useState<ColumnObjectType[]>([]);
|
||||
const companyId = useRouterCompany();
|
||||
const workspaceId = useRouterWorkspace();
|
||||
|
||||
const { search, result } = useSearchUsers({ scope: 'workspace' });
|
||||
|
||||
const prefixRoute = '/internal/services/workspaces/v1';
|
||||
const workspaceUsersRoute = `${prefixRoute}/companies/${companyId}/workspaces/${workspaceId}/users`;
|
||||
|
||||
const { set: setUserList } = useSetUserList('Members');
|
||||
const { xs, sm } = useBreakpoint();
|
||||
|
||||
useEffect(() => {
|
||||
requestWorkspaceUsers();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
delayRequest('workspace_partners_search', async () => {
|
||||
await search(filter);
|
||||
});
|
||||
}, [filter]);
|
||||
|
||||
const filteredData: WorkspaceUserType[] | null = filter
|
||||
? (result
|
||||
.map(u => ({
|
||||
user: u,
|
||||
...u,
|
||||
role: u.workspaces?.find(w => w.id === workspaceId)?.role,
|
||||
user_id: u.id || '',
|
||||
workspace_id: workspaceId || '',
|
||||
}))
|
||||
.slice(0, 50) as WorkspaceUserType[])
|
||||
: null;
|
||||
|
||||
const requestWorkspaceUsers = async (pageToken?: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const res = (await Api.get(
|
||||
`${workspaceUsersRoute}?limit=25${pageToken ? `&page_token=${pageToken}` : ''}`,
|
||||
)) as { resources: WorkspaceUserType[]; next_page_token: string };
|
||||
setPageToken(res.next_page_token || null);
|
||||
if (res.resources) {
|
||||
setData(pageToken ? _.uniqBy([...data, ...res.resources], col => col.id) : res.resources);
|
||||
setUserList(
|
||||
res.resources.map(wsUser => ({
|
||||
...wsUser.user,
|
||||
workspaces: [{ id: workspaceId, company_id: companyId }],
|
||||
})),
|
||||
);
|
||||
}
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const updateData = (updater: (data: ColumnObjectType[]) => ColumnObjectType[]) => {
|
||||
setData(updater(data));
|
||||
setServerSearchedData(updater(serverSearchedData));
|
||||
};
|
||||
|
||||
const updateWorkspaceUserRole = async (col: WorkspaceUserType & { company_id: string }) => {
|
||||
const workspaceUserRole = `${prefixRoute}/companies/${col.company_id}/workspaces/${col.workspace_id}/users/${col.user_id}`;
|
||||
|
||||
setLoading(true);
|
||||
const res = await Api.post<unknown, { resource: unknown }>(workspaceUserRole, {
|
||||
resource: {
|
||||
role: col.role === 'moderator' ? 'member' : 'moderator',
|
||||
},
|
||||
});
|
||||
|
||||
if (res.resource) {
|
||||
updateData(data =>
|
||||
data.map(d => (d.user_id === col.user_id ? (res.resource as ColumnObjectType) : d)),
|
||||
);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const deleteWorkspaceUser = (col: { [key: string]: any }) =>
|
||||
AlertManager.confirm(() => {
|
||||
const deleteWorkspaceUser = `${prefixRoute}/companies/${col.company_id}/workspaces/${col.workspace_id}/users/${col.user_id}`;
|
||||
|
||||
setLoading(true);
|
||||
Api.delete(deleteWorkspaceUser, () => {
|
||||
updateData(data => data.filter(d => d.user_id !== col.user_id));
|
||||
setLoading(false);
|
||||
});
|
||||
});
|
||||
|
||||
const leaveWorkspace = () => {
|
||||
workspacesUsers.leaveWorkspace();
|
||||
};
|
||||
|
||||
const buildMenu = (col: WorkspaceUserType & { company_id: string }) => {
|
||||
const menu: (Record<string, string | (() => void)> | boolean)[] = [];
|
||||
|
||||
if (
|
||||
workspaceUserRightsService.hasGroupPrivilege() &&
|
||||
InitService.server_infos?.configuration?.accounts.type === 'remote'
|
||||
) {
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
text: Languages.t('scenes.app.popup.workspaceparameter.edit_from_console', []),
|
||||
onClick: () =>
|
||||
window.open(
|
||||
ConsoleService.getCompanyUsersManagementUrl(WorkspaceService.currentGroupId),
|
||||
'_blank',
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (col.user_id === UserService.getCurrentUserId()) {
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
text: Languages.t('scenes.app.popup.workspaceparameter.pages.quit_workspace_menu'),
|
||||
className: 'error',
|
||||
onClick: () => {
|
||||
leaveWorkspace();
|
||||
},
|
||||
});
|
||||
} else if (workspaceUserRightsService.hasWorkspacePrivilege()) {
|
||||
menu.push(
|
||||
workspaceUserRightsService.hasWorkspacePrivilege() && {
|
||||
type: 'menu',
|
||||
text: col.role === 'moderator' ? 'Set to workspace member' : `Set to workspace moderator`,
|
||||
onClick: () => updateWorkspaceUserRole(col),
|
||||
},
|
||||
);
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
text: Languages.t('scenes.app.popup.workspaceparameter.pages.withdraw_button'),
|
||||
className: 'error',
|
||||
onClick: () => deleteWorkspaceUser(col),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="action">
|
||||
{menu.length > 0 && (
|
||||
<Menu className="option_button" style={{ paddingTop: 8, paddingRight: 8 }} menu={menu}>
|
||||
<EditIcon className="m-icon-small" />
|
||||
</Menu>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const columns: ColumnsType<ColumnObjectType> = [
|
||||
{
|
||||
title: Languages.t('scenes.app.popup.workspaceparameter.pages.table_title'),
|
||||
dataIndex: 'name',
|
||||
render: (_text, col) => {
|
||||
const fullName = UserService.getFullName(col.user);
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<div
|
||||
className="user_image"
|
||||
style={{
|
||||
backgroundImage: `url(${UserService.getThumbnail(col.user)})`,
|
||||
}}
|
||||
/>
|
||||
|
||||
{fullName.length > 0 && <Text className="small-right-margin">{fullName}</Text>}
|
||||
|
||||
{col.user.email && <Text type="secondary">{col.user.email}</Text>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.workspace_partner_tabs.members.table.tags',
|
||||
),
|
||||
dataIndex: 'tags',
|
||||
render: (_text, col) => (
|
||||
<Text type="secondary">
|
||||
<MemberGrade
|
||||
companyRole={UserService.getUserRole(col.user, companyId)}
|
||||
workspaceRole={col.role}
|
||||
/>
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'menu',
|
||||
width: 50,
|
||||
render: (_text, col) =>
|
||||
false &&
|
||||
workspaceUserRightsService.hasWorkspacePrivilege() &&
|
||||
buildMenu(col as WorkspaceUserType & { company_id: string }),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{InitService.server_infos?.configuration?.accounts.type === 'remote' && (
|
||||
<Link
|
||||
style={{ float: 'right' }}
|
||||
href="#"
|
||||
onClick={() => {
|
||||
window.open(
|
||||
ConsoleService.getCompanyUsersManagementUrl(WorkspaceService.currentGroupId),
|
||||
'_blank',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Icon type="external-link-alt" className="m-icon-small" />{' '}
|
||||
{Languages.t(
|
||||
'views.client.popup.workspace_parameter.pages.workspace_members.link_to_console',
|
||||
)}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Title level={3}>
|
||||
{Languages.t('scenes.apps.parameters.workspace_sections.members.members')}
|
||||
</Title>
|
||||
|
||||
<Table<ColumnObjectType>
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
size="small"
|
||||
pagination={false}
|
||||
dataSource={filteredData ? filteredData : data}
|
||||
scroll={{ x: xs || sm ? true : undefined }}
|
||||
/>
|
||||
{pageToken !== null && pageToken.length && (
|
||||
<Row justify="center" align="middle" className="small-y-margin">
|
||||
<Link onClick={() => requestWorkspaceUsers(pageToken)}>
|
||||
{Languages.t('components.searchpopup.load_more')}
|
||||
</Link>
|
||||
</Row>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-135
@@ -1,135 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import { Divider, Table, Typography } from 'antd';
|
||||
import Menu from 'components/menus/menu.jsx';
|
||||
import EditIcon from '@material-ui/icons/MoreHorizOutlined';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint';
|
||||
import useRouterWorkspace from '@features/router/hooks/use-router-workspace';
|
||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
||||
import AccessRightsService from '@features/workspace-members/services/workspace-members-access-rights-service';
|
||||
import WorkspaceUserAPIClient from '@features/workspace-members/api/workspace-members-api-client';
|
||||
|
||||
type PendingEmailResourceType = {
|
||||
company_role: string;
|
||||
email: string;
|
||||
role: string;
|
||||
};
|
||||
|
||||
type ColumnObjectType = { key: number } & PendingEmailResourceType;
|
||||
|
||||
export default (props: { filter: string }) => {
|
||||
const companyId = useRouterCompany();
|
||||
const workspaceId = useRouterWorkspace();
|
||||
const DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [data, _setData] = useState<ColumnObjectType[]>([]);
|
||||
const [filteredData, setFilteredData] = useState<ColumnObjectType[] | null>(null);
|
||||
|
||||
const { xs, sm } = useBreakpoint();
|
||||
|
||||
useEffect(() => {
|
||||
refreshPendingEmails();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
onSearch();
|
||||
}, [props.filter, data]);
|
||||
|
||||
const refreshPendingEmails = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setData(await WorkspaceUserAPIClient.listPending(companyId, workspaceId));
|
||||
setLoading(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const deletePendingEmail = async (col: ColumnObjectType) => {
|
||||
setLoading(true);
|
||||
await WorkspaceUserAPIClient.cancelPending(companyId, workspaceId, col.email);
|
||||
refreshPendingEmails();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const setData = (resources: PendingEmailResourceType[]) => {
|
||||
if (resources) {
|
||||
_setData(
|
||||
resources.map((o: PendingEmailResourceType, key: number) => ({ key: key + 1, ...o })),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onSearch = () => {
|
||||
if (props.filter.length) {
|
||||
const filteredData = data.filter(col =>
|
||||
(col.email || '').toLocaleLowerCase().includes(props.filter.toLocaleLowerCase()),
|
||||
);
|
||||
setFilteredData(filteredData);
|
||||
} else {
|
||||
setFilteredData(null);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ColumnsType<ColumnObjectType> = [
|
||||
{
|
||||
title: Languages.t('general.email'),
|
||||
dataIndex: 'email',
|
||||
width: 200,
|
||||
},
|
||||
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'menu',
|
||||
width: 50,
|
||||
render: (_text, col) => {
|
||||
if (!AccessRightsService.hasLevel(workspaceId, 'member')) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<div className="action">
|
||||
<Menu
|
||||
className="option_button"
|
||||
style={{ padding: 4 }}
|
||||
menu={[
|
||||
{
|
||||
type: 'menu',
|
||||
className: 'danger',
|
||||
text: Languages.t('scenes.app.popup.workspaceparameter.pages.cancel_invitation'),
|
||||
onClick: () => deletePendingEmail(col),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<EditIcon className="m-icon-small" />
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (data.length === 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Typography.Title level={3}>
|
||||
{Languages.t('scenes.apps.parameters.workspace_sections.members.pending')}
|
||||
</Typography.Title>
|
||||
<Table
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
dataSource={filteredData ? filteredData : data}
|
||||
size="small"
|
||||
pagination={{ pageSize: DEFAULT_PAGE_SIZE, simple: true }}
|
||||
scroll={{ x: xs || sm ? true : undefined }}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,215 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
|
||||
import LoginService from '@features/auth/login-service';
|
||||
import popupManager from '@deprecated/popupManager/popupManager.js';
|
||||
import userService from '@features/users/services/current-user-service';
|
||||
import currentUserService from '@deprecated/user/CurrentUser';
|
||||
import CompanyIntegration from './Pages/CompanyIntegrations';
|
||||
import WorkspacePartner from './Pages/WorkspacePartner';
|
||||
import CompanyIdendity from './Pages/CompanyIdendity.jsx';
|
||||
import WorkspaceUserRights from '@features/workspaces/services/workspace-user-rights-service';
|
||||
import WorkspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import MenuList from 'components/menus/menu-component.jsx';
|
||||
import InitService from '@features/global/services/init-service';
|
||||
import ConsoleService from '@features/console/services/console-service';
|
||||
|
||||
import './WorkspaceParameter.scss';
|
||||
import WorkspaceIdentity from './Pages/Workspace/WorkspaceIdentity';
|
||||
|
||||
export default class WorkspaceParameter extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
var user = Collections.get('users').find(userService.getCurrentUserId());
|
||||
this.state = {
|
||||
login: LoginService,
|
||||
i18n: Languages,
|
||||
users_repository: Collections.get('users'),
|
||||
workspace_user_rights: WorkspaceUserRights,
|
||||
currentUserService: currentUserService,
|
||||
page: popupManager.popupStates['workspace_parameters'] || props.initial_page || 1,
|
||||
attributeOpen: 0,
|
||||
subMenuOpened: 0,
|
||||
username: user ? user.username : '',
|
||||
last_name: user ? user.last_name : '',
|
||||
first_name: user ? user.first_name : '',
|
||||
thumbnail: false,
|
||||
options: this.props.options,
|
||||
};
|
||||
Collections.get('users').addListener(this);
|
||||
Collections.get('users').listenOnly(this, [
|
||||
Collections.get('users').find(userService.getCurrentUserId()).front_id,
|
||||
]);
|
||||
LoginService.addListener(this);
|
||||
Languages.addListener(this);
|
||||
currentUserService.addListener(this);
|
||||
}
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({ thumbnail: false });
|
||||
}
|
||||
componentWillUnmount() {
|
||||
LoginService.removeListener(this);
|
||||
Languages.removeListener(this);
|
||||
currentUserService.removeListener(this);
|
||||
Collections.get('users').removeListener(this);
|
||||
}
|
||||
displayScene() {
|
||||
if (WorkspaceUserRights.hasWorkspacePrivilege() && this.state.page === 1) {
|
||||
return <WorkspaceIdentity />;
|
||||
}
|
||||
if (this.state.page === 2) {
|
||||
return <WorkspacePartner />;
|
||||
}
|
||||
if (WorkspaceUserRights.hasWorkspacePrivilege() && this.state.page === 3) {
|
||||
return <CompanyIntegration />;
|
||||
}
|
||||
if (WorkspaceUserRights.hasGroupPrivilege('MANAGE_DATA') && this.state.page === 4) {
|
||||
return <CompanyIdendity />;
|
||||
}
|
||||
if (this.state.page === 5) {
|
||||
return (
|
||||
<div className="">
|
||||
<div className="title">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.payments_subscriptions_title',
|
||||
[],
|
||||
'Paiements et abonnements',
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="group_section" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setPage(page) {
|
||||
popupManager.popupStates['workspace_parameters'] = page;
|
||||
this.setState({ page: page });
|
||||
}
|
||||
render() {
|
||||
var subText = (
|
||||
<div>
|
||||
{Languages.t(
|
||||
'scenes.app.channelsbar.currentuser.workspace_info',
|
||||
[
|
||||
Collections.get('workspaces').find(WorkspaceService.currentWorkspaceId).name,
|
||||
Collections.get('groups').find(WorkspaceService.currentGroupId).name,
|
||||
],
|
||||
"Vous êtes dans l'espace de travail $1 du groupe $2.",
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
{WorkspaceUserRights.hasWorkspacePrivilege() &&
|
||||
WorkspaceUserRights.hasGroupPrivilege('MANAGE_PRICINGS') && (
|
||||
<span>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.admin_manager_current_status',
|
||||
[],
|
||||
"Vous êtes Administrateur et Gérant de l'entreprise.",
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{WorkspaceUserRights.hasWorkspacePrivilege() &&
|
||||
!WorkspaceUserRights.hasGroupPrivilege('MANAGE_PRICINGS') && (
|
||||
<span>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.admin_current_status',
|
||||
[],
|
||||
'Vous êtes Administrateur.',
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{!WorkspaceUserRights.hasWorkspacePrivilege() &&
|
||||
WorkspaceUserRights.hasGroupPrivilege('MANAGE_PRICINGS') && (
|
||||
<span>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.manager_current_status',
|
||||
[],
|
||||
"Vous êtes Gérant de l'entreprise.",
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
var menu = [];
|
||||
|
||||
if (WorkspaceUserRights.hasWorkspacePrivilege()) {
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
emoji: ':house_with_garden:',
|
||||
text: Languages.t(
|
||||
'scenes.apps.parameters.workspace_sections.workspace',
|
||||
[],
|
||||
'Espace de travail',
|
||||
),
|
||||
selected: this.state.page === 1 ? 'selected' : '',
|
||||
onClick: () => {
|
||||
this.setPage(1);
|
||||
},
|
||||
});
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
emoji: ':electric_plug:', // WORKSPACE INTEGRATION
|
||||
text: Languages.t('scenes.app.popup.workspaceparameter.pages.apps_connectors_title'),
|
||||
selected: this.state.page === 3 ? 'selected' : '',
|
||||
onClick: () => {
|
||||
this.setPage(3);
|
||||
},
|
||||
});
|
||||
}
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
emoji: ':handshake:',
|
||||
text: Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.collaborateurs',
|
||||
[],
|
||||
'Collaborateurs',
|
||||
),
|
||||
selected: this.state.page === 2 ? 'selected' : '',
|
||||
onClick: () => {
|
||||
this.setPage(2);
|
||||
},
|
||||
});
|
||||
|
||||
if (WorkspaceUserRights.hasGroupPrivilege('MANAGE_DATA')) {
|
||||
menu.push({ type: 'separator' });
|
||||
menu.push({
|
||||
type: 'menu',
|
||||
emoji: ':clipboard:',
|
||||
text: Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.company_identity_title',
|
||||
[],
|
||||
"Identité de l'entreprise",
|
||||
),
|
||||
selected: this.state.page === 4 ? 'selected' : '',
|
||||
onClick: () => {
|
||||
if (InitService.server_infos?.configuration?.accounts?.type === 'remote') {
|
||||
return window.open(
|
||||
ConsoleService.getCompanyManagementUrl(WorkspaceService.currentGroupId),
|
||||
'_blank',
|
||||
);
|
||||
} else {
|
||||
this.setPage(4);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({ type: 'text', text: subText });
|
||||
|
||||
return (
|
||||
<div className="workspaceParameter fade_in">
|
||||
<div className="main">
|
||||
<div className="sideBar">
|
||||
<MenuList menu={menu} />
|
||||
</div>
|
||||
<div className="content">{this.displayScene()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
.workspaceParameter {
|
||||
.parameters_loader {
|
||||
margin: auto;
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.buttonWithTimeout_container {
|
||||
text-align: right;
|
||||
.buttonValidation {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
height: 100vh;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
overflow: auto;
|
||||
padding-bottom: 40px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.group_section {
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.parameters_attribute {
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.value {
|
||||
.identity {
|
||||
flex: 1;
|
||||
.name {
|
||||
width: 240px;
|
||||
margin-left: 10px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail_container {
|
||||
margin-right: 20px;
|
||||
padding-top: 10px;
|
||||
flex: none !important;
|
||||
width: 80px;
|
||||
.thumbnail {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.mails_container {
|
||||
max-width: none !important;
|
||||
padding-top: 10px;
|
||||
font-size: 13px;
|
||||
|
||||
.mail {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.address,
|
||||
.main_mail {
|
||||
font-size: 13px;
|
||||
line-height: 35px;
|
||||
background: #f2f2f2;
|
||||
height: 34px;
|
||||
border-radius: var(--border-radius-base);
|
||||
padding: 0 10px;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
min-width: 300px;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.main_mail {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
width: auto !important;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new_secondary_mail {
|
||||
max-width: none !important;
|
||||
|
||||
.new_mail_input,
|
||||
.new_mail_input_code {
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
.new_mail_input {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.parameters_form {
|
||||
padding-bottom: 10px;
|
||||
max-width: 320px;
|
||||
flex: 1;
|
||||
|
||||
input,
|
||||
select {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.text.error {
|
||||
margin-top: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form_bottom {
|
||||
text-align: right;
|
||||
a.cancel {
|
||||
float: left;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.buttonWithTimeout_container {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom-btns {
|
||||
padding: 0px 100px;
|
||||
margin-top: 20px;
|
||||
line-height: 54px;
|
||||
display: flex;
|
||||
.return {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
.nextBtn {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
.deleteSpace {
|
||||
display: flex;
|
||||
.buttonValidation {
|
||||
margin: 4px 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import Collections from '@deprecated/CollectionsV1/Collections/Collections.js';
|
||||
import LoginService from '@features/auth/login-service';
|
||||
import popupManager from '@deprecated/popupManager/popupManager.js';
|
||||
import userService from '@features/users/services/current-user-service';
|
||||
import currentUserService from '@deprecated/user/CurrentUser';
|
||||
import WorkspaceApps from './pages/WorkspaceApps.js.js.js.js.js.js.js';
|
||||
import WorkspaceAppsInformations from './pages/WorkspaceAppsInformations.jsx';
|
||||
import TwacodeTester from './pages/TwacodeTester.jsx';
|
||||
import MenuList from 'components/menus/menu-component.jsx';
|
||||
import './AppsParameters.scss';
|
||||
|
||||
export default class AppsParameters extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
var user = Collections.get('users').find(userService.getCurrentUserId());
|
||||
this.state = {
|
||||
login: LoginService,
|
||||
i18n: Languages,
|
||||
users_repository: Collections.get('users'),
|
||||
currentUserService: currentUserService,
|
||||
page: props.initial_page || 1,
|
||||
attributeOpen: 0,
|
||||
subMenuOpened: 0,
|
||||
username: user ? user.username : '',
|
||||
last_name: user ? user.last_name : '',
|
||||
first_name: user ? user.first_name : '',
|
||||
thumbnail: false,
|
||||
};
|
||||
Collections.get('users').addListener(this);
|
||||
Collections.get('users').listenOnly(this, [
|
||||
Collections.get('users').find(userService.getCurrentUserId()).front_id,
|
||||
]);
|
||||
LoginService.addListener(this);
|
||||
Languages.addListener(this);
|
||||
currentUserService.addListener(this);
|
||||
}
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({ thumbnail: false });
|
||||
}
|
||||
componentWillUnmount() {
|
||||
LoginService.removeListener(this);
|
||||
Languages.removeListener(this);
|
||||
currentUserService.removeListener(this);
|
||||
Collections.get('users').removeListener(this);
|
||||
}
|
||||
displayScene() {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (this.state.page) {
|
||||
case 1:
|
||||
return <WorkspaceApps />;
|
||||
case 2:
|
||||
return <WorkspaceAppsInformations />;
|
||||
case 3:
|
||||
return <TwacodeTester />;
|
||||
}
|
||||
}
|
||||
|
||||
setPage(page) {
|
||||
this.setState({ page: page });
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="appsParameters fade_in">
|
||||
<div className="main">
|
||||
<div className="sideBar">
|
||||
<MenuList
|
||||
menu={[
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t(
|
||||
'scenes.app.popup.workspaceparameter.pages.your_apps_label',
|
||||
[],
|
||||
'Vos applications',
|
||||
),
|
||||
emoji: ':control_knobs:',
|
||||
selected: this.state.page === 1,
|
||||
onClick: () => this.setPage(1),
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Basic informations',
|
||||
emoji: ':information_source:',
|
||||
selected: this.state.page === 2,
|
||||
onClick: () => this.setPage(2),
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: 'Twacode tester',
|
||||
emoji: ':love_letter:',
|
||||
selected: this.state.page === 3,
|
||||
onClick: () => this.setPage(3),
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
text: Languages.t('general.back', [], 'Retour'),
|
||||
icon: 'arrow-left',
|
||||
onClick: () => popupManager.close(),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="content">{this.displayScene()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
.appsParameters {
|
||||
.parameters_loader {
|
||||
margin: auto;
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.buttonWithTimeout_container {
|
||||
text-align: right;
|
||||
.buttonValidation {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
height: 100vh;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
overflow: auto;
|
||||
padding-bottom: 40px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.group_section {
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.parameters_attribute {
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.value {
|
||||
.identity {
|
||||
flex: 1;
|
||||
.name {
|
||||
width: 240px;
|
||||
margin-left: 10px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail_container {
|
||||
margin-right: 20px;
|
||||
padding-top: 10px;
|
||||
flex: none !important;
|
||||
width: 80px;
|
||||
.thumbnail {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.mails_container {
|
||||
max-width: none !important;
|
||||
padding-top: 10px;
|
||||
font-size: 13px;
|
||||
|
||||
.mail {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.address,
|
||||
.main_mail {
|
||||
font-size: 13px;
|
||||
line-height: 35px;
|
||||
background: #f2f2f2;
|
||||
height: 34px;
|
||||
border-radius: var(--border-radius-base);
|
||||
padding: 0 10px;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
min-width: 300px;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.main_mail {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
width: auto !important;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new_secondary_mail {
|
||||
max-width: none !important;
|
||||
|
||||
.new_mail_input,
|
||||
.new_mail_input_code {
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
.new_mail_input {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.parameters_form {
|
||||
padding-bottom: 10px;
|
||||
max-width: 320px;
|
||||
flex: 1;
|
||||
|
||||
input,
|
||||
select {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.text.error {
|
||||
margin-top: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form_bottom {
|
||||
text-align: right;
|
||||
a.cancel {
|
||||
float: left;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.buttonWithTimeout_container {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom-btns {
|
||||
padding: 0px 100px;
|
||||
margin-top: 20px;
|
||||
line-height: 54px;
|
||||
display: flex;
|
||||
.return {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
}
|
||||
.nextBtn {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import AutoHeight from 'components/auto-height/auto-height.jsx';
|
||||
import Twacode from 'components/twacode/twacode';
|
||||
|
||||
import './pages.scss';
|
||||
|
||||
export default class TwacodeTester extends Component {
|
||||
constructor(props) {
|
||||
super();
|
||||
this.state = {
|
||||
content: '{}',
|
||||
logs: [],
|
||||
passives: {},
|
||||
};
|
||||
}
|
||||
|
||||
onAction(type, id, context, passives, evt) {
|
||||
//Button pressed
|
||||
if (type === 'interactive_action') {
|
||||
this.state.logs.push({
|
||||
date: new Date().toLocaleString(),
|
||||
content: Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.call_event_handlers',
|
||||
['interactive_action', id, +JSON.stringify(passives)],
|
||||
"Event of type '$1' and id '$2' with passives '$3'",
|
||||
),
|
||||
});
|
||||
this.setState({});
|
||||
}
|
||||
}
|
||||
|
||||
onPassiveChange(type, id, context, value) {
|
||||
// eslint-disable-next-line react/no-direct-mutation-state
|
||||
this.state.passives[id] = value;
|
||||
this.setState({});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="apps">
|
||||
<div className="title">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.title_tester', [], 'Testeur Twacode')}
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<div className="subtitle">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages._appareance_message_twakcode',
|
||||
[],
|
||||
"Testez l'apparence de vos messages Twacode",
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.instruction_message_tdrivecode',
|
||||
[],
|
||||
'Commencez par écrire votre message sous le format JSON.',
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AutoHeight
|
||||
placeholder={'{JSON}'}
|
||||
className={
|
||||
this.state.display_json_error
|
||||
? Languages.t('scenes.app.popup.appsparameters.pages.error_user_code', [], 'erreur')
|
||||
: Languages.t('scenes.app.popup.appsparameters.pages.ok_user_code', [], 'ok')
|
||||
}
|
||||
onChange={evt => {
|
||||
try {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var json = JSON.parse(evt.target.value);
|
||||
this.setState({
|
||||
logs: [],
|
||||
passives: {},
|
||||
content: evt.target.value,
|
||||
display_json_error: false,
|
||||
});
|
||||
} catch (e) {
|
||||
this.setState({ content: evt.target.value, display_json_error: true });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{this.state.content}
|
||||
</AutoHeight>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<div className="group_section">
|
||||
<div className="message" style={{ width: '100%' }}>
|
||||
{this.state.display_json_error && (
|
||||
<span>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.error_Json_Ttdrivecode',
|
||||
[],
|
||||
'Il y a une erreur dans votre JSON.',
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!this.state.display_json_error && (
|
||||
<Twacode
|
||||
className="allow_selection"
|
||||
id={'tester'}
|
||||
content={JSON.parse(this.state.content)}
|
||||
isApp={true}
|
||||
onAction={(type, id, context, passives, evt) =>
|
||||
this.onAction(type, id, context, passives, evt)
|
||||
}
|
||||
onPassiveChange={(type, id, context, value) =>
|
||||
this.onPassiveChange(type, id, context, value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="group_section">
|
||||
<div className="subtitle">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.message_data_next_event',
|
||||
[],
|
||||
'Données passives qui seront envoyées dans le prochain Événement',
|
||||
)}
|
||||
</div>
|
||||
|
||||
{Object.keys(this.state.passives).map(key => {
|
||||
var value = this.state.passives[key];
|
||||
return (
|
||||
<div className="text" key={key}>
|
||||
<b>{key}</b> {value}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="group_section">
|
||||
<div className="subtitle">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.event_subtitle', [], 'Événements')}
|
||||
</div>
|
||||
|
||||
{this.state.logs.reverse().map(event => {
|
||||
return (
|
||||
<div className="log text" key={event.date}>
|
||||
<b>{event.date}</b> {event.content}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-678
@@ -1,678 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Api from '@features/global/framework/api-service';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
import Icon from '@components/icon/icon';
|
||||
import Emojione from 'components/emojione/emojione';
|
||||
import Attribute from 'components/parameters/attribute';
|
||||
import Input from 'components/inputs/input';
|
||||
import TagPicker from 'components/tag-picker/tag-picker';
|
||||
import AutoHeight from 'components/auto-height/auto-height';
|
||||
import InputWithClipBoard from 'components/input-with-clip-board/input-with-clip-board';
|
||||
import { getCompanyApplication as getApplication } from '@features/applications/state/company-applications';
|
||||
|
||||
import './pages.scss';
|
||||
import WorkspacesApp from '@deprecated/workspaces/workspaces_apps.jsx';
|
||||
import InlineTagPicker from 'components/inline-tag-picker/inline-tag-picker';
|
||||
import Switch from 'components/inputs/switch';
|
||||
import ButtonWithTimeout from 'components/buttons/button-with-timeout';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
export default props => {
|
||||
const [originalApplication, setOriginalApplication] = useState({});
|
||||
|
||||
const [application, _setApplication] = useState({});
|
||||
const setApplication = data => _setApplication(_.cloneDeep(data));
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showApiKey, setShowApiKey] = useState(false);
|
||||
const [displayJsonError, setDisplayJsonError] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [errorCode, setErrorCode] = useState(false);
|
||||
|
||||
const [identity, setIdentity] = useState({});
|
||||
|
||||
let api_key_timeout = null;
|
||||
const fetchData = async () => {
|
||||
const res = await Api.get(`/internal/services/applications/v1/applications/${props.appId}`);
|
||||
setLoading(false);
|
||||
if (!res.resource) {
|
||||
console.error(res);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('APPLICATION', res.resource);
|
||||
|
||||
res.resource.categories = [];
|
||||
res.resource.available_categories = [1, 2, 3];
|
||||
|
||||
res.resource.capabilities = [];
|
||||
res.resource.available_capabilities = [1, 2, 3];
|
||||
|
||||
res.resource.privileges = [];
|
||||
res.resource.available_privileges = [1, 2, 3];
|
||||
|
||||
res.resource.hooks = [];
|
||||
res.resource.available_hooks = [1, 2, 3];
|
||||
|
||||
setApplication(res.resource);
|
||||
setOriginalApplication(res.resource);
|
||||
|
||||
setIdentity(res.resource.identity);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (props.appId && !application.id) {
|
||||
fetchData();
|
||||
}
|
||||
}, []);
|
||||
|
||||
//
|
||||
//
|
||||
// // this.state = {
|
||||
// // id: props.appId,
|
||||
// // application: {},
|
||||
// // show_api_key: false,
|
||||
// // };
|
||||
//
|
||||
// const saveApp = () => {
|
||||
// var saveApp = () => {
|
||||
// this.setState({ loading: true, error: false, error_code: false });
|
||||
//
|
||||
// var data = {
|
||||
// application: this.state.application,
|
||||
// workspace_id: workspaceService.currentWorkspaceId,
|
||||
// };
|
||||
//
|
||||
// Api.post('/ajax/market/app/update', data, res => {
|
||||
// if (res.data && res.data.id) {
|
||||
// Collections.get('applications').completeObject(res.data);
|
||||
//
|
||||
// this.props.exit();
|
||||
// } else {
|
||||
// if (res.errors.indexOf('code_used') >= 0) {
|
||||
// this.setState({ loading: false, error_code: true });
|
||||
// } else {
|
||||
// this.setState({ loading: false, error: true });
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// if (this.state.application.install_count > 0) {
|
||||
// AlertManager.confirm(
|
||||
// () => {
|
||||
// saveApp();
|
||||
// },
|
||||
// null,
|
||||
// {
|
||||
// text: Languages.t(
|
||||
// 'scenes.app.popup.appsparameters.pages.app_update',
|
||||
// [this.state.application.name, this.state.application.install_count || 0],
|
||||
// "Mettre à jour l'application $1 (l'application est utilisée $2 fois.)",
|
||||
// ),
|
||||
// },
|
||||
// );
|
||||
// } else {
|
||||
// saveApp();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const removeApp = () => {
|
||||
// AlertManager.confirm(
|
||||
// () => {
|
||||
// this.setState({ loading: true, error: false });
|
||||
//
|
||||
// var data = {
|
||||
// application_id: this.state.application.id,
|
||||
// };
|
||||
//
|
||||
// Api.post('/ajax/market/app/remove', data, res => {
|
||||
// if (res.data && !res.errors.length) {
|
||||
// Collections.get('applications').removeObject(this.state.application.front_id);
|
||||
// this.props.exit();
|
||||
// } else {
|
||||
// this.setState({ loading: false, error: true });
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// null,
|
||||
// {
|
||||
// text: Languages.t(
|
||||
// 'scenes.app.popup.appsparameters.pages.remove_app',
|
||||
// [this.state.application.name],
|
||||
// "Supprimer l'application $1 définitivement.",
|
||||
// ),
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
//
|
||||
const convertToSimpleName = value => {
|
||||
value = value || '';
|
||||
value = value.toLocaleLowerCase();
|
||||
value = value.replace(/[^a-z0-9]/g, '_');
|
||||
value = value.replace(/_+/g, '_');
|
||||
value = value.replace(/^_+/g, '');
|
||||
return value;
|
||||
};
|
||||
//
|
||||
//
|
||||
// if (this.state.application.id !== this.state.id) {
|
||||
// // eslint-disable-next-line react/no-direct-mutation-state
|
||||
// this.state.application = JSON.parse(JSON.stringify(getApplication(this.state.id) || {}));
|
||||
// }
|
||||
//
|
||||
// var isNew = false;
|
||||
// var application = this.state.application;
|
||||
//
|
||||
//
|
||||
// if (!application || !application.id) {
|
||||
// isNew = true;
|
||||
// }
|
||||
//
|
||||
// var workspace_id = workspaceService.currentWorkspaceId;
|
||||
// var workspace = Collections.get('workspaces').find(workspace_id);
|
||||
// // eslint-disable-next-line no-unused-vars
|
||||
// var group = Collections.get('groups').find(workspace?.group?.id || workspace.company_id);
|
||||
//
|
||||
// if (isNew) {
|
||||
// return (
|
||||
// <WorkspaceAppsCreator exit={this.props.exit} openApp={id => this.setState({ id: id })} />
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// var original_app = getApplication(props.appId);
|
||||
// console.log(original_app)
|
||||
|
||||
//
|
||||
var public_lock = false; // TODO
|
||||
// var public_lock = original_app.public || original_app.is_available_to_public;
|
||||
//
|
||||
return (
|
||||
(application?.id && (
|
||||
<div>
|
||||
<div className="_fade_in _app_editor">
|
||||
<div className="title">
|
||||
<Icon
|
||||
className="app_icon"
|
||||
style={{ fontSize: '24px' }}
|
||||
type={WorkspacesApp.getAppIcon(application)}
|
||||
/>
|
||||
{application.name}
|
||||
</div>
|
||||
|
||||
<div className="smalltext" style={{ opacity: 1 }}>
|
||||
<Emojione type={':exploding_head:'} /> If you do not know how to fill these, go to{' '}
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a href="https://doc.tdrive.app" target="_blank" rel="noreferrer">
|
||||
the Tdrive API documentation
|
||||
</a>{' '}
|
||||
!
|
||||
</div>
|
||||
|
||||
{public_lock && (
|
||||
<div className="smalltext" style={{ opacity: 1 }}>
|
||||
<Emojione type={':warning:'} />{' '}
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.alert_published_app',
|
||||
[],
|
||||
'Votre application est publiée, vous ne pouvez pas la modifier.',
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t('scenes.app.popup.appsparameters.pages._app_identity')}
|
||||
description={Languages.t('scenes.app.popup.appsparameters.pages.modify_public_data')}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none' }}>
|
||||
<div className="label for_input">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.app_name_label')}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.amazing_app_name',
|
||||
)}
|
||||
type="text"
|
||||
disabled={loading || public_lock}
|
||||
value={application.identity.name}
|
||||
onChange={ev => {
|
||||
application.identity.name = ev.target.value;
|
||||
setApplication(application);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.grp_section_surname_label')}
|
||||
</div>
|
||||
<div className="smalltext" style={{ paddingBottom: 0 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.string_information')}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={'my_amazing_app'}
|
||||
type="text"
|
||||
disabled={loading || public_lock}
|
||||
value={application?.identity?.code}
|
||||
onChange={ev => {
|
||||
application.identity.code = convertToSimpleName(ev.target.value);
|
||||
setApplication(application);
|
||||
}}
|
||||
/>
|
||||
{error && (
|
||||
<div className="smalltext error" style={{ opacity: 1 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.grp_section_name-error')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.icon', 'icon')}
|
||||
</div>
|
||||
<div className="smalltext" style={{ paddingBottom: 0 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.optimal_format')}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={'https://domain.com/my_icon.png'}
|
||||
type="text"
|
||||
disabled={loading || public_lock}
|
||||
value={application.identity.icon}
|
||||
onChange={ev => {
|
||||
application.identity.icon = ev.target.value;
|
||||
setApplication(application);
|
||||
}}
|
||||
/>
|
||||
{/*
|
||||
<TagPicker
|
||||
disabled={loading || public_lock}
|
||||
canCreate={false}
|
||||
inline
|
||||
data={application.available_categories.map(item => {
|
||||
return { name: item, id: item };
|
||||
})}
|
||||
value={(application.categories || []).map(item => {
|
||||
return { name: item, id: item };
|
||||
})}
|
||||
onChange={values => {
|
||||
application.categories = values.map(item => item.name);
|
||||
}}
|
||||
/>
|
||||
*/}
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.website_label')}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={'https://domain.com/'}
|
||||
type="text"
|
||||
disabled={loading || public_lock}
|
||||
value={application.identity.website}
|
||||
onChange={ev => {
|
||||
application.identity.website = ev.target.value;
|
||||
setApplication(application);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.description_label',
|
||||
[],
|
||||
'Description',
|
||||
)}
|
||||
</div>
|
||||
<AutoHeight
|
||||
placeholder={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.description_label',
|
||||
[],
|
||||
'Description',
|
||||
)}
|
||||
disabled={loading || public_lock}
|
||||
onChange={evt => {
|
||||
application.identity.description = evt.target.value;
|
||||
setApplication(application);
|
||||
}}
|
||||
>
|
||||
{application.identity.description}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.api_parameters_label',
|
||||
[],
|
||||
"Paramètres de l'API",
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.api_data_description',
|
||||
[],
|
||||
"Données utiles pour l'API Tdrive.",
|
||||
)}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 10 }}>
|
||||
<div className="label for_input">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.public_login_label',
|
||||
[],
|
||||
'Identifiant public',
|
||||
)}
|
||||
</div>
|
||||
<InputWithClipBoard disabled={true} value={application.api_id} />
|
||||
|
||||
<div className="label for_input">
|
||||
Private key
|
||||
<a
|
||||
style={{ float: 'inherit' }}
|
||||
onClick={() => {
|
||||
clearTimeout(api_key_timeout || '');
|
||||
setShowApiKey(true);
|
||||
api_key_timeout = setTimeout(() => {
|
||||
setShowApiKey(false);
|
||||
}, 5000);
|
||||
}}
|
||||
>
|
||||
Show
|
||||
</a>
|
||||
</div>
|
||||
<InputWithClipBoard
|
||||
disabled={true}
|
||||
hideBtn={!showApiKey}
|
||||
value={showApiKey ? application.api_key : '••••••••••••••'}
|
||||
/>
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.url_reception_events_label',
|
||||
[],
|
||||
'Url de reception des évènements',
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={'https://domain.com/api/tdrive/events'}
|
||||
type="text"
|
||||
disabled={loading}
|
||||
value={application.api_event_url}
|
||||
onChange={ev => {
|
||||
application.api_event_url = ev.target.value;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="label for_input">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.autorised_ip_adresses_label',
|
||||
[],
|
||||
'Adresses IP autorisée',
|
||||
)}
|
||||
</div>
|
||||
<div className="smalltext" style={{ paddingBottom: 0 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.filter_information',
|
||||
[],
|
||||
"Ce filtre permet de limiter l'utilisation de votre clé API aux serveurs de votre connecteur uniquement.",
|
||||
)}
|
||||
<br />
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.allowed_ip_adresses_method',
|
||||
[],
|
||||
'Utilisez * en développement pour autorisé toutes les adresses IP.',
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
placeholder={'ip1, ip2, ip3'}
|
||||
type="text"
|
||||
disabled={loading}
|
||||
value={application.api_allowed_ips}
|
||||
onChange={ev => {
|
||||
application.api_allowed_ips = ev.target.value;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.displayed_parameters_label',
|
||||
[],
|
||||
"Paramètres d'affichage",
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.dispalyed_parameters_description',
|
||||
[],
|
||||
"Permet de définir l'endroit où votre application sera visible.",
|
||||
)}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 0 }}>
|
||||
<div className="label for_input">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.configuration_label',
|
||||
[],
|
||||
'Configuration',
|
||||
)}
|
||||
</div>
|
||||
<div className="smalltext" style={{ paddingBottom: 0, opacity: 1 }}>
|
||||
<Emojione type={':information_source:'} />{' '}
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.json_configuration_information',
|
||||
[],
|
||||
'Rendez-vous sur la documentation afin de générer votre configuration au format JSON.',
|
||||
)}
|
||||
</div>
|
||||
<AutoHeight
|
||||
placeholder={'{JSON}'}
|
||||
className={displayJsonError ? 'error' : ''}
|
||||
disabled={loading || public_lock}
|
||||
onChange={evt => {
|
||||
try {
|
||||
application.display = JSON.parse(evt.target.value);
|
||||
setDisplayJsonError(false);
|
||||
} catch (e) {
|
||||
application.display = evt.target.value;
|
||||
setDisplayJsonError(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{typeof application.display == 'object'
|
||||
? JSON.stringify(application.display, null, 2)
|
||||
: application.display}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
{/*
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.app_privileges_label',
|
||||
[],
|
||||
"Privilèges de l'application",
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.app_privileges_information',
|
||||
[],
|
||||
'Permet de définir ce que votre application peut modifier et lire.',
|
||||
)}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 0 }}>
|
||||
<div className="label for_input" style={{ marginBottom: 5 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.write_privileges_label',
|
||||
[],
|
||||
'Privilèges en écriture',
|
||||
)}
|
||||
</div>
|
||||
<InlineTagPicker
|
||||
available={application.available_capabilities}
|
||||
value={application.capabilities}
|
||||
onChange={items => {
|
||||
application.capabilities = items;
|
||||
}}
|
||||
/>
|
||||
<div className="label for_input" style={{ marginBottom: 5 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.read_privileges_label',
|
||||
[],
|
||||
'Privilèges en lecture',
|
||||
)}
|
||||
</div>
|
||||
<InlineTagPicker
|
||||
available={application.available_privileges}
|
||||
value={application.privileges}
|
||||
onChange={items => {
|
||||
application.privileges = items;
|
||||
}}
|
||||
/>
|
||||
<div className="label for_input" style={{ marginBottom: 5 }}>
|
||||
Hooks
|
||||
</div>
|
||||
<InlineTagPicker
|
||||
available={application.available_hooks}
|
||||
value={application.hooks}
|
||||
onChange={items => {
|
||||
application.hooks = items;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.publication_label',
|
||||
[],
|
||||
'Publication',
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.publication_description',
|
||||
[],
|
||||
'Déterminez la visibilité de votre application.',
|
||||
)}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 10 }}>
|
||||
<div className="smalltext" style={{ paddingTop: 0 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.parameters_form_small_text',
|
||||
[],
|
||||
"L'application non publiée ne fonctionnera que dans votre entreprise, si vous souhaitez la rendre disponible à tous les utilisateurs de Tdrive activez cette option.",
|
||||
)}
|
||||
</div>
|
||||
|
||||
{originalApplication.public && !originalApplication.is_available_to_public && (
|
||||
<div className="smalltext" style={{ paddingTop: 0, opacity: 1 }}>
|
||||
<Emojione type={':information_source:'} />{' '}
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.available_publication_alert',
|
||||
[],
|
||||
'La publication de votre application est actuellement en attente de validation par les équipes de Tdrive.',
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Switch
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.publish_app_label',
|
||||
[],
|
||||
"Publier l'application",
|
||||
)}
|
||||
checked={application.public}
|
||||
onChange={value => {
|
||||
application.public = value;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
<Attribute
|
||||
label={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.danger_zone_label',
|
||||
[],
|
||||
'Zone dangereuse',
|
||||
)}
|
||||
description={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.danger_zone_description',
|
||||
[],
|
||||
"Supprimez l'application.",
|
||||
)}
|
||||
>
|
||||
<div className="parameters_form" style={{ maxWidth: 'none', paddingTop: 10 }}>
|
||||
<div className="smalltext" style={{ padding: 0 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.danger_zone_small_text',
|
||||
[],
|
||||
'Vous ne pouvez pas supprimer cette application si elle est publiée.',
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ButtonWithTimeout
|
||||
className="small buttonValidation danger"
|
||||
value={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.remove_app_button',
|
||||
[],
|
||||
"Supprimer l'application",
|
||||
)}
|
||||
disabled={loading || public_lock}
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
removeApp();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Attribute>
|
||||
</div>
|
||||
|
||||
<div className="group_section">
|
||||
{errorCode && (
|
||||
<div className="smalltext error" style={{ opacity: 1 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.error_app_code_message',
|
||||
[],
|
||||
'Le nom simplifié de votre application est déjà utilisé par une autre application, veuillez le changer.',
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="smalltext error" style={{ opacity: 1 }}>
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.error_app_update_message',
|
||||
[],
|
||||
"Une erreur s'est produite lors de la mise à jour de l'application.",
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ButtonWithTimeout
|
||||
className="small buttonGoBack secondary"
|
||||
value={Languages.t('general.back', [], 'Retour')}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
props.exit();
|
||||
}}
|
||||
/>
|
||||
|
||||
<ButtonWithTimeout
|
||||
className="small buttonValidation"
|
||||
value={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.update_button',
|
||||
[],
|
||||
'Mettre à jour',
|
||||
)}
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
onClick={() => {this.saveApp();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
<pre>{JSON.stringify(application, null, 2)}</pre>
|
||||
</div>
|
||||
)) || <div>Loading</div>
|
||||
);
|
||||
};
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import InputWithClipBoard from 'components/input-with-clip-board/input-with-clip-board.jsx';
|
||||
import workspaceService from '@deprecated/workspaces/workspaces.jsx';
|
||||
import userService from '@features/users/services/current-user-service';
|
||||
import './pages.scss';
|
||||
|
||||
export default class WorkspaceAppsInformations extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="apps">
|
||||
<div className="title">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.title_informations',
|
||||
'Basic informations',
|
||||
)}
|
||||
</div>
|
||||
<div className="group_section">
|
||||
<div className="subtitle">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.subtitle_informations',
|
||||
'Informations related to applications management',
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.smalltext_user_id',
|
||||
'Current user id',
|
||||
)}
|
||||
</div>
|
||||
<InputWithClipBoard disabled={true} value={userService.getCurrentUserId()} />
|
||||
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.smalltext_workspace_id',
|
||||
'Current workspace id',
|
||||
)}
|
||||
</div>
|
||||
<InputWithClipBoard disabled={true} value={workspaceService.currentWorkspaceId} />
|
||||
|
||||
<div className="smalltext">
|
||||
{Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.smalltext_group_id',
|
||||
'Current group id',
|
||||
)}
|
||||
</div>
|
||||
<InputWithClipBoard disabled={true} value={workspaceService.currentGroupId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Input, Row, Typography, Button, Col, Checkbox } from 'antd';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import { useCurrentCompany } from '@features/companies/hooks/use-companies';
|
||||
import ApplicationsAPIClient from '@features/applications/api/applications-api-client';
|
||||
import {
|
||||
buildDefaultApplicationPayload,
|
||||
getApplicationIdentityCode,
|
||||
} from '@features/applications/utils/application';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import ObjectModal from '@components/object-modal/object-modal';
|
||||
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
|
||||
|
||||
import './pages.scss';
|
||||
import ApplicationEditor from './application-editor';
|
||||
|
||||
type ErrorObjectStateType = { statusCode: number; message: string; error: string };
|
||||
|
||||
const ErrorComponent = ({ error }: { error: ErrorObjectStateType }) => (
|
||||
<Text type="danger">
|
||||
{/* TODO:translation here */}
|
||||
Error {error.statusCode}, {error.error} - {error.message}
|
||||
</Text>
|
||||
);
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
const ApplicationCreator = () => {
|
||||
const { company } = useCurrentCompany();
|
||||
const [appName, setAppName] = useState<string | undefined>(undefined);
|
||||
const [appDescription, setAppDescription] = useState<string | undefined>(undefined);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [checked, setChecked] = useState<boolean>(false);
|
||||
const [disabledBtn, setDisabledBtn] = useState<boolean>(true);
|
||||
const [error, setError] = useState<ErrorObjectStateType | undefined>(undefined);
|
||||
|
||||
const { add: addOneCompanyApplication } = useCompanyApplications(company.id);
|
||||
|
||||
const handleDisabledBtn = (inputValue?: string) => {
|
||||
if (inputValue?.length) {
|
||||
setDisabledBtn(false);
|
||||
}
|
||||
|
||||
if (!inputValue?.length) {
|
||||
setDisabledBtn(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleDisabledBtn(appName);
|
||||
}, [appName]);
|
||||
|
||||
const clearFields = () => {
|
||||
setAppName(undefined);
|
||||
setAppDescription(undefined);
|
||||
setDisabledBtn(true);
|
||||
};
|
||||
|
||||
const createApp = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const applicationPayload = buildDefaultApplicationPayload();
|
||||
|
||||
if (company.id) {
|
||||
applicationPayload.company_id = company.id;
|
||||
}
|
||||
|
||||
if (appName) {
|
||||
applicationPayload.identity.name = appName;
|
||||
applicationPayload.identity.code = getApplicationIdentityCode(appName);
|
||||
}
|
||||
|
||||
if (appDescription) {
|
||||
applicationPayload.identity.description = appDescription;
|
||||
}
|
||||
|
||||
try {
|
||||
const application = await ApplicationsAPIClient.createCustomApplication(applicationPayload);
|
||||
|
||||
if (application) {
|
||||
addOneCompanyApplication(application.id)
|
||||
.then(() => ModalManager.closeAll)
|
||||
.finally(() => {
|
||||
ModalManager.open(
|
||||
<ApplicationEditor application={application} companyId={company.id} />,
|
||||
{
|
||||
position: 'center',
|
||||
size: { width: 700 },
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
// FIXME: This is no longer an application but an error
|
||||
const err = application as unknown as ErrorObjectStateType;
|
||||
|
||||
if (err?.message?.length) {
|
||||
setError(err);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
clearFields();
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ObjectModal
|
||||
closable
|
||||
title={
|
||||
<Row align="middle" justify="start">
|
||||
<Title level={3} style={{ margin: 0 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.application_creator.title')}
|
||||
</Title>
|
||||
</Row>
|
||||
}
|
||||
footer={
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={loading || disabledBtn || !checked}
|
||||
loading={loading}
|
||||
onClick={() => AlertManager.confirm(createApp)}
|
||||
style={{
|
||||
width: 'auto',
|
||||
float: 'right',
|
||||
}}
|
||||
>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.create_my_app')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div style={{ padding: '0 24px' }}>
|
||||
{error && (
|
||||
<Row className="small-bottom-margin">
|
||||
<ErrorComponent error={error} />
|
||||
</Row>
|
||||
)}
|
||||
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.integrations_parameters.applications_table.name')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={Languages.t('scenes.app.popup.appsparameters.pages.amazing_app_name')}
|
||||
type="text"
|
||||
disabled={loading}
|
||||
value={appName}
|
||||
onChange={e => setAppName(e.target.value)}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row wrap={false}>
|
||||
<Col className="small-right-margin">
|
||||
<Title level={5}>
|
||||
{Languages.t(
|
||||
'scenes.app.integrations_parameters.company_application_popup.tab_info.descriptions.description_item',
|
||||
)}
|
||||
</Title>
|
||||
</Col>
|
||||
<Col>
|
||||
<Text type="secondary">Optionnal</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input.TextArea
|
||||
placeholder={Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.amazing_app_description',
|
||||
)}
|
||||
disabled={loading}
|
||||
value={appDescription}
|
||||
onChange={e => setAppDescription(e.target.value)}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row className="small-bottom-margin">
|
||||
<Text type="secondary">
|
||||
<Checkbox
|
||||
className="small-right-margin"
|
||||
checked={checked}
|
||||
onChange={() => setChecked(!checked)}
|
||||
>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.application_creator.checkbox')}
|
||||
</Checkbox>
|
||||
</Text>
|
||||
</Row>
|
||||
</div>
|
||||
</ObjectModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplicationCreator;
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
import React from 'react';
|
||||
import { capitalize } from 'lodash';
|
||||
import { Row, Typography } from 'antd';
|
||||
|
||||
import { TagPicker } from './tag-picker';
|
||||
import { defaultApplicationScopes } from '../default-scopes';
|
||||
import {
|
||||
Application,
|
||||
ApplicationAccess as ApplicationAccessType,
|
||||
ApplicationScopes,
|
||||
} from '@features/applications/types/application';
|
||||
import { ApplicationHelp } from './application-help';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
export const ApplicationAccess = ({
|
||||
application,
|
||||
onChangeApplicationAccess,
|
||||
}: {
|
||||
application: Application;
|
||||
onChangeApplicationAccess?: (api: Application['access']) => void;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Title level={3}>{Languages.t('tdrive.application.access.title')}</Title>
|
||||
</Row>
|
||||
<Row className="small-bottom-margin">
|
||||
<ApplicationHelp />
|
||||
</Row>
|
||||
{Object.keys(application.access).map((key: string) => {
|
||||
const currentScopes: ApplicationScopes[] = (application.access as ApplicationAccessType)[
|
||||
key as keyof ApplicationAccessType
|
||||
];
|
||||
return (
|
||||
<div key={key === 'hooks' ? 'listened events' : key} className="bottom-margin">
|
||||
<Row>
|
||||
<Title level={5}>{capitalize(key === 'hooks' ? 'listened events' : key)}</Title>
|
||||
</Row>
|
||||
{defaultApplicationScopes ? (
|
||||
<TagPicker
|
||||
defaulActiveTags={currentScopes}
|
||||
onChange={tags =>
|
||||
onChangeApplicationAccess &&
|
||||
onChangeApplicationAccess({ ...application.access, [key]: tags })
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Text type="secondary">
|
||||
{Languages.t('tdrive.application.access.no_default_scopes_available', [key])}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Input, Row, Typography } from 'antd';
|
||||
|
||||
import { Application } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
const { Text, Link, Title } = Typography;
|
||||
export const ApplicationAPI = ({
|
||||
application,
|
||||
onChangeApplicationAPI,
|
||||
}: {
|
||||
application: Application;
|
||||
onChangeApplicationAPI?: (api: Application['api']) => void;
|
||||
}) => {
|
||||
const [showApiPrivateKey, setShowApiPrivateKey] = useState<boolean>(false);
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.public_login_label')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Text copyable>{application.id}</Text>
|
||||
</Row>
|
||||
<Row align="middle" className="small-bottom-margin">
|
||||
<Title level={5} className="small-right-margin" style={{ marginBottom: 0 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.app.private_key')}
|
||||
</Title>{' '}
|
||||
<Link onClick={() => setShowApiPrivateKey(!showApiPrivateKey)}>
|
||||
{showApiPrivateKey
|
||||
? 'Hide'
|
||||
: Languages.t('scenes.app.popup.appsparameters.pages.show_button')}
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Text
|
||||
copyable={{
|
||||
text: application.api?.private_key,
|
||||
onCopy: () => application.api?.private_key,
|
||||
}}
|
||||
>
|
||||
{showApiPrivateKey
|
||||
? application.api?.private_key
|
||||
: '•••••••••••••••••••••••••••••••••••••'}
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.url_reception_events_label')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={'https://domain.com/api/tdrive/events'}
|
||||
defaultValue={application.api?.hooks_url}
|
||||
onChange={e => {
|
||||
e.target.value &&
|
||||
onChangeApplicationAPI &&
|
||||
onChangeApplicationAPI({ ...application.api, hooks_url: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.autorised_ip_adresses_label')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row>
|
||||
<Row className="small-bottom-margin">
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.filter_information')}
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.allowed_ip_adresses_method')}
|
||||
</Text>
|
||||
</Row>
|
||||
<Input
|
||||
defaultValue={application.api?.allowed_ips}
|
||||
placeholder={'ip1, ip2, ip3'}
|
||||
onChange={e => {
|
||||
e.target.value &&
|
||||
onChangeApplicationAPI &&
|
||||
onChangeApplicationAPI({ ...application.api, allowed_ips: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Input, Typography } from 'antd';
|
||||
|
||||
import { Application } from '@features/applications/types/application';
|
||||
|
||||
const { Text } = Typography;
|
||||
export const ApplicationDisplay = ({
|
||||
application,
|
||||
onChangeApplicationDisplay,
|
||||
}: {
|
||||
application: Application;
|
||||
onChangeApplicationDisplay?: (api: Application['display']) => void;
|
||||
}) => {
|
||||
const [error, setError] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="small-bottom-margin" style={{ height: 18 }}>
|
||||
{/* TODO: Tranlation here */}
|
||||
{error && <Text type="danger">Bad format</Text>}
|
||||
</div>
|
||||
<Input.TextArea
|
||||
status={error ? 'error' : ''}
|
||||
defaultValue={JSON.stringify(application.display, null, 2)}
|
||||
onChange={e => {
|
||||
try {
|
||||
const display = JSON.parse(e.target.value) as Application['display'];
|
||||
|
||||
if (display && onChangeApplicationDisplay) {
|
||||
setError(false);
|
||||
|
||||
onChangeApplicationDisplay(display);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(true);
|
||||
}
|
||||
}}
|
||||
style={{ minHeight: 400 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Row, Typography } from 'antd';
|
||||
|
||||
import Emojione from '@components/emojione/emojione';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
const { Text, Link } = Typography;
|
||||
export const ApplicationHelp = () => (
|
||||
<Row align="middle" justify="start" className="bottom-margin">
|
||||
<Text type="secondary">
|
||||
<Emojione type=":exploding_head:" />{' '}
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.application_editor.help_text')}
|
||||
<Link onClick={() => window.open('https://doc.tdrive.app', 'blank')}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.application_editor.help_link')}
|
||||
</Link>
|
||||
</Text>
|
||||
</Row>
|
||||
);
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Input, Row, Typography } from 'antd';
|
||||
|
||||
import { Application } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
export const ApplicationIdentity = ({
|
||||
application,
|
||||
onChangeApplicationIdentity,
|
||||
}: {
|
||||
application: Application;
|
||||
onChangeApplicationIdentity?: (identity: Application['identity']) => void;
|
||||
}) => (
|
||||
<>
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.integrations_parameters.applications_table.name')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={Languages.t('scenes.app.popup.appsparameters.pages.amazing_app_name')}
|
||||
defaultValue={application.identity.name}
|
||||
onChange={e =>
|
||||
onChangeApplicationIdentity &&
|
||||
onChangeApplicationIdentity({
|
||||
...application.identity,
|
||||
name: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>{Languages.t('components.richtexteditor.toolbar.code')}</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={'my_amazing_app'}
|
||||
defaultValue={application.identity.code}
|
||||
onChange={e => {
|
||||
onChangeApplicationIdentity &&
|
||||
onChangeApplicationIdentity({ ...application.identity, code: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row align="middle">
|
||||
<Title level={5} className="small-right-margin" style={{ marginBottom: 0 }}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.icon')}
|
||||
</Title>
|
||||
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.optimal_format')}
|
||||
</Text>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={'https://domain.com/my_icon.png'}
|
||||
defaultValue={application.identity.icon}
|
||||
onChange={e =>
|
||||
onChangeApplicationIdentity &&
|
||||
onChangeApplicationIdentity({
|
||||
...application.identity,
|
||||
icon: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>{Languages.t('scenes.app.popup.appsparameters.pages.website_label')}</Title>
|
||||
</Row>
|
||||
<Row className="bottom-margin">
|
||||
<Input
|
||||
placeholder={'https://domain.com/'}
|
||||
defaultValue={application.identity.website}
|
||||
onChange={e =>
|
||||
onChangeApplicationIdentity &&
|
||||
onChangeApplicationIdentity({
|
||||
...application.identity,
|
||||
website: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.description_label')}
|
||||
</Title>
|
||||
</Row>
|
||||
<Row>
|
||||
<Input.TextArea
|
||||
placeholder={Languages.t('scenes.app.popup.appsparameters.pages.description_label')}
|
||||
defaultValue={application.identity.description}
|
||||
rows={4}
|
||||
onChange={e =>
|
||||
onChangeApplicationIdentity &&
|
||||
onChangeApplicationIdentity({
|
||||
...application.identity,
|
||||
description: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Button, Row, Typography } from 'antd';
|
||||
import { Application } from '@features/applications/types/application';
|
||||
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import Emojione from '@components/emojione/emojione';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
import ApplicationsAPIClient from '@features/applications/api/applications-api-client';
|
||||
import { ToasterService } from '@features/global/services/toaster-service';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
export const ApplicationPublication = ({ application }: { application: Application }) => {
|
||||
const removeApplication = async () => {
|
||||
const res = await ApplicationsAPIClient.delete(application.id);
|
||||
|
||||
if (res.status === 'success')
|
||||
ToasterService.success(
|
||||
Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.application_editor.application_removed',
|
||||
[application.identity.name],
|
||||
),
|
||||
);
|
||||
else {
|
||||
ToasterService.error(
|
||||
Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.application_editor.application_removed_error',
|
||||
[application.identity.name],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const publishApplication = async () => {
|
||||
const publication: Application['publication'] = {
|
||||
...application.publication,
|
||||
requested: application.publication.published ? false : true,
|
||||
};
|
||||
|
||||
const res = await ApplicationsAPIClient.save(application.id, { ...application, publication });
|
||||
|
||||
if (res)
|
||||
ToasterService.success(
|
||||
Languages.t(
|
||||
'scenes.app.popup.appsparameters.pages.application_editor.application_published',
|
||||
[res.publication.published ? 'published' : 'unpublished'],
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.publication_description')}
|
||||
</Title>
|
||||
</Row>
|
||||
|
||||
<Row className="bottom-margin">
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.parameters_form_small_text')}
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
{application.publication.published && application.publication.requested && (
|
||||
<Row>
|
||||
<Text className="smalltext" style={{ opacity: 1 }}>
|
||||
<Emojione type={':information_source:'} />{' '}
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.available_publication_alert')}
|
||||
</Text>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
<Row className="bottom-margin">
|
||||
<Button type="primary" onClick={() => AlertManager.confirm(() => publishApplication())}>
|
||||
{application.publication.published || application.publication.requested
|
||||
? 'Unpublish application'
|
||||
: Languages.t('scenes.app.popup.appsparameters.pages.publish_app_label')}
|
||||
</Button>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Title level={5}>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.danger_zone_label')}
|
||||
</Title>
|
||||
</Row>
|
||||
|
||||
<Row className="bottom-margin">
|
||||
<Text type="secondary">
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.danger_zone_small_text')}
|
||||
</Text>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ backgroundColor: 'var(--error)', color: 'var(--white)' }}
|
||||
onClick={() => AlertManager.confirm(removeApplication)}
|
||||
>
|
||||
{Languages.t('scenes.app.popup.appsparameters.pages.remove_app_button')}
|
||||
</Button>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Tag } from 'antd';
|
||||
|
||||
import { ApplicationScopes } from '@features/applications/types/application';
|
||||
import { defaultApplicationScopes } from '../default-scopes';
|
||||
|
||||
export const TagPicker = ({
|
||||
defaulActiveTags,
|
||||
onChange,
|
||||
}: {
|
||||
defaulActiveTags: ApplicationScopes[];
|
||||
onChange: (tags: ApplicationScopes[]) => void;
|
||||
}) => {
|
||||
const [activeTags, setActiveTags] = useState<ApplicationScopes[]>(defaulActiveTags);
|
||||
|
||||
const handleOnClickTag = (scope: ApplicationScopes) => {
|
||||
if (activeTags.includes(scope)) {
|
||||
setActiveTags(activeTags.filter(s => s !== scope));
|
||||
onChange(activeTags.filter(s => s !== scope));
|
||||
}
|
||||
|
||||
if (!activeTags.includes(scope)) {
|
||||
setActiveTags([...activeTags, scope]);
|
||||
onChange([...activeTags, scope]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{defaultApplicationScopes.map(scope => {
|
||||
return (
|
||||
<Tag
|
||||
key={scope}
|
||||
color={activeTags.includes(scope) ? 'var(--success)' : 'var(--grey-dark)'}
|
||||
onClick={() => handleOnClickTag(scope)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{scope}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { ApplicationScopes } from '@features/applications/types/application';
|
||||
|
||||
export const defaultApplicationScopes: ApplicationScopes[] = [
|
||||
'files',
|
||||
'applications',
|
||||
'workspaces',
|
||||
'users',
|
||||
'messages',
|
||||
'channels',
|
||||
];
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import { Tabs, Button, Typography, Col, Row, Divider } from 'antd';
|
||||
|
||||
import { Application } from '@features/applications/types/application';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
import AvatarComponent from '@components/avatar/avatar';
|
||||
import ObjectModal from '@components/object-modal/object-modal';
|
||||
import AlertManager from '@features/global/services/alert-manager-service';
|
||||
|
||||
import { ApplicationPublication } from './components/application-publication';
|
||||
import { ApplicationIdentity } from './components/application-identity';
|
||||
import { ApplicationAPI } from './components/application-api';
|
||||
import { ApplicationDisplay } from './components/application-display';
|
||||
import { ApplicationAccess } from './components/application-access';
|
||||
import ApplicationsAPIClient from '@features/applications/api/applications-api-client';
|
||||
import { useCompanyApplications } from '@features/applications/hooks/use-company-applications';
|
||||
import ModalManager from '@components/modal/modal-manager';
|
||||
import { ApplicationHelp } from './components/application-help';
|
||||
|
||||
import '../../../WorkspaceParameter/Pages/Applications/ApplicationsStyles.scss';
|
||||
|
||||
type PropsType = {
|
||||
application: Application;
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Title } = Typography;
|
||||
|
||||
const ApplicationEditor = ({ application, companyId }: PropsType) => {
|
||||
const [updatedApp, setUpdatedApp] = useState<Application>(application);
|
||||
const [isSaving, setIsSaving] = useState<boolean>(false);
|
||||
const { refresh } = useCompanyApplications(companyId);
|
||||
|
||||
const onSave = async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
const res = await ApplicationsAPIClient.save(application.id, updatedApp);
|
||||
|
||||
if (res) {
|
||||
refresh();
|
||||
|
||||
ModalManager.close();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
setIsSaving(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ObjectModal
|
||||
className="company-application-popup"
|
||||
headerStyle={{ height: 32, marginTop: 24 }}
|
||||
footerDividerStyle={{ marginTop: 0 }}
|
||||
closable
|
||||
style={{ minHeight: 681, minWidth: 700 }}
|
||||
title={
|
||||
<Row align="middle" justify="start">
|
||||
<Col>
|
||||
<AvatarComponent
|
||||
url={application.identity.icon}
|
||||
fallback={`${process.env.PUBLIC_URL}/public/img/hexagon.png`}
|
||||
/>
|
||||
</Col>
|
||||
<Col className="small-x-margin">
|
||||
<Title level={3} style={{ margin: 0 }}>
|
||||
{application.identity.name}
|
||||
</Title>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
footer={
|
||||
<Row wrap={false}>
|
||||
<Button
|
||||
type="ghost"
|
||||
loading={isSaving}
|
||||
disabled={isEqual(application, updatedApp)}
|
||||
onClick={() => AlertManager.confirm(onSave)}
|
||||
>
|
||||
{Languages.t('general.save')}
|
||||
</Button>
|
||||
</Row>
|
||||
}
|
||||
>
|
||||
<Divider style={{ margin: '16px 0 0 0' }} />
|
||||
|
||||
<Tabs defaultActiveKey="1" tabPosition="left">
|
||||
<TabPane tab={Languages.t('scenes.apps.account.identity')} key={1}>
|
||||
<ApplicationIdentity
|
||||
application={application}
|
||||
onChangeApplicationIdentity={identity => setUpdatedApp({ ...updatedApp, identity })}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="API" key={2}>
|
||||
<ApplicationAPI
|
||||
application={application}
|
||||
onChangeApplicationAPI={api => setUpdatedApp({ ...updatedApp, api })}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab={Languages.t('scenes.app.popup.workspaceparameter.pages.show_button')} key={3}>
|
||||
<ApplicationHelp />
|
||||
<ApplicationDisplay
|
||||
application={application}
|
||||
onChangeApplicationDisplay={display => setUpdatedApp({ ...updatedApp, display })}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="Access" key={4}>
|
||||
<ApplicationAccess
|
||||
application={application}
|
||||
onChangeApplicationAccess={access => setUpdatedApp({ ...updatedApp, access })}
|
||||
/>
|
||||
</TabPane>
|
||||
|
||||
<TabPane
|
||||
tab={Languages.t('scenes.app.popup.appsparameters.pages.publication_label')}
|
||||
key={5}
|
||||
>
|
||||
<ApplicationPublication application={application} />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</ObjectModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplicationEditor;
|
||||
@@ -1,197 +0,0 @@
|
||||
.appsParameters {
|
||||
.image {
|
||||
background-color: rgba(0, 0, 50, 0.25);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: #000000aa;
|
||||
font-size: 40px;
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
border-radius: var(--border-radius-large);
|
||||
margin: auto;
|
||||
margin-bottom: 8px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.smalltext {
|
||||
opacity: 0.5;
|
||||
padding: 10px 0px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
.has_image {
|
||||
font-size: 0px;
|
||||
}
|
||||
.action {
|
||||
text-align: right;
|
||||
svg {
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
}
|
||||
svg:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.editLevel {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.close svg {
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
.confirm {
|
||||
width: 70%;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
.addMemberBtn {
|
||||
text-align: right;
|
||||
}
|
||||
.addMember {
|
||||
.buttonWithTimeout_container {
|
||||
margin: auto;
|
||||
}
|
||||
.group_section {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.user_image {
|
||||
vertical-align: top;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
margin-top: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.apps,
|
||||
.app_editor,
|
||||
.application_search {
|
||||
.app_icon {
|
||||
vertical-align: top;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
margin-top: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.application {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 45px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--border-radius-large);
|
||||
|
||||
&:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.market_app_image {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
margin-right: 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.market_app_content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.market_app_title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.market_app_description {
|
||||
opacity: 0.5;
|
||||
font-size: 13px;
|
||||
margin-top: 3px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.market_app_buttons {
|
||||
input {
|
||||
margin: 4px 0;
|
||||
min-width: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app_editor {
|
||||
//box-shadow: 0 0 15px rgba(0, 0, 0, 0.3);
|
||||
margin: 15px;
|
||||
margin-top: 0px;
|
||||
border-radius: var(--border-radius-large);
|
||||
padding: 15px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.application_search,
|
||||
.app_editor {
|
||||
.app_icon {
|
||||
vertical-align: initial;
|
||||
margin-bottom: -4px;
|
||||
margin-right: 6px;
|
||||
margin-left: 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.buttonWithTimeout_container {
|
||||
text-align: right;
|
||||
|
||||
input {
|
||||
width: auto;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonGoBack {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.label.for_input {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.error-button {
|
||||
color: var(--error);
|
||||
border: 1px solid var(--error);
|
||||
&:hover {
|
||||
color: var(--error);
|
||||
|
||||
border: 1px solid var(--error);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-switch:not(.ant-switch-checked) {
|
||||
background-color: var(--grey-dark) !important;
|
||||
}
|
||||
Reference in New Issue
Block a user