UNPKG

@finos/legend-application-studio

Version:
222 lines 19.2 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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; const availableAccessorOptions = dataState.availableAccessorOptions; const hasAccessorOptions = dataState.accessorOptions !== undefined && availableAccessorOptions.length > 0; const [selectedAccessor, setSelectedAccessor] = useState(availableAccessorOptions[0]?.value); 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 = () => { if (hasAccessorOptions && selectedAccessor) { const option = availableAccessorOptions.find((o) => o.value === selectedAccessor); if (option) { const relationElement = new RelationElement(); relationElement.paths = [option.value]; relationElement.columns = option.columns; relationElement.rows = []; dataState.addRelationElement(relationElement); } } else { 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(); }, className: "modal modal--dark search-modal", children: [_jsx("div", { className: "modal__title", children: "Add Relation Element" }), _jsx("div", { className: "relational-data-editor__identifier", children: hasAccessorOptions ? (_jsxs("div", { className: "relational-data-editor__identifier__values", children: [_jsx("div", { className: "panel__content__form__section__header__label", children: dataState.accessorTypeLabel }), _jsx(CustomSelectorInput, { className: "explorer__new-element-modal__driver__dropdown", options: availableAccessorOptions.map((o) => ({ value: o.value, label: o.label, })), onChange: (val) => { setSelectedAccessor(val?.value); }, value: selectedAccessor ? { value: selectedAccessor, label: selectedAccessor, } : null, placeholder: `Select ${dataState.accessorTypeLabel ?? 'option'}...`, isClearable: false, darkMode: !applicationStore.layoutService .TEMPORARY__isLightColorThemeEnabled })] })) : (_jsxs(_Fragment, { 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 }) }), _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 || (hasAccessorOptions && !selectedAccessor), children: "Add" }) })] }) })); }); export const RelationElementEditor = observer((props) => { const { relationElementState, isReadOnly, hideColumnDefinitions } = props; const editorStore = useEditorStore(); const embeddedData = relationElementState.relationElement; const canEditColumns = !isReadOnly && relationElementState.supportsColumnEditing; const fileInputRef = useRef(null); const addColumn = () => { if (canEditColumns) { const columnName = `column_${embeddedData.columns.length + 1}`; relationElementState.addColumn(columnName); } }; const removeColumn = (index) => { if (canEditColumns) { relationElementState.removeColumn(index); } }; const updateColumn = (index, value) => { if (canEditColumns) { 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 exportCSV = () => { const content = relationElementState.exportCSV(); const blob = new Blob([content], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'test_data.csv'; 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 (_jsx("div", { className: "relation-test-data-editor__content", children: _jsxs("div", { className: "relation-test-data-editor__data", children: [_jsxs("div", { className: "relation-test-data-editor__section-header", children: [_jsx("div", { className: "relation-test-data-editor__section-header__left", children: _jsxs("div", { className: "relation-test-data-editor__section-title", children: ["Test Data (", embeddedData.rows.length, " rows)"] }) }), _jsxs("div", { className: "relation-test-data-editor__toolbar", children: [_jsxs("div", { className: "relation-test-data-editor__toolbar__left", children: [_jsxs("div", { className: "relation-test-data-editor__action-control", children: [_jsx("span", { className: "relation-test-data-editor__action-control__label", children: "Add Column" }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: addColumn, disabled: !canEditColumns, title: "Add Column", children: _jsx(PlusIcon, {}) })] }), _jsxs("div", { className: "relation-test-data-editor__action-control", children: [_jsx("span", { className: "relation-test-data-editor__action-control__label", children: "Add Row" }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: addRow, disabled: isReadOnly || embeddedData.columns.length === 0, title: "Add Row", children: _jsx(PlusIcon, {}) })] })] }), _jsx("div", { className: "relation-test-data-editor__toolbar__right", children: _jsxs("div", { className: "relation-test-data-editor__import-controls", children: [_jsx("span", { className: "relation-test-data-editor__import-controls__label", children: "Upload CSV" }), _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 CSV", children: _jsx(UploadIcon, {}) })] }) })] })] }), !hideColumnDefinitions ? (_jsxs("div", { className: "relation-test-data-editor__columns", children: [_jsx("div", { className: "relation-test-data-editor__section-header", children: _jsx("div", { className: "relation-test-data-editor__section-title", children: "Column Definitions" }) }), _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: !canEditColumns }), _jsx("button", { className: "btn--icon btn--caution btn--dark btn--sm", onClick: () => removeColumn(index), disabled: !canEditColumns, title: "Remove Column", children: _jsx(TimesIcon, {}) })] }, `column-${guaranteeNonNullable(index)}`))) })] })) : null, embeddedData.rows.length === 0 && embeddedData.columns.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." }) })) : (_jsx("div", { className: "relation-test-data-editor__data-grid", children: _jsxs("table", { className: "relation-test-data-editor__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [embeddedData.columns.map((column, columnIndex) => (_jsx("th", { className: "relation-test-data-editor__th", children: _jsxs("div", { className: "relation-test-data-editor__th__inner", children: [_jsx("input", { className: "relation-test-data-editor__th__label relation-test-data-editor__th__label--editable", type: "text", value: column, onChange: (e) => updateColumn(columnIndex, e.target.value), disabled: !canEditColumns, title: column }), _jsx("button", { className: "relation-test-data-editor__th__delete", onClick: () => removeColumn(columnIndex), disabled: !canEditColumns, title: `Remove column ${column}`, children: _jsx(TimesIcon, {}) })] }) }, `col-${guaranteeNonNullable(columnIndex)}`))), _jsx("th", { className: "relation-test-data-editor__th relation-test-data-editor__th--actions" })] }) }), _jsx("tbody", { children: embeddedData.rows.map((_row, rowIndex) => (_jsxs("tr", { className: "relation-test-data-editor__tr", children: [embeddedData.columns.map((column, columnIndex) => (_jsx("td", { className: "relation-test-data-editor__td", children: _jsx("input", { type: "text", value: relationElementState.getDisplayValue(rowIndex, columnIndex), onChange: (e) => updateCellValue(rowIndex, columnIndex, e.target.value), disabled: isReadOnly, className: "relation-test-data-editor__data-input" }) }, column))), _jsx("td", { className: "relation-test-data-editor__td relation-test-data-editor__td--actions", children: _jsx("button", { className: "btn--icon btn--caution btn--dark btn--sm relation-test-data-editor__icon-button--compact", onClick: () => removeRow(rowIndex), disabled: isReadOnly, title: "Remove Row", children: _jsx(TimesIcon, {}) }) })] }, `row-${guaranteeNonNullable(rowIndex)}`))) })] }) })), _jsxs("div", { className: "relation-test-data-editor__footer-actions", children: [_jsx("div", { className: "relation-test-data-editor__footer-actions__left", children: _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: handleClearData, disabled: isReadOnly || embeddedData.rows.length === 0, title: "Clear All Data", children: _jsx(TrashIcon, {}) }) }), _jsx("div", { className: "relation-test-data-editor__footer-actions__right", children: _jsxs("div", { className: "relation-test-data-editor__action-control", children: [_jsx("span", { className: "relation-test-data-editor__action-control__label", children: "Export CSV" }), _jsx("button", { className: "btn--icon btn--dark btn--sm", onClick: exportCSV, disabled: isReadOnly, title: "Export as CSV", children: _jsx(FileImportIcon, {}) })] }) })] })] }) })); }); export const RelationElementsDataEditor = observer((props) => { const { dataState, isReadOnly, isSharedData, hideColumnDefinitions } = props; useApplicationNavigationContext(LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_RELATIONAL_EDITOR); const addRelationElement = () => { const doAdd = () => { if (dataState.accessorOptions !== undefined && dataState.availableAccessorOptions.length === 0) { dataState.editorStore.applicationStore.notificationService.notifyWarning(`All referenced ${dataState.accessorTypeLabel ?? 'item'}s' data is already present`); return; } dataState.setShowNewRelationElementModal(true); }; if (dataState.refreshAccessorOptions) { dataState.refreshAccessorOptions().then(doAdd).catch(doAdd); } else { doAdd(); } }; 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) => (_jsxs("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] })), !isReadOnly && !isSharedData && (_jsx("button", { className: "service-editor__tab__close-btn", onClick: (e) => { e.stopPropagation(); dataState.deleteRelationElement(relationElementState); }, title: "Delete", children: _jsx(TimesIcon, {}) }))] }, relationElementState.relationElement.paths.join('.')))) }), !isSharedData && (_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, ...(hideColumnDefinitions !== undefined ? { hideColumnDefinitions } : {}) }))] }), dataState.showNewRelationElementModal && (_jsx(NewRelationElementModal, { dataState: dataState, isReadOnly: isReadOnly }))] })); }); //# sourceMappingURL=RelationElementsDataEditor.js.map