PromucFlow_constructor/app/client/src/ce/pages/Applications/index.tsx

915 lines
29 KiB
TypeScript

import React, {
Component,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import styled, { ThemeContext } from "styled-components";
import { connect, useDispatch, useSelector } from "react-redux";
import MediaQuery from "react-responsive";
import { useLocation } from "react-router-dom";
import type { AppState } from "@appsmith/reducers";
import { Classes as BlueprintClasses } from "@blueprintjs/core";
import {
thinScrollbar,
truncateTextUsingEllipsis,
} from "constants/DefaultTheme";
import {
getApplicationList,
getApplicationSearchKeyword,
getCreateApplicationError,
getCurrentApplicationIdForCreateNewApp,
getDeletingMultipleApps,
getIsCreatingApplication,
getIsDeletingApplication,
getIsFetchingApplications,
getIsSavingWorkspaceInfo,
getUserApplicationsWorkspaces,
getUserApplicationsWorkspacesList,
} from "@appsmith/selectors/applicationSelectors";
import type { ApplicationPayload } from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import PageWrapper from "pages/common/PageWrapper";
import SubHeader from "pages/common/SubHeader";
import WorkspaceInviteUsersForm from "pages/workspace/WorkspaceInviteUsersForm";
import type { User } from "constants/userConstants";
import { getCurrentUser } from "selectors/usersSelectors";
import { CREATE_WORKSPACE_FORM_NAME } from "@appsmith/constants/forms";
import {
AppIconCollection,
Classes,
EditableText,
MenuItem as ListItem,
Text,
TextType,
} from "design-system-old";
import { Divider, Icon } from "design-system";
import { updateApplication } from "@appsmith/actions/applicationActions";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import type { UpdateApplicationPayload } from "@appsmith/api/ApplicationApi";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
import { loadingUserWorkspaces } from "pages/Applications/ApplicationLoaders";
import type { creatingApplicationMap } from "@appsmith/reducers/uiReducers/applicationsReducer";
import {
deleteWorkspace,
resetCurrentWorkspace,
saveWorkspace,
} from "@appsmith/actions/workspaceActions";
import { leaveWorkspace } from "actions/userActions";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import NoSearchImage from "assets/images/NoSearchResult.svg";
import { getNextEntityName, getRandomPaletteColor } from "utils/AppsmithUtils";
import { createWorkspaceSubmitHandler } from "@appsmith/pages/workspace/helpers";
import ImportApplicationModal from "pages/Applications/ImportApplicationModal";
import {
createMessage,
INVITE_USERS_PLACEHOLDER,
NO_APPS_FOUND,
SEARCH_APPS,
WORKSPACES_HEADING,
} from "@appsmith/constants/messages";
import { setHeaderMeta } from "actions/themeActions";
import SharedUserList from "pages/common/SharedUserList";
import { useIsMobileDevice } from "utils/hooks/useDeviceDetect";
import { Indices } from "constants/Layers";
import GitSyncModal from "pages/Editor/gitSync/GitSyncModal";
import DisconnectGitModal from "pages/Editor/gitSync/DisconnectGitModal";
import ReconnectDatasourceModal from "pages/Editor/gitSync/ReconnectDatasourceModal";
import LeftPaneBottomSection from "pages/Home/LeftPaneBottomSection";
import { MOBILE_MAX_WIDTH } from "constants/AppConstants";
import urlBuilder from "@appsmith/entities/URLRedirect/URLAssembly";
import RepoLimitExceededErrorModal from "pages/Editor/gitSync/RepoLimitExceededErrorModal";
import { resetEditorRequest } from "actions/initActions";
import {
hasCreateNewAppPermission,
hasDeleteWorkspacePermission,
hasManageWorkspaceEnvironmentPermission,
isPermitted,
PERMISSION_TYPE,
} from "@appsmith/utils/permissionHelpers";
import { getTenantPermissions } from "@appsmith/selectors/tenantSelectors";
import { getAppsmithConfigs } from "@appsmith/configs";
import FormDialogComponent from "components/editorComponents/form/FormDialogComponent";
import WorkspaceMenu from "@appsmith/pages/Applications/WorkspaceMenu";
import ApplicationCardList from "@appsmith/pages/Applications/ApplicationCardList";
import { usePackage } from "@appsmith/pages/Applications/helpers";
import PackageCardList from "@appsmith/pages/Applications/PackageCardList";
import WorkspaceAction from "@appsmith/pages/Applications/WorkspaceAction";
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";
import CreateNewAppsOption from "@appsmith/pages/Applications/CreateNewAppsOption";
import { resetCurrentApplicationIdForCreateNewApp } from "actions/onboardingActions";
export const { cloudHosting } = getAppsmithConfigs();
export const CONTAINER_WRAPPER_PADDING = "var(--ads-v2-spaces-7)";
export const WorkspaceDropDown = styled.div<{ isMobile?: boolean }>`
display: flex;
padding: ${(props) => (props.isMobile ? `10px 16px` : `24px 0`)};
font-size: ${(props) => props.theme.fontSizes[1]}px;
justify-content: space-between;
align-items: center;
${({ isMobile }) =>
isMobile &&
`
background-color: #fff;
z-index: ${Indices.Layer8};
`}
`;
export const WorkspaceSection = styled.div<{ isMobile?: boolean }>`
padding: ${({ isMobile }) =>
isMobile ? 0 : `0 ${CONTAINER_WRAPPER_PADDING}`};
margin-bottom: ${({ isMobile }) => (isMobile ? `8` : `0`)}px;
`;
export const LeftPaneWrapper = styled.div<{ isBannerVisible?: boolean }>`
overflow: auto;
width: ${(props) => props.theme.homePage.sidebar}px;
height: ${(props) =>
props.isBannerVisible
? `calc(100% - ${props.theme.homePage.header * 2}px)`
: "100%"};
display: flex;
padding-top: 16px;
flex-direction: column;
position: fixed;
top: calc(
${(props) => props.theme.homePage.header}px +
${(props) => (props.isBannerVisible ? "48px" : "0px")}
);
border-right: 1px solid var(--ads-v2-color-border);
padding: 0 16px;
`;
export const ApplicationContainer = styled.div<{ isMobile?: boolean }>`
${({ isMobile }) =>
isMobile &&
`
margin-left: 0;
width: 100%;
padding: 0;
`}
`;
export const ItemWrapper = styled.div`
padding: 16px;
`;
export const StyledIcon = styled(Icon)`
margin-right: 11px;
`;
export const WorkspaceShareUsers = styled.div<{ isHidden?: boolean }>`
display: flex;
align-items: center;
${(props) => props.isHidden && "opacity: 0; visibility: hidden;"}
& .t--options-icon {
margin-left: 8px;
svg {
path {
fill: #090707;
}
}
}
& .t--new-button {
margin-left: 8px;
}
`;
export const NoAppsFound = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
& > span {
margin-bottom: 24px;
}
`;
export function Item(props: {
label: string;
textType: TextType;
icon?: string;
isFetchingApplications?: boolean;
}) {
return (
<ItemWrapper>
{props.icon && <StyledIcon name={props.icon} />}
<Text
className={
!!props.isFetchingApplications ? BlueprintClasses.SKELETON : ""
}
color={"var(--ads-v2-color-fg-emphasis)"}
type={props.textType}
>
{" "}
{props.label}
</Text>
</ItemWrapper>
);
}
const LeftPaneDataSection = styled.div<{ isBannerVisible?: boolean }>`
position: relative;
height: calc(100vh - ${(props) => 48 + (props.isBannerVisible ? 48 : 0)}px);
display: flex;
flex-direction: column;
`;
export function LeftPaneSection(props: {
heading: string;
children?: any;
isFetchingApplications: boolean;
isBannerVisible?: boolean;
}) {
return (
<LeftPaneDataSection isBannerVisible={props.isBannerVisible}>
<Item label={props.heading} textType={TextType.SIDE_HEAD} />
{props.children}
</LeftPaneDataSection>
);
}
export const StyledAnchor = styled.a`
position: relative;
top: -24px;
`;
export const WorkpsacesNavigator = styled.div`
overflow: auto;
margin-bottom: 4px;
${thinScrollbar};
`;
export const textIconStyles = (props: { color: string; hover: string }) => {
return `
&&&&&& {
.${Classes.TEXT},.${Classes.ICON} svg path {
color: ${props.color};
stroke: ${props.color};
fill: ${props.color};
}
&:hover {
.${Classes.TEXT},.${Classes.ICON} svg path {
color: ${props.hover};
stroke: ${props.hover};
fill: ${props.hover};
}
}
}
`;
};
export function WorkspaceMenuItem({
isFetchingApplications,
selected,
workspace,
}: any) {
const menuRef = useRef<HTMLAnchorElement>(null);
useEffect(() => {
if (selected) {
menuRef.current?.scrollIntoView({ behavior: "smooth" });
menuRef.current?.click();
}
}, [selected]);
return (
<ListItem
containerClassName={
isFetchingApplications ? BlueprintClasses.SKELETON : ""
}
ellipsize={
isFetchingApplications ? 100 : 19
} /* this is to avoid showing tooltip for loaders */
href={`${window.location.pathname}#${workspace.workspace.id}`}
icon="workspace"
key={workspace.workspace.id}
ref={menuRef}
selected={selected}
text={workspace.workspace.name}
tooltipPos={Position.BOTTOM_LEFT}
/>
);
}
export const submitCreateWorkspaceForm = async (data: any, dispatch: any) => {
const result = await createWorkspaceSubmitHandler(data, dispatch);
return result;
};
export interface LeftPaneProps {
isBannerVisible?: boolean;
}
export function LeftPane(props: LeftPaneProps) {
const { isBannerVisible = false } = props;
const dispatch = useDispatch();
const fetchedUserWorkspaces = useSelector(getUserApplicationsWorkspaces);
const isFetchingApplications = useSelector(getIsFetchingApplications);
const isMobile = useIsMobileDevice();
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
let userWorkspaces;
if (!isFetchingApplications) {
userWorkspaces = fetchedUserWorkspaces;
} else {
userWorkspaces = loadingUserWorkspaces as any;
}
const tenantPermissions = useSelector(getTenantPermissions);
const canCreateWorkspace = getHasCreateWorkspacePermission(
isFeatureEnabled,
tenantPermissions,
);
const location = useLocation();
const urlHash = location.hash.slice(1);
if (isMobile) return null;
return (
<LeftPaneWrapper isBannerVisible={isBannerVisible}>
<LeftPaneSection
heading={createMessage(WORKSPACES_HEADING)}
isBannerVisible={isBannerVisible}
isFetchingApplications={isFetchingApplications}
>
<WorkpsacesNavigator data-testid="t--left-panel">
{canCreateWorkspace && (
<ListItem
color="var(--ads-v2-color-fg-emphasis)"
data-testid="t--workspace-new-workspace-auto-create"
icon="plus"
onSelect={async () =>
submitCreateWorkspaceForm(
{
name: getNextEntityName(
"Untitled workspace ",
fetchedUserWorkspaces.map((el: any) => el.workspace.name),
),
},
dispatch,
)
}
text={CREATE_WORKSPACE_FORM_NAME}
/>
)}
{userWorkspaces &&
userWorkspaces.map((workspace: any) => (
<WorkspaceMenuItem
isFetchingApplications={isFetchingApplications}
key={workspace.workspace.id}
selected={urlHash === workspace.workspace.id}
workspace={workspace}
/>
))}
</WorkpsacesNavigator>
<LeftPaneBottomSection />
</LeftPaneSection>
</LeftPaneWrapper>
);
}
export const CreateNewLabel = styled(Text)`
margin-top: 18px;
`;
export const WorkspaceNameElement = styled(Text)<{ isMobile?: boolean }>`
max-width: ${({ isMobile }) => (isMobile ? 220 : 500)}px;
${truncateTextUsingEllipsis};
color: var(--ads-v2-color-fg);
font-weight: var(--ads-font-weight-bold-xl);
`;
export const WorkspaceNameHolder = styled(Text)`
display: flex;
align-items: center;
`;
export const WorkspaceNameWrapper = styled.div<{ disabled?: boolean }>`
${(props) => {
const color = props.disabled
? props.theme.colors.applications.workspaceColor
: props.theme.colors.applications.hover.workspaceColor[9];
return `${textIconStyles({
color: color,
hover: color,
})}`;
}}
.${Classes.ICON} {
display: ${(props) => (!props.disabled ? "inline" : "none")};
margin-left: 8px;
color: ${(props) => props.theme.colors.applications.iconColor};
}
`;
export const WorkspaceRename = styled(EditableText)`
padding: 0 2px;
`;
export const NoSearchResultImg = styled.img`
margin: 1em;
`;
export const ApplicationsWrapper = styled.div<{ isMobile: boolean }>`
height: calc(100vh - ${(props) => props.theme.homePage.search.height - 40}px);
overflow: auto;
margin-left: ${(props) => props.theme.homePage.leftPane.width}px;
width: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px);
scroll-behavior: smooth;
${({ isMobile }) =>
isMobile
? `padding: ${CONTAINER_WRAPPER_PADDING} 0;`
: `padding: 0 0 ${CONTAINER_WRAPPER_PADDING};`}
${({ isMobile }) =>
isMobile &&
`
margin-left: 0;
width: 100%;
padding: 0;
`}
`;
export function ApplicationsSection(props: any) {
const enableImportExport = true;
const dispatch = useDispatch();
const theme = useContext(ThemeContext);
const { isFetchingPackages } = usePackage();
const isSavingWorkspaceInfo = useSelector(getIsSavingWorkspaceInfo);
const isFetchingApplications = useSelector(getIsFetchingApplications);
const userWorkspaces = useSelector(getUserApplicationsWorkspacesList);
const creatingApplicationMap = useSelector(getIsCreatingApplication);
const currentUser = useSelector(getCurrentUser);
const isMobile = useIsMobileDevice();
const deleteMultipleApplicationObject = useSelector(getDeletingMultipleApps);
const isEnabledMultipleSelection =
!!deleteMultipleApplicationObject.list?.length;
const deleteApplication = (applicationId: string) => {
if (applicationId && applicationId.length > 0) {
dispatch({
type: ReduxActionTypes.DELETE_APPLICATION_INIT,
payload: {
applicationId,
},
});
}
};
const [warnLeavingWorkspace, setWarnLeavingWorkspace] = useState(false);
const [warnDeleteWorkspace, setWarnDeleteWorkspace] = useState(false);
const [workspaceToOpenMenu, setWorkspaceToOpenMenu] = useState<string | null>(
null,
);
const isManageEnvironmentEnabled = useSelector(
allowManageEnvironmentAccessForUser,
);
const updateApplicationDispatch = (
id: string,
data: UpdateApplicationPayload,
) => {
dispatch(updateApplication(id, data));
};
const isLoadingResources = isFetchingApplications || isFetchingPackages;
const isGACEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
useEffect(() => {
// Clears URL params cache
urlBuilder.resetURLParams();
}, []);
const [
selectedWorkspaceIdForImportApplication,
setSelectedWorkspaceIdForImportApplication,
] = useState<string | undefined>();
const leaveWS = (workspaceId: string) => {
setWarnLeavingWorkspace(false);
setWorkspaceToOpenMenu(null);
dispatch(leaveWorkspace(workspaceId));
};
const handleDeleteWorkspace = useCallback(
(workspaceId: string) => {
setWarnDeleteWorkspace(false);
setWorkspaceToOpenMenu(null);
dispatch(deleteWorkspace(workspaceId));
},
[dispatch],
);
const workspaceNameChange = (newName: string, workspaceId: string) => {
dispatch(
saveWorkspace({
id: workspaceId as string,
name: newName,
}),
);
};
function WorkspaceMenuTarget(props: {
workspaceName: string;
disabled?: boolean;
workspaceSlug: string;
}) {
const { disabled, workspaceName, workspaceSlug } = props;
return (
<WorkspaceNameWrapper
className="t--workspace-name-text"
disabled={disabled}
>
<StyledAnchor id={workspaceSlug} />
<WorkspaceNameHolder
className={isLoadingResources ? BlueprintClasses.SKELETON : ""}
type={TextType.H4}
>
<WorkspaceNameElement
className={isLoadingResources ? BlueprintClasses.SKELETON : ""}
isMobile={isMobile}
type={TextType.H4}
>
{workspaceName}
</WorkspaceNameElement>
</WorkspaceNameHolder>
</WorkspaceNameWrapper>
);
}
const createNewApplication = (
applicationName: string,
workspaceId: string,
) => {
const color = getRandomPaletteColor(theme.colors.appCardColors);
const icon =
AppIconCollection[Math.floor(Math.random() * AppIconCollection.length)];
return dispatch({
type: ReduxActionTypes.CREATE_APPLICATION_INIT,
payload: {
applicationName,
workspaceId,
icon,
color,
},
});
};
let updatedWorkspaces;
if (!isLoadingResources) {
updatedWorkspaces = userWorkspaces;
} else {
updatedWorkspaces = loadingUserWorkspaces as any;
}
let workspacesListComponent;
if (
!isLoadingResources &&
props.searchKeyword &&
props.searchKeyword.trim().length > 0 &&
updatedWorkspaces.length === 0
) {
workspacesListComponent = (
<CenteredWrapper
style={{
flexDirection: "column",
position: "static",
}}
>
<CreateNewLabel type={TextType.H4}>
{createMessage(NO_APPS_FOUND)}
</CreateNewLabel>
<NoSearchResultImg alt="No result found" src={NoSearchImage} />
</CenteredWrapper>
);
} else {
workspacesListComponent = updatedWorkspaces.map(
(workspaceObject: any, index: number) => {
const isLastWorkspace = updatedWorkspaces.length === index + 1;
const { applications, packages, workspace } = workspaceObject;
const hasManageWorkspacePermissions = isPermitted(
workspace.userPermissions,
PERMISSION_TYPE.MANAGE_WORKSPACE,
);
const canInviteToWorkspace = isPermitted(
workspace.userPermissions,
PERMISSION_TYPE.INVITE_USER_TO_WORKSPACE,
);
const canDeleteWorkspace = hasDeleteWorkspacePermission(
workspace?.userPermissions || [],
);
const hasCreateNewApplicationPermission =
hasCreateNewAppPermission(workspace.userPermissions) && !isMobile;
const renderManageEnvironmentMenu =
isManageEnvironmentEnabled &&
hasManageWorkspaceEnvironmentPermission(workspace.userPermissions);
const onClickAddNewAppButton = (workspaceId: string) => {
if (
Object.entries(creatingApplicationMap).length === 0 ||
(creatingApplicationMap && !creatingApplicationMap[workspaceId])
) {
createNewApplication(
getNextEntityName(
"Untitled application ",
applications.map((el: any) => el.name),
),
workspaceId,
);
}
};
const showWorkspaceMenuOptions =
canInviteToWorkspace ||
hasManageWorkspacePermissions ||
hasCreateNewApplicationPermission ||
(canDeleteWorkspace && applications.length === 0) ||
renderManageEnvironmentMenu;
const handleResetMenuState = () => {
setWorkspaceToOpenMenu(null);
setWarnLeavingWorkspace(false);
setWarnDeleteWorkspace(false);
};
const handleWorkspaceMenuClose = (open: boolean) => {
if (!open && !warnLeavingWorkspace && !warnDeleteWorkspace) {
handleResetMenuState();
}
};
return (
<React.Fragment key={workspace.id}>
<WorkspaceSection
className="t--workspace-section"
isMobile={isMobile}
key={index}
>
<WorkspaceDropDown isMobile={isMobile}>
{(currentUser || isLoadingResources) &&
WorkspaceMenuTarget({
workspaceName: workspace.name,
workspaceSlug: workspace.id,
})}
{selectedWorkspaceIdForImportApplication && (
<ImportApplicationModal
isModalOpen={
selectedWorkspaceIdForImportApplication === workspace.id
}
onClose={() =>
setSelectedWorkspaceIdForImportApplication("")
}
workspaceId={selectedWorkspaceIdForImportApplication}
/>
)}
{!isLoadingResources && (
<WorkspaceShareUsers isHidden={isEnabledMultipleSelection}>
<SharedUserList workspaceId={workspace.id} />
{canInviteToWorkspace && !isMobile && (
<FormDialogComponent
Form={WorkspaceInviteUsersForm}
placeholder={createMessage(
INVITE_USERS_PLACEHOLDER,
!isGACEnabled,
)}
workspace={workspace}
/>
)}
<WorkspaceAction
isMobile={isMobile}
onCreateNewApplication={onClickAddNewAppButton}
workspaceId={workspace.id}
/>
{(currentUser || isLoadingResources) &&
!isMobile &&
showWorkspaceMenuOptions && (
<WorkspaceMenu
canDeleteWorkspace={
applications.length === 0 &&
packages.length === 0 &&
canDeleteWorkspace
}
canInviteToWorkspace={canInviteToWorkspace}
enableImportExport={enableImportExport}
handleDeleteWorkspace={handleDeleteWorkspace}
handleResetMenuState={handleResetMenuState}
handleWorkspaceMenuClose={handleWorkspaceMenuClose}
hasCreateNewApplicationPermission={
hasCreateNewApplicationPermission
}
hasManageWorkspacePermissions={
hasManageWorkspacePermissions
}
isFetchingResources={isLoadingResources}
isSavingWorkspaceInfo={isSavingWorkspaceInfo}
leaveWS={leaveWS}
setSelectedWorkspaceIdForImportApplication={
setSelectedWorkspaceIdForImportApplication
}
setWarnDeleteWorkspace={setWarnDeleteWorkspace}
setWarnLeavingWorkspace={setWarnLeavingWorkspace}
setWorkspaceToOpenMenu={setWorkspaceToOpenMenu}
warnDeleteWorkspace={warnDeleteWorkspace}
warnLeavingWorkspace={warnLeavingWorkspace}
workspace={workspace}
workspaceNameChange={workspaceNameChange}
workspaceToOpenMenu={workspaceToOpenMenu}
/>
)}
</WorkspaceShareUsers>
)}
</WorkspaceDropDown>
{isLoadingResources && (
<ResourceListLoader
isMobile={isMobile}
resources={applications}
/>
)}
{!isLoadingResources && (
<ApplicationCardList
applications={applications}
canInviteToWorkspace={canInviteToWorkspace}
deleteApplication={deleteApplication}
enableImportExport={enableImportExport}
hasCreateNewApplicationPermission={
hasCreateNewApplicationPermission
}
hasManageWorkspacePermissions={hasManageWorkspacePermissions}
isMobile={isMobile}
onClickAddNewButton={onClickAddNewAppButton}
updateApplicationDispatch={updateApplicationDispatch}
workspaceId={workspace.id}
/>
)}
{!isLoadingResources && (
<PackageCardList
isMobile={isMobile}
packages={packages}
workspaceId={workspace.id}
/>
)}
</WorkspaceSection>
{!isLastWorkspace && <Divider />}
</React.Fragment>
);
},
);
}
return (
<ApplicationContainer
className="t--applications-container"
isMobile={isMobile}
>
{workspacesListComponent}
<>
<GitSyncModal isImport />
<DisconnectGitModal />
</>
<ReconnectDatasourceModal />
</ApplicationContainer>
);
}
export interface ApplicationProps {
applicationList: ApplicationPayload[];
searchApplications: (keyword: string) => void;
isCreatingApplication: creatingApplicationMap;
isFetchingApplications: boolean;
createApplicationError?: string;
deleteApplication: (id: string) => void;
deletingApplication: boolean;
getAllApplication: () => void;
userWorkspaces: any;
currentUser?: User;
searchKeyword: string | undefined;
setHeaderMetaData: (
hideHeaderShadow: boolean,
showHeaderSeparator: boolean,
) => void;
resetEditor: () => void;
queryModuleFeatureFlagEnabled: boolean;
resetCurrentWorkspace: () => void;
currentApplicationIdForCreateNewApp?: string;
resetCurrentApplicationIdForCreateNewApp: () => void;
}
export interface ApplicationState {
selectedWorkspaceId: string;
showOnboardingForm: boolean;
}
export class Applications<
Props extends ApplicationProps,
State extends ApplicationState,
> extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
selectedWorkspaceId: "",
showOnboardingForm: false,
} as State;
}
componentDidMount() {
PerformanceTracker.stopTracking(PerformanceTransactionName.LOGIN_CLICK);
PerformanceTracker.stopTracking(PerformanceTransactionName.SIGN_UP);
this.props.getAllApplication();
this.props.setHeaderMetaData(true, true);
// Whenever we go back to home page from application page,
// we should reset current workspace, as this workspace is not in context anymore
this.props.resetCurrentWorkspace();
}
componentWillUnmount() {
this.props.setHeaderMetaData(false, false);
this.props.searchApplications("");
}
public render() {
return this.props.currentApplicationIdForCreateNewApp ? (
<CreateNewAppsOption
currentApplicationIdForCreateNewApp={
this.props.currentApplicationIdForCreateNewApp
}
onClickBack={this.props.resetCurrentApplicationIdForCreateNewApp}
/>
) : (
<PageWrapper displayName="Applications">
<LeftPane />
<MediaQuery maxWidth={MOBILE_MAX_WIDTH}>
{(matches: boolean) => (
<ApplicationsWrapper isMobile={matches}>
<SubHeader
search={{
placeholder: createMessage(SEARCH_APPS),
queryFn: this.props.searchApplications,
defaultValue: this.props.searchKeyword,
}}
/>
<ApplicationsSection searchKeyword={this.props.searchKeyword} />
<RepoLimitExceededErrorModal />
</ApplicationsWrapper>
)}
</MediaQuery>
</PageWrapper>
);
}
}
export const mapStateToProps = (state: AppState) => ({
applicationList: getApplicationList(state),
isFetchingApplications: getIsFetchingApplications(state),
isCreatingApplication: getIsCreatingApplication(state),
createApplicationError: getCreateApplicationError(state),
deletingApplication: getIsDeletingApplication(state),
userWorkspaces: getUserApplicationsWorkspacesList(state),
currentUser: getCurrentUser(state),
searchKeyword: getApplicationSearchKeyword(state),
currentApplicationIdForCreateNewApp:
getCurrentApplicationIdForCreateNewApp(state),
});
export const mapDispatchToProps = (dispatch: any) => ({
getAllApplication: () => {
dispatch({ type: ReduxActionTypes.GET_ALL_APPLICATION_INIT });
},
resetEditor: () => {
dispatch(resetEditorRequest());
},
searchApplications: (keyword: string) => {
dispatch({
type: ReduxActionTypes.SEARCH_APPLICATIONS,
payload: {
keyword,
},
});
},
setHeaderMetaData: (
hideHeaderShadow: boolean,
showHeaderSeparator: boolean,
) => {
dispatch(setHeaderMeta(hideHeaderShadow, showHeaderSeparator));
},
resetCurrentWorkspace: () => dispatch(resetCurrentWorkspace()),
resetCurrentApplicationIdForCreateNewApp: () =>
dispatch(resetCurrentApplicationIdForCreateNewApp()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Applications);