fix: gsheet preview data UI using tabs for preview and configuration (#28318)
This commit is contained in:
parent
8dab2c84e6
commit
72c4b4d996
|
|
@ -1903,6 +1903,11 @@ export const ERR_FETCHING_DATASOURCE_PREVIEW_DATA = () =>
|
|||
export const FETCHING_DATASOURCE_PREVIEW_DATA = () => "Loading data";
|
||||
export const SCHEMA_PREVIEW_NO_DATA = () =>
|
||||
"No data records to show or the table header begins with an index other than 1";
|
||||
export const GSHEET_SPREADSHEET_LABEL = () => "Spreadsheets";
|
||||
export const GSHEET_SPREADSHEET_LOADING = () => "Loading Spreadsheets";
|
||||
export const GSHEET_SHEET_LOADING = () => "Loading Sheets";
|
||||
export const GSHEET_DATA_LOADING = () => "Loading attributes";
|
||||
export const GSHEET_SEARCH_PLACEHOLDER = () => "Search for spreadsheet";
|
||||
|
||||
//Layout Conversion flow
|
||||
export const CONVERT = () => "Convert layout";
|
||||
|
|
@ -2161,6 +2166,8 @@ export const COMMUNITY_TEMPLATES = {
|
|||
export const EMPTY_TABLE_TITLE_TEXT = () => "Empty table";
|
||||
export const EMPTY_TABLE_MESSAGE_TEXT = () =>
|
||||
"There are no data records to show";
|
||||
export const LOADING_RECORDS_TITLE_TEXT = () => "Loading records";
|
||||
export const LOADING_RECORDS_MESSAGE_TEXT = () => "This may take a few seconds";
|
||||
export const EMPTY_TABLE_SVG_ALT_TEXT = () => "Empty table image";
|
||||
|
||||
export const DATA_PANE_TITLE = () => "Datasources in your Workspace";
|
||||
|
|
|
|||
|
|
@ -350,7 +350,8 @@ export type DATASOURCE_SCHEMA_EVENTS =
|
|||
| "GSHEET_PREVIEW_DATA_SHOWN"
|
||||
| "DATASOURCE_GENERATE_PAGE_BUTTON_CLICKED"
|
||||
| "DATASOURCE_PREVIEW_TABLE_CHANGE"
|
||||
| "DATASOURCE_PREVIEW_DATA_SHOWN";
|
||||
| "DATASOURCE_PREVIEW_DATA_SHOWN"
|
||||
| "GSHEET_SPREADSHEET_SEARCH";
|
||||
|
||||
type WALKTHROUGH_EVENTS = "WALKTHROUGH_DISMISSED" | "WALKTHROUGH_SHOWN";
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
|
|||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import { getHasManagePagePermission } from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
|
||||
import type { Datasource } from "entities/Datasource";
|
||||
import { DatasourceStructureContext } from "entities/Datasource";
|
||||
|
||||
const SCHEMA_GUIDE_GIF = `${ASSETS_CDN_URL}/schema.gif`;
|
||||
|
|
@ -152,7 +153,7 @@ const Placeholder = styled.div`
|
|||
`;
|
||||
|
||||
const DataStructureListWrapper = styled.div`
|
||||
overflow-y: auto;
|
||||
overflow-y: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -176,7 +177,7 @@ interface CollapsibleProps {
|
|||
label: string;
|
||||
CustomLabelComponent?: (props: any) => JSX.Element;
|
||||
isDisabled?: boolean;
|
||||
datasourceId?: string;
|
||||
datasource?: Partial<Datasource>;
|
||||
containerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
|
|
@ -189,7 +190,7 @@ export function Collapsible({
|
|||
children,
|
||||
containerRef,
|
||||
CustomLabelComponent,
|
||||
datasourceId,
|
||||
datasource,
|
||||
expand = true,
|
||||
label,
|
||||
}: CollapsibleProps) {
|
||||
|
|
@ -220,7 +221,7 @@ export function Collapsible({
|
|||
/>
|
||||
{!!CustomLabelComponent ? (
|
||||
<CustomLabelComponent
|
||||
datasourceId={datasourceId}
|
||||
datasource={datasource}
|
||||
onRefreshCallback={() => handleCollapse(true)}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -427,7 +428,7 @@ function ActionSidebar({
|
|||
<Collapsible
|
||||
CustomLabelComponent={DatasourceStructureHeader}
|
||||
containerRef={schemaRef}
|
||||
datasourceId={datasourceId}
|
||||
datasource={{ id: datasourceId }}
|
||||
expand={!showSuggestedWidgets}
|
||||
label="Schema"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -181,9 +181,11 @@ export function renderDatasourceSection(
|
|||
if (
|
||||
!value &&
|
||||
!!viewMode &&
|
||||
!!section.hidden &&
|
||||
"comparison" in section.hidden &&
|
||||
section.hidden.comparison === ComparisonOperationsEnum.VIEW_MODE
|
||||
(!section.hidden ||
|
||||
(!!section.hidden &&
|
||||
"comparison" in section.hidden &&
|
||||
section.hidden.comparison ===
|
||||
ComparisonOperationsEnum.VIEW_MODE))
|
||||
) {
|
||||
value = section.initialValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,12 +78,16 @@ export const ResizerContentContainer = styled.div`
|
|||
border-bottom: 1px solid var(--ads-v2-color-border);
|
||||
}
|
||||
}
|
||||
&.db-form-resizer-content.db-form-resizer-content-show-tabs {
|
||||
&.db-form-resizer-content.db-form-resizer-content-show-tabs,
|
||||
&.saas-form-resizer-content.saas-form-resizer-content-show-tabs {
|
||||
padding: 0;
|
||||
& .t--ds-form-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
&.saas-form-resizer-content.saas-form-resizer-content-show-tabs form {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
border-top: none;
|
||||
.db-form-content-container {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -871,16 +871,9 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
shouldShowTabs = () => {
|
||||
const {
|
||||
isEnabledForDSViewModeSchema,
|
||||
isPluginAllowedToPreviewData,
|
||||
pluginDatasourceForm,
|
||||
} = this.props;
|
||||
const isRestAPI =
|
||||
pluginDatasourceForm === DatasourceComponentTypes.RestAPIDatasourceForm;
|
||||
return (
|
||||
isEnabledForDSViewModeSchema && isPluginAllowedToPreviewData && !isRestAPI
|
||||
);
|
||||
const { isEnabledForDSViewModeSchema, isPluginAllowedToPreviewData } =
|
||||
this.props;
|
||||
return isEnabledForDSViewModeSchema && isPluginAllowedToPreviewData;
|
||||
};
|
||||
|
||||
renderTabsForViewMode = () => {
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ import DatasourceViewModeSchema from "./DatasourceViewModeSchema";
|
|||
import { getCurrentEnvironmentId } from "@appsmith/selectors/environmentSelectors";
|
||||
import { isEnvironmentValid } from "@appsmith/utils/Environments";
|
||||
import type { Datasource } from "entities/Datasource";
|
||||
import { isGoogleSheetPluginDS } from "utils/editorContextUtils";
|
||||
import { getPluginNameFromId } from "@appsmith/selectors/entitiesSelector";
|
||||
import {
|
||||
isDatasourceAuthorizedForQueryCreation,
|
||||
isGoogleSheetPluginDS,
|
||||
} from "utils/editorContextUtils";
|
||||
import { getPlugin } from "@appsmith/selectors/entitiesSelector";
|
||||
import GoogleSheetSchema from "./GoogleSheetSchema";
|
||||
|
||||
const TabsContainer = styled(Tabs)`
|
||||
|
|
@ -55,14 +58,22 @@ const DatasourceTabs = (props: DatasourceTabProps) => {
|
|||
const setDatasourceViewModeFlagClick = (value: boolean) => {
|
||||
dispatch(setDatasourceViewModeFlag(value));
|
||||
};
|
||||
const pluginName = useSelector((state) =>
|
||||
getPluginNameFromId(state, props.datasource.pluginId),
|
||||
const plugin = useSelector((state) =>
|
||||
getPlugin(state, props.datasource.pluginId),
|
||||
);
|
||||
const isGoogleSheetPlugin = isGoogleSheetPluginDS(pluginName);
|
||||
const isGoogleSheetPlugin = isGoogleSheetPluginDS(plugin?.packageName);
|
||||
const isPluginAuthorized =
|
||||
!!plugin && !!props.datasource
|
||||
? isDatasourceAuthorizedForQueryCreation(
|
||||
props.datasource,
|
||||
plugin,
|
||||
currentEnvironmentId,
|
||||
)
|
||||
: false;
|
||||
return (
|
||||
<TabsContainer
|
||||
defaultValue={
|
||||
isDatasourceValid
|
||||
isDatasourceValid || isPluginAuthorized
|
||||
? VIEW_MODE_TABS.VIEW_DATA
|
||||
: VIEW_MODE_TABS.CONFIGURATIONS
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,10 @@ import React, { memo, useEffect, useState, useContext } from "react";
|
|||
import EntityPlaceholder from "../Explorer/Entity/Placeholder";
|
||||
import DatasourceStructure from "./DatasourceStructure";
|
||||
import { SearchInput, Text } from "design-system";
|
||||
import styled from "styled-components";
|
||||
import { getIsFetchingDatasourceStructure } from "@appsmith/selectors/entitiesSelector";
|
||||
import { useSelector } from "react-redux";
|
||||
import type { AppState } from "@appsmith/reducers";
|
||||
import DatasourceStructureLoadingContainer from "./DatasourceStructureLoadingContainer";
|
||||
import ItemLoadingIndicator from "./ItemLoadingIndicator";
|
||||
import DatasourceStructureNotFound from "./DatasourceStructureNotFound";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { PluginName } from "entities/Action";
|
||||
|
|
@ -23,6 +22,7 @@ import WalkthroughContext from "components/featureWalkthrough/walkthroughContext
|
|||
import { setFeatureWalkthroughShown } from "utils/storage";
|
||||
import { FEATURE_WALKTHROUGH_KEYS } from "constants/WalkthroughConstants";
|
||||
import { SCHEMA_SECTION_ID } from "entities/Action";
|
||||
import { DatasourceStructureSearchContainer } from "./SchemaViewModeCSS";
|
||||
|
||||
interface Props {
|
||||
datasourceId: string;
|
||||
|
|
@ -50,15 +50,6 @@ export const SCHEMALESS_PLUGINS: Array<string> = [
|
|||
PluginName.OPEN_AI,
|
||||
];
|
||||
|
||||
const DatasourceStructureSearchContainer = styled.div`
|
||||
margin-bottom: 8px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
background: white;
|
||||
`;
|
||||
|
||||
const Container = (props: Props) => {
|
||||
const isLoading = useSelector((state: AppState) =>
|
||||
getIsFetchingDatasourceStructure(state, props.datasourceId),
|
||||
|
|
@ -113,7 +104,7 @@ const Container = (props: Props) => {
|
|||
|
||||
const filteredDastasourceStructure =
|
||||
props.datasourceStructure.tables.filter((table) =>
|
||||
table.name.includes(value),
|
||||
table.name.toLowerCase().includes(value.toLowerCase()),
|
||||
);
|
||||
|
||||
setDatasourceStructure({ tables: filteredDastasourceStructure });
|
||||
|
|
@ -129,7 +120,9 @@ const Container = (props: Props) => {
|
|||
view = (
|
||||
<>
|
||||
{props.context !== DatasourceStructureContext.EXPLORER && (
|
||||
<DatasourceStructureSearchContainer>
|
||||
<DatasourceStructureSearchContainer
|
||||
className={`t--search-container--${props.context.toLowerCase()}`}
|
||||
>
|
||||
<SearchInput
|
||||
className="datasourceStructure-search"
|
||||
endIcon="close"
|
||||
|
|
@ -137,7 +130,7 @@ const Container = (props: Props) => {
|
|||
placeholder={createMessage(
|
||||
DATASOURCE_STRUCTURE_INPUT_PLACEHOLDER_TEXT,
|
||||
)}
|
||||
size={"md"}
|
||||
size={"sm"}
|
||||
startIcon="search"
|
||||
type="text"
|
||||
/>
|
||||
|
|
@ -199,7 +192,7 @@ const Container = (props: Props) => {
|
|||
props.context !== DatasourceStructureContext.EXPLORER &&
|
||||
isLoading
|
||||
) {
|
||||
view = <DatasourceStructureLoadingContainer />;
|
||||
view = <ItemLoadingIndicator type="SCHEMA" />;
|
||||
}
|
||||
|
||||
return view;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
import React, { useCallback } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Button, Text } from "design-system";
|
||||
import styled from "styled-components";
|
||||
import { refreshDatasourceStructure } from "actions/datasourceActions";
|
||||
import { SCHEMA_LABEL, createMessage } from "@appsmith/constants/messages";
|
||||
import {
|
||||
GSHEET_SPREADSHEET_LABEL,
|
||||
SCHEMA_LABEL,
|
||||
createMessage,
|
||||
} from "@appsmith/constants/messages";
|
||||
import type { Datasource } from "entities/Datasource";
|
||||
import { DatasourceStructureContext } from "entities/Datasource";
|
||||
import { getPluginPackageNameFromId } from "@appsmith/selectors/entitiesSelector";
|
||||
import { isGoogleSheetPluginDS } from "utils/editorContextUtils";
|
||||
|
||||
interface Props {
|
||||
datasourceId: string;
|
||||
datasource?: Datasource;
|
||||
onRefreshCallback?: () => void;
|
||||
paddingBottom?: boolean;
|
||||
refetchFn?: () => void;
|
||||
}
|
||||
|
||||
const HeaderWrapper = styled.div<{ paddingBottom: boolean }>`
|
||||
|
|
@ -24,27 +32,41 @@ export default function DatasourceStructureHeader(props: Props) {
|
|||
const dispatch = useDispatch();
|
||||
|
||||
const dispatchRefresh = useCallback(
|
||||
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
event.stopPropagation();
|
||||
dispatch(
|
||||
refreshDatasourceStructure(
|
||||
props.datasourceId,
|
||||
DatasourceStructureContext.QUERY_EDITOR,
|
||||
),
|
||||
);
|
||||
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
if (props.datasource?.id) {
|
||||
event.stopPropagation();
|
||||
|
||||
!!props.onRefreshCallback && props.onRefreshCallback();
|
||||
if (props.refetchFn) {
|
||||
props.refetchFn();
|
||||
} else {
|
||||
dispatch(
|
||||
refreshDatasourceStructure(
|
||||
props.datasource?.id,
|
||||
DatasourceStructureContext.QUERY_EDITOR,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
!!props.onRefreshCallback && props.onRefreshCallback();
|
||||
}
|
||||
},
|
||||
[dispatch, props.datasourceId],
|
||||
[dispatch, props.datasource?.id],
|
||||
);
|
||||
|
||||
const pluginPackageName = useSelector((state) =>
|
||||
getPluginPackageNameFromId(state, props.datasource?.pluginId || ""),
|
||||
);
|
||||
const isGoogleSheetPlugin = isGoogleSheetPluginDS(pluginPackageName);
|
||||
|
||||
return (
|
||||
<HeaderWrapper
|
||||
className="datasourceStructure-header"
|
||||
paddingBottom={!!props.paddingBottom}
|
||||
>
|
||||
<Text kind="heading-xs" renderAs="h3">
|
||||
{createMessage(SCHEMA_LABEL)}
|
||||
{createMessage(
|
||||
isGoogleSheetPlugin ? GSHEET_SPREADSHEET_LABEL : SCHEMA_LABEL,
|
||||
)}
|
||||
</Text>
|
||||
<Button
|
||||
className="datasourceStructure-refresh"
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import React from "react";
|
||||
import { createMessage, LOADING_SCHEMA } from "@appsmith/constants/messages";
|
||||
import { Spinner, Text } from "design-system";
|
||||
import styled from "styled-components";
|
||||
|
||||
const LoadingContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
& > p {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const SpinnerWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const DatasourceStructureLoadingContainer = () => {
|
||||
return (
|
||||
<LoadingContainer>
|
||||
<SpinnerWrapper>
|
||||
<Spinner size={"sm"} />
|
||||
</SpinnerWrapper>
|
||||
<Text kind="body-m" renderAs="p">
|
||||
{createMessage(LOADING_SCHEMA)}
|
||||
</Text>
|
||||
</LoadingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatasourceStructureLoadingContainer;
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { DatasourceStructureContainer as DatasourceStructureList } from "./DatasourceStructureContainer";
|
||||
import {
|
||||
|
|
@ -8,16 +7,10 @@ import {
|
|||
getNumberOfEntitiesInCurrentPage,
|
||||
} from "@appsmith/selectors/entitiesSelector";
|
||||
import DatasourceStructureHeader from "./DatasourceStructureHeader";
|
||||
import { MessageWrapper, TableWrapper } from "./GoogleSheetSchema";
|
||||
import { Spinner, Text, Button } from "design-system";
|
||||
import { Button } from "design-system";
|
||||
import {
|
||||
ERR_FETCHING_DATASOURCE_PREVIEW_DATA,
|
||||
FETCHING_DATASOURCE_PREVIEW_DATA,
|
||||
DATASOURCE_GENERATE_PAGE_BUTTON,
|
||||
EMPTY_TABLE_TITLE_TEXT,
|
||||
EMPTY_TABLE_MESSAGE_TEXT,
|
||||
createMessage,
|
||||
EMPTY_TABLE_SVG_ALT_TEXT,
|
||||
} from "@appsmith/constants/messages";
|
||||
import Table from "pages/Editor/QueryEditor/Table";
|
||||
import { generateTemplateToUpdatePage } from "actions/pageActions";
|
||||
|
|
@ -44,98 +37,22 @@ import {
|
|||
getHasCreatePagePermission,
|
||||
hasCreateDSActionPermissionInApp,
|
||||
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
|
||||
import EmptyTableSVG from "assets/images/empty-table-in-display-preview.svg";
|
||||
|
||||
const ViewModeSchemaContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const DataWrapperContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StructureContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
padding: var(--ads-v2-spaces-4) var(--ads-v2-spaces-5);
|
||||
padding-left: var(--ads-v2-spaces-7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid var(--ads-v2-color-gray-300);
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const DatasourceDataContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const DatasourceListContainer = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
div {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
div ~ div {
|
||||
flex: 1;
|
||||
}
|
||||
.t--schema-virtuoso-container {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const SchemaStateMessageWrapper = styled.div`
|
||||
width: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
img {
|
||||
padding-bottom: var(--ads-v2-spaces-7);
|
||||
}
|
||||
span:first-child {
|
||||
padding-bottom: var(--ads-v2-spaces-2);
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid var(--ads-v2-color-gray-300);
|
||||
padding: var(--ads-v2-spaces-4);
|
||||
`;
|
||||
import RenderInterimDataState from "./RenderInterimDataState";
|
||||
import {
|
||||
ButtonContainer,
|
||||
DataWrapperContainer,
|
||||
DatasourceDataContainer,
|
||||
DatasourceListContainer,
|
||||
StructureContainer,
|
||||
TableWrapper,
|
||||
ViewModeSchemaContainer,
|
||||
} from "./SchemaViewModeCSS";
|
||||
|
||||
interface Props {
|
||||
datasource: Datasource;
|
||||
setDatasourceViewModeFlag: (viewMode: boolean) => void;
|
||||
}
|
||||
|
||||
const renderEmptyTablePage = () => {
|
||||
return (
|
||||
<SchemaStateMessageWrapper>
|
||||
{/* Render empty table image */}
|
||||
<img alt={createMessage(EMPTY_TABLE_SVG_ALT_TEXT)} src={EmptyTableSVG} />
|
||||
{/* Show description below the image */}
|
||||
{/* Show title */}
|
||||
<Text style={{ fontWeight: "bold" }}>
|
||||
{createMessage(EMPTY_TABLE_TITLE_TEXT)}
|
||||
</Text>
|
||||
{/* Show description */}
|
||||
<Text>{createMessage(EMPTY_TABLE_MESSAGE_TEXT)}</Text>
|
||||
</SchemaStateMessageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const DatasourceViewModeSchema = (props: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
|
@ -200,6 +117,7 @@ const DatasourceViewModeSchema = (props: Props) => {
|
|||
if (
|
||||
isDatasourceStructureLoading ||
|
||||
!datasourceStructure ||
|
||||
!datasourceStructure.tables ||
|
||||
(datasourceStructure && datasourceStructure?.error)
|
||||
) {
|
||||
setPreviewData([]);
|
||||
|
|
@ -248,6 +166,10 @@ const DatasourceViewModeSchema = (props: Props) => {
|
|||
}
|
||||
}, [previewData]);
|
||||
|
||||
useEffect(() => {
|
||||
setPreviewData([]);
|
||||
}, [props.datasource.id]);
|
||||
|
||||
const onEntityTableClick = (table: string) => {
|
||||
AnalyticsUtil.logEvent("DATASOURCE_PREVIEW_TABLE_CHANGE", {
|
||||
datasourceId: props.datasource.id,
|
||||
|
|
@ -304,10 +226,12 @@ const DatasourceViewModeSchema = (props: Props) => {
|
|||
<ViewModeSchemaContainer>
|
||||
<DataWrapperContainer>
|
||||
<StructureContainer>
|
||||
<DatasourceStructureHeader
|
||||
datasourceId={props.datasource.id}
|
||||
paddingBottom
|
||||
/>
|
||||
{props.datasource && (
|
||||
<DatasourceStructureHeader
|
||||
datasource={props.datasource}
|
||||
paddingBottom
|
||||
/>
|
||||
)}
|
||||
<DatasourceListContainer>
|
||||
<DatasourceStructureList
|
||||
context={DatasourceStructureContext.DATASOURCE_VIEW_MODE}
|
||||
|
|
@ -322,31 +246,25 @@ const DatasourceViewModeSchema = (props: Props) => {
|
|||
</StructureContainer>
|
||||
<DatasourceDataContainer>
|
||||
<TableWrapper>
|
||||
{isLoading && (
|
||||
<MessageWrapper>
|
||||
<Spinner size="md" />
|
||||
<Text style={{ marginLeft: "8px" }}>
|
||||
{createMessage(FETCHING_DATASOURCE_PREVIEW_DATA)}
|
||||
</Text>
|
||||
</MessageWrapper>
|
||||
{(isLoading || isDatasourceStructureLoading) && (
|
||||
<RenderInterimDataState state="LOADING" />
|
||||
)}
|
||||
{!isLoading && failedFetchingPreviewData && (
|
||||
<MessageWrapper>
|
||||
<Text color="var(--ads-color-red-500)">
|
||||
{createMessage(ERR_FETCHING_DATASOURCE_PREVIEW_DATA)}
|
||||
</Text>
|
||||
</MessageWrapper>
|
||||
)}
|
||||
{!isLoading &&
|
||||
!failedFetchingPreviewData &&
|
||||
!previewDataError &&
|
||||
previewData?.length > 0 && <Table data={previewData} />}
|
||||
{!isLoading &&
|
||||
!failedFetchingPreviewData &&
|
||||
!previewDataError &&
|
||||
previewData?.length < 1 && (
|
||||
<MessageWrapper>{renderEmptyTablePage()}</MessageWrapper>
|
||||
{(!isLoading || !isDatasourceStructureLoading) &&
|
||||
(failedFetchingPreviewData || previewDataError) && (
|
||||
<RenderInterimDataState state="FAILED" />
|
||||
)}
|
||||
{!isLoading &&
|
||||
!isDatasourceStructureLoading &&
|
||||
!failedFetchingPreviewData &&
|
||||
!previewDataError &&
|
||||
previewData?.length > 0 && (
|
||||
<Table data={previewData} shouldResize={false} />
|
||||
)}
|
||||
{!isLoading &&
|
||||
!isDatasourceStructureLoading &&
|
||||
!failedFetchingPreviewData &&
|
||||
!previewDataError &&
|
||||
!previewData?.length && <RenderInterimDataState state="NODATA" />}
|
||||
</TableWrapper>
|
||||
</DatasourceDataContainer>
|
||||
</DataWrapperContainer>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,16 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import type { DropdownOption } from "design-system-old";
|
||||
import { Button, Option, Select, Spinner, Text } from "design-system";
|
||||
import { Button, SearchInput } from "design-system";
|
||||
import {
|
||||
useSheetData,
|
||||
useSheetsList,
|
||||
useSpreadSheets,
|
||||
} from "../GeneratePage/components/GeneratePageForm/hooks";
|
||||
import type {
|
||||
DatasourceTableDropdownOption,
|
||||
DropdownOptions,
|
||||
} from "../GeneratePage/components/constants";
|
||||
import {
|
||||
DEFAULT_DROPDOWN_OPTION,
|
||||
DROPDOWN_DIMENSION,
|
||||
} from "../GeneratePage/components/constants";
|
||||
import { SelectWrapper } from "../GeneratePage/components/GeneratePageForm/styles";
|
||||
import type { DropdownOptions } from "../GeneratePage/components/constants";
|
||||
import { DEFAULT_DROPDOWN_OPTION } from "../GeneratePage/components/constants";
|
||||
import { isEmpty } from "lodash";
|
||||
import Table from "pages/Editor/QueryEditor/Table";
|
||||
import styled from "styled-components";
|
||||
import {
|
||||
getCurrentApplicationId,
|
||||
getPagePermissions,
|
||||
|
|
@ -26,10 +18,8 @@ import {
|
|||
import { generateTemplateToUpdatePage } from "actions/pageActions";
|
||||
import {
|
||||
createMessage,
|
||||
ERR_FETCHING_DATASOURCE_PREVIEW_DATA,
|
||||
FETCHING_DATASOURCE_PREVIEW_DATA,
|
||||
DATASOURCE_GENERATE_PAGE_BUTTON,
|
||||
SCHEMA_PREVIEW_NO_DATA,
|
||||
GSHEET_SEARCH_PLACEHOLDER,
|
||||
} from "@appsmith/constants/messages";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { getCurrentApplication } from "@appsmith/selectors/applicationSelectors";
|
||||
|
|
@ -41,76 +31,23 @@ import {
|
|||
getHasCreatePagePermission,
|
||||
hasCreateDSActionPermissionInApp,
|
||||
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
|
||||
|
||||
export const MessageWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 200px;
|
||||
`;
|
||||
|
||||
export const TableWrapper = styled.div`
|
||||
overflow-x: auto;
|
||||
height: 100%;
|
||||
&& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&& > ${MessageWrapper} {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
&& .t--table-response {
|
||||
border: none;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
&& .tableWrap {
|
||||
overflow: auto;
|
||||
}
|
||||
& .table {
|
||||
background: none;
|
||||
}
|
||||
& .table div:first-of-type .tr {
|
||||
background: var(--ads-v2-color-black-5);
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--ads-v2-color-black-75);
|
||||
}
|
||||
&& .table div.tbody .tr {
|
||||
background: var(--ads-v2-color-white);
|
||||
border-bottom: 1px solid var(--ads-v2-color-black-75);
|
||||
}
|
||||
&& .table .td,
|
||||
&& .table .th {
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
button {
|
||||
margin-right: 24px;
|
||||
}
|
||||
&& .tableWrap {
|
||||
}
|
||||
`;
|
||||
|
||||
const SelectContainer = styled.div`
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
.t--datasource-generate-page {
|
||||
align-self: flex-end;
|
||||
margin-left: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const SelectListWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 278px;
|
||||
margin-right: 16px;
|
||||
& div {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
`;
|
||||
import RenderInterimDataState from "./RenderInterimDataState";
|
||||
import {
|
||||
ButtonContainer,
|
||||
DataWrapperContainer,
|
||||
DatasourceAttributesWrapper,
|
||||
DatasourceDataContainer,
|
||||
DatasourceListContainer,
|
||||
DatasourceStructureSearchContainer,
|
||||
StructureContainer,
|
||||
TableWrapper,
|
||||
ViewModeSchemaContainer,
|
||||
} from "./SchemaViewModeCSS";
|
||||
import DatasourceStructureHeader from "./DatasourceStructureHeader";
|
||||
import Entity from "../Explorer/Entity";
|
||||
import DatasourceField from "./DatasourceField";
|
||||
import { setEntityCollapsibleState } from "actions/editorContextActions";
|
||||
import ItemLoadingIndicator from "./ItemLoadingIndicator";
|
||||
|
||||
interface Props {
|
||||
datasourceId: string;
|
||||
|
|
@ -122,25 +59,47 @@ const MAX_SHEET_ROWS_LENGTH = 12;
|
|||
// ---------- GoogleSheetSchema Component -------
|
||||
|
||||
function GoogleSheetSchema(props: Props) {
|
||||
const [datasourceTableOptions, setSelectedDatasourceTableOptions] =
|
||||
useState<DropdownOptions>([]);
|
||||
const [selectedDatasourceIsInvalid, setSelectedDatasourceIsInvalid] =
|
||||
useState(false);
|
||||
const { fetchAllSpreadsheets, isFetchingSpreadsheets } = useSpreadSheets({
|
||||
setSelectedDatasourceTableOptions,
|
||||
setSelectedDatasourceIsInvalid,
|
||||
});
|
||||
const {
|
||||
failedFetchingSheetsList,
|
||||
fetchSheetsList,
|
||||
isFetchingSheetsList,
|
||||
sheetsList,
|
||||
} = useSheetsList();
|
||||
const { fetchSheetData, isFetchingSheetData, sheetData } = useSheetData();
|
||||
const [spreadsheetOptions, setSpreadsheetOptions] = useState<DropdownOptions>(
|
||||
[],
|
||||
);
|
||||
const [sheetOptions, setSheetOptions] = useState<DropdownOptions>([]);
|
||||
const [sheetData, setSheetData] = useState<any>([]);
|
||||
const [selectedSpreadsheet, setSelectedSpreadsheet] =
|
||||
useState<DropdownOption>({});
|
||||
const [selectedSheet, setSelectedSheet] = useState<DropdownOption>({});
|
||||
const [currentSheetData, setCurrentSheetData] = useState<any>();
|
||||
const [searchString, setSearchString] = useState<string>("");
|
||||
const [selectedDatasourceIsInvalid, setSelectedDatasourceIsInvalid] =
|
||||
useState(false);
|
||||
const { fetchAllSpreadsheets, isFetchingSpreadsheets } = useSpreadSheets({
|
||||
setSelectedDatasourceTableOptions: setSpreadsheetOptions,
|
||||
setSelectedDatasourceIsInvalid,
|
||||
});
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchString(value.toLowerCase());
|
||||
|
||||
AnalyticsUtil.logEvent("GSHEET_SPREADSHEET_SEARCH", {
|
||||
datasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
};
|
||||
|
||||
const setSlicedSheetData = (response: DropdownOptions) => {
|
||||
// Getting the top 12 rows as for experimentation we need to keep this number fixed for preview
|
||||
AnalyticsUtil.logEvent("GSHEET_PREVIEW_DATA_SHOWN", {
|
||||
datasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
setSheetData(response.slice(0, MAX_SHEET_ROWS_LENGTH));
|
||||
};
|
||||
|
||||
const { failedFetchingSheetsList, fetchSheetsList, isFetchingSheetsList } =
|
||||
useSheetsList({ setSheetOptions });
|
||||
const { failedFetchingSheetData, fetchSheetData, isFetchingSheetData } =
|
||||
useSheetData({
|
||||
setSheetData: setSlicedSheetData,
|
||||
});
|
||||
|
||||
const applicationId: string = useSelector(getCurrentApplicationId);
|
||||
const datasource = useSelector((state) =>
|
||||
getDatasource(state, props.datasourceId),
|
||||
|
|
@ -148,101 +107,176 @@ function GoogleSheetSchema(props: Props) {
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Fetch spreadsheets if datasourceId present
|
||||
useEffect(() => {
|
||||
const scrollIntoView = (
|
||||
elementId: string,
|
||||
containerId: string,
|
||||
offset: number = 0,
|
||||
) => {
|
||||
try {
|
||||
const element = document.querySelector(elementId);
|
||||
const container = document.querySelector(containerId);
|
||||
if (element && container) {
|
||||
const elementRect = element.getBoundingClientRect();
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const scrollTop = container.scrollTop;
|
||||
|
||||
const scrollAmount =
|
||||
elementRect.top - containerRect.top + scrollTop + offset;
|
||||
|
||||
container.scrollTop = scrollAmount;
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const collapseAccordions = (
|
||||
datasourceId: string,
|
||||
spreadSheet?: string,
|
||||
sheet?: string,
|
||||
collapseSpreadsheet: boolean = true,
|
||||
) => {
|
||||
if (!isEmpty(spreadSheet) && !isEmpty(sheet)) {
|
||||
dispatch(
|
||||
setEntityCollapsibleState(
|
||||
`${datasourceId}-${spreadSheet}-${sheet}`,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!isEmpty(spreadSheet) && collapseSpreadsheet) {
|
||||
dispatch(
|
||||
setEntityCollapsibleState(`${datasourceId}-${spreadSheet}`, false),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const selectSpreadsheetAndToggle = (option: DropdownOption) => {
|
||||
collapseAccordions(
|
||||
datasource?.id || "",
|
||||
selectedSpreadsheet.value,
|
||||
selectedSheet.value,
|
||||
);
|
||||
setSelectedSheet(DEFAULT_DROPDOWN_OPTION);
|
||||
setSheetOptions([]);
|
||||
setSheetData(undefined);
|
||||
dispatch(
|
||||
setEntityCollapsibleState(`${datasource?.id}-${option.value}`, true),
|
||||
);
|
||||
scrollIntoView(
|
||||
`#${CSS.escape(`entity-${datasource?.id}-${option.value}`)}`,
|
||||
".t--gsheet-structure",
|
||||
);
|
||||
setSelectedSpreadsheet(option);
|
||||
fetchSheetsList({
|
||||
requestObject: {},
|
||||
selectedDatasourceId: props.datasourceId,
|
||||
selectedSpreadsheetUrl: option.value || "",
|
||||
pluginId: props.pluginId || "",
|
||||
});
|
||||
};
|
||||
|
||||
const selectSheetAndToggle = (option: DropdownOption) => {
|
||||
collapseAccordions(
|
||||
datasource?.id || "",
|
||||
selectedSpreadsheet.value,
|
||||
selectedSheet.value,
|
||||
false,
|
||||
);
|
||||
dispatch(
|
||||
setEntityCollapsibleState(
|
||||
`${datasource?.id}-${selectedSpreadsheet.value}-${option.value}`,
|
||||
true,
|
||||
),
|
||||
);
|
||||
scrollIntoView(
|
||||
`#${CSS.escape(
|
||||
`entity-${datasource?.id}-${selectedSpreadsheet.value}-${option.value}`,
|
||||
)}`,
|
||||
".t--gsheet-structure",
|
||||
-30,
|
||||
);
|
||||
setSelectedSheet(option);
|
||||
setSheetData(undefined);
|
||||
fetchSheetData({
|
||||
selectedDatasourceId: datasource?.id || "",
|
||||
selectedSpreadsheetUrl: selectedSpreadsheet.value || "",
|
||||
selectedSheetName: option.value || "",
|
||||
pluginId: props.pluginId || "",
|
||||
});
|
||||
};
|
||||
|
||||
const refetchAllSpreadsheets = () => {
|
||||
if (!!props.datasourceId && !!props.pluginId) {
|
||||
fetchAllSpreadsheets({
|
||||
selectedDatasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId || "",
|
||||
requestObject: {},
|
||||
});
|
||||
setSpreadsheetOptions([]);
|
||||
setSheetOptions([]);
|
||||
setSheetData(undefined);
|
||||
setSelectedSpreadsheet((ss) => {
|
||||
setSelectedSheet((s) => {
|
||||
collapseAccordions(datasource?.id || "", ss.value, s.value);
|
||||
return {};
|
||||
});
|
||||
return {};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch spreadsheets if datasourceId present
|
||||
useEffect(() => {
|
||||
fetchAllSpreadsheets({
|
||||
selectedDatasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId || "",
|
||||
requestObject: {},
|
||||
});
|
||||
}, [props.datasourceId, props.pluginId, dispatch]);
|
||||
|
||||
// When user selects a spreadsheet
|
||||
// Fetch all sheets inside that spreadsheet
|
||||
useEffect(() => {
|
||||
if (!!props.datasourceId && !!props.pluginId && selectedSpreadsheet.value) {
|
||||
setSelectedSheet(DEFAULT_DROPDOWN_OPTION);
|
||||
setCurrentSheetData(undefined);
|
||||
fetchSheetsList({
|
||||
requestObject: {},
|
||||
selectedDatasourceId: props.datasourceId,
|
||||
selectedSpreadsheetUrl: selectedSpreadsheet.value,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
selectedSpreadsheet.value,
|
||||
props.datasourceId,
|
||||
props.pluginId,
|
||||
dispatch,
|
||||
fetchSheetsList,
|
||||
]);
|
||||
|
||||
// When user selects a sheet name
|
||||
// Fetch all sheet data inside that sheet
|
||||
useEffect(() => {
|
||||
if (!!props.datasourceId && !!props.pluginId && selectedSheet.value) {
|
||||
setCurrentSheetData(undefined);
|
||||
fetchSheetData({
|
||||
selectedDatasourceId: props.datasourceId,
|
||||
selectedSpreadsheetUrl: selectedSpreadsheet.value || "",
|
||||
selectedSheetName: selectedSheet.value,
|
||||
pluginId: props.pluginId || "",
|
||||
});
|
||||
}
|
||||
}, [
|
||||
selectedSheet.value,
|
||||
props.datasourceId,
|
||||
props.pluginId,
|
||||
dispatch,
|
||||
fetchSheetData,
|
||||
]);
|
||||
|
||||
// Set first spreadsheet as default option in the dropdown
|
||||
useEffect(() => {
|
||||
if (
|
||||
datasourceTableOptions?.length > 0 &&
|
||||
isEmpty(selectedSpreadsheet.value)
|
||||
) {
|
||||
setSelectedSpreadsheet(datasourceTableOptions[0]);
|
||||
if (spreadsheetOptions?.length > 0 && isEmpty(selectedSpreadsheet.value)) {
|
||||
selectSpreadsheetAndToggle(spreadsheetOptions[0]);
|
||||
}
|
||||
}, [selectedSpreadsheet, datasourceTableOptions]);
|
||||
}, [selectedSpreadsheet, spreadsheetOptions]);
|
||||
|
||||
// Set first sheet as default option in the dropdown
|
||||
useEffect(() => {
|
||||
if (
|
||||
sheetsList?.length > 0 &&
|
||||
sheetOptions?.length > 0 &&
|
||||
isEmpty(selectedSheet.value) &&
|
||||
!isFetchingSheetsList
|
||||
!isFetchingSheetsList &&
|
||||
!isFetchingSpreadsheets
|
||||
) {
|
||||
setSelectedSheet(sheetsList[0]);
|
||||
selectSheetAndToggle(sheetOptions[0]);
|
||||
}
|
||||
}, [selectedSheet, sheetsList, isFetchingSheetsList]);
|
||||
}, [
|
||||
selectedSheet,
|
||||
sheetOptions,
|
||||
isFetchingSheetsList,
|
||||
isFetchingSpreadsheets,
|
||||
]);
|
||||
|
||||
// Set current sheet data
|
||||
useEffect(() => {
|
||||
if (sheetData) {
|
||||
// Getting the top 12 rows as for experimentation we need to keep this number fixed for preview
|
||||
AnalyticsUtil.logEvent("GSHEET_PREVIEW_DATA_SHOWN", {
|
||||
datasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
setCurrentSheetData(sheetData.slice(0, MAX_SHEET_ROWS_LENGTH));
|
||||
}
|
||||
}, [sheetData]);
|
||||
return () => {
|
||||
collapseAccordions(
|
||||
datasource?.id || "",
|
||||
selectedSpreadsheet.value,
|
||||
selectedSheet.value,
|
||||
);
|
||||
};
|
||||
}, [datasource?.id]);
|
||||
|
||||
const onSelectSpreadsheet = (
|
||||
table: string | undefined,
|
||||
tableObj: DatasourceTableDropdownOption | undefined,
|
||||
tableObj: DropdownOption | undefined,
|
||||
) => {
|
||||
if (table && tableObj) {
|
||||
AnalyticsUtil.logEvent("GSHEET_PREVIEW_SPREADSHEET_CHANGE", {
|
||||
datasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
setSelectedSpreadsheet(tableObj);
|
||||
selectSpreadsheetAndToggle(tableObj);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -255,25 +289,22 @@ function GoogleSheetSchema(props: Props) {
|
|||
datasourceId: props.datasourceId,
|
||||
pluginId: props.pluginId,
|
||||
});
|
||||
setSelectedSheet(sheetObj);
|
||||
selectSheetAndToggle(sheetObj);
|
||||
}
|
||||
};
|
||||
|
||||
const isError = selectedDatasourceIsInvalid || failedFetchingSheetsList;
|
||||
const isError =
|
||||
selectedDatasourceIsInvalid ||
|
||||
failedFetchingSheetsList ||
|
||||
failedFetchingSheetData;
|
||||
const isLoading =
|
||||
isFetchingSpreadsheets ||
|
||||
isFetchingSheetsList ||
|
||||
isFetchingSheetData ||
|
||||
(!isError && !currentSheetData);
|
||||
isFetchingSpreadsheets || isFetchingSheetsList || isFetchingSheetData;
|
||||
|
||||
const onGsheetGeneratePage = () => {
|
||||
const payload = {
|
||||
applicationId: applicationId || "",
|
||||
pageId: "",
|
||||
columns:
|
||||
!!currentSheetData && currentSheetData.length > 0
|
||||
? Object.keys(currentSheetData[0])
|
||||
: [],
|
||||
columns: sheetData?.length > 0 ? Object.keys(sheetData[0]) : [],
|
||||
searchColumn: "",
|
||||
tableName: selectedSheet?.value || "",
|
||||
datasourceId: props.datasourceId || "",
|
||||
|
|
@ -312,76 +343,144 @@ function GoogleSheetSchema(props: Props) {
|
|||
pagePermissions,
|
||||
);
|
||||
|
||||
const refreshSpreadSheetButton = (option: DropdownOption) => (
|
||||
<Button
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
onClick={() => selectSpreadsheetAndToggle(option)}
|
||||
startIcon="refresh"
|
||||
/>
|
||||
);
|
||||
|
||||
const showGeneratePageBtn =
|
||||
!isLoading &&
|
||||
!isError &&
|
||||
currentSheetData &&
|
||||
sheetData?.length &&
|
||||
canCreateDatasourceActions &&
|
||||
canCreatePages;
|
||||
|
||||
const filteredSpreadsheets = spreadsheetOptions.filter(
|
||||
(option) => (option.label || "").toLowerCase()?.includes(searchString),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SelectContainer>
|
||||
{!!props.datasourceId ? (
|
||||
<SelectListWrapper>
|
||||
<Text>Spreadsheet</Text>
|
||||
<SelectWrapper width={DROPDOWN_DIMENSION.WIDTH}>
|
||||
<Select
|
||||
data-testid="t--table-dropdown"
|
||||
isLoading={isFetchingSpreadsheets}
|
||||
onChange={(value: any) =>
|
||||
onSelectSpreadsheet(
|
||||
value,
|
||||
datasourceTableOptions.find(
|
||||
(table) => table.value === value,
|
||||
) as DatasourceTableDropdownOption,
|
||||
)
|
||||
}
|
||||
value={selectedSpreadsheet}
|
||||
>
|
||||
{datasourceTableOptions.map((table) => {
|
||||
<ViewModeSchemaContainer>
|
||||
<DataWrapperContainer>
|
||||
<StructureContainer>
|
||||
{datasource && (
|
||||
<DatasourceStructureHeader
|
||||
datasource={datasource}
|
||||
paddingBottom
|
||||
refetchFn={refetchAllSpreadsheets}
|
||||
/>
|
||||
)}
|
||||
<DatasourceListContainer className="t--gsheet-structure">
|
||||
{!isFetchingSpreadsheets && (
|
||||
<DatasourceStructureSearchContainer className="t--gsheet-search-container">
|
||||
<SearchInput
|
||||
className="datasourceStructure-search"
|
||||
endIcon="close"
|
||||
onChange={(value) => handleSearch(value)}
|
||||
placeholder={createMessage(GSHEET_SEARCH_PLACEHOLDER)}
|
||||
size={"sm"}
|
||||
startIcon="search"
|
||||
type="text"
|
||||
/>
|
||||
</DatasourceStructureSearchContainer>
|
||||
)}
|
||||
<div className="t--gsheet-structure-list">
|
||||
{isFetchingSpreadsheets ? (
|
||||
<ItemLoadingIndicator type="SPREADSHEET" />
|
||||
) : (
|
||||
filteredSpreadsheets.map((spreadsheet) => {
|
||||
return (
|
||||
<Option key={table.value} value={table.value}>
|
||||
{table.label}
|
||||
</Option>
|
||||
<Entity
|
||||
className="t--spreadsheet-structure"
|
||||
customAddButton={refreshSpreadSheetButton(spreadsheet)}
|
||||
entityId={`${datasource?.id}-${spreadsheet.value}`}
|
||||
icon={null}
|
||||
key={`${datasource?.id}-${spreadsheet.value}`}
|
||||
name={spreadsheet.label as string}
|
||||
onToggle={(isOpen) => {
|
||||
isOpen &&
|
||||
onSelectSpreadsheet(spreadsheet.value, spreadsheet);
|
||||
}}
|
||||
showAddButton={
|
||||
spreadsheet.value === selectedSpreadsheet.value
|
||||
}
|
||||
step={0}
|
||||
>
|
||||
{isFetchingSheetsList ? (
|
||||
<ItemLoadingIndicator type="SHEET" />
|
||||
) : sheetOptions.length > 0 ? (
|
||||
sheetOptions.map((sheet) => (
|
||||
<Entity
|
||||
className={`t--sheet-structure ${
|
||||
sheet.value === selectedSheet.value
|
||||
? "t--sheet-structure-active"
|
||||
: ""
|
||||
}`}
|
||||
entityId={`${datasource?.id}-${selectedSpreadsheet.value}-${sheet.value}`}
|
||||
icon={null}
|
||||
key={`${datasource?.id}-${selectedSpreadsheet.value}-${sheet.value}`}
|
||||
name={sheet.label as string}
|
||||
onToggle={(isOpen) => {
|
||||
isOpen && onSelectSheetOption(sheet.value, sheet);
|
||||
}}
|
||||
step={1}
|
||||
>
|
||||
{selectedSheet.value === sheet.value ? (
|
||||
isFetchingSheetData ? (
|
||||
<ItemLoadingIndicator type="DATA" />
|
||||
) : sheetData?.length > 0 ? (
|
||||
<DatasourceAttributesWrapper>
|
||||
{Object.keys(sheetData[0]).map(
|
||||
(fieldValue, index) => (
|
||||
<DatasourceField
|
||||
field={{
|
||||
name: fieldValue,
|
||||
type: "string",
|
||||
}}
|
||||
key={`${fieldValue}${index}`}
|
||||
step={2}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</DatasourceAttributesWrapper>
|
||||
) : null
|
||||
) : (
|
||||
<ItemLoadingIndicator type="DATA" />
|
||||
)}
|
||||
</Entity>
|
||||
))
|
||||
) : (
|
||||
<ItemLoadingIndicator type="SPREADSHEET" />
|
||||
)}
|
||||
</Entity>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</SelectWrapper>
|
||||
</SelectListWrapper>
|
||||
) : null}
|
||||
{selectedSpreadsheet.value ? (
|
||||
<SelectListWrapper>
|
||||
<Text>Sheet</Text>
|
||||
<SelectWrapper width={DROPDOWN_DIMENSION.WIDTH}>
|
||||
<Select
|
||||
data-testid="t--sheetName-dropdown"
|
||||
isLoading={isFetchingSheetsList}
|
||||
onChange={(value: any) =>
|
||||
onSelectSheetOption(
|
||||
value,
|
||||
sheetsList.find(
|
||||
(sheet: DropdownOption) => sheet.value === value,
|
||||
),
|
||||
)
|
||||
}
|
||||
value={selectedSheet}
|
||||
>
|
||||
{sheetsList.map((sheet) => {
|
||||
return (
|
||||
<Option key={sheet.label} value={sheet.label}>
|
||||
{sheet?.label}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</SelectWrapper>
|
||||
</SelectListWrapper>
|
||||
) : null}
|
||||
{showGeneratePageBtn && (
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</DatasourceListContainer>
|
||||
</StructureContainer>
|
||||
<DatasourceDataContainer>
|
||||
<TableWrapper>
|
||||
{isLoading ? (
|
||||
<RenderInterimDataState state="LOADING" />
|
||||
) : isError ? (
|
||||
<RenderInterimDataState state="FAILED" />
|
||||
) : sheetData?.length > 0 ? (
|
||||
<Table data={sheetData} shouldResize={false} />
|
||||
) : (
|
||||
<RenderInterimDataState state="NODATA" />
|
||||
)}
|
||||
</TableWrapper>
|
||||
</DatasourceDataContainer>
|
||||
</DataWrapperContainer>
|
||||
{showGeneratePageBtn && (
|
||||
<ButtonContainer>
|
||||
<Button
|
||||
className="t--datasource-generate-page"
|
||||
isDisabled={!currentSheetData || currentSheetData?.length == 0}
|
||||
key="datasource-generate-page"
|
||||
kind="secondary"
|
||||
onClick={onGsheetGeneratePage}
|
||||
|
|
@ -389,29 +488,9 @@ function GoogleSheetSchema(props: Props) {
|
|||
>
|
||||
{createMessage(DATASOURCE_GENERATE_PAGE_BUTTON)}
|
||||
</Button>
|
||||
)}
|
||||
</SelectContainer>
|
||||
<TableWrapper>
|
||||
{isLoading ? (
|
||||
<MessageWrapper>
|
||||
<Spinner size="md" />
|
||||
<Text style={{ marginLeft: "8px" }}>
|
||||
{createMessage(FETCHING_DATASOURCE_PREVIEW_DATA)}
|
||||
</Text>
|
||||
</MessageWrapper>
|
||||
) : isError ? (
|
||||
<Text color="var(--ads-color-red-500)">
|
||||
{createMessage(ERR_FETCHING_DATASOURCE_PREVIEW_DATA)}
|
||||
</Text>
|
||||
) : currentSheetData?.length > 0 ? (
|
||||
<Table data={currentSheetData} />
|
||||
) : (
|
||||
<MessageWrapper>
|
||||
<Text>{createMessage(SCHEMA_PREVIEW_NO_DATA)}</Text>
|
||||
</MessageWrapper>
|
||||
)}
|
||||
</TableWrapper>
|
||||
</>
|
||||
</ButtonContainer>
|
||||
)}
|
||||
</ViewModeSchemaContainer>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
import React from "react";
|
||||
import {
|
||||
createMessage,
|
||||
GSHEET_DATA_LOADING,
|
||||
GSHEET_SHEET_LOADING,
|
||||
GSHEET_SPREADSHEET_LOADING,
|
||||
LOADING_SCHEMA,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { Spinner, Text } from "design-system";
|
||||
import { MessageWrapper } from "./SchemaViewModeCSS";
|
||||
|
||||
type LoadingItemType = "SPREADSHEET" | "SHEET" | "DATA" | "SCHEMA";
|
||||
|
||||
const ItemLoadingIndicator = ({ type }: { type: LoadingItemType }) => {
|
||||
return (
|
||||
<MessageWrapper
|
||||
className={`t--item-loading-indicator t--item-loading-indicator--${type.toLowerCase()}`}
|
||||
>
|
||||
<Spinner size="md" />
|
||||
<Text style={{ marginLeft: "8px" }}>
|
||||
{createMessage(
|
||||
type === "SPREADSHEET"
|
||||
? GSHEET_SPREADSHEET_LOADING
|
||||
: type === "SHEET"
|
||||
? GSHEET_SHEET_LOADING
|
||||
: type === "SCHEMA"
|
||||
? LOADING_SCHEMA
|
||||
: GSHEET_DATA_LOADING,
|
||||
)}
|
||||
</Text>
|
||||
</MessageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemLoadingIndicator;
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
import EmptyTableSVG from "assets/images/empty-table-in-display-preview.svg";
|
||||
import { Spinner, Text } from "design-system";
|
||||
import {
|
||||
EMPTY_TABLE_TITLE_TEXT,
|
||||
EMPTY_TABLE_MESSAGE_TEXT,
|
||||
createMessage,
|
||||
EMPTY_TABLE_SVG_ALT_TEXT,
|
||||
LOADING_RECORDS_MESSAGE_TEXT,
|
||||
LOADING_RECORDS_TITLE_TEXT,
|
||||
ERR_FETCHING_DATASOURCE_PREVIEW_DATA,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { MessageWrapper, SchemaStateMessageWrapper } from "./SchemaViewModeCSS";
|
||||
|
||||
type InterimState = "LOADING" | "NODATA" | "FAILED";
|
||||
|
||||
interface RenderInterimDataStateProps {
|
||||
state: InterimState;
|
||||
}
|
||||
|
||||
const RenderInterimDataState = ({ state }: RenderInterimDataStateProps) => {
|
||||
return (
|
||||
<MessageWrapper>
|
||||
<SchemaStateMessageWrapper>
|
||||
{state === "NODATA" ? (
|
||||
<>
|
||||
{/* Render empty table image */}
|
||||
<img
|
||||
alt={createMessage(EMPTY_TABLE_SVG_ALT_TEXT)}
|
||||
src={EmptyTableSVG}
|
||||
/>
|
||||
{/* Show description below the image */}
|
||||
{/* Show title */}
|
||||
<Text style={{ fontWeight: "bold" }}>
|
||||
{createMessage(EMPTY_TABLE_TITLE_TEXT)}
|
||||
</Text>
|
||||
{/* Show description */}
|
||||
<Text>{createMessage(EMPTY_TABLE_MESSAGE_TEXT)}</Text>
|
||||
</>
|
||||
) : state === "FAILED" ? (
|
||||
<Text color="var(--ads-color-red-500)">
|
||||
{createMessage(ERR_FETCHING_DATASOURCE_PREVIEW_DATA)}
|
||||
</Text>
|
||||
) : state === "LOADING" ? (
|
||||
<>
|
||||
{/* Show spinner */}
|
||||
<Spinner size="md" />
|
||||
{/* Show title */}
|
||||
<Text style={{ fontWeight: "bold" }}>
|
||||
{createMessage(LOADING_RECORDS_TITLE_TEXT)}
|
||||
</Text>
|
||||
{/* Show description */}
|
||||
<Text>{createMessage(LOADING_RECORDS_MESSAGE_TEXT)}</Text>
|
||||
</>
|
||||
) : null}
|
||||
</SchemaStateMessageWrapper>
|
||||
</MessageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenderInterimDataState;
|
||||
212
app/client/src/pages/Editor/DatasourceInfo/SchemaViewModeCSS.tsx
Normal file
212
app/client/src/pages/Editor/DatasourceInfo/SchemaViewModeCSS.tsx
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
export const ViewModeSchemaContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const DataWrapperContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
.t--datasource-column:hover {
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
`;
|
||||
|
||||
export const StructureContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
padding: var(--ads-v2-spaces-4) 0 var(--ads-v2-spaces-4)
|
||||
var(--ads-v2-spaces-5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid var(--ads-v2-color-gray-300);
|
||||
flex-shrink: 0;
|
||||
& > .datasourceStructure-header {
|
||||
padding: 0 var(--ads-v2-spaces-5);
|
||||
}
|
||||
`;
|
||||
|
||||
export const DatasourceDataContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 75%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const DatasourceListContainer = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: var(--ads-v2-spaces-5);
|
||||
.t--entity.datasourceStructure-datasource-view-mode {
|
||||
padding-right: var(--ads-v2-spaces-5);
|
||||
}
|
||||
&.t--gsheet-structure {
|
||||
padding-bottom: var(--ads-v2-spaces-8);
|
||||
.t--gsheet-structure-list {
|
||||
overflow-y: auto;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 0;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.t--spreadsheet-structure > .t--entity-item {
|
||||
font-weight: var(--ads-v2-font-weight-bold);
|
||||
height: 36px;
|
||||
padding-left: 0;
|
||||
& .ads-v2-button__content:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
.t--spreadsheet-structure {
|
||||
padding-right: var(--ads-spaces-3);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.t--sheet-structure:not(:last-of-type) {
|
||||
padding-bottom: var(--ads-spaces-3);
|
||||
}
|
||||
.t--sheet-structure .t--datasource-column {
|
||||
padding-top: var(--ads-spaces-1);
|
||||
padding-bottom: var(--ads-spaces-1);
|
||||
}
|
||||
.t--sheet-structure > .t--entity-item {
|
||||
color: var(--ads-v2-color-gray-600);
|
||||
height: 36px;
|
||||
padding-left: var(--ads-spaces-10);
|
||||
}
|
||||
.t--sheet-structure .t--datasource-column {
|
||||
color: var(--ads-v2-color-gray-600);
|
||||
height: 30px;
|
||||
padding-left: var(--ads-spaces-10);
|
||||
}
|
||||
.t--sheet-structure.t--sheet-structure-active > .t--entity-item {
|
||||
background: var(--ads-v2-color-gray-100);
|
||||
}
|
||||
.t--sheet-structure .t--entity-name {
|
||||
padding: var(--ads-spaces-2) 0;
|
||||
}
|
||||
}
|
||||
.t--schema-virtuoso-container {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const DatasourceAttributesWrapper = styled.div`
|
||||
padding: var(--ads-spaces-1) 0 var(--ads-spaces-1) var(--ads-spaces-10);
|
||||
.t--datasource-column > div > div:first-of-type {
|
||||
padding-right: var(--ads-spaces-3);
|
||||
}
|
||||
.t--datasource-column > div > div:last-of-type {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid var(--ads-v2-color-gray-300);
|
||||
padding: var(--ads-v2-spaces-4);
|
||||
`;
|
||||
|
||||
export const MessageWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
&.t--item-loading-indicator {
|
||||
justify-content: flex-start;
|
||||
padding: var(--ads-spaces-1) 0 var(--ads-spaces-1) var(--ads-spaces-11);
|
||||
}
|
||||
&.t--item-loading-indicator--spreadsheet,
|
||||
&.t--item-loading-indicator--schema {
|
||||
padding-left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SchemaStateMessageWrapper = styled.div`
|
||||
width: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
img {
|
||||
padding-bottom: var(--ads-v2-spaces-7);
|
||||
}
|
||||
span:first-child {
|
||||
padding-bottom: var(--ads-v2-spaces-2);
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableWrapper = styled.div`
|
||||
overflow-x: auto;
|
||||
height: 100%;
|
||||
&& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&& > ${MessageWrapper} {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
&& .t--table-response {
|
||||
border: none;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
&& .tableWrap {
|
||||
overflow: auto;
|
||||
}
|
||||
& .table {
|
||||
background: none;
|
||||
& > div:first-child {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
& .draggable-header {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
& .table div:first-of-type .tr {
|
||||
background: var(--ads-v2-color-black-5);
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--ads-v2-color-black-75);
|
||||
}
|
||||
&& .table div.tbody .tr {
|
||||
background: var(--ads-v2-color-white);
|
||||
border-bottom: 1px solid var(--ads-v2-color-black-75);
|
||||
}
|
||||
&& .table .td,
|
||||
&& .table .th {
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
button {
|
||||
margin-right: 24px;
|
||||
}
|
||||
&& .tableWrap {
|
||||
}
|
||||
`;
|
||||
|
||||
export const DatasourceStructureSearchContainer = styled.div`
|
||||
margin: var(--ads-v2-spaces-3) 0 var(--ads-v2-spaces-4) 0;
|
||||
background: white;
|
||||
flex-shrink: 0;
|
||||
padding-right: var(--ads-v2-spaces-5);
|
||||
&.t--gsheet-search-container {
|
||||
margin-top: var(--ads-v2-spaces-4);
|
||||
margin-bottom: var(--ads-v2-spaces-3);
|
||||
}
|
||||
&.t--search-container--query-editor {
|
||||
padding-right: 0;
|
||||
}
|
||||
`;
|
||||
|
|
@ -272,7 +272,17 @@ export interface UseSheetDataReturn {
|
|||
}: FetchSheetData) => void;
|
||||
}
|
||||
|
||||
export const useSheetsList = (): UseSheetListReturn => {
|
||||
export interface UseSheetListProps {
|
||||
setSheetOptions?: (tableOptions: DropdownOptions) => void;
|
||||
}
|
||||
|
||||
export interface UseSheetDataProps {
|
||||
setSheetData?: (tableOptions: DropdownOptions) => void;
|
||||
}
|
||||
|
||||
export const useSheetsList = (
|
||||
props: UseSheetListProps = {},
|
||||
): UseSheetListReturn => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [sheetsList, setSheetsList] = useState<DropdownOption[]>([]);
|
||||
|
|
@ -298,12 +308,13 @@ export const useSheetsList = (): UseSheetListReturn => {
|
|||
const responseBody = payload.data.trigger;
|
||||
if (Array.isArray(responseBody)) {
|
||||
setSheetsList(responseBody);
|
||||
props.setSheetOptions && props.setSheetOptions(responseBody);
|
||||
} else {
|
||||
// to handle error like "401 Unauthorized"
|
||||
}
|
||||
}
|
||||
},
|
||||
[setSheetsList, setIsFetchingSheetsList],
|
||||
[setSheetsList, setIsFetchingSheetsList, props.setSheetOptions],
|
||||
);
|
||||
|
||||
const fetchSheetsList = useCallback(
|
||||
|
|
@ -314,6 +325,7 @@ export const useSheetsList = (): UseSheetListReturn => {
|
|||
selectedSpreadsheetUrl,
|
||||
}: FetchSheetsList) => {
|
||||
setSheetsList([]);
|
||||
props.setSheetOptions && props.setSheetOptions([]);
|
||||
setIsFetchingSheetsList(true);
|
||||
setFailedFetchingSheetsList(false);
|
||||
const formattedRequestData = {
|
||||
|
|
@ -343,6 +355,7 @@ export const useSheetsList = (): UseSheetListReturn => {
|
|||
onFetchAllSheetFailure,
|
||||
setIsFetchingSheetsList,
|
||||
setFailedFetchingSheetsList,
|
||||
props.setSheetOptions,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -354,7 +367,9 @@ export const useSheetsList = (): UseSheetListReturn => {
|
|||
};
|
||||
};
|
||||
|
||||
export const useSheetData = (): UseSheetDataReturn => {
|
||||
export const useSheetData = (
|
||||
props: UseSheetDataProps = {},
|
||||
): UseSheetDataReturn => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [sheetData, setSheetData] = useState<any>([]);
|
||||
|
|
@ -380,12 +395,13 @@ export const useSheetData = (): UseSheetDataReturn => {
|
|||
const responseBody = payload.data.trigger;
|
||||
if (Array.isArray(responseBody)) {
|
||||
setSheetData(responseBody);
|
||||
props.setSheetData && props.setSheetData(responseBody);
|
||||
} else {
|
||||
// to handle error like "401 Unauthorized"
|
||||
}
|
||||
}
|
||||
},
|
||||
[setSheetData, setIsFetchingSheetData],
|
||||
[setSheetData, setIsFetchingSheetData, props.setSheetData],
|
||||
);
|
||||
|
||||
const fetchSheetData = useCallback(
|
||||
|
|
@ -396,6 +412,7 @@ export const useSheetData = (): UseSheetDataReturn => {
|
|||
selectedSpreadsheetUrl,
|
||||
}: FetchSheetData) => {
|
||||
setSheetData([]);
|
||||
props.setSheetData && props.setSheetData([]);
|
||||
setIsFetchingSheetData(true);
|
||||
setFailedFetchingSheetData(false);
|
||||
const formattedRequestData = {
|
||||
|
|
@ -428,6 +445,7 @@ export const useSheetData = (): UseSheetDataReturn => {
|
|||
onFetchAllSheetFailure,
|
||||
setIsFetchingSheetData,
|
||||
setFailedFetchingSheetData,
|
||||
props.setSheetData,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import type { Theme } from "constants/DefaultTheme";
|
|||
interface TableProps {
|
||||
data: Record<string, any>[];
|
||||
tableBodyHeight?: number;
|
||||
shouldResize?: boolean;
|
||||
}
|
||||
|
||||
const TABLE_SIZES = {
|
||||
|
|
@ -208,6 +209,7 @@ export const getScrollBarWidth = (tableBodyEle: any, scrollBarW: number) => {
|
|||
function Table(props: TableProps) {
|
||||
const theme = useTheme() as Theme;
|
||||
const tableBodyRef = React.useRef<HTMLElement>();
|
||||
const { shouldResize = true } = props;
|
||||
|
||||
const data = React.useMemo(() => {
|
||||
/* Check for length greater than 0 of rows returned from the query for mappings keys */
|
||||
|
|
@ -346,12 +348,14 @@ function Table(props: TableProps) {
|
|||
>
|
||||
<AutoToolTipComponent title={column.render("Header")}>
|
||||
{column.render("Header")}
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={`resizer ${
|
||||
column.isResizing ? "isResizing" : ""
|
||||
}`}
|
||||
/>
|
||||
{shouldResize && (
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={`resizer ${
|
||||
column.isResizing ? "isResizing" : ""
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</AutoToolTipComponent>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ import {
|
|||
} from "@appsmith/utils/BusinessFeatures/permissionPageHelpers";
|
||||
import { selectFeatureFlagCheck } from "@appsmith/selectors/featureFlagsSelectors";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import GoogleSheetSchema from "../DatasourceInfo/GoogleSheetSchema";
|
||||
import DatasourceTabs from "../DatasourceInfo/DatasorceTabs";
|
||||
|
||||
const ViewModeContainer = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -468,13 +468,77 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
}
|
||||
|
||||
renderDatasourceInfo = () => {
|
||||
const { datasource, formConfig, viewMode } = this.props;
|
||||
const {
|
||||
datasource,
|
||||
formConfig,
|
||||
formData,
|
||||
isPluginAuthFailed,
|
||||
isPluginAuthorized,
|
||||
pageId,
|
||||
plugin,
|
||||
pluginPackageName,
|
||||
viewMode,
|
||||
} = this.props;
|
||||
const isGoogleSheetPlugin = isGoogleSheetPluginDS(pluginPackageName);
|
||||
const authErrorMessage = getDatasourceErrorMessage(
|
||||
formData,
|
||||
plugin,
|
||||
this.state.filterParams.id,
|
||||
);
|
||||
const hideDatasourceSection =
|
||||
isGoogleSheetPlugin &&
|
||||
!isPluginAuthorized &&
|
||||
authErrorMessage == GSHEET_AUTHORIZATION_ERROR;
|
||||
|
||||
return (
|
||||
<DatasourceInformation
|
||||
config={formConfig[0]}
|
||||
datasource={datasource}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
<ViewModeWrapper data-testid="t--ds-review-section">
|
||||
{datasource &&
|
||||
isGoogleSheetPlugin &&
|
||||
isPluginAuthFailed &&
|
||||
datasource.id !== TEMP_DATASOURCE_ID && (
|
||||
<AuthMessage
|
||||
actionType={ActionType.AUTHORIZE}
|
||||
datasource={datasource}
|
||||
description={authErrorMessage}
|
||||
isInViewMode
|
||||
pageId={pageId}
|
||||
/>
|
||||
)}
|
||||
{!isNil(formConfig) && !isNil(datasource) && !hideDatasourceSection && (
|
||||
<DatasourceInformation
|
||||
config={formConfig[0]}
|
||||
datasource={datasource}
|
||||
viewMode={viewMode}
|
||||
/>
|
||||
)}
|
||||
</ViewModeWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
shouldShowTabs = () => {
|
||||
const { datasource, isPluginAuthorized, pluginPackageName } = this.props;
|
||||
|
||||
const isGoogleSheetPlugin = isGoogleSheetPluginDS(pluginPackageName);
|
||||
|
||||
const isGoogleSheetSchemaAvailable =
|
||||
isGoogleSheetPlugin && isPluginAuthorized;
|
||||
|
||||
return isGoogleSheetSchemaAvailable && datasource;
|
||||
};
|
||||
|
||||
renderTabsForViewMode = () => {
|
||||
const { datasource } = this.props;
|
||||
return (
|
||||
<ViewModeContainer>
|
||||
{this.shouldShowTabs() ? (
|
||||
<DatasourceTabs
|
||||
configChild={this.renderDatasourceInfo()}
|
||||
datasource={datasource as Datasource}
|
||||
/>
|
||||
) : (
|
||||
this.renderDatasourceInfo()
|
||||
)}
|
||||
</ViewModeContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -486,7 +550,6 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
datasource,
|
||||
datasourceButtonConfiguration,
|
||||
datasourceId,
|
||||
formConfig,
|
||||
formData,
|
||||
gsheetProjectID,
|
||||
gsheetToken,
|
||||
|
|
@ -526,13 +589,8 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
GOOGLE_SHEETS_INFO_BANNER_MESSAGE,
|
||||
);
|
||||
|
||||
const hideDatasourceSection =
|
||||
isGoogleSheetPlugin &&
|
||||
!isPluginAuthorized &&
|
||||
authErrorMessage == GSHEET_AUTHORIZATION_ERROR;
|
||||
|
||||
const isGoogleSheetSchemaAvailable =
|
||||
isGoogleSheetPlugin && isPluginAuthorized;
|
||||
const showingTabsOnViewMode =
|
||||
this.shouldShowTabs() && viewMode && !isInsideReconnectModal;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -546,6 +604,7 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
isDeleting={isDeleting}
|
||||
isNewDatasource={createFlow}
|
||||
isPluginAuthorized={isPluginAuthorized}
|
||||
noBottomBorder={showingTabsOnViewMode}
|
||||
pluginImage={pluginImage}
|
||||
pluginName={plugin?.name || ""}
|
||||
pluginType={plugin?.type || ""}
|
||||
|
|
@ -554,7 +613,11 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
/>
|
||||
)}
|
||||
<ResizerMainContainer>
|
||||
<ResizerContentContainer className="saas-form-resizer-content">
|
||||
<ResizerContentContainer
|
||||
className={`saas-form-resizer-content ${
|
||||
showingTabsOnViewMode && "saas-form-resizer-content-show-tabs"
|
||||
}`}
|
||||
>
|
||||
<DSEditorWrapper>
|
||||
<DSDataFilter
|
||||
filterId={this.state.filterParams.id}
|
||||
|
|
@ -601,35 +664,9 @@ class DatasourceSaaSEditor extends JSONtoForm<Props, State> {
|
|||
{""}
|
||||
</>
|
||||
)}
|
||||
{viewMode && !isInsideReconnectModal && (
|
||||
<ViewModeContainer>
|
||||
<ViewModeWrapper>
|
||||
{datasource &&
|
||||
isGoogleSheetPlugin &&
|
||||
isPluginAuthFailed ? (
|
||||
<AuthMessage
|
||||
actionType={ActionType.AUTHORIZE}
|
||||
datasource={datasource}
|
||||
description={authErrorMessage}
|
||||
isInViewMode
|
||||
pageId={pageId}
|
||||
/>
|
||||
) : null}
|
||||
{!isNil(formConfig) &&
|
||||
!isNil(datasource) &&
|
||||
!hideDatasourceSection
|
||||
? this.renderDatasourceInfo()
|
||||
: undefined}
|
||||
</ViewModeWrapper>
|
||||
{isGoogleSheetSchemaAvailable && datasource && (
|
||||
<GoogleSheetSchema
|
||||
datasourceId={datasourceId}
|
||||
key={datasourceId}
|
||||
pluginId={plugin?.id}
|
||||
/>
|
||||
)}
|
||||
</ViewModeContainer>
|
||||
)}
|
||||
{viewMode &&
|
||||
!isInsideReconnectModal &&
|
||||
this.renderTabsForViewMode()}
|
||||
</Form>
|
||||
{/* Render datasource form call-to-actions */}
|
||||
{datasource && (
|
||||
|
|
|
|||
|
|
@ -622,4 +622,5 @@ export const ColumnWrapper = styled.div`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user