## Description Improve code splitting of FE components to avoid minimal changes needed on EE when CE is modified. #### PR fixes following issue(s) Fixes [#24184](https://github.com/appsmithorg/appsmith/issues/24184) #### Type of change - Chore (housekeeping or task changes that don't impact user perception) ## Testing #### How Has This Been Tested? - [x] Manual - [ ] JUnit - [x] Jest - [x] Cypress ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
246 lines
7.2 KiB
TypeScript
246 lines
7.2 KiB
TypeScript
import React, { useEffect } from "react";
|
|
import styled from "styled-components";
|
|
import * as Sentry from "@sentry/react";
|
|
import { isEmpty } from "lodash";
|
|
import { Switch, Route, useRouteMatch } from "react-router-dom";
|
|
import { SearchInput, Text } from "design-system";
|
|
import TemplateList from "./TemplateList";
|
|
import TemplateView from "./TemplateView";
|
|
import Filters from "pages/Templates/Filters";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { setHeaderMeta } from "actions/themeActions";
|
|
import {
|
|
getAllTemplates,
|
|
getTemplateFilters,
|
|
setTemplateSearchQuery,
|
|
} from "actions/templateActions";
|
|
import {
|
|
allTemplatesFiltersSelector,
|
|
getForkableWorkspaces,
|
|
getSearchedTemplateList,
|
|
getTemplateFiltersLength,
|
|
getTemplateSearchQuery,
|
|
isFetchingTemplatesSelector,
|
|
} from "selectors/templatesSelectors";
|
|
import { fetchDefaultPlugins } from "actions/pluginActions";
|
|
import type { AppState } from "@appsmith/reducers";
|
|
import { editorInitializer } from "utils/editor/EditorUtils";
|
|
import {
|
|
getIsFetchingApplications,
|
|
getUserApplicationsWorkspacesList,
|
|
} from "@appsmith/selectors/applicationSelectors";
|
|
import { getAllApplications } from "@appsmith/actions/applicationActions";
|
|
import { createMessage, SEARCH_TEMPLATES } from "@appsmith/constants/messages";
|
|
import LeftPaneBottomSection from "pages/Home/LeftPaneBottomSection";
|
|
import type { Template } from "api/TemplatesApi";
|
|
import LoadingScreen from "./TemplatesModal/LoadingScreen";
|
|
import ReconnectDatasourceModal from "pages/Editor/gitSync/ReconnectDatasourceModal";
|
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
|
|
|
const SentryRoute = Sentry.withSentryRouting(Route);
|
|
|
|
const PageWrapper = styled.div`
|
|
margin-top: ${(props) => props.theme.homePage.header}px;
|
|
display: flex;
|
|
height: calc(100vh - ${(props) => props.theme.homePage.header}px);
|
|
`;
|
|
|
|
const SidebarWrapper = styled.div`
|
|
width: ${(props) => props.theme.homePage.sidebar}px;
|
|
height: 100%;
|
|
display: flex;
|
|
padding: 16px 16px 0;
|
|
flex-direction: column;
|
|
border-right: 1px solid var(--ads-v2-color-border);
|
|
position: fixed;
|
|
`;
|
|
|
|
const SecondaryWrapper = styled.div`
|
|
height: calc(
|
|
100vh - ${(props) => props.theme.homePage.header + props.theme.spaces[11]}px
|
|
);
|
|
position: relative;
|
|
`;
|
|
|
|
export const TemplateListWrapper = styled.div`
|
|
padding-top: ${(props) => props.theme.spaces[11]}px;
|
|
width: calc(100% - ${(props) => props.theme.homePage.sidebar}px);
|
|
height: calc(100vh - ${(props) => props.theme.headerHeight});
|
|
margin-left: ${(props) => props.theme.homePage.sidebar}px;
|
|
`;
|
|
|
|
export const ResultsCount = styled(Text)`
|
|
color: var(--ads-v2-color-fg-emphasis);
|
|
margin-top: 20px;
|
|
margin-left: ${(props) => props.theme.spaces[12] - 8}px;
|
|
padding-bottom: 20px;
|
|
`;
|
|
|
|
const SearchWrapper = styled.div<{ sticky?: boolean }>`
|
|
margin-left: ${(props) => props.theme.spaces[11]}px;
|
|
/* max-width: 250px; */
|
|
.templates-search {
|
|
max-width: 250px;
|
|
}
|
|
${(props) =>
|
|
props.sticky &&
|
|
`position: sticky;
|
|
top: 0;
|
|
position: -webkit-sticky;
|
|
z-index: 1;
|
|
background-color: var(--ads-v2-color-bg);
|
|
padding: var(--ads-v2-spaces-7);
|
|
margin-left: 0; `}
|
|
`;
|
|
|
|
function TemplateRoutes() {
|
|
const { path } = useRouteMatch();
|
|
const dispatch = useDispatch();
|
|
const workspaceListLength = useSelector(
|
|
(state: AppState) => getUserApplicationsWorkspacesList(state).length,
|
|
);
|
|
const pluginListLength = useSelector(
|
|
(state: AppState) => state.entities.plugins.defaultPluginList.length,
|
|
);
|
|
const templatesCount = useSelector(
|
|
(state: AppState) => state.ui.templates.templates.length,
|
|
);
|
|
const filters = useSelector(allTemplatesFiltersSelector);
|
|
|
|
useEffect(() => {
|
|
dispatch(setHeaderMeta(true, true));
|
|
// Generate the widget config list
|
|
editorInitializer();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!templatesCount) {
|
|
dispatch(getAllTemplates());
|
|
}
|
|
}, [templatesCount]);
|
|
|
|
useEffect(() => {
|
|
if (!workspaceListLength) {
|
|
dispatch(getAllApplications());
|
|
}
|
|
}, [workspaceListLength]);
|
|
|
|
useEffect(() => {
|
|
if (!pluginListLength) {
|
|
dispatch(fetchDefaultPlugins());
|
|
}
|
|
}, [pluginListLength]);
|
|
|
|
useEffect(() => {
|
|
if (isEmpty(filters.functions)) {
|
|
dispatch(getTemplateFilters());
|
|
}
|
|
}, [filters]);
|
|
|
|
return (
|
|
<Switch>
|
|
<SentryRoute component={TemplateView} path={`${path}/:templateId`} />
|
|
<SentryRoute component={Templates} path={path} />
|
|
</Switch>
|
|
);
|
|
}
|
|
|
|
type TemplatesContentProps = {
|
|
onTemplateClick?: (id: string) => void;
|
|
onForkTemplateClick?: (template: Template) => void;
|
|
stickySearchBar?: boolean;
|
|
isForkingEnabled: boolean;
|
|
filterWithAllowPageImport?: boolean;
|
|
};
|
|
|
|
export function TemplatesContent(props: TemplatesContentProps) {
|
|
const templateSearchQuery = useSelector(getTemplateSearchQuery);
|
|
const isFetchingApplications = useSelector(getIsFetchingApplications);
|
|
const isFetchingTemplates = useSelector(isFetchingTemplatesSelector);
|
|
const isLoading = isFetchingApplications || isFetchingTemplates;
|
|
const dispatch = useDispatch();
|
|
const onChange = (query: string) => {
|
|
dispatch(setTemplateSearchQuery(query));
|
|
};
|
|
const filterWithAllowPageImport = props.filterWithAllowPageImport || false;
|
|
const templates = useSelector(getSearchedTemplateList).filter((template) =>
|
|
filterWithAllowPageImport ? !!template.allowPageImport : true,
|
|
);
|
|
const filterCount = useSelector(getTemplateFiltersLength);
|
|
|
|
useEffect(() => {
|
|
dispatch({
|
|
type: ReduxActionTypes.RESET_TEMPLATE_FILTERS,
|
|
});
|
|
}, []);
|
|
let resultsText =
|
|
templates.length > 1
|
|
? `Showing all ${templates.length} templates`
|
|
: templates.length === 1
|
|
? "Showing 1 template"
|
|
: "No templates to show";
|
|
|
|
if (templates.length) {
|
|
resultsText +=
|
|
filterCount > 1
|
|
? ` matching ${filterCount} filters`
|
|
: filterCount === 1
|
|
? " matching 1 filter"
|
|
: "";
|
|
}
|
|
|
|
if (isLoading) {
|
|
return <LoadingScreen text="Loading templates" />;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<SearchWrapper sticky={props.stickySearchBar}>
|
|
<div className="templates-search">
|
|
<SearchInput
|
|
data-testid={"t--application-search-input"}
|
|
isDisabled={isLoading}
|
|
onChange={onChange}
|
|
placeholder={createMessage(SEARCH_TEMPLATES)}
|
|
value={templateSearchQuery}
|
|
/>
|
|
</div>
|
|
</SearchWrapper>
|
|
<ResultsCount
|
|
data-testid="t--application-templates-results-header"
|
|
kind="heading-m"
|
|
renderAs="h1"
|
|
>
|
|
{resultsText}
|
|
</ResultsCount>
|
|
<TemplateList
|
|
isForkingEnabled={props.isForkingEnabled}
|
|
onForkTemplateClick={props.onForkTemplateClick}
|
|
onTemplateClick={props.onTemplateClick}
|
|
templates={templates}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
function Templates() {
|
|
const workspaceList = useSelector(getForkableWorkspaces);
|
|
|
|
return (
|
|
<PageWrapper>
|
|
<SidebarWrapper>
|
|
<SecondaryWrapper>
|
|
<ReconnectDatasourceModal />
|
|
<Filters />
|
|
<LeftPaneBottomSection />
|
|
</SecondaryWrapper>
|
|
</SidebarWrapper>
|
|
<TemplateListWrapper>
|
|
<TemplatesContent isForkingEnabled={!!workspaceList.length} />
|
|
</TemplateListWrapper>
|
|
</PageWrapper>
|
|
);
|
|
}
|
|
|
|
export default TemplateRoutes;
|