Merge branch 'main' into release-sharepoint
|
|
@ -401,40 +401,43 @@ class ManageAppUsersComponent extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{((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"
|
||||
{/* shows embedded app link only for released apps */}
|
||||
{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')}
|
||||
>
|
||||
<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>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</Modal.Body>
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@ const AppList = (props) => {
|
|||
className={`d-block text-center text-body ${props.darkMode && 'text-white-50'}`}
|
||||
data-cy="empty-folder-text"
|
||||
>
|
||||
{/* removed this error message display for now -> as it was leading to multiple message being shown in the UI*/}
|
||||
{/* {props.currentFolder?.count == 0
|
||||
? t('homePage.thisFolderIsEmpty', 'This folder is empty')
|
||||
: t('homePage.nonAccessibleFolderApps', 'You do not have access to any applications in this folder.')} */}
|
||||
{props.currentFolder?.count == 0 &&
|
||||
props.apps?.length == 0 &&
|
||||
props.appSearchKey == '' &&
|
||||
t('homePage.thisFolderIsEmpty', 'This folder is empty')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ export const Folders = function Folders({
|
|||
|
||||
const { t } = useTranslation();
|
||||
const { updateSidebarNAV } = useContext(BreadCrumbContext);
|
||||
|
||||
useEffect(() => {
|
||||
setLoadingStatus(foldersLoading);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ class HomePageComponent extends React.Component {
|
|||
this.setState({
|
||||
apps: data.apps,
|
||||
meta: { ...this.state.meta, ...data.meta },
|
||||
searchedAppCount: appSearchKey ? data.apps.length : this.state.currentFolder.count,
|
||||
isLoading: false,
|
||||
})
|
||||
);
|
||||
|
|
@ -900,7 +901,7 @@ class HomePageComponent extends React.Component {
|
|||
canCreateApp={this.canCreateApp}
|
||||
/>
|
||||
)}
|
||||
{!isLoading && meta.total_count === 0 && appSearchKey && (
|
||||
{!isLoading && apps?.length === 0 && appSearchKey && (
|
||||
<div>
|
||||
<span className={`d-block text-center text-body pt-5 ${this.props.darkMode && 'text-white-50'}`}>
|
||||
{this.props.t('homePage.noApplicationFound', 'No Applications found')}
|
||||
|
|
@ -921,6 +922,7 @@ class HomePageComponent extends React.Component {
|
|||
darkMode={this.props.darkMode}
|
||||
appActionModal={this.appActionModal}
|
||||
removeAppFromFolder={this.removeAppFromFolder}
|
||||
appSearchKey={this.state.appSearchKey}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -171,14 +171,16 @@ function AddEditResourcePermissionsModal({
|
|||
</div>
|
||||
</label>
|
||||
</OverlayTrigger>
|
||||
<AppsSelect
|
||||
disabled={!isCustom}
|
||||
allowSelectAll={true}
|
||||
value={selectedApps}
|
||||
onChange={setSelectedApps}
|
||||
options={addableApps}
|
||||
data-value="test"
|
||||
/>
|
||||
{isCustom && (
|
||||
<AppsSelect
|
||||
disabled={!isCustom}
|
||||
allowSelectAll={true}
|
||||
value={selectedApps}
|
||||
onChange={setSelectedApps}
|
||||
options={addableApps}
|
||||
data-value="test"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalBase>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import { SearchBox } from '@/_components/SearchBox';
|
|||
import EditRoleErrorModal from '@/ManageGroupPermissionsV2/ErrorModal/ErrorModal';
|
||||
import ChangeRoleModal from '@/ManageGroupPermissionResourcesV2/ChangeRoleModal';
|
||||
import { ToolTip } from '@/_components/ToolTip';
|
||||
import Avatar from '@/_ui/Avatar';
|
||||
|
||||
class ManageGroupPermissionResourcesComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -339,7 +341,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
this.setState({ isChangeRoleModalOpen: true, updatingUserRole: updatingUser });
|
||||
|
||||
showChangeRoleModalMessage = () => {
|
||||
console.log('called');
|
||||
this.setState({ showRoleEditMessage: true });
|
||||
};
|
||||
|
||||
|
|
@ -435,7 +436,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
autoRoleChangeModalList,
|
||||
autoRoleChangeMessageType,
|
||||
} = this.state;
|
||||
|
||||
const isBasicPlan = false;
|
||||
const isPaidPlan = false;
|
||||
|
||||
|
|
@ -737,11 +737,16 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
key={user.id}
|
||||
className="manage-group-users-row"
|
||||
data-cy={`${String(user.email).toLowerCase().replace(/\s+/g, '-')}-user-row`}
|
||||
style={{ alignItems: 'center' }}
|
||||
>
|
||||
<p className="tj-text-sm d-flex align-items-center">
|
||||
<div className="name-avatar">
|
||||
{`${user?.firstName?.[0] ?? ''} ${user?.lastName?.[0] ?? ''}`}
|
||||
</div>
|
||||
<Avatar
|
||||
className="name-avatar"
|
||||
avatarId={user.avatarId}
|
||||
text={`${user.first_name ? user.first_name[0] : ''}${
|
||||
user.last_name ? user.last_name[0] : ''
|
||||
}`}
|
||||
/>
|
||||
<span>{`${user?.firstName ?? ''} ${user?.lastName ?? ''}`}</span>
|
||||
</p>
|
||||
<p className="tj-text-sm d-flex align-items-center" style={{ paddingLeft: '12px' }}>
|
||||
|
|
|
|||
|
|
@ -684,7 +684,14 @@ class ManageGroupPermissionsComponent extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{filteredGroup.length === 0 && showGroupSearchBar && groups.length !== 0 && (
|
||||
<div className="empty-custom-group-info">
|
||||
<SolidIcon className="info-icon" name="information" width="18px" />
|
||||
<span className="tj-text-xsm text-center info-label" data-cy="empty-custom-group-info">
|
||||
No custom groups found
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{groups.length ? (
|
||||
filteredGroup.map((permissionGroup) => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import EmptyState from './EmptyState';
|
|||
import FolderList from '@/_ui/FolderList/FolderList';
|
||||
import { BreadCrumbContext } from '@/App';
|
||||
import './ConstantFormStyle.scss';
|
||||
import { Constants } from '@/_helpers/utils';
|
||||
|
||||
import { Constants, redirectToWorkspace } from '@/_helpers/utils';
|
||||
import { SearchBox } from '@/_components/SearchBox';
|
||||
const MODES = Object.freeze({
|
||||
CREATE: 'create',
|
||||
EDIT: 'edit',
|
||||
|
|
@ -47,6 +47,7 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [globalCount, setGlobalCount] = useState(0);
|
||||
const [secretCount, setSecretCount] = useState(0);
|
||||
const NoPermissionMessage = 'You do not have permissions to perform this action';
|
||||
|
||||
const handleTabChange = (tab) => {
|
||||
setCurrentPage(1);
|
||||
|
|
@ -62,6 +63,14 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
updateTableData(constants, activeTabEnvironment?.name, 0, perPage, true, activeTab, searchTerm);
|
||||
};
|
||||
|
||||
const handleSearchClear = () => {
|
||||
const searchTerm = '';
|
||||
setSearchTerm(searchTerm);
|
||||
|
||||
// Re-filter the constants based on the current search term and active tab
|
||||
updateTableData(constants, activeTabEnvironment?.name, 0, perPage, true, activeTab, searchTerm);
|
||||
};
|
||||
|
||||
const onCancelBtnClicked = () => {
|
||||
setSelectedConstant(null);
|
||||
setIsManageVarDrawerOpen(false);
|
||||
|
|
@ -323,6 +332,9 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
.catch(({ error }) => {
|
||||
setErrors(error);
|
||||
toast.error(error);
|
||||
if (error === NoPermissionMessage) {
|
||||
redirectToWorkspace();
|
||||
}
|
||||
})
|
||||
.finally(() => fetchConstantsAndEnvironments());
|
||||
}
|
||||
|
|
@ -335,7 +347,10 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
})
|
||||
.catch(({ error }) => {
|
||||
setErrors(error);
|
||||
toast.error('Constant could not be created');
|
||||
toast.error(error || 'Constant could not be created');
|
||||
if (error === NoPermissionMessage) {
|
||||
redirectToWorkspace();
|
||||
}
|
||||
})
|
||||
.finally(() => fetchConstantsAndEnvironments());
|
||||
};
|
||||
|
|
@ -358,6 +373,9 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
})
|
||||
.catch(({ error }) => {
|
||||
toast.error(error);
|
||||
if (error === NoPermissionMessage) {
|
||||
redirectToWorkspace();
|
||||
}
|
||||
})
|
||||
.finally(() => fetchConstantsAndEnvironments());
|
||||
};
|
||||
|
|
@ -460,12 +478,13 @@ const ManageOrgConstantsComponent = ({ darkMode }) => {
|
|||
</div>
|
||||
|
||||
<div className="search-bar">
|
||||
<input
|
||||
type="text"
|
||||
<SearchBox
|
||||
width={250}
|
||||
callBack={handleSearchChange}
|
||||
customClass="tj-common-search-input-group"
|
||||
autoFocus={true}
|
||||
placeholder={activeTab === Constants.Global ? 'Search global constants' : 'Search secrets'}
|
||||
value={searchTerm}
|
||||
onChange={handleSearchChange}
|
||||
className="search-input"
|
||||
onClearCallback={handleSearchClear}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -42,19 +42,20 @@ export function OrganizationSettings(props) {
|
|||
if (selectedTabFromRoute === 'workspace-settings') {
|
||||
// No Sub routes added loading first one
|
||||
setSelectedTab(admin ? workspaceSettingsLinks[0].id : 'workspacevariables');
|
||||
navigate(admin ? workspaceSettingsLinks[0].route : 'workspace-variables');
|
||||
} else {
|
||||
const selectedWorkspaceSetting = workspaceSettingsLinks?.find((m) => m.id === selectedTabFromRoute);
|
||||
updateSidebarNAV(selectedWorkspaceSetting?.name || '');
|
||||
setSelectedTab(getMenuFromRoute(selectedTabFromRoute)?.id);
|
||||
}
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
}, [authenticationService.currentSessionValue?.admin]);
|
||||
|
||||
useEffect(() => {
|
||||
const menu = workspaceSettingsLinks?.find((m) => m.id === selectedTab);
|
||||
updateSidebarNAV(menu?.name || '');
|
||||
navigate(menu?.route || '');
|
||||
}, [selectedTab]);
|
||||
}, [admin, location.pathname]);
|
||||
|
||||
const handleClick = (data) => {
|
||||
setSelectedTab(data.id);
|
||||
updateSidebarNAV(data?.name || '');
|
||||
};
|
||||
return (
|
||||
<Layout switchDarkMode={props.switchDarkMode} darkMode={props.darkMode}>
|
||||
<div className="wrapper organization-settings-page">
|
||||
|
|
@ -67,6 +68,7 @@ export function OrganizationSettings(props) {
|
|||
<Wrapper key={index}>
|
||||
<Link
|
||||
key={index}
|
||||
to={item.route}
|
||||
style={{
|
||||
textDecoration: 'none',
|
||||
border: 'none',
|
||||
|
|
@ -79,7 +81,7 @@ export function OrganizationSettings(props) {
|
|||
className="workspace-settings-nav-items"
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setSelectedTab(item.id);
|
||||
handleClick(item);
|
||||
}}
|
||||
selectedItem={selectedTab == item.id}
|
||||
renderBadgeForItems={[]}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ export const CreateOrganization = ({ showCreateOrg, setShowCreateOrg }) => {
|
|||
};
|
||||
|
||||
const handleInputChange = async (value, field) => {
|
||||
const trimmedValue = value?.trim();
|
||||
if (field === 'slug') {
|
||||
setSlug({
|
||||
...slug,
|
||||
|
|
@ -83,7 +84,7 @@ export const CreateOrganization = ({ showCreateOrg, setShowCreateOrg }) => {
|
|||
if (error?.status === true) {
|
||||
try {
|
||||
await organizationService.checkWorkspaceUniqueness(
|
||||
field === 'name' ? value : null,
|
||||
field === 'name' ? trimmedValue : null,
|
||||
field === 'slug' ? value : null
|
||||
);
|
||||
} catch (errResponse) {
|
||||
|
|
|
|||
|
|
@ -1,52 +1,45 @@
|
|||
import React, { useState } from 'react';
|
||||
import Select from '@/_ui/Select';
|
||||
import { components } from 'react-select';
|
||||
import { EditOrganization } from './EditOrganization';
|
||||
import { CreateOrganization } from './CreateOrganization';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authenticationService } from '@/_services';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { ToolTip } from '@/_components';
|
||||
import { decodeEntities } from '@/_helpers/utils';
|
||||
|
||||
const Menu = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const { admin } = authenticationService.currentSessionValue;
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
return (
|
||||
<components.Menu {...props}>
|
||||
<div className={darkMode && 'dark-theme'} style={{ padding: '4px' }}>
|
||||
{admin && (
|
||||
<>
|
||||
<div
|
||||
className="org-custom-select-header-wrap"
|
||||
style={{ padding: '8px 12px' }}
|
||||
onClick={() => props.selectProps.setShowEditOrg(true)}
|
||||
>
|
||||
<div className="row cursor-pointer d-flex align-items-center">
|
||||
<div className="col-10">{props?.selectProps?.value?.label}</div>
|
||||
<div className="col-1 tj-secondary-btn org-edit-icon">
|
||||
<SolidIcon name="editrectangle" width="14" fill="#3E63DD" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`org-dropdown-shadow ${darkMode && 'dark-theme'}`}
|
||||
style={{ paddingTop: '4px', paddingBottom: '4px' }}
|
||||
>
|
||||
<>
|
||||
<div className="org-custom-select-header-wrap" style={{ padding: '8px 12px' }}>
|
||||
<div className="row cursor-pointer d-flex align-items-center">
|
||||
<div className="col-10 select-header-font">Workspaces ({props.options.length})</div>
|
||||
{admin && (
|
||||
<ToolTip message={'Add new workspace'} position="top">
|
||||
<div className="col-1" style={{ paddingRight: '24px' }} onClick={props.selectProps.setShowCreateOrg}>
|
||||
<SolidIcon
|
||||
name="plus"
|
||||
fill="var(--icon-strong)"
|
||||
className=""
|
||||
dataCy="add-new-workspace-link"
|
||||
width="15"
|
||||
/>
|
||||
</div>
|
||||
</ToolTip>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className={`${darkMode && 'dark-theme'}`}>{props.children}</div>
|
||||
<div
|
||||
className="cursor-pointer d-flex align-items-center add-workspace-button"
|
||||
style={{ padding: '4px 12px', color: '#3E63DD' }}
|
||||
onClick={props.selectProps.setShowCreateOrg}
|
||||
>
|
||||
<div className="add-new-workspace-icon-old-wrap">
|
||||
<SolidIcon name="plus" fill="#FDFDFE" className="add-new-workspace-icon-old" />
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="add-new-workspace-icon-wrap">
|
||||
<SolidIcon name="plus" fill="#3E63DD" className="add-new-workspace-icon" dataCy="add-new-workspace-link" />
|
||||
</div>
|
||||
<span className="p-1 tj-text-xsm">{t('header.organization.addNewWorkSpace', 'Add new workspace')}</span>
|
||||
</div>
|
||||
<div className={`${darkMode && 'dark-theme'}`}>{props.children}</div>
|
||||
</div>
|
||||
</components.Menu>
|
||||
);
|
||||
|
|
@ -65,22 +58,18 @@ const SingleValue = ({ selectProps }) => {
|
|||
};
|
||||
|
||||
export const CustomSelect = ({ ...props }) => {
|
||||
const [showEditOrg, setShowEditOrg] = useState(false);
|
||||
const [showCreateOrg, setShowCreateOrg] = useState(false);
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const currentValue = props?.options.find((option) => option?.value === props?.value);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateOrganization showCreateOrg={showCreateOrg} setShowCreateOrg={setShowCreateOrg} />
|
||||
<EditOrganization showEditOrg={showEditOrg} setShowEditOrg={setShowEditOrg} currentValue={currentValue} />
|
||||
|
||||
<Select
|
||||
className={`react-select-container ${darkMode && 'dark-theme'}`}
|
||||
width={'262px'}
|
||||
hasSearch={false}
|
||||
components={{ Menu, SingleValue }}
|
||||
setShowEditOrg={setShowEditOrg}
|
||||
setShowCreateOrg={setShowCreateOrg}
|
||||
styles={{ border: 0, cursor: 'pointer' }}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { CustomSelect } from './CustomSelect';
|
||||
import { getAvatar, decodeEntities } from '@/_helpers/utils';
|
||||
|
|
@ -6,13 +6,15 @@ import { appendWorkspaceId, getWorkspaceIdOrSlugFromURL } from '@/_helpers/route
|
|||
import { ToolTip } from '@/_components';
|
||||
import { useCurrentSessionStore } from '@/_stores/currentSessionStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { EditOrganization } from './EditOrganization';
|
||||
|
||||
/* TODO:
|
||||
each workspace related component has organizations list component which can be moved to a single wrapper.
|
||||
otherwise this component will intiate everytime we switch between pages
|
||||
*/
|
||||
export const OrganizationList = function () {
|
||||
const { current_organization_id } = authenticationService.currentSessionValue;
|
||||
const { current_organization_id, admin } = authenticationService.currentSessionValue;
|
||||
const { fetchOrganizations, organizationList, isGettingOrganizations } = useCurrentSessionStore(
|
||||
(state) => ({
|
||||
organizationList: state.organizations,
|
||||
|
|
@ -32,34 +34,71 @@ export const OrganizationList = function () {
|
|||
const organization = organizationList.find((org) => org.id === id);
|
||||
if (![id, organization.slug].includes(getWorkspaceIdOrSlugFromURL())) {
|
||||
const newPath = appendWorkspaceId(organization.slug || id, location.pathname, true);
|
||||
window.history.replaceState(null, null, newPath);
|
||||
window.location.reload();
|
||||
window.open(newPath, '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
const options = organizationList.map((org) => ({
|
||||
value: org.id,
|
||||
name: org.name,
|
||||
slug: org.slug,
|
||||
label: (
|
||||
<div className={`align-items-center d-flex tj-org-dropdown ${darkMode && 'dark-theme'}`}>
|
||||
<div
|
||||
className="dashboard-org-avatar "
|
||||
data-cy={`${String(org.name).toLowerCase().replace(/\s+/g, '-')}-avatar`}
|
||||
>
|
||||
{getAvatar(org.name)}
|
||||
const options = organizationList
|
||||
.map((org) => ({
|
||||
value: org.id,
|
||||
name: org.name,
|
||||
slug: org.slug,
|
||||
label: (
|
||||
<div className={`align-items-center d-flex tj-org-dropdown ${darkMode && 'dark-theme'}`}>
|
||||
{org.id === current_organization_id ? (
|
||||
<div className="current-org-avatar">
|
||||
<SolidIcon name="tick" fill="#3E63DD" dataCy="add-new-workspace-link" width="20" viewBox="0 0 17 15" />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="dashboard-org-avatar "
|
||||
data-cy={`${String(org.name).toLowerCase().replace(/\s+/g, '-')}-avatar`}
|
||||
>
|
||||
{getAvatar(org.name)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ToolTip message={org.name} placement="right">
|
||||
<div className="org-name" data-cy={`${String(org.name).toLowerCase().replace(/\s+/g, '-')}-name-selector`}>
|
||||
<span style={{ color: org.id === current_organization_id ? '#3E63DD' : 'var(--slate12)' }}>
|
||||
{decodeEntities(org.name)}
|
||||
</span>
|
||||
</div>
|
||||
</ToolTip>
|
||||
{org.id === current_organization_id && admin ? (
|
||||
<ToolTip message="Edit" placement="top">
|
||||
<div
|
||||
className="current-org-indicator"
|
||||
data-cy="current-org-indicator"
|
||||
onClick={() => setShowEditOrg(true)}
|
||||
>
|
||||
<SolidIcon name="editable" fill="#3E63DD" dataCy="add-new-workspace-link" width="16" />
|
||||
</div>
|
||||
</ToolTip>
|
||||
) : (
|
||||
org.id !== current_organization_id && (
|
||||
<ToolTip message="Open in new tab" placement="top">
|
||||
<div
|
||||
className="current-org-indicator"
|
||||
data-cy="current-org-indicator"
|
||||
onClick={() => switchOrganization(org.id)}
|
||||
>
|
||||
<SolidIcon name="newtab" fill="var(--icon-strong)" width="16" className="add-new-workspace-icon" />
|
||||
</div>
|
||||
</ToolTip>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<ToolTip message={org.name} placement="right">
|
||||
<div className="org-name" data-cy={`${String(org.name).toLowerCase().replace(/\s+/g, '-')}-name-selector`}>
|
||||
{decodeEntities(org.name)}
|
||||
</div>
|
||||
</ToolTip>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
),
|
||||
}))
|
||||
.sort((a, b) => (a.value === current_organization_id ? -1 : b.value === current_organization_id ? 1 : 0));
|
||||
|
||||
const [showEditOrg, setShowEditOrg] = useState(false);
|
||||
const currentValue = organizationList.find((option) => option?.id === current_organization_id);
|
||||
|
||||
return (
|
||||
<div className="org-select-container">
|
||||
<EditOrganization showEditOrg={showEditOrg} setShowEditOrg={setShowEditOrg} currentValue={currentValue} />
|
||||
<CustomSelect
|
||||
isLoading={isGettingOrganizations}
|
||||
options={options}
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@ export function validateDates({ validationObject, widgetValue, currentState, cus
|
|||
|
||||
export function validateEmail(email) {
|
||||
const emailRegex =
|
||||
/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
|
||||
/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[a-zA-Z]{2,})$/i;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
|
|
@ -1099,9 +1099,9 @@ export const validateName = (
|
|||
checkReservedWords = false,
|
||||
allowAllCases = false
|
||||
) => {
|
||||
const newName = name.trim();
|
||||
const newName = name;
|
||||
let errorMsg = '';
|
||||
if (emptyCheck && !newName) {
|
||||
if (emptyCheck && (!newName || newName.trim().length === 0)) {
|
||||
errorMsg = `${nameType} can't be empty`;
|
||||
showError &&
|
||||
toast.error(errorMsg, {
|
||||
|
|
|
|||
|
|
@ -9841,7 +9841,7 @@ tbody {
|
|||
.manage-group-users-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
padding: 12px 6px;
|
||||
width: 612px !important;
|
||||
height: 64px;
|
||||
|
|
@ -10567,7 +10567,14 @@ tbody {
|
|||
}
|
||||
|
||||
.disable {
|
||||
color: var(--slate7);
|
||||
color: var(--slate9);
|
||||
}
|
||||
.disable {
|
||||
color: var(--slate9);
|
||||
|
||||
&.dark-theme {
|
||||
color: var(--slate11);
|
||||
}
|
||||
}
|
||||
|
||||
&__danger {
|
||||
|
|
@ -10663,6 +10670,33 @@ tbody {
|
|||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.current-org-avatar {
|
||||
margin-right: 11px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 7px 8px;
|
||||
gap: 10px;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.current-org-indicator {
|
||||
padding: 0px 8px 0px 8px;
|
||||
margin-left: auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.current-org-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .current-org-indicator {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.org-name {
|
||||
color: var(--slate12) !important;
|
||||
white-space: nowrap;
|
||||
|
|
@ -10671,6 +10705,10 @@ tbody {
|
|||
}
|
||||
}
|
||||
|
||||
.org-dropdown-shadow{
|
||||
box-shadow: var(--elevation-400-box-shadow)
|
||||
}
|
||||
|
||||
.css-1q0xftk-menu {
|
||||
background-color: var(--base-black) !important;
|
||||
border: 1px solid hsl(197, 6.8%, 13.6%) !important;
|
||||
|
|
@ -10688,6 +10726,14 @@ tbody {
|
|||
|
||||
.org-custom-select-header-wrap {
|
||||
border-bottom: 1px solid var(--slate5);
|
||||
|
||||
.select-header-font{
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
color: var(--text-default);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.btn-close:focus {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
.query-manager-border-color{
|
||||
input.form-control,
|
||||
textarea,
|
||||
.input-control {
|
||||
|
|
@ -51,7 +52,7 @@ textarea,
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.empty-key-value {
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
|
|
|
|||
21
frontend/src/_ui/Icon/solidIcons/NewTab.jsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
|
||||
const NewTab = ({ fill = '#6A727C', width = '25', className = '', viewBox = '0 0 25 25' }) => (
|
||||
<svg
|
||||
width={width}
|
||||
height={width}
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M13.8139 1.86633C14.6029 1.86633 15.2425 2.50593 15.2425 3.2949V10.914C15.2425 11.7029 14.6029 12.3425 13.8139 12.3425H6.19489C5.40591 12.3425 4.76632 11.7029 4.76632 10.914V3.2949C4.76632 2.50593 5.40591 1.86633 6.19489 1.86633H13.8139ZM11.4883 8.4777L10.5648 7.5542L8.60473 9.51427C8.32579 9.79321 7.87352 9.79321 7.59458 9.51427C7.31564 9.23532 7.31564 8.78306 7.59458 8.50411L9.55465 6.54405L8.63116 5.62056C8.46092 5.45032 8.41 5.1943 8.50212 4.97188C8.59426 4.74946 8.8113 4.60443 9.05206 4.60443H11.9092C12.2379 4.60443 12.5044 4.87092 12.5044 5.19967V8.05681C12.5044 8.29756 12.3594 8.51461 12.137 8.60674C11.9145 8.69887 11.6585 8.64794 11.4883 8.4777ZM3.33775 5.91395C3.33775 5.51947 3.01795 5.19967 2.62347 5.19967C2.22898 5.19967 1.90918 5.51947 1.90918 5.91395V13.0568C1.90918 14.2402 2.86857 15.1997 4.05204 15.1997H11.1949C11.5894 15.1997 11.9092 14.8799 11.9092 14.4854C11.9092 14.0909 11.5894 13.7711 11.1949 13.7711H4.05204C3.65755 13.7711 3.33775 13.4513 3.33775 13.0568V5.91395Z"
|
||||
fill={fill}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default NewTab;
|
||||
|
|
@ -123,6 +123,7 @@ import AddRectangle from './AddRectangle.jsx';
|
|||
import Lock from './Lock.jsx';
|
||||
import Mail from './Mail.jsx';
|
||||
import Logs from './Logs.jsx';
|
||||
import NewTab from './NewTab.jsx';
|
||||
import Marketplace from './Marketplace.jsx';
|
||||
import Minimize from './Minimize.jsx';
|
||||
import Maximize from './Maximize.jsx';
|
||||
|
|
@ -354,6 +355,8 @@ const Icon = (props) => {
|
|||
return <NotificationSilent {...props} />;
|
||||
case 'notificationunread':
|
||||
return <NotificationUnread {...props} />;
|
||||
case 'newtab':
|
||||
return <NewTab {...props} />;
|
||||
case 'options':
|
||||
return <Options {...props} />;
|
||||
case 'open':
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { validateEmail } from '@/_helpers/utils';
|
||||
import { OnboardingFormWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { FormTextInput, SubmitButton, FormHeader } from '@/modules/common/components';
|
||||
import { retrieveWhiteLabelText } from '@white-label/whiteLabelling';
|
||||
import './resources/styles/forgot-password-form.styles.scss';
|
||||
|
|
@ -43,7 +43,7 @@ const ForgotPasswordForm = ({ onSubmit }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
<div className="forgot-password-form">
|
||||
<FormHeader>{t('forgotPasswordPage.forgotPassword', 'Forgot Password')}</FormHeader>
|
||||
|
|
@ -77,7 +77,7 @@ const ForgotPasswordForm = ({ onSubmit }) => {
|
|||
</form>
|
||||
</div>
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { OnboardingFormWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper } from '@/modules/onboarding/components';
|
||||
import { FormHeader } from '@/modules/common/components';
|
||||
import './resources/styles/forgot-password-info.styles.scss';
|
||||
import SepratorComponent from '@/modules/common/components/SepratorComponent';
|
||||
|
|
@ -19,7 +19,7 @@ const ForgotPasswordInfoScreen = ({ email }) => {
|
|||
|
||||
return (
|
||||
<div className="forgot-password-info-wrapper info-screen">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<FormHeader>{t('forgotPasswordInfo.header', 'Check your mail')}</FormHeader>
|
||||
<p className="message">{message}</p>
|
||||
<span className="info">{info}</span>
|
||||
|
|
@ -29,7 +29,7 @@ const ForgotPasswordInfoScreen = ({ email }) => {
|
|||
<span className="button-text">{t('forgotPasswordInfo.backToLogin', 'Back to login')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { validateEmail, validatePassword } from '@/_helpers/utils';
|
||||
import { OnboardingFormWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { FormTextInput, PasswordInput, SubmitButton, FormHeader, SSOAuthModule } from '@/modules/common/components';
|
||||
import { redirectToDashboard } from '@/_helpers/routes';
|
||||
import './resources/styles/login-form.styles.scss';
|
||||
|
|
@ -107,7 +107,7 @@ const LoginForm = ({
|
|||
|
||||
return (
|
||||
<div className="login-form">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
{noLoginMethodsEnabled ? (
|
||||
<div className="text-center-onboard">
|
||||
|
|
@ -184,7 +184,7 @@ const LoginForm = ({
|
|||
</>
|
||||
)}
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { authenticationService } from '@/_services';
|
||||
import { OnboardingFormWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { PasswordInput, SubmitButton, FormHeader } from '@/modules/common/components';
|
||||
import './resources/styles/reset-password-form.styles.scss';
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ const ResetPasswordForm = ({ token, onResetSuccess }) => {
|
|||
};
|
||||
return (
|
||||
<div className="reset-password-form">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
<FormHeader>{t('Reset Password')}</FormHeader>
|
||||
<span className="free-space"></span>
|
||||
|
|
@ -114,7 +114,7 @@ const ResetPasswordForm = ({ token, onResetSuccess }) => {
|
|||
<SubmitButton buttonText={t('Reset password')} isLoading={isLoading} disabled={isLoading || !isFormValid} />
|
||||
</form>
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { OnboardingFormWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper } from '@/modules/onboarding/components';
|
||||
import { FormHeader } from '@/modules/common/components';
|
||||
import { retrieveWhiteLabelText } from '@white-label/whiteLabelling';
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ const ForgotPasswordInfoScreen = ({ email }) => {
|
|||
|
||||
return (
|
||||
<div className="forgot-password-info-wrapper info-screen">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<FormHeader>Password has been reset</FormHeader>
|
||||
<p className="message">{message}</p>
|
||||
<div className="action-buttons pt-3">
|
||||
|
|
@ -31,7 +31,7 @@ const ForgotPasswordInfoScreen = ({ email }) => {
|
|||
<span className="button-text">{t('forgotPasswordInfo.backToLogin', 'Back to login')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
display: block;
|
||||
margin-bottom: 2px;
|
||||
font-family: "IBM Plex Sans";
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
|
@ -15,21 +15,21 @@
|
|||
}
|
||||
|
||||
&__required-disabled {
|
||||
color: #495057;
|
||||
color: #545B64;
|
||||
}
|
||||
|
||||
&__field {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 7px 6px;
|
||||
height: 40px;
|
||||
padding: 7px 12px;
|
||||
font-family: "IBM Plex Sans";
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
color: var(--Text-default, #1B1F24);
|
||||
background-clip: padding-box;
|
||||
border-radius: var(--Border-radius, 8px);
|
||||
border-radius: var(--Border-radius, 10px);
|
||||
border: 1px solid var(--Border-default, #CCD1D5);
|
||||
background: var(--Background-surface-layer-01, #FFF);
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
|
|
@ -64,6 +64,7 @@
|
|||
font-size: 10px;
|
||||
margin-top: 2px;
|
||||
height: 14px;
|
||||
|
||||
&__error-enabled {
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
display: block;
|
||||
margin-bottom: 2px;
|
||||
font-family: "IBM Plex Sans";
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
|
@ -22,17 +22,17 @@
|
|||
|
||||
&__field {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 7px 6px;
|
||||
padding-right: 30px;
|
||||
height: 40px;
|
||||
padding: 7px 12px;
|
||||
padding-right: 25px;
|
||||
font-family: "IBM Plex Sans";
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
color: var(--Text-default, #1B1F24);
|
||||
background-clip: padding-box;
|
||||
border-radius: var(--Border-radius, 8px);
|
||||
border-radius: var(--Border-radius, 10px);
|
||||
border: 1px solid var(--Border-default, #CCD1D5);
|
||||
background: var(--Background-surface-layer-01, #FFF);
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
|
|
@ -66,9 +66,7 @@
|
|||
cursor: pointer;
|
||||
font-size: .7rem;
|
||||
color: #6c757d;
|
||||
padding: 0.5rem;
|
||||
padding-left: 4px;
|
||||
margin-left: 10px;
|
||||
padding: 0.25rem;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
.submit-button {
|
||||
height: 32px;
|
||||
height: 40px;
|
||||
width: fit-content;
|
||||
min-width: 87px;
|
||||
border-radius: var(--4, 8px);
|
||||
margin: 10px 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: var(--Border-radius, 10px);
|
||||
margin: 10px;
|
||||
padding: 10px 20px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--Border-gap, 8px);
|
||||
|
||||
.button-text {
|
||||
color: var(--Text-on-color, #FFF);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-family: 'IBM Plex Sans';
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
|
|
@ -32,4 +38,4 @@
|
|||
cursor: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { FormHeader, FormDescription, SubmitButton } from '@/modules/common/components';
|
||||
import { OnboardingFormWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import useOnboardingStore from '@/modules/onboarding/stores/onboardingStore';
|
||||
import useInvitationsStore from '@/modules/onboarding/stores/invitationsStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
|
@ -48,7 +48,7 @@ const OnboardingForm = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
<div className={formClasses}>
|
||||
{shouldShowSteps && (
|
||||
|
|
@ -75,7 +75,7 @@ const OnboardingForm = ({
|
|||
</form>
|
||||
</div>
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -38,4 +38,4 @@
|
|||
|
||||
.__ce {
|
||||
padding-top: 24px !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +1,26 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Logo from '@/modules/common/resources/images/Logo';
|
||||
import './resources/styles/onboarding-form-wrapper.styles.scss';
|
||||
import {
|
||||
retrieveWhiteLabelLogo,
|
||||
fetchWhiteLabelDetails,
|
||||
defaultWhiteLabellingSettings,
|
||||
} from '@white-label/whiteLabelling';
|
||||
import { getSubpath } from '@/_helpers/routes';
|
||||
import WhiteLabellingFormWrapper from '@/modules/onboarding/components/WhiteLabellingFormWrapper';
|
||||
import { defaultWhiteLabellingSettings, retrieveWhiteLabelFavicon } from '@white-label/whiteLabelling';
|
||||
const OnboardingFormWrapper = ({ children: components }) => {
|
||||
const [whiteLabelLogo, setWhiteLableLogo] = useState(null);
|
||||
const [imageWidth, setImageWidth] = useState(130);
|
||||
const img = new Image();
|
||||
const handleLoad = () => {
|
||||
const { naturalWidth } = img;
|
||||
setImageWidth(naturalWidth < 130 ? naturalWidth : 130);
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchWhiteLabelDetails();
|
||||
const data = retrieveWhiteLabelLogo();
|
||||
setWhiteLableLogo(data);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!whiteLabelLogo) return;
|
||||
img.src = whiteLabelLogo;
|
||||
img.addEventListener('load', handleLoad);
|
||||
return () => {
|
||||
img.removeEventListener('load', handleLoad);
|
||||
};
|
||||
}, [whiteLabelLogo]);
|
||||
const whiteLabelLogoTest = retrieveWhiteLabelFavicon();
|
||||
const defaultWhiteLabelLogoTest = defaultWhiteLabellingSettings.WHITE_LABEL_FAVICON;
|
||||
const isWhiteLabelApplied = !(whiteLabelLogoTest === defaultWhiteLabelLogoTest);
|
||||
const redirectToLoginPage = () => {
|
||||
window.location.href = getSubpath() ? `${getSubpath()}` : '/';
|
||||
};
|
||||
if (window.location.pathname != '/setup' && isWhiteLabelApplied == null) {
|
||||
return <div></div>;
|
||||
}
|
||||
if (window.location.pathname != '/setup' && isWhiteLabelApplied) {
|
||||
return <WhiteLabellingFormWrapper>{components}</WhiteLabellingFormWrapper>;
|
||||
}
|
||||
return (
|
||||
<div className="onboarding-form-wrapper">
|
||||
<div>
|
||||
<div className="tooljet-header cursor-pointer" onClick={redirectToLoginPage}>
|
||||
{whiteLabelLogo != '' &&
|
||||
window.location.pathname != '/setup' &&
|
||||
whiteLabelLogo != defaultWhiteLabellingSettings.WHITE_LABEL_LOGO &&
|
||||
imageWidth != null ? (
|
||||
<img width={imageWidth} height="26px" src={whiteLabelLogo} />
|
||||
) : (
|
||||
<Logo />
|
||||
)}
|
||||
<Logo />
|
||||
</div>
|
||||
{components}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,4 +23,4 @@
|
|||
.onboarding-form-wrapper {
|
||||
margin-top: 240px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import OnboardingFormWrapper from '../OnboardingFormWrapper/OnboardingFormWrapper';
|
||||
import './resources/styles/onboarding-ui-wrapper.styles.scss';
|
||||
|
||||
const OnboardingUIWrapper = ({ children: components }) => {
|
||||
const pageLocation = window.location.pathname;
|
||||
if (pageLocation == '/setup') {
|
||||
return (
|
||||
<div className="onboarding-setup-wrapper">
|
||||
<OnboardingFormWrapper>{components}</OnboardingFormWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="auth-pages-wrapper">
|
||||
<OnboardingFormWrapper>{components}</OnboardingFormWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingUIWrapper;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './OnboardingUIWrapper';
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* Default styles */
|
||||
.onboarding-setup-wrapper {
|
||||
margin-top: 95px !important;
|
||||
/* Default margin-top */
|
||||
}
|
||||
|
||||
.auth-pages-wrapper {
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
min-height: 100vh !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* Media query for 1366x768 resolution */
|
||||
@media screen and (min-width: 1366px) and (min-height: 768px) {
|
||||
.onboarding-setup-wrapper {
|
||||
margin-top: 95px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Media query for 1440x900 resolution */
|
||||
@media screen and (min-width: 1440px) and (min-height: 900px) {
|
||||
.onboarding-setup-wrapper {
|
||||
margin-top: 180px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Media query for 1920x1080 resolution and above */
|
||||
@media screen and (min-width: 1920px) and (min-height: 1080px) {
|
||||
.onboarding-setup-wrapper {
|
||||
margin-top: 240px !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
import './resources/styles/background.styles.scss';
|
||||
const WhiteLabellingBackgroundWrapper = ({ MiddleComponent }) => {
|
||||
return (
|
||||
<div className="white-labelling-background-wrapper">
|
||||
<div className="container-fluid h-100">
|
||||
{MiddleComponent && (
|
||||
<div className="row h-100">
|
||||
<div className="col-12 d-flex justify-content-center align-items-center">
|
||||
<MiddleComponent />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WhiteLabellingBackgroundWrapper;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './WhiteLabellingBackgroundWrapper';
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
.white-labelling-background-wrapper {
|
||||
margin-top: 0px !important;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.container-fluid {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.row {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Logo from '@/modules/common/resources/images/Logo';
|
||||
import './resources/styles/whitelabelling-form-wrapper.styles.scss';
|
||||
import { getSubpath } from '@/_helpers/routes';
|
||||
import { defaultWhiteLabellingSettings, retrieveWhiteLabelFavicon } from '@white-label/whiteLabelling';
|
||||
const WhiteLabellingFormWrapper = ({ children: components }) => {
|
||||
const IMAGE_WIDTH = 36;
|
||||
const IMAGE_HEIGHT = 36;
|
||||
const [whiteLabelLogo, setWhiteLabelLogo] = useState(null);
|
||||
useEffect(() => {
|
||||
// Note : Currently, We are using favicon for white labelling in all the onboarding flow pages
|
||||
const data = retrieveWhiteLabelFavicon();
|
||||
setWhiteLabelLogo(data);
|
||||
}, []);
|
||||
const redirectToLoginPage = () => {
|
||||
window.location.href = getSubpath() ? `${getSubpath()}` : '/';
|
||||
};
|
||||
if (whiteLabelLogo == null) {
|
||||
return <div></div>;
|
||||
}
|
||||
return (
|
||||
<div className="white-labelling-form-wrapper">
|
||||
<div className="tooljet-header cursor-pointer" onClick={redirectToLoginPage}>
|
||||
{whiteLabelLogo != '' &&
|
||||
window.location.pathname != '/setup' &&
|
||||
whiteLabelLogo != defaultWhiteLabellingSettings.WHITE_LABEL_LOGO ? (
|
||||
<img width={IMAGE_WIDTH} height={IMAGE_HEIGHT} src={whiteLabelLogo} />
|
||||
) : (
|
||||
<Logo />
|
||||
)}
|
||||
</div>
|
||||
{components}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WhiteLabellingFormWrapper;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './WhiteLabellingFormWrapper';
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* Default styles */
|
||||
.white-labelling-form-wrapper {
|
||||
margin-top: 0px;
|
||||
/* Default margin-top */
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import OnboardingFormWrapper from './OnboardingFormWrapper';
|
|||
import OnboardingFormInsideWrapper from './OnboardingFormInsideWrapper';
|
||||
import OnboardingQuestions from './OnboardingQuestions';
|
||||
import OnboardingForm from './OnboardingForm';
|
||||
import OnboardingUIWrapper from './OnboardingUIWrapper';
|
||||
|
||||
export {
|
||||
OnboardingBackgroundWrapper,
|
||||
|
|
@ -10,4 +11,5 @@ export {
|
|||
OnboardingFormInsideWrapper,
|
||||
OnboardingQuestions,
|
||||
OnboardingForm,
|
||||
OnboardingUIWrapper,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { OnboardingFormWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper } from '@/modules/onboarding/components';
|
||||
import { FormTextInput, PasswordInput, SubmitButton, FormHeader } from '@/modules/common/components';
|
||||
import useOnboardingStore from '@/modules/onboarding/stores/onboardingStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
|
@ -86,7 +86,7 @@ const SetupAdminForm = () => {
|
|||
};
|
||||
useEnterKeyPress(() => handleSubmit());
|
||||
return (
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<div className="onboarding-form-width">
|
||||
<FormHeader>Set up your admin account</FormHeader>
|
||||
<form onSubmit={handleSubmit} className="form-input-area" style={formAreaStyles}>
|
||||
|
|
@ -135,7 +135,7 @@ const SetupAdminForm = () => {
|
|||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import {
|
||||
OnboardingBackgroundWrapper,
|
||||
OnboardingFormInsideWrapper,
|
||||
OnboardingFormWrapper,
|
||||
OnboardingUIWrapper,
|
||||
} from '@/modules/onboarding/components';
|
||||
import { SubmitButton, FormHeader, FormDescription, GeneralFeatureImage } from '@/modules/common/components';
|
||||
import { useEnterKeyPress } from '@/modules/common/hooks';
|
||||
|
|
@ -32,13 +32,13 @@ const SetupToolJetPage = () => {
|
|||
const LeftSideComponent = () => {
|
||||
return (
|
||||
<div className="setup-tooljet-page">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
<FormHeader>{headerText}</FormHeader>
|
||||
<FormDescription>{description}</FormDescription>
|
||||
<SubmitButton className="accept-invite-button" buttonText="Set up ToolJet" onClick={handleClick} />
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { OnboardingFormWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper, OnboardingFormInsideWrapper } from '@/modules/onboarding/components';
|
||||
import { FormTextInput, PasswordInput, SubmitButton, FormHeader, SSOAuthModule } from '@/modules/common/components';
|
||||
import SignupStatusCard from './components/SignupStatusCard';
|
||||
import './resources/styles/sign-up-form.styles.scss';
|
||||
|
|
@ -149,7 +149,7 @@ const SignupForm = ({
|
|||
|
||||
return (
|
||||
<div className="signup-form">
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<OnboardingFormInsideWrapper>
|
||||
<FormHeader>{t('loginSignupPage.signUp', 'Sign up')}</FormHeader>
|
||||
{(organizationId || shouldShowSignInCTA) && (
|
||||
|
|
@ -241,7 +241,7 @@ const SignupForm = ({
|
|||
</>
|
||||
)}
|
||||
</OnboardingFormInsideWrapper>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import OnboardingBackgroundWrapper from '@/modules/onboarding/components/OnboardingBackgroundWrapper';
|
||||
import { OnboardingFormWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper } from '@/modules/onboarding/components';
|
||||
import { FormHeader } from '@/modules/common/components';
|
||||
import './resources/styles/email-verification.styles.scss';
|
||||
import ResendVerificationEmail from './components/ResendVerificationEmail/ResendVerificationEmail';
|
||||
|
|
@ -12,7 +12,7 @@ const SignupSuccessInfo = ({ email, name, backToSignup, organizationId, redirect
|
|||
const info = `Did not receive an email? Check your spam folder!`;
|
||||
return (
|
||||
<div className="email-verification-wrapper" style={{ width: '356px' }}>
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<FormHeader>Check your mail</FormHeader>
|
||||
<p className="message">{message}</p>
|
||||
<span className="message">{info}</span>
|
||||
|
|
@ -23,7 +23,7 @@ const SignupSuccessInfo = ({ email, name, backToSignup, organizationId, redirect
|
|||
<span className="button-text">Back to sign up</span>
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { OnboardingFormWrapper } from '@/modules/onboarding/components';
|
||||
import { OnboardingUIWrapper } from '@/modules/onboarding/components';
|
||||
import {
|
||||
FormTextInput,
|
||||
SubmitButton,
|
||||
|
|
@ -73,7 +73,7 @@ const WorkspaceInvitationPage = (props) => {
|
|||
|
||||
const LeftSideComponent = () => {
|
||||
return (
|
||||
<OnboardingFormWrapper>
|
||||
<OnboardingUIWrapper>
|
||||
<div className="onboarding-form-width">
|
||||
<FormHeader>{`Join ${invitedOrganizationName}`}</FormHeader>
|
||||
<FormDescription>{`You are invited to ${
|
||||
|
|
@ -122,7 +122,7 @@ const WorkspaceInvitationPage = (props) => {
|
|||
</p>
|
||||
)}
|
||||
</div>
|
||||
</OnboardingFormWrapper>
|
||||
</OnboardingUIWrapper>
|
||||
);
|
||||
};
|
||||
return <OnboardingBackgroundWrapper LeftSideComponent={LeftSideComponent} RightSideComponent={GeneralFeatureImage} />;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 4.9 KiB |
|
|
@ -204,6 +204,13 @@
|
|||
margin-right: 10px;
|
||||
height: 20px !important;
|
||||
}
|
||||
.social-icon-fit {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.social-icon-large {
|
||||
height: 22px !important;
|
||||
|
||||
}
|
||||
|
||||
/* RESPONSIVE */
|
||||
@media only screen and (max-width: 600px) {
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@
|
|||
</p>
|
||||
<div class="social-links">
|
||||
<a target="_blank" href="https://github.com/ToolJet/ToolJet/stargazers">
|
||||
<img class="social-icons" alt="Company" src="cid:github" />
|
||||
<img height="20" width="auto" class="social-icons" alt="Company" src="cid:github" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.linkedin.com/company/tooljet">
|
||||
<img class="social-icons" alt="Company" src="cid:linkedin" />
|
||||
<img height="20" width="auto" class="social-icons" alt="Company" src="cid:linkedin" />
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://www.youtube.com/channel/UCf1p2G5Z7fPpvlBPf4l2I1w"
|
||||
>
|
||||
<img class="social-icons" alt="Company" src="cid:youtube" />
|
||||
<img height="22" width="auto" class="social-icons social-icon-fit social-icon-large" alt="Company" src="cid:youtube" />
|
||||
</a>
|
||||
<a target="_blank" href="https://twitter.com/ToolJet">
|
||||
<img class="social-icons" alt="Company" src="cid:twitter" />
|
||||
<img height="20" width="auto" class="social-icons social-icon-fit" alt="Company" src="cid:twitter" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<div class="header">
|
||||
<div class="logo-container padding-x-40 padding-y-20">
|
||||
{{#if (eq whiteLabelText "ToolJet")}}
|
||||
<img class="company-logo" alt="Company" src="cid:rocket" />
|
||||
<img height="30" width="auto" class="company-logo" alt="Company" src="cid:rocket" />
|
||||
{{else}}
|
||||
<img class="white-label-logo" alt="Company" src={{whiteLabelLogo}} />
|
||||
<img height="30" width="30" class="white-label-logo" alt="Company" src={{whiteLabelLogo}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -234,6 +234,7 @@ export function getUserInGroupQuery(
|
|||
'users.firstName',
|
||||
'users.lastName',
|
||||
'users.email',
|
||||
'users.avatarId',
|
||||
'userRole.id',
|
||||
'role.name',
|
||||
'organizationUsers.status',
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export class FoldersService {
|
|||
.innerJoin('folderApp.app', 'app', 'folderApp.folderId = :id', {
|
||||
id: folder.id,
|
||||
})
|
||||
.where('app.name LIKE :name', { name: `%${searchKey}%` })
|
||||
.where('LOWER(app.name) LIKE :name', { name: `%${(searchKey ?? '').toLowerCase()}%` })
|
||||
.getMany();
|
||||
|
||||
const userPermission = await this.abilityService.resourceActionsPermission(user, {
|
||||
|
|
|
|||