Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@tanstack/react-query": "5.90.21",
"@tanstack/react-query-devtools": "5.91.3",
"@xyflow/react": "12.10.1",
"ace-builds": "1.43.6",
"antd": "4.24.16",
"axios": "1.13.5",
"clsx": "2.1.1",
Expand All @@ -32,6 +33,7 @@
"i18next": "24.2.3",
"rc-picker": "4.11.3",
"react": "18.3.1",
"react-ace": "14.0.1",
"react-dom": "18.3.1",
"react-error-boundary": "4.1.2",
"react-i18next": "15.7.4",
Expand Down
1 change: 1 addition & 0 deletions src/app/styles/variables.less
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
@typography-title-margin-bottom: 0;

@input-bg: #f1f3f5;
@input-border: 1px solid #d9d9d9;
@select-background: #f1f3f5;
@picker-bg: #f1f3f5;
@tooltip-bg: #000000;
Expand Down
3 changes: 3 additions & 0 deletions src/entities/transformation/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const TRANSFORMATIONS_FORM_DEFAULT_VALUE: TransformationsForm = {
[TransformationType.FILTER_FILE]: [],
[TransformationType.FILTER_ROWS]: [],
[TransformationType.FILTER_COLUMNS]: [],
[TransformationType.FILTER_SQL]: [],
};

export const TRANSFORMATIONS_REQUEST_DEFAULT_VALUE: Transformations = [
Expand Down Expand Up @@ -71,6 +72,8 @@ const FILE_TRANSFORMATION_TYPES = Object.values(TransformationType);

const DB_TRANSFORMATION_TYPES = FILE_TRANSFORMATION_TYPES.filter((type) => type !== TransformationType.FILTER_FILE);

export const DEFAULT_TRANSFORMATION_TYPES = DB_TRANSFORMATION_TYPES;

export const CONNECTION_TYPE_SUPPORT_TRANSFORMATION_TYPES: Record<ConnectionType, TransformationType[]> = {
[ConnectionType.CLICKHOUSE]: DB_TRANSFORMATION_TYPES,
[ConnectionType.FTP]: FILE_TRANSFORMATION_TYPES,
Expand Down
33 changes: 31 additions & 2 deletions src/entities/transformation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum TransformationType {
FILTER_ROWS = 'dataframe_rows_filter',
FILTER_COLUMNS = 'dataframe_columns_filter',
FILTER_FILE = 'file_metadata_filter',
FILTER_SQL = 'sql',
}

export enum TransformationFilterRowsType {
Expand Down Expand Up @@ -107,15 +108,43 @@ export interface TransformationFilterFileForm {
filters: Array<TransformationFilterFileRegexpItemForm | TransformationFilterFileSizeItemForm>;
}

export type Transformations = Array<TransformationFilterRows | TransformationFilterColumns | TransformationFilterFile>;
export enum TransformationFilterSqlDialect {
SPARK = 'spark',
}

export type TransformationFilterSql = {
type: TransformationType.FILTER_SQL;
dialect: TransformationFilterSqlDialect;
query: string;
};

export type Transformations = Array<
TransformationFilterRows | TransformationFilterColumns | TransformationFilterFile | TransformationFilterSql
>;

export interface TransformationFilterSqlSparkItemForm {
dialect: TransformationFilterSqlDialect;
query: string;
}

export type TransformationFormFilterSql = {
type: TransformationType.FILTER_SQL;
filters: Array<TransformationFilterSqlSparkItemForm>;
};

export interface TransformationsForm {
[TransformationType.FILTER_FILE]?: TransformationFilterFileForm['filters'];
[TransformationType.FILTER_ROWS]?: TransformationFilterRows['filters'];
[TransformationType.FILTER_COLUMNS]?: TransformationFilterColumns['filters'];
[TransformationType.FILTER_SQL]?: TransformationFormFilterSql['filters'];
}

export type TransformationsFormNestedType<T extends keyof TransformationsForm> =
export type TransformationsFormWithNestedType =
| TransformationType.FILTER_FILE
| TransformationType.FILTER_ROWS
| TransformationType.FILTER_COLUMNS;

export type TransformationsFormNestedType<T extends TransformationsFormWithNestedType> =
Required<TransformationsForm>[T][number]['type'];

export interface ShowButtonsContextProps {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Button, Form, FormListFieldData } from 'antd';
import React, { memo, useLayoutEffect } from 'react';
import { memo, useLayoutEffect } from 'react';
import { useTranslation } from 'react-i18next';

import { useShowButtons } from '../../hooks';
import { Transformations, TransformationType } from '../../types';
import { TransformationsForm, TransformationType } from '../../types';

import { TransformationFormItem } from './components';
import { TransformationFormProps } from './types';
Expand All @@ -17,20 +17,24 @@ const TransformationFormComponent = <T extends TransformationType>({
const { isDisplayed } = useShowButtons();

const formInstance = Form.useFormInstance();
const filtersValues: Transformations[number]['filters'] | undefined = formInstance.getFieldValue([
const filtersValues: TransformationsForm[T] | undefined = formInstance.getFieldValue([
'transformations',
transformationType,
]);
const isFilterEmpty = !filtersValues?.length;

/** Add at least one element to array form value here,
* because it is inconvenient to check for the presence of a default value of this array,
* when forming a request to backend or initial form values */
useLayoutEffect(() => {
const needFillEmpty = !canHaveEmptyRecordsList && !filtersValues?.length;
const needFillEmpty = !canHaveEmptyRecordsList && isFilterEmpty;
if (needFillEmpty) {
formInstance.setFieldValue(['transformations', transformationType], [{}]);
}
}, [formInstance, filtersValues, transformationType, canHaveEmptyRecordsList]);
}, [formInstance, isFilterEmpty, transformationType, canHaveEmptyRecordsList]);

const isNeedShowAddNew = (fields: FormListFieldData[]) =>
isDisplayed && !(transformationType === TransformationType.FILTER_SQL && fields.length);

const canRemoveItem = ({ name }: FormListFieldData) => (name || canHaveEmptyRecordsList) && isDisplayed;

Expand All @@ -47,9 +51,11 @@ const TransformationFormComponent = <T extends TransformationType>({
key={field.key}
/>
))}
<Button className="nodrag" size="large" type="primary" onClick={() => add()} hidden={!isDisplayed}>
{t('add')}
</Button>
{isNeedShowAddNew(fields) && (
<Button className="nodrag" size="large" type="primary" onClick={() => add()}>
{t('add')}
</Button>
)}
</div>
)}
</Form.List>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React from 'react';
import { TransformationsFormNestedType, TransformationType } from '@entities/transformation';
import {
TransformationsFormNestedType,
TransformationsFormWithNestedType,
TransformationType,
} from '@entities/transformation';

import { FilterColumnsValue } from '../FilterColumnsValue';
import { FilterFileValue } from '../FilterFileValue';
import { FilterRowsValue } from '../FilterRowsValue';

import { FilterComponentProps } from './types';

export const FilterComponent = <T extends TransformationType>({
export const FilterComponent = <T extends TransformationsFormWithNestedType>({
transformationType,
nestedType,
name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TransformationsFormNestedType, TransformationType } from '@entities/transformation';
import { TransformationsFormNestedType, TransformationsFormWithNestedType } from '@entities/transformation';

export interface FilterComponentProps<T extends TransformationType> {
export interface FilterComponentProps<T extends TransformationsFormWithNestedType> {
name: number;
transformationType: T;
nestedType?: TransformationsFormNestedType<T>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const LINE_HEIGHT = 1.7;

export const WIDTH = '100%';

export const HEIGHT = '100px';

export const FONT_SIZE = 1.5;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AceSqlEditor } from '@shared/ui/AceSqlEditor';
import { getCountLines } from '@shared/utils';

import { FilterSqlProps } from './types';
import { FONT_SIZE, LINE_HEIGHT, WIDTH } from './constants';
import * as classes from './styles.module.less';

export const FilterSql = ({ autoHeightMaxLineCount, value, onChange }: FilterSqlProps) => {
/** Calc the number of lines in the SQL expression to set the height of the editor */
const linesCount = Math.max(1, getCountLines(value as string));
const heightLinesCount = autoHeightMaxLineCount ? Math.min(linesCount, autoHeightMaxLineCount) : linesCount;

return (
<AceSqlEditor
className={classes.root}
tabSize={2}
fontSize={`${FONT_SIZE}em`}
width={WIDTH}
height={`${heightLinesCount * LINE_HEIGHT + 0.2}em`}
lineHeight={`${LINE_HEIGHT}em`}
value={value}
onChange={onChange}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.root {
border: @input-border;
background-color: @input-bg;
border-radius: 8px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface FilterSqlProps {
autoHeightMaxLineCount: number;
value?: string;
onChange?: (value: string) => void;
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,49 @@
import { Button, Form, Input } from 'antd';
import React, { useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import { DeleteOutlined } from '@ant-design/icons';
import { Select } from '@shared/ui';
import { useTranslation } from 'react-i18next';
import clsx from 'clsx';

import { TransformationsFormNestedType, TransformationType } from '../../../../types';
import {
TransformationsFormNestedType,
TransformationsFormWithNestedType,
TransformationType,
} from '../../../../types';
import { FilterComponent } from '../FilterComponent';
import { FilterSql } from '../FilterSql';

import { TransformationFormItemProps } from './types';
import { useGetNestedTypesSelectOptions } from './hooks';
import * as classes from './styles.module.less';
import { SQL_AUTO_HEIGHT_MAX_LINE_COUNT } from './constants';

export const TransformationFormItem = <T extends TransformationType>({
name,
transformationType,
nestedTypeSelectLabel,
hasColumnField,
hasNestedTypeSelectField,
hasFilterComponent,
hasSqlField,
onRemove,
}: TransformationFormItemProps<T>) => {
const { t } = useTranslation('transformation');
const nestedTypesSelectOptions = useGetNestedTypesSelectOptions(transformationType);
const nestedTypesSelectOptions = useGetNestedTypesSelectOptions(
transformationType as TransformationsFormWithNestedType,
);
const formInstance = Form.useFormInstance();

const initialType: TransformationsFormNestedType<T> | undefined = useMemo(() => {
return formInstance.getFieldValue(['transformations', transformationType, name, 'type']);
}, [formInstance, name, transformationType]);
const initialType = useMemo(() => {
if (!hasNestedTypeSelectField) return undefined;

return formInstance.getFieldValue([
'transformations',
transformationType as TransformationsFormWithNestedType,
name,
'type',
]) as TransformationsFormNestedType<TransformationsFormWithNestedType>;
}, [formInstance, hasNestedTypeSelectField, name, transformationType]);

/** Use custom type state, because Form.useWatch doesn't support dynamic fieldname like in Form.List */
const [type, setType] = useState(() => initialType);
Expand All @@ -41,23 +59,36 @@ export const TransformationFormItem = <T extends TransformationType>({
<Input className="nodrag" size="large" />
</Form.Item>
)}
<Form.Item
className={classes.type}
label={nestedTypeSelectLabel}
name={[name, 'type']}
rules={[{ required: true }]}
>
<Select
/** className "nodrag" and "nowheel" for select in custom node React Flow https://reactflow.dev/api-reference/react-flow#no-drag-class-name */
className="nodrag"
popupClassName="nowheel"
size="large"
options={nestedTypesSelectOptions}
onChange={setType}
placeholder={t('selectOption')}
{hasFilterComponent && (
<Form.Item
className={classes.type}
label={nestedTypeSelectLabel}
name={[name, 'type']}
rules={[{ required: true }]}
>
<Select
/** className "nodrag" and "nowheel" for select in custom node React Flow https://reactflow.dev/api-reference/react-flow#no-drag-class-name */
className="nodrag"
popupClassName="nowheel"
size="large"
options={nestedTypesSelectOptions}
onChange={setType}
placeholder={t('selectOption')}
/>
</Form.Item>
)}
{hasNestedTypeSelectField && (
<FilterComponent
name={name}
nestedType={type}
transformationType={transformationType as TransformationsFormWithNestedType}
/>
</Form.Item>
<FilterComponent name={name} nestedType={type} transformationType={transformationType} />
)}
{hasSqlField && (
<Form.Item className={classes.sql} label={t('query')} name={[name, 'query']} rules={[{ required: true }]}>
<FilterSql autoHeightMaxLineCount={SQL_AUTO_HEIGHT_MAX_LINE_COUNT} />
</Form.Item>
)}
{onRemove && (
<Button
className={clsx('nodrag', [classes.deleteButton])}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// The maximum number of lines by which we stretch the editor before the scrollbar appears
export const SQL_AUTO_HEIGHT_MAX_LINE_COUNT = 20;
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import {
TransformationFilterRowsType,
TRANSFORMATION_FILTER_ROWS_TYPE_DISPLAY,
TransformationType,
TransformationsFormWithNestedType,
} from '@entities/transformation';
import { OptionItem, prepareOptionsForSelect } from '@shared/ui';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';

export const useGetNestedTypesSelectOptions = (transformationType: TransformationType) => {
export const useGetNestedTypesSelectOptions = (transformationType: TransformationsFormWithNestedType) => {
const { t } = useTranslation('transformation');

const options = useMemo(() => {
Expand All @@ -34,7 +35,7 @@ export const useGetNestedTypesSelectOptions = (transformationType: Transformatio
});

const filterOptions: Record<
TransformationType,
TransformationsFormWithNestedType,
OptionItem<TransformationFilterRowsType | TransformationFilterColumnsType | TransformationFilterFileType>[]
> = {
[TransformationType.FILTER_FILE]: filterFileTypeSelectOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
flex-grow: 1;
}

.sql {
flex-grow: 1;
}

.deleteButton {
margin-top: calc(1rem * @line-height-base);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ export interface TransformationFormItemProps<T extends TransformationType> exten
/** Transformation type */
transformationType: T;
/** Label for select of nested types options */
nestedTypeSelectLabel: string;
nestedTypeSelectLabel?: string;
/** Need to render Column field */
hasColumnField?: boolean;
/** Need to render NestedType field */
hasNestedTypeSelectField?: boolean;
/** Need to render Filter field */
hasFilterComponent?: boolean;
/** Need to render SQL field */
hasSqlField?: boolean;
/** Callback for deleting transformation form item */
onRemove?: FormListOperation['remove'];
}
Loading