-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: Add custom data views with aggregation query #2888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 19 commits
1e707d5
5f61237
eab8cf3
d485379
8825239
49cd9fc
6ed37ba
bb8eabd
e97fbd9
1de7b4c
5781c2b
c0ca3e2
0404b3e
1708ad9
d490260
7c6230a
510dea2
d18cb6a
80ef8c1
eef72bd
146ec5d
5b96bea
f9514dd
dc36630
d74b4f8
d657be1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import Dropdown from 'components/Dropdown/Dropdown.react'; | ||
| import Field from 'components/Field/Field.react'; | ||
| import Label from 'components/Label/Label.react'; | ||
| import Modal from 'components/Modal/Modal.react'; | ||
| import Option from 'components/Dropdown/Option.react'; | ||
| import React from 'react'; | ||
| import TextInput from 'components/TextInput/TextInput.react'; | ||
| import Checkbox from 'components/Checkbox/Checkbox.react'; | ||
|
|
||
| function isValidJSON(value) { | ||
| try { | ||
| JSON.parse(value); | ||
| return true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| export default class CreateViewDialog extends React.Component { | ||
| constructor() { | ||
| super(); | ||
| this.state = { | ||
| name: '', | ||
| className: '', | ||
| query: '[]', | ||
| showCounter: false, | ||
| }; | ||
| } | ||
|
|
||
| valid() { | ||
| return ( | ||
| this.state.name.length > 0 && | ||
| this.state.className.length > 0 && | ||
| isValidJSON(this.state.query) | ||
| ); | ||
| } | ||
|
|
||
| render() { | ||
| const { classes, onConfirm, onCancel } = this.props; | ||
| return ( | ||
| <Modal | ||
| type={Modal.Types.INFO} | ||
| icon="plus" | ||
| iconSize={40} | ||
| title="Create a new view?" | ||
| subtitle="Define a custom query to display data." | ||
| confirmText="Create" | ||
| cancelText="Cancel" | ||
| disabled={!this.valid()} | ||
| onCancel={onCancel} | ||
| onConfirm={() => | ||
| onConfirm({ | ||
| name: this.state.name, | ||
| className: this.state.className, | ||
| query: JSON.parse(this.state.query), | ||
| showCounter: this.state.showCounter, | ||
| }) | ||
| } | ||
| > | ||
| <Field | ||
| label={<Label text="Name" />} | ||
| input={ | ||
| <TextInput | ||
| value={this.state.name} | ||
| onChange={name => this.setState({ name })} | ||
| /> | ||
| } | ||
| /> | ||
| <Field | ||
| label={<Label text="Class" />} | ||
| input={ | ||
| <Dropdown | ||
| value={this.state.className} | ||
| onChange={className => this.setState({ className })} | ||
| > | ||
| {classes.map(c => ( | ||
| <Option key={c} value={c}> | ||
| {c} | ||
| </Option> | ||
| ))} | ||
| </Dropdown> | ||
| } | ||
| /> | ||
| <Field | ||
| label={ | ||
| <Label | ||
| text="Query" | ||
| description="An aggregation pipeline that returns an array of items." | ||
| /> | ||
| } | ||
| input={ | ||
| <TextInput | ||
| multiline={true} | ||
| value={this.state.query} | ||
| onChange={query => this.setState({ query })} | ||
| /> | ||
| } | ||
| /> | ||
| <Field | ||
| label={<Label text="Show object counter" />} | ||
| input={ | ||
| <Checkbox | ||
| checked={this.state.showCounter} | ||
| onChange={showCounter => this.setState({ showCounter })} | ||
| /> | ||
| } | ||
| /> | ||
| </Modal> | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import Field from 'components/Field/Field.react'; | ||
| import Label from 'components/Label/Label.react'; | ||
| import Modal from 'components/Modal/Modal.react'; | ||
| import React from 'react'; | ||
| import TextInput from 'components/TextInput/TextInput.react'; | ||
|
|
||
| export default class DeleteViewDialog extends React.Component { | ||
| constructor() { | ||
| super(); | ||
| this.state = { | ||
| confirmation: '', | ||
| }; | ||
| } | ||
|
|
||
| valid() { | ||
| return this.state.confirmation === this.props.name; | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <Modal | ||
| type={Modal.Types.DANGER} | ||
| icon="warn-outline" | ||
| title="Delete view?" | ||
| subtitle="This action cannot be undone!" | ||
| disabled={!this.valid()} | ||
| confirmText="Delete" | ||
| cancelText="Cancel" | ||
| onCancel={this.props.onCancel} | ||
| onConfirm={this.props.onConfirm} | ||
| > | ||
| <Field | ||
| label={<Label text="Confirm this action" description="Enter the view name to continue." />} | ||
| input={ | ||
| <TextInput | ||
| placeholder="View name" | ||
| value={this.state.confirmation} | ||
| onChange={confirmation => this.setState({ confirmation })} | ||
| /> | ||
| } | ||
| /> | ||
| </Modal> | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| import Dropdown from 'components/Dropdown/Dropdown.react'; | ||
| import Field from 'components/Field/Field.react'; | ||
| import Label from 'components/Label/Label.react'; | ||
| import Modal from 'components/Modal/Modal.react'; | ||
| import Option from 'components/Dropdown/Option.react'; | ||
| import React from 'react'; | ||
| import TextInput from 'components/TextInput/TextInput.react'; | ||
| import Checkbox from 'components/Checkbox/Checkbox.react'; | ||
|
|
||
| function isValidJSON(value) { | ||
| try { | ||
| JSON.parse(value); | ||
| return true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
|
Comment on lines
+10
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Extract isValidJSON to a shared utility. This function is duplicated from Create a new utility file export function isValidJSON(value) {
try {
JSON.parse(value);
return true;
} catch {
return false;
}
}Then import and use it in both files: -function isValidJSON(value) {
- try {
- JSON.parse(value);
- return true;
- } catch {
- return false;
- }
-}
+import { isValidJSON } from 'lib/jsonUtils';🤖 Prompt for AI Agents |
||
|
|
||
| export default class EditViewDialog extends React.Component { | ||
| constructor(props) { | ||
| super(); | ||
| const view = props.view || {}; | ||
| this.state = { | ||
| name: view.name || '', | ||
| className: view.className || '', | ||
| query: JSON.stringify(view.query || [], null, 2), | ||
| showCounter: !!view.showCounter, | ||
| }; | ||
| } | ||
|
|
||
| valid() { | ||
| return ( | ||
| this.state.name.length > 0 && | ||
| this.state.className.length > 0 && | ||
| isValidJSON(this.state.query) | ||
| ); | ||
| } | ||
|
|
||
| render() { | ||
| const { classes, onConfirm, onCancel } = this.props; | ||
| return ( | ||
| <Modal | ||
| type={Modal.Types.INFO} | ||
| icon="edit-solid" | ||
| iconSize={40} | ||
| title="Edit view?" | ||
| subtitle="Update the custom query." | ||
| confirmText="Save" | ||
| cancelText="Cancel" | ||
| disabled={!this.valid()} | ||
| onCancel={onCancel} | ||
| onConfirm={() => | ||
| onConfirm({ | ||
| name: this.state.name, | ||
| className: this.state.className, | ||
| query: JSON.parse(this.state.query), | ||
| showCounter: this.state.showCounter, | ||
| }) | ||
| } | ||
| > | ||
| <Field | ||
| label={<Label text="Name" />} | ||
| input={ | ||
| <TextInput | ||
| value={this.state.name} | ||
| onChange={name => this.setState({ name })} | ||
| /> | ||
| } | ||
| /> | ||
| <Field | ||
| label={<Label text="Class" />} | ||
| input={ | ||
| <Dropdown | ||
| value={this.state.className} | ||
| onChange={className => this.setState({ className })} | ||
| > | ||
| {classes.map(c => ( | ||
| <Option key={c} value={c}> | ||
| {c} | ||
| </Option> | ||
| ))} | ||
| </Dropdown> | ||
| } | ||
| /> | ||
| <Field | ||
| label={ | ||
| <Label | ||
| text="Query" | ||
| description="An aggregation pipeline that returns an array of items." | ||
| /> | ||
| } | ||
| input={ | ||
| <TextInput | ||
| multiline={true} | ||
| value={this.state.query} | ||
| onChange={query => this.setState({ query })} | ||
| /> | ||
| } | ||
| /> | ||
| <Field | ||
| label={<Label text="Show object counter" />} | ||
| input={ | ||
| <Checkbox | ||
| checked={this.state.showCounter} | ||
| onChange={showCounter => this.setState({ showCounter })} | ||
| /> | ||
| } | ||
| /> | ||
| </Modal> | ||
| ); | ||
| } | ||
| } | ||
|
Comment on lines
+19
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add prop validation for component props. The component expects several props but doesn't validate them. Add prop validation after the component class: EditViewDialog.propTypes = {
classes: PropTypes.arrayOf(PropTypes.string).isRequired,
view: PropTypes.shape({
name: PropTypes.string,
className: PropTypes.string,
query: PropTypes.array,
showCounter: PropTypes.bool,
}),
onCancel: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
};And import PropTypes: +import PropTypes from 'prop-types';
import Dropdown from 'components/Dropdown/Dropdown.react';🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add prop validation for required props.
The component expects
name,onCancel, andonConfirmprops but doesn't validate them. Consider adding PropTypes validation to ensure proper usage.Add prop validation after the component class:
And import PropTypes at the top:
+import PropTypes from 'prop-types'; import Field from 'components/Field/Field.react';🤖 Prompt for AI Agents