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:
Rohan Lahori 2024-08-19 10:36:57 +05:30 committed by GitHub
parent b49dbf9b1c
commit e1be2df4bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 242 additions and 170 deletions

View file

@ -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>
);
}
}

View file

@ -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 = {

View file

@ -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;
}