Continued cleaning

This commit is contained in:
Romaric Mourgues
2023-04-18 11:55:44 +02:00
parent 0ff1cc97d4
commit 3e6afcf14b
39 changed files with 4 additions and 5244 deletions
@@ -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');
},
},
@@ -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>
);
}
}
@@ -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;
}
}
}
@@ -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;
}
@@ -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>
</>
);
};
@@ -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>
);
};
@@ -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>
);
};
@@ -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>
);
}
}
@@ -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>
</>
);
};
@@ -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;
}
}
@@ -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>
) : (
<></>
);
};
@@ -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;
@@ -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;
}
}
@@ -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>
);
};
@@ -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 <></>;
};
@@ -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>
</>
);
};
@@ -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>
);
}
}
@@ -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>
);
}
}
@@ -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;
}
}
}
@@ -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>
);
}
}
@@ -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>
);
};
@@ -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>
);
}
}
@@ -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;
@@ -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>
);
})}
</>
);
};
@@ -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>
</>
);
};
@@ -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 }}
/>
</>
);
};
@@ -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>
);
@@ -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>
</>
);
@@ -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>
</>
);
};
@@ -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>
);
})}
</>
);
};
@@ -1,10 +0,0 @@
import { ApplicationScopes } from '@features/applications/types/application';
export const defaultApplicationScopes: ApplicationScopes[] = [
'files',
'applications',
'workspaces',
'users',
'messages',
'channels',
];
@@ -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;
}