@finos/legend-application-studio
Version:
Legend Studio application core
201 lines • 15.5 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Copyright (c) 2025-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { observer } from 'mobx-react-lite';
import React, { useState, useRef } from 'react';
import { BlankPanelPlaceholder, PanelContent, PanelHeader, PlusIcon, TimesIcon, LockIcon, TrashIcon, UploadIcon, FileImportIcon, Dialog, clsx, CustomSelectorInput, } from '@finos/legend-art';
import { RelationElement } from '@finos/legend-graph';
import { guaranteeNonNullable } from '@finos/legend-shared';
import { ActionAlertActionType, ActionAlertType, useApplicationNavigationContext, } from '@finos/legend-application';
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
import { useEditorStore } from '../../EditorStoreProvider.js';
const NewRelationElementModal = observer((props) => {
const { isReadOnly, dataState } = props;
const applicationStore = dataState.editorStore.applicationStore;
let PathType;
(function (PathType) {
PathType["SCHEMA_TABLE"] = "Schema and Table";
})(PathType || (PathType = {}));
const pathTypeOptions = Object.values(PathType).map((type) => ({
label: type,
value: type,
}));
const [pathType, setPathType] = useState(pathTypeOptions[0]);
const onPathTypeChange = (val) => {
setPathType(val);
};
const [schemaName, setSchemaName] = useState();
const [tableName, setTableName] = useState();
const changeSchemaValue = (event) => {
setSchemaName(event.target.value);
};
const changeTableValue = (event) => {
setTableName(event.target.value);
};
const closeModal = () => dataState.setShowNewRelationElementModal(false);
const handleSubmit = () => {
const path = [];
if (pathType && schemaName && tableName) {
path.push(schemaName);
path.push(tableName);
}
const relationElement = new RelationElement();
relationElement.columns = [];
relationElement.rows = [];
relationElement.paths = path;
dataState.addRelationElement(relationElement);
closeModal();
};
return (_jsx(Dialog, { open: dataState.showNewRelationElementModal, onClose: closeModal, classes: { container: 'search-modal__container' }, slotProps: {
paper: {
classes: { root: 'search-modal__inner-container' },
},
}, children: _jsxs("form", { onSubmit: (event) => {
event.preventDefault();
handleSubmit();
closeModal();
}, className: "modal modal--dark search-modal", children: [_jsx("div", { className: "modal__title", children: "Add Relation Element" }), _jsxs("div", { className: "relational-data-editor__identifier", children: [_jsx("div", { className: "relational-data-editor__identifier__values", children: _jsx(CustomSelectorInput, { className: "explorer__new-element-modal__driver__dropdown", options: pathTypeOptions, onChange: onPathTypeChange, value: pathType, isClearable: false, darkMode: !applicationStore.layoutService
.TEMPORARY__isLightColorThemeEnabled }) }), _jsxs(_Fragment, { children: [_jsx("div", { className: "relational-data-editor__identifier__values", children: _jsx("input", { className: "panel__content__form__section__input", disabled: isReadOnly, placeholder: "schemaName", value: schemaName, onChange: changeSchemaValue }) }), _jsx("div", { className: "relational-data-editor__identifier__values", children: _jsx("input", { className: "relational-data-editor__identifier__values panel__content__form__section__input", disabled: isReadOnly, placeholder: "tableName", value: tableName, onChange: changeTableValue }) })] })] }), _jsx("div", { className: "search-modal__actions", children: _jsx("button", { className: "btn btn--dark", disabled: isReadOnly, children: "Add" }) })] }) }));
});
export const RelationElementEditor = observer((props) => {
const { relationElementState, isReadOnly } = props;
const editorStore = useEditorStore();
const embeddedData = relationElementState.relationElement;
const [exportFormat, setExportFormat] = useState('json');
const fileInputRef = useRef(null);
const addColumn = () => {
if (!isReadOnly) {
const columnName = `column_${embeddedData.columns.length + 1}`;
relationElementState.addColumn(columnName);
}
};
const removeColumn = (index) => {
if (!isReadOnly) {
relationElementState.removeColumn(index);
}
};
const updateColumn = (index, value) => {
if (!isReadOnly) {
const column = embeddedData.columns[index];
if (column) {
relationElementState.updateColumn(index, value);
}
}
};
const addRow = () => {
if (!isReadOnly) {
relationElementState.addRow();
}
};
const removeRow = (index) => {
if (!isReadOnly) {
relationElementState.removeRow(index);
}
};
const updateCellValue = (rowIndex, columnIndex, value) => {
if (!isReadOnly) {
relationElementState.updateRow(rowIndex, columnIndex, value);
}
};
const handleFileUpload = (event) => {
const file = event.target.files?.[0];
if (file && file.type === 'text/csv') {
const reader = new FileReader();
reader.onload = (e) => {
const csvContent = e.target?.result;
if (csvContent) {
relationElementState.importCSV(csvContent);
}
};
reader.readAsText(file);
}
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const exportData = () => {
let content = '';
let filename = '';
let mimeType = '';
switch (exportFormat) {
case 'json':
content = relationElementState.exportJSON();
filename = 'test_data.json';
mimeType = 'application/json';
break;
case 'csv':
content = relationElementState.exportCSV();
filename = 'test_data.csv';
mimeType = 'text/csv';
break;
case 'sql':
content = relationElementState.exportSQL();
filename = 'test_data.sql';
mimeType = 'text/sql';
break;
default:
content = relationElementState.exportJSON();
filename = 'test_data.json';
mimeType = 'application/json';
break;
}
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const handleClearData = () => {
editorStore.applicationStore.alertService.setActionAlertInfo({
message: 'Are you sure you want to clear all test data?',
type: ActionAlertType.CAUTION,
actions: [
{
label: 'Confirm',
type: ActionAlertActionType.PROCEED_WITH_CAUTION,
handler: () => {
relationElementState.clearAllData();
},
},
{
label: 'Cancel',
type: ActionAlertActionType.PROCEED,
default: true,
},
],
});
};
return (_jsxs("div", { className: "relation-test-data-editor__content", children: [_jsxs("div", { className: "relation-test-data-editor__columns", children: [_jsxs("div", { className: "relation-test-data-editor__section-header", children: [_jsx("div", { className: "relation-test-data-editor__section-title", children: "Column Definitions" }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: addColumn, disabled: isReadOnly, title: "Add Column", children: _jsx(PlusIcon, {}) })] }), _jsx("div", { className: "relation-test-data-editor__columns-grid", children: embeddedData.columns.map((column, index) => (_jsxs("div", { className: "relation-test-data-editor__column-row", children: [_jsx("input", { className: "relation-test-data-editor__column-input", type: "text", value: column, onChange: (e) => updateColumn(index, e.target.value), placeholder: "Column Name", disabled: isReadOnly }), _jsx("button", { className: "btn--icon btn--caution btn--dark btn--sm", onClick: () => removeColumn(index), disabled: isReadOnly, title: "Remove Column", children: _jsx(TimesIcon, {}) })] }, `column-${guaranteeNonNullable(index)}`))) })] }), _jsxs("div", { className: "relation-test-data-editor__data", children: [_jsx("div", { className: "relation-test-data-editor__section-header", children: _jsxs("div", { className: "relation-test-data-editor__section-title", children: ["Test Data (", embeddedData.rows.length, " rows)"] }) }), embeddedData.rows.length === 0 ? (_jsx("div", { className: "relation-test-data-editor__empty-data", children: _jsx("div", { className: "relation-test-data-editor__empty-text", children: "No test data rows. Click \"+\" below to start entering data." }) })) : (_jsxs("div", { className: "relation-test-data-editor__data-grid", children: [_jsxs("div", { className: "relation-test-data-editor__data-header", children: [embeddedData.columns.map((column) => (_jsx("div", { className: "relation-test-data-editor__data-header-cell", children: column }, column))), _jsx("div", { className: "relation-test-data-editor__data-header-cell relation-test-data-editor__data-actions", children: "Actions" })] }), embeddedData.rows.map((row, rowIndex) => (_jsxs("div", { className: "relation-test-data-editor__data-row", children: [embeddedData.columns.map((column, columnIndex) => (_jsx("div", { className: "relation-test-data-editor__data-cell", children: _jsx("input", { type: "text", value: row.values[columnIndex] ?? '', onChange: (e) => updateCellValue(rowIndex, columnIndex, e.target.value), disabled: isReadOnly, className: "relation-test-data-editor__data-input" }) }, column))), _jsx("div", { className: "relation-test-data-editor__data-cell relation-test-data-editor__data-actions", children: _jsx("button", { className: "btn--icon btn--caution btn--dark btn--sm", onClick: () => removeRow(rowIndex), disabled: isReadOnly, title: "Remove Row", children: _jsx(TimesIcon, {}) }) })] }, `row-${guaranteeNonNullable(rowIndex)}`)))] })), _jsxs("div", { className: "relation-test-data-editor__export-controls", children: [_jsxs("div", { className: "relation-test-data-editor__export-controls__btn-group", children: [_jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: addRow, disabled: isReadOnly || embeddedData.columns.length === 0, title: "Add Row", children: _jsx(PlusIcon, {}) }), _jsx("button", { className: "btn--icon btn--caution btn--dark btn--sm", onClick: handleClearData, disabled: isReadOnly || embeddedData.rows.length === 0, title: "Clear All Data", children: _jsx(TrashIcon, {}) })] }), _jsxs("div", { className: "relation-test-data-editor__export-format", children: [_jsx("label", { htmlFor: "exportFormat", children: "Export as:" }), _jsxs("select", { id: "exportFormat", value: exportFormat, onChange: (e) => setExportFormat(e.target.value), disabled: isReadOnly, className: "relation-test-data-editor__export-select", children: [_jsx("option", { value: "json", children: "JSON" }), _jsx("option", { value: "csv", children: "CSV" }), _jsx("option", { value: "sql", children: "SQL INSERT" })] }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: exportData, disabled: isReadOnly, title: `Export as ${exportFormat.toUpperCase()}`, children: _jsx(FileImportIcon, {}) })] }), _jsxs("div", { className: "relation-test-data-editor__import-controls", children: [_jsx("input", { ref: fileInputRef, type: "file", accept: ".csv", onChange: handleFileUpload, style: { display: 'none' }, disabled: isReadOnly }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: () => fileInputRef.current?.click(), disabled: isReadOnly, title: "Upload a file of CSV", children: _jsx(UploadIcon, {}) })] })] })] })] }));
});
export const RelationElementsDataEditor = observer((props) => {
const { dataState, isReadOnly } = props;
useApplicationNavigationContext(LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_RELATIONAL_EDITOR);
const addRelationElement = () => {
dataState.setShowNewRelationElementModal(true);
};
const changeRelationElement = (newRelationElement) => {
dataState.setActiveRelationElement(newRelationElement);
};
return (_jsxs("div", { className: "panel relation-test-data-editor", children: [_jsx(PanelHeader, { children: _jsxs("div", { className: "panel__header__title", children: [isReadOnly && (_jsx("div", { className: "panel__header__lock", children: _jsx(LockIcon, {}) })), _jsx("div", { className: "panel__header__title__label", children: dataState.label() })] }) }), _jsxs(PanelContent, { children: [_jsxs("div", { className: "panel__header service-editor__header--with-tabs", children: [_jsx("div", { className: "uml-element-editor__tabs", children: dataState.relationElementStates.map((relationElementState) => (_jsx("div", { onClick: () => changeRelationElement(relationElementState), className: clsx('service-editor__tab', {
'service-editor__tab--active': relationElementState === dataState.activeRelationElement,
}), children: relationElementState.relationElement.paths.length > 1 ? (_jsx("span", { children: relationElementState.relationElement.paths.join('.') })) : (_jsx("span", { children: relationElementState.relationElement.paths[0] })) }, relationElementState.relationElement.paths.join('.')))) }), _jsx("button", { onClick: addRelationElement, disabled: isReadOnly, title: "Add Relation Element", children: _jsx(PlusIcon, {}) })] }), dataState.relationElementStates.length === 0 ||
dataState.activeRelationElement === undefined ? (_jsx(BlankPanelPlaceholder, { text: "Add a relation element to define your test data structure", onClick: addRelationElement, clickActionType: "add", tooltipText: "Add Relation Element", disabled: isReadOnly })) : (_jsx(RelationElementEditor, { relationElementState: dataState.activeRelationElement, isReadOnly: isReadOnly }))] }), dataState.showNewRelationElementModal && (_jsx(NewRelationElementModal, { dataState: dataState, isReadOnly: isReadOnly }))] }));
});
//# sourceMappingURL=RelationElementsDataEditor.js.map