193 lines
5.8 KiB
TypeScript
193 lines
5.8 KiB
TypeScript
import React, { useEffect } from "react";
|
|
import styled from "styled-components";
|
|
import * as Sentry from "@sentry/react";
|
|
import { Classes, ControlGroup } from "@blueprintjs/core";
|
|
import { debounce, noop } from "lodash";
|
|
import { Switch, Route, useRouteMatch } from "react-router-dom";
|
|
import SearchInput, { SearchVariant } from "components/ads/SearchInput";
|
|
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,
|
|
setTemplateSearchQuery,
|
|
} from "actions/templateActions";
|
|
import {
|
|
getSearchedTemplateList,
|
|
getTemplateFiltersLength,
|
|
getTemplateSearchQuery,
|
|
isFetchingTemplatesSelector,
|
|
} from "selectors/templatesSelectors";
|
|
import { fetchDefaultPlugins } from "actions/pluginActions";
|
|
import { editorInitializer } from "utils/EditorUtils";
|
|
import { AppState } from "reducers";
|
|
import {
|
|
getIsFetchingApplications,
|
|
getUserApplicationsOrgsList,
|
|
} from "selectors/applicationSelectors";
|
|
import { getAllApplications } from "actions/applicationActions";
|
|
import { getTypographyByKey } from "constants/DefaultTheme";
|
|
import { Colors } from "constants/Colors";
|
|
import { createMessage, SEARCH_TEMPLATES } from "@appsmith/constants/messages";
|
|
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);
|
|
padding-left: 8vw;
|
|
`;
|
|
|
|
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});
|
|
overflow: auto;
|
|
padding-right: 8vw;
|
|
`;
|
|
|
|
export const ResultsCount = styled.div`
|
|
${(props) => getTypographyByKey(props, "h1")}
|
|
color: ${Colors.CODE_GRAY};
|
|
margin-top: ${(props) => props.theme.spaces[5]}px;
|
|
margin-left: ${(props) => props.theme.spaces[12]}px;
|
|
padding-bottom: ${(props) => props.theme.spaces[11]}px;
|
|
`;
|
|
|
|
const Loader = styled(TemplateListWrapper)`
|
|
height: 100vh;
|
|
.results-count {
|
|
height: 20px;
|
|
width: 100px;
|
|
}
|
|
`;
|
|
|
|
const LoadingTemplateList = styled.div`
|
|
margin-top: ${(props) => props.theme.spaces[11]}px;
|
|
// 200 is to have some space at the bottom
|
|
height: calc(100% - 200px);
|
|
margin-right: ${(props) => props.theme.spaces[9]}px;
|
|
margin-left: ${(props) => props.theme.spaces[12] + 2}px;
|
|
`;
|
|
|
|
const SearchWrapper = styled.div`
|
|
margin-left: ${(props) => props.theme.spaces[11]}px;
|
|
`;
|
|
|
|
function TemplateRoutes() {
|
|
const { path } = useRouteMatch();
|
|
const dispatch = useDispatch();
|
|
const organizationListLength = useSelector(
|
|
(state: AppState) => getUserApplicationsOrgsList(state).length,
|
|
);
|
|
const pluginListLength = useSelector(
|
|
(state: AppState) => state.entities.plugins.defaultPluginList.length,
|
|
);
|
|
const templatesCount = useSelector(
|
|
(state: AppState) => state.ui.templates.templates.length,
|
|
);
|
|
|
|
useEffect(() => {
|
|
dispatch(setHeaderMeta(true, true));
|
|
// Generate the widget config list
|
|
editorInitializer();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!templatesCount) {
|
|
dispatch(getAllTemplates());
|
|
}
|
|
}, [templatesCount]);
|
|
|
|
useEffect(() => {
|
|
if (!organizationListLength) {
|
|
dispatch(getAllApplications());
|
|
}
|
|
}, [organizationListLength]);
|
|
|
|
useEffect(() => {
|
|
if (!pluginListLength) {
|
|
dispatch(fetchDefaultPlugins());
|
|
}
|
|
}, [pluginListLength]);
|
|
|
|
return (
|
|
<Switch>
|
|
<SentryRoute component={TemplateView} path={`${path}/:templateId`} />
|
|
<SentryRoute component={Templates} path={path} />
|
|
</Switch>
|
|
);
|
|
}
|
|
|
|
function TemplateListLoader() {
|
|
return (
|
|
<Loader>
|
|
<ResultsCount className={`results-count ${Classes.SKELETON}`} />
|
|
<LoadingTemplateList className={Classes.SKELETON} />
|
|
</Loader>
|
|
);
|
|
}
|
|
|
|
function Templates() {
|
|
const templates = useSelector(getSearchedTemplateList);
|
|
const templateSearchQuery = useSelector(getTemplateSearchQuery);
|
|
const isFetchingApplications = useSelector(getIsFetchingApplications);
|
|
const isFetchingTemplates = useSelector(isFetchingTemplatesSelector);
|
|
const filterCount = useSelector(getTemplateFiltersLength);
|
|
const dispatch = useDispatch();
|
|
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"
|
|
: "";
|
|
}
|
|
|
|
const isLoading = isFetchingApplications || isFetchingTemplates;
|
|
|
|
const onChange = (query: string) => {
|
|
dispatch(setTemplateSearchQuery(query));
|
|
};
|
|
const debouncedOnChange = debounce(onChange, 250, { maxWait: 1000 });
|
|
|
|
return (
|
|
<PageWrapper>
|
|
<Filters />
|
|
<TemplateListWrapper>
|
|
{isLoading ? (
|
|
<TemplateListLoader />
|
|
) : (
|
|
<>
|
|
<SearchWrapper>
|
|
<ControlGroup>
|
|
<SearchInput
|
|
cypressSelector={"t--application-search-input"}
|
|
defaultValue={templateSearchQuery}
|
|
disabled={isLoading}
|
|
onChange={debouncedOnChange || noop}
|
|
placeholder={createMessage(SEARCH_TEMPLATES)}
|
|
variant={SearchVariant.BACKGROUND}
|
|
/>
|
|
</ControlGroup>
|
|
</SearchWrapper>
|
|
<ResultsCount>{resultsText}</ResultsCount>
|
|
<TemplateList templates={templates} />
|
|
</>
|
|
)}
|
|
</TemplateListWrapper>
|
|
</PageWrapper>
|
|
);
|
|
}
|
|
|
|
export default TemplateRoutes;
|