chore: [one click binding] gsheets query adaptor (#23390)
## Description Developed Gsheets query generator for one click binding epic. #### PR fixes following issue(s) Fixes #23255 #### Type of change - New feature (non-breaking change which adds functionality) - Chore (housekeeping or task changes that don't impact user perception) ## Testing > #### How Has This Been Tested? - [x] Manual - [x] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [ ] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [ ] 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/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#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
This commit is contained in:
parent
6702ed2ea5
commit
f9ad42f667
268
app/client/src/WidgetQueryGenerators/GSheets/index.test.ts
Normal file
268
app/client/src/WidgetQueryGenerators/GSheets/index.test.ts
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
import GSheets from ".";
|
||||
|
||||
describe("GSheets WidgetQueryGenerator", () => {
|
||||
const initialValues = {
|
||||
actionConfiguration: {
|
||||
formData: {
|
||||
entityType: {
|
||||
data: "ROWS",
|
||||
},
|
||||
tableHeaderIndex: {
|
||||
data: "1",
|
||||
},
|
||||
projection: {
|
||||
data: [],
|
||||
},
|
||||
queryFormat: {
|
||||
data: "ROWS",
|
||||
},
|
||||
range: {
|
||||
data: "",
|
||||
},
|
||||
where: {
|
||||
data: {
|
||||
condition: "AND",
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
data: {
|
||||
limit: "{{Table1.pageSize}}",
|
||||
offset: "{{Table1.pageOffset}}",
|
||||
},
|
||||
},
|
||||
smartSubstitution: {
|
||||
data: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test("should build select form data correctly", () => {
|
||||
const expr = GSheets.build(
|
||||
{
|
||||
select: {
|
||||
limit: "data_table.pageSize",
|
||||
where: "data_table.searchText",
|
||||
offset: "(data_table.pageNo - 1) * data_table.pageSize",
|
||||
orderBy: "data_table.sortOrder.column || 'genres'",
|
||||
sortOrder: 'data_table.sortOrder.order == "desc" ? -1 : 1',
|
||||
},
|
||||
totalRecord: false,
|
||||
},
|
||||
{
|
||||
tableName: "someTableUrl",
|
||||
datasourceId: "someId",
|
||||
// ignore columns
|
||||
aliases: [{ name: "someColumn1", alias: "someColumn1" }],
|
||||
widgetId: "someWidgetId",
|
||||
searchableColumn: "title",
|
||||
columns: [],
|
||||
primaryColumn: "",
|
||||
sheetName: "someSheet",
|
||||
tableHeaderIndex: 1,
|
||||
},
|
||||
initialValues,
|
||||
);
|
||||
|
||||
expect(expr).toEqual([
|
||||
{
|
||||
name: "Find_query",
|
||||
payload: {
|
||||
formData: {
|
||||
command: {
|
||||
data: "FETCH_MANY",
|
||||
},
|
||||
entityType: {
|
||||
data: "ROWS",
|
||||
},
|
||||
pagination: {
|
||||
data: {
|
||||
limit: "{{data_table.pageSize}}",
|
||||
offset: "{{(data_table.pageNo - 1) * data_table.pageSize}}",
|
||||
},
|
||||
},
|
||||
projection: {
|
||||
data: [],
|
||||
},
|
||||
queryFormat: {
|
||||
data: "ROWS",
|
||||
},
|
||||
range: {
|
||||
data: "",
|
||||
},
|
||||
sheetName: {
|
||||
data: "someSheet",
|
||||
},
|
||||
sheetUrl: {
|
||||
data: "someTableUrl",
|
||||
},
|
||||
smartSubstitution: {
|
||||
data: true,
|
||||
},
|
||||
sortBy: {
|
||||
data: [
|
||||
{
|
||||
column: "{{data_table.sortOrder.column || 'genres'}}",
|
||||
order: 'data_table.sortOrder.order == "desc" ? -1 : 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
tableHeaderIndex: {
|
||||
data: "1",
|
||||
},
|
||||
where: {
|
||||
data: {
|
||||
children: [
|
||||
{
|
||||
condition: "CONTAINS",
|
||||
key: '{{data_table.searchText ? "title" : ""}}',
|
||||
value: "{{data_table.searchText}}",
|
||||
},
|
||||
],
|
||||
condition: "AND",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: "select",
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.where.data",
|
||||
},
|
||||
{
|
||||
key: "formData.sortBy.data",
|
||||
},
|
||||
{
|
||||
key: "formData.pagination.data",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("should build update form data correctly ", () => {
|
||||
const expr = GSheets.build(
|
||||
{
|
||||
update: {
|
||||
value: "update_form.formData",
|
||||
},
|
||||
totalRecord: false,
|
||||
},
|
||||
{
|
||||
tableName: "someTableUrl",
|
||||
datasourceId: "someId",
|
||||
// ignore columns
|
||||
aliases: [{ name: "someColumn1", alias: "someColumn1" }],
|
||||
widgetId: "someWidgetId",
|
||||
searchableColumn: "title",
|
||||
columns: [],
|
||||
primaryColumn: "",
|
||||
sheetName: "someSheet",
|
||||
tableHeaderIndex: 1,
|
||||
},
|
||||
initialValues,
|
||||
);
|
||||
|
||||
expect(expr).toEqual([
|
||||
{
|
||||
name: "Update_query",
|
||||
payload: {
|
||||
formData: {
|
||||
command: {
|
||||
data: "UPDATE_ONE",
|
||||
},
|
||||
entityType: {
|
||||
data: "ROWS",
|
||||
},
|
||||
queryFormat: {
|
||||
data: "ROWS",
|
||||
},
|
||||
rowObjects: {
|
||||
data: "{{update_form.formData}}",
|
||||
},
|
||||
sheetName: {
|
||||
data: "someSheet",
|
||||
},
|
||||
sheetUrl: {
|
||||
data: "someTableUrl",
|
||||
},
|
||||
smartSubstitution: {
|
||||
data: true,
|
||||
},
|
||||
tableHeaderIndex: {
|
||||
data: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.rowObjects.data",
|
||||
},
|
||||
],
|
||||
type: "update",
|
||||
},
|
||||
]);
|
||||
});
|
||||
test("should build insert form data correctly ", () => {
|
||||
const expr = GSheets.build(
|
||||
{
|
||||
create: {
|
||||
value: "insert_form.formData",
|
||||
},
|
||||
totalRecord: false,
|
||||
},
|
||||
{
|
||||
tableName: "someTableUrl",
|
||||
datasourceId: "someId",
|
||||
// ignore columns
|
||||
aliases: [{ name: "someColumn1", alias: "someColumn1" }],
|
||||
widgetId: "someWidgetId",
|
||||
searchableColumn: "title",
|
||||
columns: [],
|
||||
primaryColumn: "",
|
||||
sheetName: "someSheet",
|
||||
tableHeaderIndex: 1,
|
||||
},
|
||||
initialValues,
|
||||
);
|
||||
expect(expr).toEqual([
|
||||
{
|
||||
name: "Insert_query",
|
||||
payload: {
|
||||
formData: {
|
||||
command: {
|
||||
data: "INSERT_ONE",
|
||||
},
|
||||
entityType: {
|
||||
data: "ROWS",
|
||||
},
|
||||
queryFormat: {
|
||||
data: "ROWS",
|
||||
},
|
||||
rowObjects: {
|
||||
data: "{{insert_form.formData}}",
|
||||
},
|
||||
sheetName: {
|
||||
data: "someSheet",
|
||||
},
|
||||
sheetUrl: {
|
||||
data: "someTableUrl",
|
||||
},
|
||||
smartSubstitution: {
|
||||
data: true,
|
||||
},
|
||||
tableHeaderIndex: {
|
||||
data: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
type: "create",
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.rowObjects.data",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
291
app/client/src/WidgetQueryGenerators/GSheets/index.ts
Normal file
291
app/client/src/WidgetQueryGenerators/GSheets/index.ts
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
import { isEmpty, isNumber, merge } from "lodash";
|
||||
import { BaseQueryGenerator } from "WidgetQueryGenerators/BaseQueryGenerator";
|
||||
import { QUERY_TYPE } from "WidgetQueryGenerators/types";
|
||||
import type {
|
||||
WidgetQueryGenerationConfig,
|
||||
WidgetQueryGenerationFormConfig,
|
||||
GSheetsFormData,
|
||||
ActionConfigurationGSheets,
|
||||
} from "WidgetQueryGenerators/types";
|
||||
|
||||
enum COMMAND_TYPES {
|
||||
"FIND" = "FETCH_MANY",
|
||||
"INSERT" = "INSERT_ONE",
|
||||
"UPDATE" = "UPDATE_ONE",
|
||||
"COUNT" = "FETCH_MANY",
|
||||
}
|
||||
const COMMON_INITIAL_VALUE_KEYS = [
|
||||
"smartSubstitution",
|
||||
"entityType",
|
||||
"queryFormat",
|
||||
];
|
||||
const SELECT_INITAL_VALUE_KEYS = [
|
||||
"range",
|
||||
"where",
|
||||
"pagination",
|
||||
"tableHeaderIndex",
|
||||
"projection",
|
||||
];
|
||||
|
||||
export default abstract class GSheets extends BaseQueryGenerator {
|
||||
private static buildBasicConfig(
|
||||
command: COMMAND_TYPES,
|
||||
tableName: string,
|
||||
sheetName?: string,
|
||||
tableHeaderIndex?: number,
|
||||
) {
|
||||
return {
|
||||
command: { data: command },
|
||||
sheetUrl: { data: tableName },
|
||||
sheetName: { data: sheetName },
|
||||
tableHeaderIndex: {
|
||||
data: isNumber(tableHeaderIndex)
|
||||
? tableHeaderIndex.toString()
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
private static buildFind(
|
||||
widgetConfig: WidgetQueryGenerationConfig,
|
||||
formConfig: WidgetQueryGenerationFormConfig,
|
||||
) {
|
||||
const { select } = widgetConfig;
|
||||
|
||||
if (select) {
|
||||
return {
|
||||
type: QUERY_TYPE.SELECT,
|
||||
name: "Find_query",
|
||||
formData: {
|
||||
where: {
|
||||
data: {
|
||||
children: [
|
||||
{
|
||||
condition: "CONTAINS",
|
||||
key: `{{${select["where"]} ? "${formConfig.searchableColumn}" : ""}}`,
|
||||
value: `{{${select["where"]}}}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
sortBy: {
|
||||
data: [
|
||||
{
|
||||
column: `{{${select["orderBy"]}}}`,
|
||||
order: select["sortOrder"],
|
||||
},
|
||||
],
|
||||
},
|
||||
pagination: {
|
||||
data: {
|
||||
limit: `{{${select["limit"]}}}`,
|
||||
offset: `{{${select["offset"]}}}`,
|
||||
},
|
||||
},
|
||||
...this.buildBasicConfig(
|
||||
COMMAND_TYPES.FIND,
|
||||
formConfig.tableName,
|
||||
formConfig.sheetName,
|
||||
formConfig.tableHeaderIndex,
|
||||
),
|
||||
},
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.where.data",
|
||||
},
|
||||
{
|
||||
key: "formData.sortBy.data",
|
||||
},
|
||||
{
|
||||
key: "formData.pagination.data",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static buildTotalRecord(
|
||||
widgetConfig: WidgetQueryGenerationConfig,
|
||||
formConfig: WidgetQueryGenerationFormConfig,
|
||||
) {
|
||||
const { select } = widgetConfig;
|
||||
|
||||
if (select) {
|
||||
return {
|
||||
type: QUERY_TYPE.TOTAL_RECORD,
|
||||
name: "Total_record_query",
|
||||
formData: {
|
||||
where: {
|
||||
data: {
|
||||
children: [
|
||||
{
|
||||
condition: "CONTAINS",
|
||||
key: `{{${select["where"]} ? "${formConfig.searchableColumn}" : ""}}`,
|
||||
value: `{{${select["where"]}}}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...this.buildBasicConfig(
|
||||
COMMAND_TYPES.COUNT,
|
||||
formConfig.tableName,
|
||||
formConfig.sheetName,
|
||||
formConfig.tableHeaderIndex,
|
||||
),
|
||||
},
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.where.data",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static buildUpdate(
|
||||
widgetConfig: WidgetQueryGenerationConfig,
|
||||
formConfig: WidgetQueryGenerationFormConfig,
|
||||
): Record<string, object | string> | undefined {
|
||||
const { update } = widgetConfig;
|
||||
|
||||
if (update) {
|
||||
return {
|
||||
type: QUERY_TYPE.UPDATE,
|
||||
name: "Update_query",
|
||||
formData: {
|
||||
rowObjects: {
|
||||
data: `{{${update.value}}}`,
|
||||
},
|
||||
...this.buildBasicConfig(
|
||||
COMMAND_TYPES.UPDATE,
|
||||
formConfig.tableName,
|
||||
formConfig.sheetName,
|
||||
formConfig.tableHeaderIndex,
|
||||
),
|
||||
},
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.rowObjects.data",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static buildInsert(
|
||||
widgetConfig: WidgetQueryGenerationConfig,
|
||||
formConfig: WidgetQueryGenerationFormConfig,
|
||||
) {
|
||||
const { create } = widgetConfig;
|
||||
|
||||
if (create) {
|
||||
return {
|
||||
type: QUERY_TYPE.CREATE,
|
||||
name: "Insert_query",
|
||||
formData: {
|
||||
rowObjects: {
|
||||
data: `{{${create.value}}}`,
|
||||
},
|
||||
...this.buildBasicConfig(
|
||||
COMMAND_TYPES.INSERT,
|
||||
formConfig.tableName,
|
||||
formConfig.sheetName,
|
||||
formConfig.tableHeaderIndex,
|
||||
),
|
||||
},
|
||||
dynamicBindingPathList: [
|
||||
{
|
||||
key: "formData.rowObjects.data",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static createPayload(
|
||||
initialValues: GSheetsFormData,
|
||||
commandKey: string,
|
||||
builtValues: Record<string, object | string> | undefined,
|
||||
) {
|
||||
if (!builtValues || isEmpty(builtValues)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initialValues || isEmpty(initialValues)) {
|
||||
return builtValues;
|
||||
}
|
||||
|
||||
const allowedInitialValueKeys = [
|
||||
...COMMON_INITIAL_VALUE_KEYS,
|
||||
...(commandKey === "find" ? SELECT_INITAL_VALUE_KEYS : []),
|
||||
];
|
||||
const scrubedOutInitialValues = allowedInitialValueKeys
|
||||
.filter((key) => initialValues[key as keyof GSheetsFormData])
|
||||
.reduce((acc, key) => {
|
||||
acc[key] = initialValues[key as keyof GSheetsFormData];
|
||||
return acc;
|
||||
}, {} as Record<string, object>);
|
||||
|
||||
const { formData, ...rest } = builtValues;
|
||||
|
||||
return {
|
||||
payload: {
|
||||
formData: merge({}, scrubedOutInitialValues, formData),
|
||||
},
|
||||
...rest,
|
||||
};
|
||||
}
|
||||
|
||||
static build(
|
||||
widgetConfig: WidgetQueryGenerationConfig,
|
||||
formConfig: WidgetQueryGenerationFormConfig,
|
||||
pluginInitalValues: { actionConfiguration: ActionConfigurationGSheets },
|
||||
) {
|
||||
const configs = [];
|
||||
|
||||
const initialValues = pluginInitalValues?.actionConfiguration?.formData;
|
||||
|
||||
if (widgetConfig.select) {
|
||||
configs.push(
|
||||
this.createPayload(
|
||||
initialValues,
|
||||
"find",
|
||||
this.buildFind(widgetConfig, formConfig),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widgetConfig.update) {
|
||||
configs.push(
|
||||
this.createPayload(
|
||||
initialValues,
|
||||
"updateMany",
|
||||
this.buildUpdate(widgetConfig, formConfig),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widgetConfig.create) {
|
||||
configs.push(
|
||||
this.createPayload(
|
||||
initialValues,
|
||||
"insert",
|
||||
this.buildInsert(widgetConfig, formConfig),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (widgetConfig.totalRecord) {
|
||||
configs.push(
|
||||
this.createPayload(
|
||||
initialValues,
|
||||
"count",
|
||||
this.buildTotalRecord(widgetConfig, formConfig),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return configs.filter((val) => !!val);
|
||||
}
|
||||
|
||||
static getTotalRecordExpression(binding: string) {
|
||||
return `${binding}.length`;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import { PluginPackageName } from "entities/Action";
|
||||
import WidgetQueryGeneratorRegistry from "utils/WidgetQueryGeneratorRegistry";
|
||||
import GSheets from "./GSheets";
|
||||
import MongoDB from "./MongoDB";
|
||||
import PostgreSQL from "./PostgreSQL";
|
||||
|
||||
WidgetQueryGeneratorRegistry.register(PluginPackageName.MONGO, MongoDB);
|
||||
WidgetQueryGeneratorRegistry.register(PluginPackageName.POSTGRES, PostgreSQL);
|
||||
WidgetQueryGeneratorRegistry.register(PluginPackageName.GOOGLE_SHEETS, GSheets);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
type GsheetConfig = {
|
||||
sheetName?: string;
|
||||
tableHeaderIndex?: number;
|
||||
};
|
||||
export type WidgetQueryGenerationFormConfig = {
|
||||
tableName: string;
|
||||
datasourceId: string;
|
||||
|
|
@ -9,7 +13,7 @@ export type WidgetQueryGenerationFormConfig = {
|
|||
searchableColumn: string;
|
||||
columns: string[];
|
||||
primaryColumn: string;
|
||||
};
|
||||
} & GsheetConfig;
|
||||
|
||||
export type WidgetQueryGenerationConfig = {
|
||||
select?: {
|
||||
|
|
@ -24,7 +28,7 @@ export type WidgetQueryGenerationConfig = {
|
|||
};
|
||||
update?: {
|
||||
value: string;
|
||||
where: string;
|
||||
where?: string;
|
||||
};
|
||||
totalRecord: boolean;
|
||||
};
|
||||
|
|
@ -59,3 +63,17 @@ export type ActionConfigurationMongoDB = {
|
|||
export type ActionConfigurationPostgreSQL = {
|
||||
pluginSpecifiedTemplates: Array<object>;
|
||||
};
|
||||
|
||||
export type GSheetsFormData = {
|
||||
entityType: object;
|
||||
tableHeaderIndex: object;
|
||||
projection: object;
|
||||
queryFormat: object;
|
||||
range: object;
|
||||
where: object;
|
||||
pagination: object;
|
||||
smartSubstitution: object;
|
||||
};
|
||||
export type ActionConfigurationGSheets = {
|
||||
formData: GSheetsFormData;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { memo } from "react";
|
||||
import { ErrorMessage, SelectWrapper } from "../../styles";
|
||||
import { ErrorMessage, Label, SelectWrapper } from "../../styles";
|
||||
import { useTableOrSpreadsheet } from "./useTableOrSpreadsheet";
|
||||
import { Select, Option } from "design-system";
|
||||
import { Select, Option, Tooltip } from "design-system";
|
||||
import { DropdownOption } from "../DatasourceDropdown/DropdownOption";
|
||||
import type { DefaultOptionType } from "rc-select/lib/Select";
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ function TableOrSpreadsheetDropdown() {
|
|||
error,
|
||||
isLoading,
|
||||
label,
|
||||
labelText,
|
||||
onSelect,
|
||||
options,
|
||||
selected,
|
||||
|
|
@ -20,7 +21,9 @@ function TableOrSpreadsheetDropdown() {
|
|||
if (show) {
|
||||
return (
|
||||
<SelectWrapper className="space-y-2">
|
||||
{label}
|
||||
<Tooltip content={labelText}>
|
||||
<Label>{label}</Label>
|
||||
</Tooltip>
|
||||
<Select
|
||||
data-testid="t--one-click-binding-table-selector"
|
||||
dropdownStyle={{
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ export function useTableOrSpreadsheet() {
|
|||
error: isGoogleSheetPluginDS(selectedDatasourcePluginPackageName)
|
||||
? spreadSheets?.error
|
||||
: datasourceStructure?.error?.message,
|
||||
labelText: `Select ${fieldName} from ${selectedDatasource?.name}`,
|
||||
label: (
|
||||
<Label>
|
||||
Select {fieldName} from <Bold>{selectedDatasource?.name}</Bold>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import type { AppState } from "@appsmith/reducers";
|
||||
import { PluginPackageName } from "entities/Action";
|
||||
import { isNumber } from "lodash";
|
||||
import { useContext } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getWidget } from "sagas/selectors";
|
||||
|
|
@ -9,6 +8,7 @@ import { getPluginPackageFromDatasourceId } from "selectors/entitiesSelector";
|
|||
import { getisOneClickBindingConnectingForWidget } from "selectors/oneClickBindingSelectors";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { WidgetQueryGeneratorFormContext } from "..";
|
||||
import { isValidGsheetConfig } from "../utils";
|
||||
import { useColumns } from "../WidgetSpecificControls/ColumnDropdown/useColumns";
|
||||
|
||||
export function useConnectData() {
|
||||
|
|
@ -29,8 +29,10 @@ export function useConnectData() {
|
|||
const onClick = () => {
|
||||
const payload = {
|
||||
tableName: config.table,
|
||||
sheetName: config.sheet,
|
||||
datasourceId: config.datasource,
|
||||
widgetId: widgetId,
|
||||
tableHeaderIndex: config.tableHeaderIndex,
|
||||
searchableColumn: config.searchableColumn,
|
||||
columns: columns.map((column) => column.name),
|
||||
primaryColumn,
|
||||
|
|
@ -63,9 +65,7 @@ export function useConnectData() {
|
|||
const disabled =
|
||||
!config.table ||
|
||||
(selectedDatasourcePluginPackageName === PluginPackageName.GOOGLE_SHEETS &&
|
||||
(!config.tableHeaderIndex ||
|
||||
!isNumber(Number(config.tableHeaderIndex)) ||
|
||||
isNaN(Number(config.tableHeaderIndex))));
|
||||
!isValidGsheetConfig(config));
|
||||
|
||||
return {
|
||||
show,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,45 @@
|
|||
import { DROPDOWN_TRIGGER_DIMENSION } from "components/editorComponents/WidgetQueryGeneratorForm/constants";
|
||||
import {
|
||||
ErrorMessage,
|
||||
Label,
|
||||
SelectWrapper,
|
||||
} from "components/editorComponents/WidgetQueryGeneratorForm/styles";
|
||||
import { Dropdown } from "design-system-old";
|
||||
import { Tooltip, Select } from "design-system";
|
||||
import React, { memo } from "react";
|
||||
import { useSheets } from "./useSheets";
|
||||
|
||||
export default memo(function SheetsDropdown() {
|
||||
const { error, isLoading, label, onSelect, options, selected, show } =
|
||||
useSheets();
|
||||
const {
|
||||
error,
|
||||
isLoading,
|
||||
label,
|
||||
labelText,
|
||||
onSelect,
|
||||
options,
|
||||
selected,
|
||||
show,
|
||||
} = useSheets();
|
||||
|
||||
if (show) {
|
||||
return (
|
||||
<SelectWrapper className="space-y-2">
|
||||
<Label>{label}</Label>
|
||||
<Dropdown
|
||||
<Tooltip content={labelText}>
|
||||
<Label>{label}</Label>
|
||||
</Tooltip>
|
||||
<Select
|
||||
data-testid="t--sheetName-dropdown"
|
||||
dropdownMaxHeight={"300px"}
|
||||
errorMsg={error}
|
||||
fillOptions
|
||||
height={DROPDOWN_TRIGGER_DIMENSION.HEIGHT}
|
||||
dropdownStyle={{
|
||||
minWidth: "350px",
|
||||
maxHeight: "300px",
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
isValid={!error}
|
||||
onSelect={onSelect}
|
||||
options={options}
|
||||
selected={selected}
|
||||
showLabelOnly
|
||||
width={DROPDOWN_TRIGGER_DIMENSION.WIDTH}
|
||||
placeholder="Select sheet"
|
||||
showSearch
|
||||
value={selected}
|
||||
/>
|
||||
<ErrorMessage>{error}</ErrorMessage>
|
||||
</SelectWrapper>
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ export function useSheets() {
|
|||
error: sheets?.error,
|
||||
options,
|
||||
isLoading,
|
||||
labelText: "Select sheet from " + config.table,
|
||||
label: (
|
||||
<Label>
|
||||
Select sheet from <Bold>{config.table}</Bold>
|
||||
|
|
|
|||
|
|
@ -7,21 +7,15 @@ import { Colors } from "constants/Colors";
|
|||
import { Icon } from "design-system";
|
||||
import { Tooltip } from "design-system";
|
||||
import React, { memo } from "react";
|
||||
import {
|
||||
Row,
|
||||
RowHeading,
|
||||
SelectWrapper,
|
||||
TooltipWrapper,
|
||||
} from "../../../styles";
|
||||
import { Label, Row, RowHeading, SelectWrapper } from "../../../styles";
|
||||
import styled from "styled-components";
|
||||
import { useTableHeaderIndex } from "./useTableHeader";
|
||||
import { Input } from "design-system";
|
||||
|
||||
const RoundBg = styled.div`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 16px;
|
||||
background-color: ${Colors.GRAY};
|
||||
background-color: ${Colors.WHITE};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
@ -33,22 +27,29 @@ export default memo(function TableHeaderIndex() {
|
|||
if (show) {
|
||||
return (
|
||||
<SelectWrapper className="space-y-2">
|
||||
<Row>
|
||||
<RowHeading>{createMessage(GEN_CRUD_TABLE_HEADER_LABEL)}</RowHeading>
|
||||
<TooltipWrapper>
|
||||
<Label>
|
||||
<Row>
|
||||
<RowHeading>
|
||||
{createMessage(GEN_CRUD_TABLE_HEADER_LABEL)}
|
||||
</RowHeading>
|
||||
<Tooltip
|
||||
content={createMessage(GEN_CRUD_TABLE_HEADER_TOOLTIP_DESC)}
|
||||
>
|
||||
<RoundBg>
|
||||
<Icon name="help" />
|
||||
<Icon name="question-line" size="md" />
|
||||
</RoundBg>
|
||||
</Tooltip>
|
||||
</TooltipWrapper>
|
||||
</Row>
|
||||
</Row>
|
||||
</Label>
|
||||
<Input
|
||||
className="space-y-4"
|
||||
errorMessage={error}
|
||||
isRequired
|
||||
labelPosition="top"
|
||||
onChange={onChange}
|
||||
placeholder="Table Header Index"
|
||||
placeholder="Table header index"
|
||||
size="md"
|
||||
type="number"
|
||||
value={value.toString()}
|
||||
/>
|
||||
</SelectWrapper>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { isNumber } from "lodash";
|
|||
import { useCallback, useContext } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getDatasource } from "selectors/entitiesSelector";
|
||||
import { isValidGsheetConfig } from "components/editorComponents/WidgetQueryGeneratorForm/utils";
|
||||
|
||||
export function useTableHeaderIndex() {
|
||||
const dispatch = useDispatch();
|
||||
|
|
@ -45,13 +46,9 @@ export function useTableHeaderIndex() {
|
|||
);
|
||||
|
||||
return {
|
||||
error:
|
||||
(!config.tableHeaderIndex ||
|
||||
!isNumber(Number(config.tableHeaderIndex)) ||
|
||||
isNaN(Number(config.tableHeaderIndex))) &&
|
||||
"Please enter a positive number",
|
||||
error: !isValidGsheetConfig(config) && "Please enter a positive number",
|
||||
value: config.tableHeaderIndex,
|
||||
onChange,
|
||||
show: !!config.table,
|
||||
show: !!config.table && !!config.sheet,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
import ColumnDropdown from "./ColumnDropdown";
|
||||
import { Section } from "../styles";
|
||||
import { noop } from "lodash";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -30,9 +29,9 @@ export default function WidgetSpecificControls(props: Props) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<>
|
||||
{searchableColumn}
|
||||
{aliasPicker}
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ export const SelectWrapper = styled.div`
|
|||
export const Label = styled.p`
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
export const Bold = styled.span`
|
||||
|
|
@ -29,10 +31,6 @@ export const Row = styled.div`
|
|||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
export const TooltipWrapper = styled.div`
|
||||
margin-top: 2px;
|
||||
`;
|
||||
|
||||
export const RowHeading = styled.p`
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,11 @@
|
|||
import { isNumber } from "lodash";
|
||||
|
||||
export const getSheetUrl = (sheetId: string): string =>
|
||||
`https://docs.google.com/spreadsheets/d/${sheetId}/edit#gid=0`;
|
||||
|
||||
export const isValidGsheetConfig = (config: Record<string, any>) =>
|
||||
config.sheet &&
|
||||
config.tableHeaderIndex &&
|
||||
isNumber(Number(config.tableHeaderIndex)) &&
|
||||
!isNaN(Number(config.tableHeaderIndex)) &&
|
||||
config.tableHeaderIndex > 0;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> {
|
|||
this.props.propertyName
|
||||
];
|
||||
|
||||
if (errorObj && errorObj.length && errorObj[0].errorMessage) {
|
||||
if (errorObj?.[0]?.errorMessage) {
|
||||
return errorObj[0].errorMessage.message;
|
||||
} else {
|
||||
return "";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user