mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
* feat: jsonb datatype support in tooljet database inprogress
* feat: jsonb support added for tooljet database column operations as well as create and edit table
* feat: added support to query JSONB values in JOIN operation
* added dto validation for join operation
* Added json data type in tjdb
* single line editor bug fixed
* Basic UI implementation for create table drawer for jsonb column type
* removed the console
* Added the sanitization for json default value in the dto
* Added jsonb svg for jsonb column type
* Updated UI for created column form
* Updated UI for edit column form
* Change the UI for create row
* Updated UI for edit row form
* Show dummy {...} value on table cell
* Setting up the codehinter in tjdb dashboard
* Created codehinter wrapper for tjdb table celljsonb data type
* Codehineter for tjdb cell
* Made changes in tjdb column and row drawers
* removed unwanted code
* Added maximum height for codehinter wrapper in each drawers
Avoided keydown event in create column drawer
* Set max height to codehinter for tjdb table cell
* Added jsonb path option for list row operation [rebase]
* Added filtering out column with json datatype in udpate rows operation
* Added filter option with jsonpath in delete row operation
* Made changes for join tables
* added json path in jon filter condition
* fix: parsing the jsonb default values in view table api
* Made on change and initial value value changes for all tjdb dashboard changes
* updated intial value and component name for codehinter in all drawers for tooljetdb
* Table cell edit codehinter initial value
* Updated codehinter onchange and initial value
* Added json path field for join sort section
* TJDB query manager updates for jsonb column type
* Tjdb dashboard bug fixes
* fix: joins jsonbpath expression can be sent without single quotes
* Added error validation for JSON in codehinter
* Removed console
* Added codehinter wrapper for tjdb cell
* Made default functional
* Made set to null functionality working
* Toggle functionality for default value
* Toggle null functionality
* Clean code
* create row form added handle disable input click
* cutom-footer css and add new data css
* Fixed tooltip
* Updated tooltip for join codehinters in query manager
* fix: jsonb column values validation in server side
* Made the initial value empty string if value is undefined
* active tab in edit row form bug
* Error state in tjdb hinter inside cell
* code mirror breaks, on the initial render
* Added placeholder and adjusted icon size in dropdown
* Fixed: Opening the cell with keyboard nav shows ‘enter to save’.
* bug fixed
* json icon alignment in the dropdown fixed
* In create and edit table, codehinter text is vertcially centered aligned
* Create row and update row info message for jsonb column type fixed
* SHowing popover when clicked on the jsonb cell
* Showing unique constraint in disable state for jsonb
* bugfix: bulkupload in tjdb for jsonb datatype should accept different json format
* Bug fixed for cellhinter and row form
* Avoided flickering in column form
* removed console
* zindex issue for codehinter in react portal for table cell
* Single line editor file changes removed
* row form bug fixed
* single quotes string escaped as it needs to be inserted as default value in JSONB column
* Bug fixed
* Edit and create row active tab bug fixed
* If value is empty, then dont show error in the codehinter
* fix: error handling for invalid jsonpath in join expression
* Handled null and Null edge cases for edit and create row
* Removed console
* Bug fix for save chages button in edit and create row drawer
* Error toast message for invalid syntax
* removed debounce time
* Copied all query manager and codehinter changes from editor to appbuilder directory
* Updated imports of codehinter in tjdb forms inside dashboard
* Discarded all changes made to editor directory
* Removed console
* Fixed flickering effect in the edit and create row drawer in TJDB dashboard
* Added focused state to codehinter in tjdb drawer, updated font size for the same
* Updated error message
* bug fixed : Pop up icon not visible inside codehinter
* Fixed : border issue in tjdb codehinter
* bugfix: array is not allowed to insert in JSONB column (#2734)
* Error message in tjdb drawers
* Error state for table schema
* Error message for cell hinter wrapper
* fixed : space between {} and table name in popup
* Spacing issue in create and update column UI in query manager fixed
* Show tooltip in fk column dropdown
* Bug fixed: edit drawer on first render cutom value is always empty
* Bug fixed
* Reverting react portal changes
* bug fixed for error copyright
* z index issue fixed for tableschema
* reverting back the changes for table form from last commit
* Bug fix: Flickering issue in table schema for codehinter input
* Bug fixed : toggle bwtween null and default value
* Handled spacing between the component name in react portal
* added new schema definition as we have introduced jsonb datatype default values can have Object and Array
* fix: removed the support for error code mapping for undefined function error because database has different error text for it based on the scenario like jsonb aggregates etc
---------
Co-authored-by: Ganesh Kumar <ganesh8056234@gmail.com>
Co-authored-by: Ganesh Kumar <40178541+ganesh8056@users.noreply.github.com>
Co-authored-by: Akshay <akshaysasidharan93@gmail.com>
265 lines
8.4 KiB
JavaScript
265 lines
8.4 KiB
JavaScript
import React, { useRef, useState } from 'react';
|
|
import CodeHinter from '@/AppBuilder/CodeEditor';
|
|
import { resolveReferences } from '@/AppBuilder/CodeEditor/utils';
|
|
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
|
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
|
import cx from 'classnames';
|
|
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|
import { Popover } from 'react-bootstrap';
|
|
import _ from 'lodash';
|
|
|
|
const transformvalue = (value = '') => {
|
|
if (typeof value !== 'string') {
|
|
return JSON.stringify(value);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
export const CellHinterWrapper = ({
|
|
isNotNull,
|
|
defaultValue,
|
|
selectedValue,
|
|
setSelectedValue,
|
|
saveFunction,
|
|
isEditCell,
|
|
columnDetails,
|
|
close,
|
|
closePopover,
|
|
show,
|
|
previousCellValue,
|
|
}) => {
|
|
const initialValueRef = useRef(selectedValue);
|
|
|
|
const defaultValueRef = useRef(defaultValue);
|
|
|
|
const [shouldUpdateToDefaultVal, setShouldUpdateDefaultVal] = useState(false);
|
|
|
|
const [shouldUpdateToNullVal, setShouldUpdateNullVal] = useState(false);
|
|
|
|
const [disabledSaveButton, setDisabledSaveButton] = useState(false);
|
|
|
|
const handleInputError = (bool = false) => {
|
|
setDisabledSaveButton(bool);
|
|
};
|
|
|
|
const handleKeyDown = (e) => {
|
|
e.stopPropagation();
|
|
|
|
if (e.key === 'Escape') {
|
|
const event = new Event('click', { bubbles: true, cancelable: true });
|
|
document.body.dispatchEvent(event);
|
|
}
|
|
};
|
|
const darkMode = localStorage.getItem('darkMode') === 'true';
|
|
|
|
const tranformedValue = (rawValue) => {
|
|
if (!rawValue) {
|
|
return JSON.stringify(rawValue);
|
|
}
|
|
const [_, __, resolvedValue] = resolveReferences(`{{${rawValue}}}`);
|
|
return resolvedValue;
|
|
};
|
|
|
|
const SaveChangesSection = () => {
|
|
const handleNullToggle = (value) => {
|
|
setShouldUpdateNullVal(false);
|
|
|
|
if (value) {
|
|
setSelectedValue(null);
|
|
shouldUpdateToDefaultVal && setShouldUpdateDefaultVal(false);
|
|
} else {
|
|
setSelectedValue(tranformedValue(previousCellValue));
|
|
}
|
|
setShouldUpdateNullVal(value);
|
|
};
|
|
|
|
const handleDefaultToggle = (value) => {
|
|
setShouldUpdateDefaultVal(false);
|
|
|
|
if (value) {
|
|
setSelectedValue(defaultValue);
|
|
shouldUpdateToNullVal && setShouldUpdateNullVal(false);
|
|
defaultValueRef.current = defaultValue;
|
|
} else {
|
|
const val = tranformedValue(previousCellValue);
|
|
defaultValueRef.current = val;
|
|
setSelectedValue(val);
|
|
}
|
|
setShouldUpdateDefaultVal(true);
|
|
};
|
|
|
|
return (
|
|
<div className="d-flex justify-content-between align-items-center">
|
|
<div className="d-flex flex-column align-items-start gap-1">
|
|
{
|
|
<div className="d-flex align-items-center gap-1">
|
|
<div className={`fw-500 tjdbCellMenuShortcutsInfo`} id="enterbutton">
|
|
<SolidIcon name="enterbutton" />
|
|
</div>
|
|
<div className={`fw-400 tjdbCellMenuShortcutsText`}>Press Enter to go on next line</div>
|
|
</div>
|
|
}
|
|
<div className="d-flex align-items-center gap-1">
|
|
<div className={`fw-500 tjdbCellMenuShortcutsInfo`} id="escbutton">
|
|
Esc
|
|
</div>
|
|
<div className={`fw-400 tjdbCellMenuShortcutsText`}>Discard Changes</div>
|
|
</div>
|
|
</div>
|
|
<div className="d-flex flex-column align-items-end gap-1">
|
|
{isNotNull === false && (
|
|
<div className="d-flex align-items-center gap-2">
|
|
<div className="d-flex flex-column">
|
|
<span style={{ width: 'auto' }} className="fw-400 fs-12">
|
|
Set to null
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<label className={`form-switch`}>
|
|
<input
|
|
className="form-check-input"
|
|
type="checkbox"
|
|
checked={selectedValue === null}
|
|
onChange={(e) => handleNullToggle(e.target.checked)}
|
|
/>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{defaultValue !== null && (
|
|
<div className="d-flex align-items-center gap-2">
|
|
<div className="d-flex flex-column">
|
|
<span style={{ width: 'auto' }} className="fw-400 fs-12">
|
|
Set to default
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<label className={`form-switch`}>
|
|
<input
|
|
className="form-check-input"
|
|
type="checkbox"
|
|
checked={_.isEqual(selectedValue, defaultValue)}
|
|
onChange={(e) => handleDefaultToggle(e.target.checked)}
|
|
/>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
const event = new Event('click', { bubbles: true, cancelable: true });
|
|
document.body.dispatchEvent(event);
|
|
setShouldUpdateDefaultVal(false);
|
|
setShouldUpdateNullVal(false);
|
|
};
|
|
|
|
const handleSave = (e) => {
|
|
if (e) {
|
|
e.stopPropagation();
|
|
}
|
|
saveFunction(selectedValue);
|
|
const event = new Event('click', { bubbles: true, cancelable: true });
|
|
document.body.dispatchEvent(event);
|
|
setShouldUpdateDefaultVal(false);
|
|
setShouldUpdateNullVal(false);
|
|
defaultValueRef.current = defaultValue;
|
|
};
|
|
|
|
const SaveChangesFooter = () => {
|
|
return (
|
|
<div className={cx('d-flex align-items-center', 'justify-content-end')}>
|
|
<div className="d-flex" style={{ gap: '8px' }}>
|
|
<ButtonSolid onClick={handleCancel} variant="tertiary" size="sm" className="fs-12 p-2">
|
|
Cancel
|
|
</ButtonSolid>
|
|
<ButtonSolid
|
|
onClick={(e) => handleSave(e)}
|
|
disabled={disabledSaveButton}
|
|
variant="primary"
|
|
size="sm"
|
|
className="fs-12 p-2"
|
|
>
|
|
Save
|
|
</ButtonSolid>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
const popover = (
|
|
<Popover className={`${darkMode && 'dark-theme'} tjdb-table-cell-edit-popover jsonb-popover`}>
|
|
{disabledSaveButton && (
|
|
<Popover.Header className="tjdb-cell-hinter-invalid-syntax-header">
|
|
<div className="d-flex align-items-center">
|
|
<span className="d-flex mx-2">
|
|
{' '}
|
|
<SolidIcon name="warning" width="16px" fill={'var(--tomato9)'} />
|
|
</span>
|
|
<span>Invalid JSON syntax</span>
|
|
</div>
|
|
</Popover.Header>
|
|
)}
|
|
<Popover.Body className={`${darkMode && 'dark-theme'}`} onClick={(e) => e.stopPropagation()}>
|
|
<div className={`d-flex flex-column gap-3`}>
|
|
<SaveChangesSection />
|
|
<SaveChangesFooter />
|
|
</div>
|
|
</Popover.Body>
|
|
</Popover>
|
|
);
|
|
|
|
const customFooter = () => {
|
|
return (
|
|
<div
|
|
className={`tjdb-dashboard-codehinter custom-footer ${darkMode && 'dark-theme'} `}
|
|
tabIndex="0"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{disabledSaveButton && (
|
|
<div className="tjdb-cell-hinter-invalid-syntax-header">
|
|
<div className="d-flex align-items-center">
|
|
<span className="d-flex mx-2">
|
|
{' '}
|
|
<SolidIcon name="warning" width="16px" fill={'var(--tomato9)'} />
|
|
</span>
|
|
<span>Invalid JSON syntax</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="main-body d-flex flex-column gap-3">
|
|
<SaveChangesSection />
|
|
<SaveChangesFooter />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<OverlayTrigger trigger="click" placement="bottom-start" rootclose overlay={popover}>
|
|
<div className="tjdb-dashboard-codehinter-wrapper-cell" onKeyDown={handleKeyDown}>
|
|
<CodeHinter
|
|
type="tjdbHinter"
|
|
inEditor={false}
|
|
initialValue={initialValueRef.current ? transformvalue(initialValueRef.current) : ''}
|
|
lang="javascript"
|
|
onChange={(value) => {
|
|
const [_, __, resolvedValue] = resolveReferences(`{{${value}}}`);
|
|
setSelectedValue(resolvedValue);
|
|
}}
|
|
enablePreview={false}
|
|
footerComponent={customFooter}
|
|
componentName={`{} ${columnDetails.Header}`}
|
|
errorCallback={handleInputError}
|
|
defaultValue={defaultValueRef.current}
|
|
reset={shouldUpdateToDefaultVal}
|
|
shouldUpdateToNullVal={shouldUpdateToNullVal}
|
|
columnName={columnDetails?.Header}
|
|
/>
|
|
</div>
|
|
</OverlayTrigger>
|
|
);
|
|
};
|