mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
enabled app sharing before release of app (#10483)
* enabled app sharing before release of app * removed tooltip and added css on copy icon * made share button behaviour consistent to preview * added conditional rendering for check box in the modal * added empty state component * removed unnecessary code * fixed alignment in both versions * added css for empty state component * fixed version released modal * added tooltip css fixes pending * fixed alignment using custom css * removed share button disable dependencies * fixed cursor pointer issue in share button * changed copywriting message * removed unnecessary methods
This commit is contained in:
parent
b49dbf9b1c
commit
e1be2df4bc
3 changed files with 242 additions and 170 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { appService, appsService, authenticationService } from '@/_services';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
|
@ -15,6 +15,7 @@ import { ToolTip } from '@/_components/ToolTip';
|
|||
import { TOOLTIP_MESSAGES } from '@/_helpers/constants';
|
||||
import { useAppDataStore } from '@/_stores/appDataStore';
|
||||
import { retrieveWhiteLabelText } from '@white-label/whiteLabelling';
|
||||
import InfoIcon from '@assets/images/icons/info.svg';
|
||||
|
||||
class ManageAppUsersComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -32,6 +33,7 @@ class ManageAppUsersComponent extends React.Component {
|
|||
value: null,
|
||||
error: '',
|
||||
},
|
||||
isHovered: false,
|
||||
isSlugUpdated: false,
|
||||
};
|
||||
}
|
||||
|
|
@ -85,7 +87,6 @@ class ManageAppUsersComponent extends React.Component {
|
|||
toast.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
toggleAppVisibility = () => {
|
||||
const newState = !this.props.isPublic;
|
||||
this.setState({
|
||||
|
|
@ -170,7 +171,13 @@ class ManageAppUsersComponent extends React.Component {
|
|||
});
|
||||
}
|
||||
};
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ isHovered: true });
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ isHovered: false });
|
||||
};
|
||||
render() {
|
||||
const { appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state;
|
||||
|
||||
|
|
@ -178,66 +185,104 @@ class ManageAppUsersComponent extends React.Component {
|
|||
const shareableLink = appLink + (this.props.slug || appId);
|
||||
const slugButtonClass = !_.isEmpty(newSlug.error) ? 'is-invalid' : 'is-valid';
|
||||
const embeddableLink = `<iframe width="560" height="315" src="${appLink}${this.props.slug}" title="${this.whiteLabelText} app - ${this.props.slug}" frameborder="0" allowfullscreen></iframe>`;
|
||||
const shouldWeDisableShareModal = !this.props.isVersionReleased;
|
||||
const { isHovered } = this.state.isHovered;
|
||||
|
||||
return (
|
||||
<ToolTip
|
||||
message={TOOLTIP_MESSAGES.SHARE_URL_UNAVAILABLE}
|
||||
placement={!this.props.isVersionReleased ? 'bottom' : 'left'}
|
||||
show={shouldWeDisableShareModal}
|
||||
>
|
||||
<div
|
||||
title={!shouldWeDisableShareModal ? 'Share' : ''}
|
||||
className="manage-app-users editor-header-icon tj-secondary-btn"
|
||||
data-cy="share-button-link"
|
||||
<div title={'Share'} className="manage-app-users" data-cy="share-button-link">
|
||||
<span
|
||||
className="manage-app-users tj-secondary-btn editor-header-icon cursor-pointer"
|
||||
onClick={() => {
|
||||
this.validateThePreExistingSlugs();
|
||||
this.setState({ showModal: true });
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={cx('d-flex', {
|
||||
'share-disabled': shouldWeDisableShareModal,
|
||||
'share-disabled': false,
|
||||
})}
|
||||
onClick={() => {
|
||||
this.validateThePreExistingSlugs();
|
||||
!shouldWeDisableShareModal && this.setState({ showModal: true });
|
||||
}}
|
||||
>
|
||||
<SolidIcon name="share" width="14" className="cursor-pointer" fill="#3E63DD" />
|
||||
</span>
|
||||
<Modal
|
||||
show={this.state.showModal}
|
||||
size="lg"
|
||||
backdrop="static"
|
||||
centered={true}
|
||||
keyboard={true}
|
||||
animation={false}
|
||||
onEscapeKeyDown={this.hideModal}
|
||||
className={`app-sharing-modal animation-fade ${this.props.darkMode ? 'dark-theme' : ''}`}
|
||||
contentClassName={this.props.darkMode ? 'dark-theme' : ''}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title data-cy="modal-header">{this.props.t('editor.share', 'Share')}</Modal.Title>
|
||||
<span onClick={this.hideModal} data-cy="modal-close-button">
|
||||
<SolidIcon name="remove" className="cursor-pointer" aria-label="Close" />
|
||||
</span>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{
|
||||
<div class="shareable-link-container">
|
||||
<div className="make-public mb-3">
|
||||
<div className="form-check form-switch d-flex align-items-center">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
onClick={this.toggleAppVisibility}
|
||||
checked={this?.props?.isPublic}
|
||||
disabled={this.state.ischangingVisibility}
|
||||
data-cy="make-public-app-toggle"
|
||||
/>
|
||||
<span className="form-check-label field-name" data-cy="make-public-app-label">
|
||||
{this.props.t('editor.shareModal.makeApplicationPublic', 'Make application public')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<Modal
|
||||
show={this.state.showModal}
|
||||
size="lg"
|
||||
backdrop="static"
|
||||
centered={true}
|
||||
keyboard={true}
|
||||
animation={false}
|
||||
onEscapeKeyDown={this.hideModal}
|
||||
className={`app-sharing-modal animation-fade ${this.props.darkMode ? 'dark-theme' : ''}`}
|
||||
contentClassName={this.props.darkMode ? 'dark-theme' : ''}
|
||||
>
|
||||
<Modal.Header>
|
||||
<Modal.Title data-cy="modal-header">{this.props.t('editor.share', 'Share')}</Modal.Title>
|
||||
<span onClick={this.hideModal} data-cy="modal-close-button">
|
||||
<SolidIcon name="remove" className="cursor-pointer" aria-label="Close" />
|
||||
</span>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{
|
||||
<div class="shareable-link-container">
|
||||
<div className="make-public mb-3">
|
||||
<div className="form-check form-switch d-flex align-items-center">
|
||||
{this.props.isVersionReleased ? (
|
||||
<div>
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
onClick={this.toggleAppVisibility}
|
||||
checked={this?.props?.isPublic}
|
||||
disabled={this.state.ischangingVisibility}
|
||||
data-cy="make-public-app-toggle"
|
||||
/>
|
||||
<span className="form-check-label field-name" data-cy="make-public-app-label">
|
||||
{this.props.t('editor.shareModal.makeApplicationPublic', 'Make application public')}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', alignItems: 'left', gap: '8px' }}>
|
||||
<ToolTip
|
||||
message={TOOLTIP_MESSAGES.RELEASE_VERSION_URL_UNAVAILABLE}
|
||||
placement={'top'}
|
||||
show={isHovered}
|
||||
>
|
||||
<div
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
style={{
|
||||
width: '32px',
|
||||
height: '18px',
|
||||
marginLeft: '-40px',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
disabled
|
||||
style={{
|
||||
opacity: 0.3,
|
||||
cursor: 'default',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ToolTip>
|
||||
|
||||
<span
|
||||
className="form-check-label field-name"
|
||||
data-cy="make-public-app-label"
|
||||
style={{ opacity: 0.6 }}
|
||||
>
|
||||
{this.props.t('editor.shareModal.makeApplicationPublic', 'Make application public')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.props.isVersionReleased ? (
|
||||
<div className="shareable-link tj-app-input mb-2">
|
||||
<label data-cy="shareable-app-link-label" className="field-name">
|
||||
{this.props.t('editor.shareModal.shareableLink', 'Shareable app link')}
|
||||
|
|
@ -259,6 +304,7 @@ class ManageAppUsersComponent extends React.Component {
|
|||
style={{ maxWidth: '150px' }}
|
||||
defaultValue={this.props.slug}
|
||||
data-cy="app-name-slug-input"
|
||||
disabled={!this.props.isVersionReleased}
|
||||
/>
|
||||
{isSlugVerificationInProgress && (
|
||||
<div className="icon-container">
|
||||
|
|
@ -344,60 +390,69 @@ class ManageAppUsersComponent extends React.Component {
|
|||
>{`URL-friendly 'slug' consists of lowercase letters, numbers, and hyphens`}</label>
|
||||
)}
|
||||
</div>
|
||||
{(this?.props?.isPublic || window?.public_config?.ENABLE_PRIVATE_APP_EMBED === 'true') && (
|
||||
<div className="tj-app-input">
|
||||
<label className="field-name" data-cy="iframe-link-label">
|
||||
Embedded app link
|
||||
</label>
|
||||
<span className={`tj-text-input justify-content-between ${this.props.darkMode ? 'dark' : ''}`}>
|
||||
<span data-cy="iframe-link">{embeddableLink}</span>
|
||||
<span className="copy-container">
|
||||
<CopyToClipboard
|
||||
text={embeddableLink}
|
||||
onCopy={() => toast.success('Link copied to clipboard')}
|
||||
>
|
||||
<svg
|
||||
className="cursor-pointer"
|
||||
width="17"
|
||||
height="18"
|
||||
viewBox="0 0 17 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-cy="iframe-link-copy-button"
|
||||
>
|
||||
<path
|
||||
d="M9.11154 5.18031H5.88668V4.83302C5.88668 3.29859 7.13059 2.05469 8.66502 2.05469H12.8325C14.3669 2.05469 15.6109 3.29859 15.6109 4.83302V9.00052C15.6109 10.535 14.3669 11.7789 12.8325 11.7789H12.4852V8.554C12.4852 6.69076 10.9748 5.18031 9.11154 5.18031Z"
|
||||
fill="#889096"
|
||||
/>
|
||||
<path
|
||||
d="M8.66502 15.9464H4.49752C2.96309 15.9464 1.71918 14.7025 1.71918 13.168V9.00052C1.71918 7.46609 2.96309 6.22219 4.49752 6.22219H8.66502C10.1994 6.22219 11.4434 7.46609 11.4434 9.00052V13.168C11.4434 14.7025 10.1994 15.9464 8.66502 15.9464Z"
|
||||
fill="#889096"
|
||||
/>
|
||||
</svg>
|
||||
</CopyToClipboard>
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
<div className="shareable-link tj-app-input mb-2">
|
||||
<label data-cy="shareable-app-link-label" className="field-name">
|
||||
{this.props.t('editor.shareModal.shareableLink', 'Shareable app link')}
|
||||
</label>
|
||||
<div className="empty-version">
|
||||
<InfoIcon style={{ width: '12px', marginRight: '5px' }} />
|
||||
<span>This version has not been released yet</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</Modal.Body>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Modal.Footer className="manage-app-users-footer">
|
||||
{this.isUserAdmin && (
|
||||
<Link
|
||||
to={getPrivateRoute('workspace_settings')}
|
||||
target="_blank"
|
||||
className={`btn border-0 default-secondary-button float-right1`}
|
||||
data-cy="manage-users-button"
|
||||
>
|
||||
Manage users
|
||||
</Link>
|
||||
)}
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
</ToolTip>
|
||||
{((this?.props?.isVersionReleased && this?.props?.isPublic) ||
|
||||
window?.public_config?.ENABLE_PRIVATE_APP_EMBED === 'true') && (
|
||||
<div className="tj-app-input">
|
||||
<label className="field-name" data-cy="iframe-link-label">
|
||||
Embedded app link
|
||||
</label>
|
||||
<span className={`tj-text-input justify-content-between ${this.props.darkMode ? 'dark' : ''}`}>
|
||||
<span data-cy="iframe-link">{embeddableLink}</span>
|
||||
<span className="copy-container">
|
||||
<CopyToClipboard text={embeddableLink} onCopy={() => toast.success('Link copied to clipboard')}>
|
||||
<svg
|
||||
className="cursor-pointer"
|
||||
width="17"
|
||||
height="18"
|
||||
viewBox="0 0 17 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
data-cy="iframe-link-copy-button"
|
||||
>
|
||||
<path
|
||||
d="M9.11154 5.18031H5.88668V4.83302C5.88668 3.29859 7.13059 2.05469 8.66502 2.05469H12.8325C14.3669 2.05469 15.6109 3.29859 15.6109 4.83302V9.00052C15.6109 10.535 14.3669 11.7789 12.8325 11.7789H12.4852V8.554C12.4852 6.69076 10.9748 5.18031 9.11154 5.18031Z"
|
||||
fill="#889096"
|
||||
/>
|
||||
<path
|
||||
d="M8.66502 15.9464H4.49752C2.96309 15.9464 1.71918 14.7025 1.71918 13.168V9.00052C1.71918 7.46609 2.96309 6.22219 4.49752 6.22219H8.66502C10.1994 6.22219 11.4434 7.46609 11.4434 9.00052V13.168C11.4434 14.7025 10.1994 15.9464 8.66502 15.9464Z"
|
||||
fill="#889096"
|
||||
/>
|
||||
</svg>
|
||||
</CopyToClipboard>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</Modal.Body>
|
||||
|
||||
<Modal.Footer className="manage-app-users-footer">
|
||||
{this.isUserAdmin && (
|
||||
<Link
|
||||
to={getPrivateRoute('workspace_settings')}
|
||||
target="_blank"
|
||||
className={`btn border-0 default-secondary-button float-right1`}
|
||||
data-cy="manage-users-button"
|
||||
>
|
||||
Manage users
|
||||
</Link>
|
||||
)}
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ export const ERROR_MESSAGES = {
|
|||
|
||||
export const TOOLTIP_MESSAGES = {
|
||||
SHARE_URL_UNAVAILABLE: 'Share URL is unavailable until current version is released',
|
||||
RELEASE_VERSION_URL_UNAVAILABLE: 'Release the version to make it public',
|
||||
};
|
||||
|
||||
export const DATA_SOURCE_TYPE = {
|
||||
|
|
|
|||
|
|
@ -1,78 +1,94 @@
|
|||
input.form-control,
|
||||
textarea,
|
||||
.input-control {
|
||||
gap: 16px !important;
|
||||
background: var(--base) !important;
|
||||
border: 1px solid var(--slate7) !important;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 4px !important;
|
||||
color: var(--slate12) !important;
|
||||
transition: none;
|
||||
height: 35px;
|
||||
padding-left: 0.4375rem;
|
||||
padding-right: 0.4375rem;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
overflow-x: 'auto';
|
||||
white-space: 'nowrap';
|
||||
textarea,
|
||||
.input-control {
|
||||
gap: 16px !important;
|
||||
background: var(--base) !important;
|
||||
border: 1px solid var(--slate7) !important;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 4px !important;
|
||||
color: var(--slate12) !important;
|
||||
transition: none;
|
||||
height: 35px;
|
||||
padding-left: 0.4375rem;
|
||||
padding-right: 0.4375rem;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
overflow-x: 'auto';
|
||||
white-space: 'nowrap';
|
||||
|
||||
|
||||
&:hover {
|
||||
background: var(--slate1) !important;
|
||||
border: 1px solid var(--slate8) !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: var(--indigo2) !important;
|
||||
border: 1px solid var(--indigo9) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&.input-error-border {
|
||||
border-color: #DB4324 !important;
|
||||
}
|
||||
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px var(--base) inset !important;
|
||||
-webkit-text-fill-color: var(--slate12) !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--slate1) !important;
|
||||
border: 1px solid var(--slate8) !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1000px var(--slate1) inset !important;
|
||||
-webkit-text-fill-color: var(--slate12) !important;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: var(--indigo2) !important;
|
||||
border: 1px solid var(--indigo9) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&.input-error-border {
|
||||
border-color: #DB4324 !important;
|
||||
}
|
||||
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px var(--base) inset !important;
|
||||
box-shadow: 0 0 0 1000px var(--indigo2) inset !important;
|
||||
-webkit-text-fill-color: var(--slate12) !important;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1000px var(--slate1) inset !important;
|
||||
-webkit-text-fill-color: var(--slate12) !important;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: 0 0 0 1000px var(--indigo2) inset !important;
|
||||
-webkit-text-fill-color: var(--slate12) !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.empty-key-value {
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
width: 625px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
color: #687076;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
border: 1px dashed #E6E8EB;
|
||||
}
|
||||
}
|
||||
|
||||
.trash {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
justify-content: 'center';
|
||||
align-items: 'center';
|
||||
}
|
||||
|
||||
.empty-key-value {
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
width: 625px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
color: #687076;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
border: 1px dashed #E6E8EB;
|
||||
}
|
||||
|
||||
.trash {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
justify-content: 'center';
|
||||
align-items: 'center';
|
||||
}
|
||||
|
||||
.empty-version {
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
color: #687076;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
border: 1px dashed #E6E8EB;
|
||||
}
|
||||
Loading…
Reference in a new issue