feat: manage env code split (#28215)
Coed split PR for custom environments [EE PR](https://github.com/appsmithorg/appsmith-ee/pull/2207)
This commit is contained in:
parent
80e0458cbc
commit
fc56e8fbbd
168
app/client/src/ce/components/WorkspaceSettingsTabs/index.tsx
Normal file
168
app/client/src/ce/components/WorkspaceSettingsTabs/index.tsx
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import React, { useCallback, useEffect, useMemo } from "react";
|
||||
import {
|
||||
useRouteMatch,
|
||||
Route,
|
||||
useLocation,
|
||||
useHistory,
|
||||
} from "react-router-dom";
|
||||
import MemberSettings from "@appsmith/pages/workspace/Members";
|
||||
import { GeneralSettings } from "pages/workspace/General";
|
||||
import { Tabs, Tab, TabsList, TabPanel } from "design-system";
|
||||
import { navigateToTab } from "@appsmith/pages/workspace/helpers";
|
||||
import styled from "styled-components";
|
||||
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { APPLICATIONS_URL } from "constants/routes/baseRoutes";
|
||||
export const SentryRoute = Sentry.withSentryRouting(Route);
|
||||
|
||||
export const TabsWrapper = styled.div`
|
||||
padding-top: var(--ads-v2-spaces-4);
|
||||
|
||||
.ads-v2-tabs {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-panel {
|
||||
height: calc(100% - 46px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface TabProp {
|
||||
key: string;
|
||||
title: string;
|
||||
count?: number;
|
||||
panelComponent?: JSX.Element;
|
||||
}
|
||||
|
||||
export interface WorkspaceSettingsTabsProps {
|
||||
currentTab: string | undefined;
|
||||
isMemberofTheWorkspace: boolean;
|
||||
hasManageWorkspacePermissions: boolean;
|
||||
searchValue: string;
|
||||
setTabArrLen: (tabArrLen: number) => void;
|
||||
workspacePermissions?: string[];
|
||||
// EE Tab Props
|
||||
addTabComponent?: () => TabProp;
|
||||
eeTabRedirect?: boolean;
|
||||
}
|
||||
|
||||
enum TABS {
|
||||
GENERAL = "general",
|
||||
MEMBERS = "members",
|
||||
}
|
||||
|
||||
export const WorkspaceSettingsTabs = ({
|
||||
addTabComponent,
|
||||
currentTab,
|
||||
eeTabRedirect,
|
||||
hasManageWorkspacePermissions,
|
||||
isMemberofTheWorkspace,
|
||||
searchValue,
|
||||
setTabArrLen,
|
||||
workspacePermissions,
|
||||
}: WorkspaceSettingsTabsProps) => {
|
||||
const { path } = useRouteMatch();
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
const shouldRedirect = useMemo(() => {
|
||||
// If the permissions are not yet fetched, don't redirect
|
||||
if (!workspacePermissions) {
|
||||
return false;
|
||||
}
|
||||
// If user doesn't have manage workspace permissions & is on settings page, redirect to applications
|
||||
if (currentTab === TABS.GENERAL && !hasManageWorkspacePermissions)
|
||||
return true;
|
||||
// If user doesn't have manage members permissions & is on members page, redirect to applications
|
||||
if (currentTab === TABS.MEMBERS && !isMemberofTheWorkspace) return true;
|
||||
// If the redirect flag is set to true by EE application, redirect to applications
|
||||
if (eeTabRedirect) return true;
|
||||
return false;
|
||||
}, [
|
||||
workspacePermissions,
|
||||
isMemberofTheWorkspace,
|
||||
hasManageWorkspacePermissions,
|
||||
currentTab,
|
||||
eeTabRedirect,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldRedirect) {
|
||||
history.replace(APPLICATIONS_URL);
|
||||
}
|
||||
}, [shouldRedirect]);
|
||||
|
||||
const GeneralSettingsComponent = (
|
||||
<SentryRoute
|
||||
component={GeneralSettings}
|
||||
location={location}
|
||||
path={`${path}/general`}
|
||||
/>
|
||||
);
|
||||
|
||||
const MemberSettingsComponent = (
|
||||
<SentryRoute
|
||||
component={useCallback(
|
||||
(props: any) => (
|
||||
<MemberSettings {...props} searchValue={searchValue} />
|
||||
),
|
||||
[location, searchValue],
|
||||
)}
|
||||
location={location}
|
||||
path={`${path}/members`}
|
||||
/>
|
||||
);
|
||||
|
||||
const tabArr: TabProp[] = [
|
||||
hasManageWorkspacePermissions && {
|
||||
key: "general",
|
||||
title: "General Settings",
|
||||
panelComponent: GeneralSettingsComponent,
|
||||
},
|
||||
isMemberofTheWorkspace && {
|
||||
key: "members",
|
||||
title: "Members",
|
||||
panelComponent: MemberSettingsComponent,
|
||||
},
|
||||
addTabComponent && addTabComponent(),
|
||||
].filter(Boolean) as TabProp[];
|
||||
|
||||
useEffect(() => {
|
||||
setTabArrLen(tabArr.length);
|
||||
}, [tabArr.length, setTabArrLen]);
|
||||
|
||||
return (
|
||||
<TabsWrapper
|
||||
className="tabs-wrapper"
|
||||
data-testid="t--user-edit-tabs-wrapper"
|
||||
>
|
||||
<Tabs
|
||||
defaultValue={currentTab}
|
||||
onValueChange={(key: string) => navigateToTab(key, location, history)}
|
||||
value={currentTab}
|
||||
>
|
||||
<TabsList>
|
||||
{tabArr.map((tab) => {
|
||||
return (
|
||||
<Tab
|
||||
data-testid={`t--tab-${tab.key}`}
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
>
|
||||
<div className="tab-item">{tab.title}</div>
|
||||
</Tab>
|
||||
);
|
||||
})}
|
||||
</TabsList>
|
||||
{tabArr.map((tab) => {
|
||||
return (
|
||||
<TabPanel className="tab-panel" key={tab.key} value={tab.key}>
|
||||
{tab.panelComponent}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</TabsWrapper>
|
||||
);
|
||||
};
|
||||
|
|
@ -72,11 +72,18 @@ export interface AppsmithUIConfigs {
|
|||
customerPortalUrl: string;
|
||||
}
|
||||
|
||||
export interface DatasourceMeta {
|
||||
configuredDatasources: number;
|
||||
totalDatasources: number;
|
||||
}
|
||||
|
||||
// Type for one environment
|
||||
export interface EnvironmentType {
|
||||
id: string;
|
||||
name: string;
|
||||
workspaceId: string;
|
||||
isDefault?: boolean;
|
||||
isLocked: boolean; // Whether the environment is locked (disables editing and deleting of the env)
|
||||
userPermissions?: string[];
|
||||
datasourceMeta?: DatasourceMeta;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -508,6 +508,7 @@ export const PAGE_SERVER_UNAVAILABLE_ERROR_MESSAGES = (
|
|||
export const POST = () => "Post";
|
||||
export const CANCEL = () => "Cancel";
|
||||
export const REMOVE = () => "Remove";
|
||||
export const CREATE = () => "Create";
|
||||
|
||||
// Showcase Carousel
|
||||
export const NEXT = () => "NEXT";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
export const ManageEnvironmentsMenu = ({}: {
|
||||
workspaceId: string;
|
||||
workspacePermissions: string[];
|
||||
}) => {
|
||||
return null;
|
||||
};
|
||||
|
|
@ -18,6 +18,7 @@ import {
|
|||
DropdownOnSelectActions,
|
||||
getOnSelectAction,
|
||||
} from "pages/common/CustomizedDropdown/dropdownHelpers";
|
||||
import { ManageEnvironmentsMenu } from "@appsmith/pages/Applications/ManageEnvironmentsMenu";
|
||||
|
||||
interface WorkspaceMenuProps {
|
||||
canDeleteWorkspace: boolean;
|
||||
|
|
@ -162,6 +163,10 @@ function WorkspaceMenu({
|
|||
Members
|
||||
</MenuItem>
|
||||
)}
|
||||
<ManageEnvironmentsMenu
|
||||
workspaceId={workspace.id}
|
||||
workspacePermissions={workspace.userPermissions || []}
|
||||
/>
|
||||
{canInviteToWorkspace && (
|
||||
<MenuItem
|
||||
className="error-menuitem"
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ import { resetEditorRequest } from "actions/initActions";
|
|||
import {
|
||||
hasCreateNewAppPermission,
|
||||
hasDeleteWorkspacePermission,
|
||||
hasManageWorkspaceEnvironmentPermission,
|
||||
isPermitted,
|
||||
PERMISSION_TYPE,
|
||||
} from "@appsmith/utils/permissionHelpers";
|
||||
|
|
@ -102,6 +103,7 @@ import ResourceListLoader from "@appsmith/pages/Applications/ResourceListLoader"
|
|||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import { getHasCreateWorkspacePermission } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
|
||||
import { allowManageEnvironmentAccessForUser } from "@appsmith/selectors/environmentSelectors";
|
||||
|
||||
export const { cloudHosting } = getAppsmithConfigs();
|
||||
|
||||
|
|
@ -468,6 +470,9 @@ export function ApplicationsSection(props: any) {
|
|||
const [workspaceToOpenMenu, setWorkspaceToOpenMenu] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const isManageEnvironmentEnabled = useSelector(
|
||||
allowManageEnvironmentAccessForUser,
|
||||
);
|
||||
const updateApplicationDispatch = (
|
||||
id: string,
|
||||
data: UpdateApplicationPayload,
|
||||
|
|
@ -605,6 +610,10 @@ export function ApplicationsSection(props: any) {
|
|||
const hasCreateNewApplicationPermission =
|
||||
hasCreateNewAppPermission(workspace.userPermissions) && !isMobile;
|
||||
|
||||
const renderManageEnvironmentMenu =
|
||||
isManageEnvironmentEnabled &&
|
||||
hasManageWorkspaceEnvironmentPermission(workspace.userPermissions);
|
||||
|
||||
const onClickAddNewAppButton = (workspaceId: string) => {
|
||||
if (
|
||||
Object.entries(creatingApplicationMap).length === 0 ||
|
||||
|
|
@ -624,7 +633,8 @@ export function ApplicationsSection(props: any) {
|
|||
canInviteToWorkspace ||
|
||||
hasManageWorkspacePermissions ||
|
||||
hasCreateNewApplicationPermission ||
|
||||
(canDeleteWorkspace && applications.length === 0);
|
||||
(canDeleteWorkspace && applications.length === 0) ||
|
||||
renderManageEnvironmentMenu;
|
||||
|
||||
const handleResetMenuState = () => {
|
||||
setWorkspaceToOpenMenu(null);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ export const MembersWrapper = styled.div<{
|
|||
isMobile?: boolean;
|
||||
}>`
|
||||
&.members-wrapper {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
${(props) => (props.isMobile ? "width: 100%; margin: auto" : null)}
|
||||
table {
|
||||
table-layout: fixed;
|
||||
|
|
@ -124,7 +126,7 @@ export const UserCard = styled(Card)`
|
|||
border: 1px solid var(--ads-v2-color-border);
|
||||
border-radius: var(--ads-v2-border-radius);
|
||||
padding: ${(props) =>
|
||||
`${props.theme.spaces[15]}px ${props.theme.spaces[7] * 4}px;`}
|
||||
`${props.theme.spaces[15]}px ${props.theme.spaces[7] * 4}px;`};
|
||||
width: 100%;
|
||||
height: 201px;
|
||||
margin: auto;
|
||||
|
|
|
|||
|
|
@ -23,3 +23,5 @@ export const getCurrentEnvironmentDetails = (state: AppState) => ({
|
|||
name: "",
|
||||
editingId: "unused_env",
|
||||
});
|
||||
|
||||
export const allowManageEnvironmentAccessForUser = (state: AppState) => false;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ export const getCurrentWorkspaceId = (state: AppState) =>
|
|||
export const getWorkspaces = (state: AppState) => {
|
||||
return state.ui.applications.userWorkspaces;
|
||||
};
|
||||
export const getWorkspaceFromId = (state: AppState, workspaceId: string) => {
|
||||
const filteredWorkspaces = state.ui.applications.userWorkspaces.filter(
|
||||
(el) => el.workspace.id === workspaceId,
|
||||
);
|
||||
return !!filteredWorkspaces && filteredWorkspaces.length > 0
|
||||
? filteredWorkspaces[0].workspace
|
||||
: undefined;
|
||||
};
|
||||
export const getCurrentWorkspace = (state: AppState) => {
|
||||
return state.ui.applications.userWorkspaces.map((el) => el.workspace);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -101,3 +101,7 @@ export const hasDeleteActionPermission = (_permissions?: string[]) => true;
|
|||
export const hasExecuteActionPermission = (_permissions?: string[]) => true;
|
||||
|
||||
export const hasAuditLogsReadPermission = (_permissions?: string[]) => true;
|
||||
|
||||
export const hasManageWorkspaceEnvironmentPermission = (
|
||||
_permissions?: string[],
|
||||
) => false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export * from "ce/components/WorkspaceSettingsTabs";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from "ce/pages/Applications/ManageEnvironmentsMenu";
|
||||
|
|
@ -397,6 +397,8 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
|
|||
const { configProperty, controlType, isRequired } = config;
|
||||
const configDetails = this.state.configDetails;
|
||||
const requiredFields = this.state.requiredFields;
|
||||
if (!configProperty || !configProperty.includes(this.getEnvironmentId()))
|
||||
return;
|
||||
configDetails[configProperty] = controlType;
|
||||
if (isRequired) requiredFields[configProperty] = config;
|
||||
|
||||
|
|
@ -683,6 +685,8 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
|
|||
name,
|
||||
userPermissions,
|
||||
},
|
||||
configDetails: {},
|
||||
requiredFields: {},
|
||||
});
|
||||
this.blockRoutes();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ const TabPanelContainer = styled(TabPanel)`
|
|||
|
||||
const ConfigurationsTabPanelContainer = styled(TabPanel)`
|
||||
margin-top: 0;
|
||||
overflow: hidden;
|
||||
overflow: scroll;
|
||||
flex-grow: 1;
|
||||
padding: 0 var(--ads-v2-spaces-7);
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ import { Classes } from "@blueprintjs/core";
|
|||
import { getIsFetchingApplications } from "@appsmith/selectors/applicationSelectors";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
|
||||
// This wrapper ensures that the scroll behaviour is consistent with the other tabs
|
||||
const ScrollWrapper = styled.div`
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// trigger tests
|
||||
const GeneralWrapper = styled.div<{
|
||||
isMobile?: boolean;
|
||||
|
|
@ -170,86 +177,90 @@ export function GeneralSettings() {
|
|||
});
|
||||
|
||||
return (
|
||||
<GeneralWrapper isMobile={isMobile} isPortrait={isPortrait}>
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-name-input"
|
||||
defaultValue={currentWorkspace && currentWorkspace.name}
|
||||
isRequired
|
||||
label="Workspace name"
|
||||
labelPosition="top"
|
||||
onChange={onWorkspaceNameChange}
|
||||
placeholder="Workspace name"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
<ScrollWrapper>
|
||||
<GeneralWrapper isMobile={isMobile} isPortrait={isPortrait}>
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-name-input"
|
||||
defaultValue={currentWorkspace && currentWorkspace.name}
|
||||
isRequired
|
||||
label="Workspace name"
|
||||
labelPosition="top"
|
||||
onChange={onWorkspaceNameChange}
|
||||
placeholder="Workspace name"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
|
||||
<SettingWrapper>
|
||||
<Row className="t--workspace-settings-filepicker">
|
||||
<InputLabelWrapper>
|
||||
<Text type={TextType.P1}>Upload logo</Text>
|
||||
</InputLabelWrapper>
|
||||
{isFetchingWorkspace && (
|
||||
<FilePickerLoader className={Classes.SKELETON} />
|
||||
)}
|
||||
{!isFetchingWorkspace && (
|
||||
<FilePickerV2
|
||||
fileType={FileType.IMAGE}
|
||||
fileUploader={FileUploader}
|
||||
logoUploadError={logoUploadError.message}
|
||||
onFileRemoved={DeleteLogo}
|
||||
url={currentWorkspace && currentWorkspace.logoUrl}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper>
|
||||
<Row className="t--workspace-settings-filepicker">
|
||||
<InputLabelWrapper>
|
||||
<Text type={TextType.P1}>Upload logo</Text>
|
||||
</InputLabelWrapper>
|
||||
{isFetchingWorkspace && (
|
||||
<FilePickerLoader className={Classes.SKELETON} />
|
||||
)}
|
||||
{!isFetchingWorkspace && (
|
||||
<FilePickerV2
|
||||
fileType={FileType.IMAGE}
|
||||
fileUploader={FileUploader}
|
||||
logoUploadError={logoUploadError.message}
|
||||
onFileRemoved={DeleteLogo}
|
||||
url={currentWorkspace && currentWorkspace.logoUrl}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-website-input"
|
||||
defaultValue={
|
||||
(currentWorkspace && currentWorkspace.website) || ""
|
||||
}
|
||||
label="Website"
|
||||
labelPosition="top"
|
||||
onChange={onWebsiteChange}
|
||||
placeholder="Your website"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-website-input"
|
||||
defaultValue={
|
||||
(currentWorkspace && currentWorkspace.website) || ""
|
||||
}
|
||||
label="Website"
|
||||
labelPosition="top"
|
||||
onChange={onWebsiteChange}
|
||||
placeholder="Your website"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-email-input"
|
||||
defaultValue={(currentWorkspace && currentWorkspace.email) || ""}
|
||||
label="Email"
|
||||
labelPosition="top"
|
||||
onChange={onEmailChange}
|
||||
placeholder="Email"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
</GeneralWrapper>
|
||||
<SettingWrapper>
|
||||
<Row>
|
||||
{isFetchingApplications && <Loader className={Classes.SKELETON} />}
|
||||
{!isFetchingApplications && (
|
||||
<Input
|
||||
data-testid="t--workspace-email-input"
|
||||
defaultValue={
|
||||
(currentWorkspace && currentWorkspace.email) || ""
|
||||
}
|
||||
label="Email"
|
||||
labelPosition="top"
|
||||
onChange={onEmailChange}
|
||||
placeholder="Email"
|
||||
renderAs="input"
|
||||
size="md"
|
||||
type="text"
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</SettingWrapper>
|
||||
</GeneralWrapper>
|
||||
</ScrollWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ describe("<Settings />", () => {
|
|||
it("displays tabs", () => {
|
||||
renderComponent();
|
||||
const tabList = screen.getAllByRole("tab");
|
||||
expect(tabList).toHaveLength(2);
|
||||
expect(tabList.length).toBeGreaterThanOrEqual(2);
|
||||
expect(tabList.length).toBeLessThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,49 +1,29 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
useRouteMatch,
|
||||
useLocation,
|
||||
useParams,
|
||||
Route,
|
||||
useHistory,
|
||||
} from "react-router-dom";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import { getCurrentWorkspace } from "@appsmith/selectors/workspaceSelectors";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Tabs, Tab, TabsList, TabPanel } from "design-system";
|
||||
import MemberSettings from "@appsmith/pages/workspace/Members";
|
||||
import { GeneralSettings } from "./General";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { getAllApplications } from "@appsmith/actions/applicationActions";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
import { BackButton, StickyHeader } from "components/utils/helperComponents";
|
||||
import { debounce } from "lodash";
|
||||
import WorkspaceInviteUsersForm from "pages/workspace/WorkspaceInviteUsersForm";
|
||||
import { SettingsPageHeader } from "./SettingsPageHeader";
|
||||
import { navigateToTab } from "@appsmith/pages/workspace/helpers";
|
||||
import {
|
||||
isPermitted,
|
||||
PERMISSION_TYPE,
|
||||
} from "@appsmith/utils/permissionHelpers";
|
||||
import {
|
||||
createMessage,
|
||||
DOCUMENTATION,
|
||||
INVITE_USERS_PLACEHOLDER,
|
||||
SEARCH_USERS,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { APPLICATIONS_URL } from "constants/routes";
|
||||
import FormDialogComponent from "components/editorComponents/form/FormDialogComponent";
|
||||
import { debounce } from "lodash";
|
||||
import { WorkspaceSettingsTabs } from "@appsmith/components/WorkspaceSettingsTabs";
|
||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
|
||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||
|
||||
interface TabProp {
|
||||
key: string;
|
||||
title: string;
|
||||
count?: number;
|
||||
panelComponent?: JSX.Element;
|
||||
}
|
||||
|
||||
const SettingsWrapper = styled.div<{
|
||||
isMobile?: boolean;
|
||||
}>`
|
||||
|
|
@ -77,19 +57,6 @@ const StyledStickyHeader = styled(StickyHeader)<{ isMobile?: boolean }>`
|
|||
width: 954px;
|
||||
`}
|
||||
`;
|
||||
export const TabsWrapper = styled.div`
|
||||
padding-top: var(--ads-v2-spaces-4);
|
||||
|
||||
.ads-v2-tabs {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.tab-panel {
|
||||
overflow: auto;
|
||||
height: calc(100% - 46px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
enum TABS {
|
||||
GENERAL = "general",
|
||||
|
|
@ -101,7 +68,6 @@ export default function Settings() {
|
|||
const currentWorkspace = useSelector(getCurrentWorkspace).filter(
|
||||
(el) => el.id === workspaceId,
|
||||
)[0];
|
||||
const { path } = useRouteMatch();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
|
@ -109,13 +75,11 @@ export default function Settings() {
|
|||
const [searchValue, setSearchValue] = useState("");
|
||||
|
||||
const [pageTitle, setPageTitle] = useState<string>("");
|
||||
|
||||
const history = useHistory();
|
||||
const [tabArrLen, setTabArrLen] = useState<number>(0);
|
||||
|
||||
const isGACEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||
|
||||
const currentTab = location.pathname.split("/").pop();
|
||||
// const [selectedTab, setSelectedTab] = useState(currentTab);
|
||||
|
||||
const isMemberofTheWorkspace = isPermitted(
|
||||
currentWorkspace?.userPermissions || [],
|
||||
|
|
@ -125,58 +89,31 @@ export default function Settings() {
|
|||
currentWorkspace?.userPermissions,
|
||||
PERMISSION_TYPE.MANAGE_WORKSPACE,
|
||||
);
|
||||
const shouldRedirect = useMemo(
|
||||
() =>
|
||||
currentWorkspace &&
|
||||
((!isMemberofTheWorkspace && currentTab === TABS.MEMBERS) ||
|
||||
(!hasManageWorkspacePermissions && currentTab === TABS.GENERAL)),
|
||||
[
|
||||
currentWorkspace,
|
||||
isMemberofTheWorkspace,
|
||||
hasManageWorkspacePermissions,
|
||||
currentTab,
|
||||
],
|
||||
);
|
||||
const showMembersTab =
|
||||
isMemberofTheWorkspace && hasManageWorkspacePermissions;
|
||||
|
||||
const onButtonClick = () => {
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldRedirect) {
|
||||
history.replace(APPLICATIONS_URL);
|
||||
}
|
||||
if (currentWorkspace) {
|
||||
setPageTitle(`${currentWorkspace?.name}`);
|
||||
}
|
||||
}, [currentWorkspace, shouldRedirect]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentWorkspace) {
|
||||
dispatch(getAllApplications());
|
||||
} else {
|
||||
setPageTitle(`${currentWorkspace?.name}`);
|
||||
}
|
||||
}, [dispatch, currentWorkspace]);
|
||||
|
||||
const GeneralSettingsComponent = (
|
||||
<SentryRoute
|
||||
component={GeneralSettings}
|
||||
location={location}
|
||||
path={`${path}/general`}
|
||||
/>
|
||||
);
|
||||
const pageMenuItems: any[] = [
|
||||
{
|
||||
icon: "book-line",
|
||||
className: "documentation-page-menu-item",
|
||||
onSelect: () => {},
|
||||
text: createMessage(DOCUMENTATION),
|
||||
},
|
||||
];
|
||||
|
||||
const MemberSettingsComponent = (
|
||||
<SentryRoute
|
||||
component={useCallback(
|
||||
(props: any) => (
|
||||
<MemberSettings {...props} searchValue={searchValue} />
|
||||
),
|
||||
[location, searchValue],
|
||||
)}
|
||||
location={location}
|
||||
path={`${path}/members`}
|
||||
/>
|
||||
);
|
||||
const isMembersPage = tabArrLen > 1 && currentTab === TABS.MEMBERS;
|
||||
|
||||
const onSearch = debounce((search: string) => {
|
||||
if (search.trim().length > 0) {
|
||||
|
|
@ -186,33 +123,6 @@ export default function Settings() {
|
|||
}
|
||||
}, 300);
|
||||
|
||||
const tabArr: TabProp[] = [
|
||||
isMemberofTheWorkspace && {
|
||||
key: "members",
|
||||
title: "Members",
|
||||
panelComponent: MemberSettingsComponent,
|
||||
},
|
||||
{
|
||||
key: "general",
|
||||
title: "General Settings",
|
||||
panelComponent: GeneralSettingsComponent,
|
||||
},
|
||||
].filter(Boolean) as TabProp[];
|
||||
|
||||
const pageMenuItems: any[] = [
|
||||
{
|
||||
icon: "book-line",
|
||||
className: "documentation-page-menu-item",
|
||||
onSelect: () => {
|
||||
/*console.log("hello onSelect")*/
|
||||
},
|
||||
text: "Documentation",
|
||||
},
|
||||
];
|
||||
|
||||
const isMembersPage = tabArr.length > 1 && currentTab === TABS.MEMBERS;
|
||||
// const isGeneralPage = tabArr.length === 1 && currentTab === TABS.GENERAL;
|
||||
|
||||
const isMobile: boolean = useMediaQuery({ maxWidth: 767 });
|
||||
return (
|
||||
<>
|
||||
|
|
@ -230,39 +140,14 @@ export default function Settings() {
|
|||
title={pageTitle}
|
||||
/>
|
||||
</StyledStickyHeader>
|
||||
<TabsWrapper
|
||||
className="tabs-wrapper"
|
||||
data-testid="t--user-edit-tabs-wrapper"
|
||||
>
|
||||
<Tabs
|
||||
defaultValue={currentTab}
|
||||
onValueChange={(key: string) =>
|
||||
navigateToTab(key, location, history)
|
||||
}
|
||||
value={currentTab}
|
||||
>
|
||||
<TabsList>
|
||||
{tabArr.map((tab) => {
|
||||
return (
|
||||
<Tab
|
||||
data-testid={`t--tab-${tab.key}`}
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
>
|
||||
<div className="tab-item">{tab.title}</div>
|
||||
</Tab>
|
||||
);
|
||||
})}
|
||||
</TabsList>
|
||||
{tabArr.map((tab) => {
|
||||
return (
|
||||
<TabPanel className="tab-panel" key={tab.key} value={tab.key}>
|
||||
{tab.panelComponent}
|
||||
</TabPanel>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</TabsWrapper>
|
||||
<WorkspaceSettingsTabs
|
||||
currentTab={currentTab}
|
||||
hasManageWorkspacePermissions={hasManageWorkspacePermissions}
|
||||
isMemberofTheWorkspace={showMembersTab}
|
||||
searchValue={searchValue}
|
||||
setTabArrLen={setTabArrLen}
|
||||
workspacePermissions={currentWorkspace?.userPermissions}
|
||||
/>
|
||||
</SettingsWrapper>
|
||||
{currentWorkspace && (
|
||||
<FormDialogComponent
|
||||
|
|
|
|||
|
|
@ -452,12 +452,12 @@ function* updateDatasourceSaga(
|
|||
>,
|
||||
) {
|
||||
try {
|
||||
const currentEnvDetails: { id: string; name: string } = yield select(
|
||||
const currentEnvDetails: { editingId: string; name: string } = yield select(
|
||||
getCurrentEnvironmentDetails,
|
||||
);
|
||||
const queryParams = getQueryParams();
|
||||
const currentEnvironment =
|
||||
actionPayload.payload?.currEditingEnvId || currentEnvDetails.id;
|
||||
actionPayload.payload?.currEditingEnvId || currentEnvDetails.editingId;
|
||||
const datasourcePayload = omit(actionPayload.payload, "name");
|
||||
const datasourceStoragePayload =
|
||||
datasourcePayload.datasourceStorages[currentEnvironment];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user