Skip to content

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

Merged
merged 26 commits into from
Jul 13, 2025

Conversation

mtrezza
Copy link
Member

@mtrezza mtrezza commented Jul 10, 2025

Summary

  • add local storage model for saved views
  • implement a views page with create dialog and table
  • register Views in sidebar and routes

Testing

  • npm test

https://chatgpt.com/codex/tasks/task_e_686f92867e84832da406a36bad1fbe19

Summary by CodeRabbit

  • New Features
    • Introduced a "Views" section in the dashboard for managing customizable data views.
    • Added the ability to create, edit, and delete data views using modal dialogs with form validation.
    • Views can be configured with custom queries, class selection, and an optional object counter.
    • Views and preferences are saved per application and persist across sessions.
    • Added new routes and navigation links for easy access to the Views section within the dashboard.
    • Enhanced the Views interface with sortable columns, data type inference, interactive navigation links, and column resizing.
    • Included notifications for errors and informational messages within the Views section.
    • Added a dialog to display detailed view values in a formatted, read-only modal.
  • Documentation
    • Updated README to introduce the Views feature, explaining saved queries, aggregation pipelines, and object counters.
  • Bug Fixes
    • Improved navigation behavior by preventing last location overrides when filters are present in the URL.
  • Style
    • Updated styles for Views tables, headers, and action icons to improve layout and readability.

Copy link

The label codex cannot be used here.

Copy link

I will reformat the title to use the proper commit message syntax.

Copy link

coderabbitai bot commented Jul 10, 2025

📝 Walkthrough

Walkthrough

The changes introduce a new "Views" feature to the dashboard, allowing users to create, manage, and display custom data views. This includes new routing for "views," a sidebar entry, a React component for the views table, modals for creating, editing, and deleting views, a localStorage-backed utility for persisting view preferences per application, and associated styling.

Changes

File(s) Change Summary
src/dashboard/Dashboard.js Added import and route entries for the new Views feature.
src/dashboard/DashboardView.react.js Added "Views" to the sidebar core subsections.
src/dashboard/Data/Views/Views.react.js Added main Views React component managing views list, querying, counts, editing, deleting, and UI interactions.
src/dashboard/Data/Views/CreateViewDialog.react.js Added modal React component for creating new views with validation and form handling.
src/dashboard/Data/Views/EditViewDialog.react.js Added modal React component for editing existing views with validation and form handling.
src/dashboard/Data/Views/DeleteViewDialog.react.js Added modal React component for confirming deletion of a view with name confirmation.
src/dashboard/Data/Views/ViewValueDialog.react.js Added modal React functional component for displaying a formatted value in a read-only text area.
src/lib/ViewPreferences.js Added versioned localStorage utility functions for getting and saving view preferences by application ID.
src/dashboard/Data/Views/Views.scss Added stylesheet defining styles for the Views component and its table elements.
src/components/BrowserMenu/BrowserMenu.react.js Updated prop type validation for children prop to accept either a single node or an array of nodes.
src/dashboard/Data/Browser/Browser.react.js Fixed constant typo, added early return in addLocation if URL contains filters, and minor code cleanups.
src/dashboard/Data/Browser/BrowserTable.react.js Changed inline style key from 'overflow-x' string to camelCase overflowX for horizontal overflow.
src/dashboard/TableView.scss Adjusted table cell line-height and padding styles.
src/dashboard/Data/Config/Config.scss Reduced size of .configActionIcon from 25px to 20px.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Dashboard
    participant ViewsComponent
    participant ViewPreferences
    participant ModalDialog
    participant ParseServer

    User->>Dashboard: Navigates to /views
    Dashboard->>ViewsComponent: Render Views component
    ViewsComponent->>ViewPreferences: getViews(appId)
    ViewPreferences-->>ViewsComponent: Return saved views
    ViewsComponent->>ParseServer: Fetch schema and counts for views (if needed)
    ParseServer-->>ViewsComponent: Return schema and count data
    ViewsComponent->>User: Display views list and table

    User->>ViewsComponent: Click "Create View"
    ViewsComponent->>ModalDialog: Show CreateViewDialog
    User->>ModalDialog: Fill form and confirm
    ModalDialog->>ViewsComponent: onConfirm(newView)
    ViewsComponent->>ViewPreferences: saveViews(appId, updatedViews)
    ViewsComponent->>ViewsComponent: Reload views and data
    ViewsComponent->>User: Update UI with new view

    User->>ViewsComponent: Select view and click "Edit"
    ViewsComponent->>ModalDialog: Show EditViewDialog
    User->>ModalDialog: Modify form and confirm
    ModalDialog->>ViewsComponent: onConfirm(updatedView)
    ViewsComponent->>ViewPreferences: saveViews(appId, updatedViews)
    ViewsComponent->>ViewsComponent: Reload views and data
    ViewsComponent->>User: Update UI with edited view

    User->>ViewsComponent: Select view and click "Delete"
    ViewsComponent->>ModalDialog: Show DeleteViewDialog
    User->>ModalDialog: Confirm deletion by typing name
    ModalDialog->>ViewsComponent: onConfirm(viewName)
    ViewsComponent->>ViewPreferences: saveViews(appId, updatedViews)
    ViewsComponent->>ViewsComponent: Reload views and data
    ViewsComponent->>User: Update UI removing deleted view
Loading

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error code ERR_SSL_WRONG_VERSION_NUMBER
npm error errno ERR_SSL_WRONG_VERSION_NUMBER
npm error request to https://10.0.0.28:4873/npm/-/npm-10.9.2.tgz failed, reason: C0ACC639747F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:354:
npm error
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-13T01_09_35_491Z-debug-0.log


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@parse-github-assistant parse-github-assistant bot changed the title feat: add custom views page feat: Add custom views page Jul 10, 2025
Copy link

parse-github-assistant bot commented Jul 10, 2025

🚀 Thanks for opening this pull request!

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Jul 10, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/lib/ViewPreferences.js (1)

17-21: Consider logging errors instead of silently catching them

The empty catch block could hide important errors such as quota exceeded or security exceptions. Consider logging these errors for debugging purposes.

export function saveViews(appId, views) {
  try {
    localStorage.setItem(path(appId), JSON.stringify(views));
-  } catch {}
+  } catch (error) {
+    console.error('Failed to save views:', error);
+  }
}
src/dashboard/Data/Views/Views.react.js (1)

103-103: Consider using a stable key for table rows

Using JSON.stringify(row) as a key could cause performance issues with large datasets and may not be stable if object property order changes.

Consider using a unique identifier from the row data if available, or generate stable IDs when loading the data.

src/dashboard/Data/Views/CreateViewDialog.react.js (1)

10-17: Consider reusing the existing isValidJSON utility

There's already an isValidJSON function in src/dashboard/Push/PushNew.react.js that has similar functionality. Consider extracting it to a shared utility module to avoid duplication.

Would you like me to help extract this function to a shared utilities module?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3335696 and 1e707d5.

📒 Files selected for processing (5)
  • src/dashboard/Dashboard.js (2 hunks)
  • src/dashboard/DashboardView.react.js (2 hunks)
  • src/dashboard/Data/Views/CreateViewDialog.react.js (1 hunks)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
  • src/lib/ViewPreferences.js (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
src/lib/ViewPreferences.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2769
File: src/lib/ClassPreferences.js:26-26
Timestamp: 2025-05-02T11:55:52.809Z
Learning: Preference reads and writes in the ClassPreferences.js module are expected to be infrequent operations, so optimizing for performance (like caching) is unnecessary in this context.
src/dashboard/Data/Views/CreateViewDialog.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🧬 Code Graph Analysis (1)
src/dashboard/Data/Views/CreateViewDialog.react.js (1)
src/dashboard/Push/PushNew.react.js (1)
  • isValidJSON (55-68)
🪛 GitHub Check: Lint
src/dashboard/DashboardView.react.js

[failure] 70-70:
Expected indentation of 4 spaces but found 6

src/dashboard/Data/Views/Views.react.js

[failure] 87-87:
Expected { after 'else'


[failure] 86-86:
Expected { after 'if' condition


[failure] 85-85:
Expected { after 'if' condition


[failure] 84-84:
Expected { after 'if' condition


[failure] 83-83:
Expected { after 'if' condition


[failure] 81-81:
Expected { after 'if' condition


[failure] 80-80:
Expected { after 'if' condition


[failure] 77-77:
Expected { after 'if' condition


[failure] 61-61:
'app' is assigned a value but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Node 18
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (5)
src/lib/ViewPreferences.js (1)

1-25: LGTM! Clean implementation for view preferences storage

The implementation follows a similar pattern to ClassPreferences.js and appropriately handles localStorage operations with error handling. The versioned storage key is a good practice for future migrations.

Based on past learnings from ClassPreferences.js, these preference operations are expected to be infrequent, so the current implementation without caching is appropriate.

src/dashboard/DashboardView.react.js (1)

79-82: Views section correctly integrated

The new Views section is properly added to the core subsections and will appear in the sidebar navigation.

src/dashboard/Dashboard.js (1)

47-47: Routing properly configured for Views feature

The Views component is correctly imported and integrated into the routing configuration with appropriate paths for both the views list and individual view pages.

Also applies to: 274-275

src/dashboard/Data/Views/Views.react.js (1)

49-49: Verify the use of master key for aggregate queries

The queries use useMasterKey: true which bypasses all security rules. Please ensure this is intentional and that proper access controls are in place.

Also applies to: 72-72

src/dashboard/Data/Views/CreateViewDialog.react.js (1)

19-111: Well-structured dialog component

The component properly validates inputs, handles state management, and provides a clean interface for creating views. The use of controlled components and validation logic ensures data integrity.

Comment on lines 30 to 31
componentWillMount() {
this.loadViews(this.context);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Replace deprecated lifecycle methods

componentWillMount and componentWillReceiveProps are deprecated. Use componentDidMount and componentDidUpdate instead.

-  componentWillMount() {
+  componentDidMount() {
    this.loadViews(this.context);
  }

-  componentWillReceiveProps(nextProps, nextContext) {
-    if (this.context !== nextContext) {
-      this.loadViews(nextContext);
-    }
-    if (this.props.params.name !== nextProps.params.name || this.context !== nextContext) {
-      this.loadData(nextProps.params.name, nextContext);
-    }
+  componentDidUpdate(prevProps, prevState, prevContext) {
+    if (this.context !== prevContext) {
+      this.loadViews(this.context);
+    }
+    if (this.props.params.name !== prevProps.params.name || this.context !== prevContext) {
+      this.loadData(this.props.params.name, this.context);
+    }
  }

Also applies to: 34-41

🤖 Prompt for AI Agents
In src/dashboard/Data/Views/Views.react.js around lines 30 to 41, replace the
deprecated lifecycle methods componentWillMount and componentWillReceiveProps
with componentDidMount and componentDidUpdate respectively. Move the logic
inside componentWillMount to componentDidMount, and move the logic inside
componentWillReceiveProps to componentDidUpdate, adjusting the method signatures
and conditions as needed to maintain the same behavior.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/dashboard/Data/Views/Views.react.js (3)

34-49: Replace deprecated lifecycle methods with modern alternatives.

The deprecated lifecycle methods componentWillMount and componentWillReceiveProps should be replaced with componentDidMount and componentDidUpdate.


56-63: Add error handling for Parse aggregate queries.

The Parse query lacks error handling, which could lead to unhandled promise rejections if the query fails.


79-112: Add error handling for data loading Parse queries.

The Parse query in loadData method lacks error handling, which could cause unhandled promise rejections.

🧹 Nitpick comments (5)
src/dashboard/Data/Views/Views.react.js (5)

123-123: Improve cell value rendering for better user experience.

Converting all values to strings may not provide the best user experience, especially for dates, booleans, and objects.

-          <td key={name}>{String(row[name])}</td>
+          <td key={name}>{this.formatCellValue(row[name], this.state.columns[name])}</td>

Consider adding a formatCellValue method to handle different data types appropriately:

formatCellValue(value, column) {
  if (value === null || value === undefined) return '';
  
  switch (column?.type) {
    case 'Date':
      return new Date(value).toLocaleString();
    case 'Boolean':
      return value ? 'true' : 'false';
    case 'Object':
      return JSON.stringify(value);
    default:
      return String(value);
  }
}

131-131: Use dynamic width calculation for table headers.

The hardcoded width of 20 for table headers may not be appropriate for all column types and content lengths.

-      <TableHeader key={name} width={20} >
+      <TableHeader key={name} width={column.width || 150} >

176-185: Optimize state updates and reduce unnecessary re-renders.

The current implementation calls loadViews again after saving, which could cause unnecessary re-renders and data fetching.

          onConfirm={view => {
-            this.setState(
-              state => ({ showCreate: false, views: [...state.views, view] }),
-              () => {
-                ViewPreferences.saveViews(
-                  this.context.applicationId,
-                  this.state.views
-                );
-                this.loadViews(this.context);
-              }
-            );
+            const newViews = [...this.state.views, view];
+            ViewPreferences.saveViews(this.context.applicationId, newViews);
+            this.setState({ showCreate: false, views: newViews });
          }}

74-74: Handle undefined state consistently.

The fallback to empty array suggests this.state.views might be undefined, but this should be handled at the state initialization level.

Since views is initialized as an empty array in the constructor, the || fallback is unnecessary:

-    const view = (this.state.views || []).find(v => v.name === name);
+    const view = this.state.views.find(v => v.name === name);

165-165: Use consistent optional chaining throughout the component.

Optional chaining is used here but not consistently throughout the component where similar checks are needed.

Consider applying optional chaining consistently, such as in the loadViews method where views array is accessed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f61237 and eab8cf3.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64


renderRow(row) {
return (
<tr key={JSON.stringify(row)}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use a more efficient key for table rows.

Using JSON.stringify(row) as a React key is inefficient and potentially problematic for large datasets. Consider using a unique identifier from the data.

-      <tr key={JSON.stringify(row)}>
+      <tr key={row.objectId || row.id || JSON.stringify(row)}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<tr key={JSON.stringify(row)}>
<tr key={row.objectId || row.id || JSON.stringify(row)}>
🤖 Prompt for AI Agents
In src/dashboard/Data/Views/Views.react.js at line 121, replace the use of
JSON.stringify(row) as the key for the table row with a unique and stable
identifier from the row data, such as an ID property. This change improves
rendering performance and avoids potential issues with key collisions or
inefficiency when handling large datasets.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/dashboard/Data/Views/Views.react.js (2)

39-54: Replace deprecated lifecycle methods

componentWillMount and componentWillReceiveProps are deprecated. Use componentDidMount and componentDidUpdate instead.


139-139: Use a more efficient key for table rows

Using JSON.stringify(row) as a React key is inefficient and potentially problematic for large datasets. Consider using a unique identifier from the data.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eab8cf3 and d485379.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🪛 GitHub Check: Lint
src/dashboard/Data/Views/Views.react.js

[failure] 122-122:
Expected indentation of 8 spaces but found 6


[failure] 121-121:
Expected indentation of 8 spaces but found 6


[failure] 120-120:
Expected indentation of 8 spaces but found 6


[failure] 119-119:
Expected indentation of 10 spaces but found 8


[failure] 118-118:
Expected indentation of 12 spaces but found 10

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/dashboard/Data/Views/Views.react.js (3)

39-54: Replace deprecated lifecycle methods with modern alternatives.

The component still uses deprecated lifecycle methods that were flagged in previous reviews. These methods will be removed in future React versions.

-  componentWillMount() {
+  componentDidMount() {
    this.props.schema
      .dispatch(SchemaActionTypes.FETCH)
      .then(() => this.loadViews(this.context));
  }

-  componentWillReceiveProps(nextProps, nextContext) {
-    if (this.context !== nextContext) {
-      this.props.schema
-        .dispatch(SchemaActionTypes.FETCH)
-        .then(() => this.loadViews(nextContext));
-    }
-    if (this.props.params.name !== nextProps.params.name || this.context !== nextContext) {
-      this.loadData(nextProps.params.name);
-    }
+  componentDidUpdate(prevProps, prevState, prevContext) {
+    if (this.context !== prevContext) {
+      this.props.schema
+        .dispatch(SchemaActionTypes.FETCH)
+        .then(() => this.loadViews(this.context));
+    }
+    if (this.props.params.name !== prevProps.params.name || this.context !== prevContext) {
+      this.loadData(this.props.params.name);
+    }
  }

90-132: Fix critical logic error - code executing outside Promise chain.

The code that processes columns and updates state is incorrectly placed outside the .then() block, causing it to execute before the query completes. This will result in undefined behavior and empty data.

    new Parse.Query(view.className)
      .aggregate(view.query, { useMasterKey: true })
      .then(results => {
        const columns = {};
        results.forEach(item => {
          Object.keys(item).forEach(key => {
            if (columns[key]) {
              return;
            }
            const val = item[key];
            let type = 'String';
            if (typeof val === 'number') {
              type = 'Number';
            } else if (typeof val === 'boolean') {
              type = 'Boolean';
            } else if (val && typeof val === 'object') {
              if (val.__type === 'Date') {
                type = 'Date';
              } else if (val.__type === 'Pointer') {
                type = 'Pointer';
              } else if (val.__type === 'File') {
                type = 'File';
              } else if (val.__type === 'GeoPoint') {
                type = 'GeoPoint';
              } else {
                type = 'Object';
              }
            }
-          columns[key] = { type };
-        });
-      });
-      const colNames = Object.keys(columns);
-      const width = colNames.length > 0 ? 100 / colNames.length : 0;
-      const order = colNames.map(name => ({ name, width }));
-      this.setState({ data: results, order, columns });
+            columns[key] = { type };
+          });
+        });
+        const colNames = Object.keys(columns);
+        const width = colNames.length > 0 ? 100 / colNames.length : 0;
+        const order = colNames.map(name => ({ name, width }));
+        this.setState({ data: results, order, columns });
      })
      .catch(error => {
        this.showNote(
          `Request failed: ${error.message || 'Unknown error occurred'}`,
          true
        );
        this.setState({ data: [], order: [], columns: {} });
      });

141-141: Optimize React key for better performance.

Using JSON.stringify(row) as a React key is inefficient for rendering performance, especially with large datasets.

-      <tr key={JSON.stringify(row)}>
+      <tr key={row.objectId || row.id || JSON.stringify(row)}>
🧹 Nitpick comments (1)
src/dashboard/Data/Views/Views.react.js (1)

122-123: Consider width calculation edge cases.

The width calculation 100 / colNames.length could result in very narrow columns when there are many columns, potentially making the table unusable.

Consider implementing a minimum column width or using a more sophisticated width calculation:

-      const width = colNames.length > 0 ? 100 / colNames.length : 0;
+      const minWidth = 10; // minimum 10% width
+      const calculatedWidth = colNames.length > 0 ? Math.max(minWidth, 100 / colNames.length) : 0;
+      const width = calculatedWidth;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d485379 and 8825239.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🧬 Code Graph Analysis (1)
src/dashboard/Data/Views/Views.react.js (5)
src/lib/withRouter.js (1)
  • withRouter (4-26)
src/dashboard/TableView.react.js (1)
  • TableView (13-66)
src/components/CategoryList/CategoryList.react.js (1)
  • CategoryList (15-177)
src/dashboard/Data/Views/CreateViewDialog.react.js (1)
  • CreateViewDialog (19-111)
src/dashboard/Data/Browser/Notification.react.js (1)
  • Notification (13-80)
🪛 GitHub Check: Lint
src/dashboard/Data/Views/Views.react.js

[failure] 124-124:
Expected indentation of 8 spaces but found 6


[failure] 123-123:
Expected indentation of 8 spaces but found 6


[failure] 122-122:
Expected indentation of 8 spaces but found 6


[failure] 121-121:
Expected indentation of 8 spaces but found 6


[failure] 120-120:
Expected indentation of 8 spaces but found 6


[failure] 119-119:
Expected indentation of 10 spaces but found 8


[failure] 118-118:
Expected indentation of 12 spaces but found 10

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Node 18
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (1)
src/dashboard/Data/Views/Views.react.js (1)

225-238: LGTM: Well-implemented notification system.

The notification system properly handles both error and success messages with appropriate timeouts and cleanup.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/dashboard/Data/Views/Views.react.js (3)

43-47: Replace deprecated lifecycle method

componentWillMount is deprecated and should be replaced with componentDidMount.

-  componentWillMount() {
+  componentDidMount() {
    this.props.schema
      .dispatch(SchemaActionTypes.FETCH)
      .then(() => this.loadViews(this.context));
  }

49-58: Replace deprecated lifecycle method

componentWillReceiveProps is deprecated and should be replaced with componentDidUpdate.

-  componentWillReceiveProps(nextProps, nextContext) {
-    if (this.context !== nextContext) {
-      this.props.schema
-        .dispatch(SchemaActionTypes.FETCH)
-        .then(() => this.loadViews(nextContext));
-    }
-    if (this.props.params.name !== nextProps.params.name || this.context !== nextContext) {
-      this.loadData(nextProps.params.name);
-    }
-  }
+  componentDidUpdate(prevProps, prevState, prevContext) {
+    if (this.context !== prevContext) {
+      this.props.schema
+        .dispatch(SchemaActionTypes.FETCH)
+        .then(() => this.loadViews(this.context));
+    }
+    if (this.props.params.name !== prevProps.params.name || this.context !== prevContext) {
+      this.loadData(this.props.params.name);
+    }
+  }

145-145: Use more efficient React key

Using JSON.stringify(row) as a React key is inefficient for large datasets and can cause performance issues.

-      <tr key={JSON.stringify(row)}>
+      <tr key={row.objectId || row.id || JSON.stringify(row)}>
🧹 Nitpick comments (1)
src/dashboard/Data/Views/Views.react.js (1)

17-17: Fix typo in constant name

There's a typo in the constant name that could cause confusion.

-const BROWSER_LAST_LOCATION = 'brower_last_location';
+const BROWSER_LAST_LOCATION = 'browser_last_location';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8825239 and 6ed37ba.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🪛 GitHub Check: Lint
src/dashboard/Data/Views/Views.react.js

[failure] 128-128:
Expected indentation of 8 spaces but found 6


[failure] 127-127:
Expected indentation of 8 spaces but found 6


[failure] 126-126:
Expected indentation of 8 spaces but found 6


[failure] 125-125:
Expected indentation of 8 spaces but found 6


[failure] 124-124:
Expected indentation of 8 spaces but found 6


[failure] 123-123:
Expected indentation of 10 spaces but found 8


[failure] 122-122:
Expected indentation of 12 spaces but found 10

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/dashboard/Data/Views/Views.react.js (2)

43-58: Replace deprecated lifecycle methods with modern alternatives

The deprecated lifecycle methods componentWillMount and componentWillReceiveProps should be replaced with their modern equivalents to ensure compatibility with current React versions.

Apply this diff to update the lifecycle methods:

- componentWillMount() {
+ componentDidMount() {
    this.props.schema
      .dispatch(SchemaActionTypes.FETCH)
      .then(() => this.loadViews(this.context));
  }

- componentWillReceiveProps(nextProps, nextContext) {
+ componentDidUpdate(prevProps, prevState, prevContext) {
-   if (this.context !== nextContext) {
+   if (this.context !== prevContext) {
      this.props.schema
        .dispatch(SchemaActionTypes.FETCH)
-       .then(() => this.loadViews(nextContext));
+       .then(() => this.loadViews(this.context));
    }
-   if (this.props.params.name !== nextProps.params.name || this.context !== nextContext) {
+   if (this.props.params.name !== prevProps.params.name || this.context !== prevContext) {
-     this.loadData(nextProps.params.name);
+     this.loadData(this.props.params.name);
    }
  }

154-154: Use a more efficient key for table rows

Using JSON.stringify(row) as a React key is inefficient and potentially problematic for large datasets. Consider using a unique identifier from the data.

- <tr key={JSON.stringify(row)} className={styles.tableRow}>
+ <tr key={row.objectId || row.id || `row-${JSON.stringify(row)}`} className={styles.tableRow}>
🧹 Nitpick comments (3)
src/dashboard/Data/Views/Views.react.js (3)

98-99: Consider extracting width computation logic

The computeWidth function is defined inline within the loadData method. Consider extracting it as a class method for better readability and reusability.

+ computeWidth(str) {
+   return Math.min(100, Math.max((String(str).length + 2) * 8, 40));
+ }

  loadData(name) {
    // ... existing code ...
    new Parse.Query(view.className)
      .aggregate(view.query, { useMasterKey: true })
      .then(results => {
        const columns = {};
-       const computeWidth = str =>
-         Math.min(100, Math.max((String(str).length + 2) * 8, 40));
        results.forEach(item => {
          Object.keys(item).forEach(key => {
            // ... existing code ...
-           const width = computeWidth(content || key);
+           const width = this.computeWidth(content || key);
            // ... rest of the code ...

128-133: Optimize column width calculation logic

The current logic for updating column widths could be streamlined. Instead of checking if columns[key] exists and then updating width, consider using a more concise approach.

- if (!columns[key]) {
-   columns[key] = { type, width };
- } else if (width > columns[key].width) {
-   columns[key].width = width;
- }
+ columns[key] = columns[key] 
+   ? { ...columns[key], type, width: Math.max(columns[key].width, width) }
+   : { type, width };

243-253: Consider using useCallback for onConfirm handler

The onConfirm handler in the CreateViewDialog is recreated on every render. For better performance, consider extracting it as a class method.

Add this method to the class:

+ handleCreateView = (view) => {
+   this.setState(
+     state => ({ showCreate: false, views: [...state.views, view] }),
+     () => {
+       ViewPreferences.saveViews(
+         this.context.applicationId,
+         this.state.views
+       );
+       this.loadViews(this.context);
+     }
+   );
+ }

Then use it in the render method:

- onConfirm={view => {
-   this.setState(
-     state => ({ showCreate: false, views: [...state.views, view] }),
-     () => {
-       ViewPreferences.saveViews(
-         this.context.applicationId,
-         this.state.views
-       );
-       this.loadViews(this.context);
-     }
-   );
- }}
+ onConfirm={this.handleCreateView}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ed37ba and bb8eabd.

📒 Files selected for processing (2)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
  • src/dashboard/Data/Views/Views.scss (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/dashboard/Data/Views/Views.scss
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (3)
src/dashboard/Data/Views/Views.react.js (3)

22-41: LGTM - Well-structured component initialization

The component setup follows React best practices with proper state initialization and sidebar action configuration.


60-82: LGTM - Robust view loading implementation

The loadViews method properly handles loading views from preferences and includes error handling for counter queries.


272-281: LGTM - Clean navigation handling

The pointer click handler properly constructs filters and navigates to the browser view with appropriate URL encoding.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/dashboard/Data/Views/Views.react.js (3)

46-61: Replace deprecated lifecycle methods
componentWillMount and componentWillReceiveProps are long-deprecated and may be removed in upcoming React versions. Migrate the logic to componentDidMount and componentDidUpdate respectively to avoid unexpected breakage.


191-199: Use a stable ID instead of JSON.stringify(row) for React keys
Serialising the whole row on every render hurts performance and risks key collisions for large or cyclic objects. Prefer a unique field such as row.objectId || row.id.


335-348: Clear noteTimeout on unmount to prevent memory leaks
showNote creates a setTimeout, but the timer isn’t cleared when the component unmounts. Add a componentWillUnmount (or useEffect cleanup if converted to hooks) to call clearTimeout(this.noteTimeout).

🧹 Nitpick comments (1)
src/dashboard/Data/Views/Views.react.js (1)

194-194: Remove unused variable width to satisfy lint
width is destructured but never referenced, triggering the lint error you can see in CI.

-        {this.state.order.map(({ name, width }) => {
+        {this.state.order.map(({ name }) => {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5781c2b and c0ca3e2.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🪛 GitHub Check: Lint
src/dashboard/Data/Views/Views.react.js

[failure] 194-194:
'width' is defined but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
src/dashboard/Data/Views/Views.react.js (4)

49-53: Replace deprecated lifecycle method componentWillMount

componentWillMount is deprecated. Use componentDidMount instead.


70-79: Replace deprecated lifecycle method componentWillReceiveProps

componentWillReceiveProps is deprecated. Use componentDidUpdate instead.


65-68: Add cleanup for timeout in componentWillUnmount

The component creates a timeout in showNote but doesn't clean it up when the component unmounts, which could lead to memory leaks.


212-212: Use a more efficient key for table rows

Using JSON.stringify(row) as a React key is inefficient and potentially problematic for large datasets. Consider using a unique identifier from the data.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0ca3e2 and 0404b3e.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🪛 GitHub Check: Lint
src/dashboard/Data/Views/Views.react.js

[failure] 213-213:
'width' is defined but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/dashboard/Data/Views/Views.react.js (2)

55-59: Replace deprecated lifecycle methods

componentWillMount and componentWillReceiveProps are deprecated. Use componentDidMount and componentDidUpdate instead.

Also applies to: 65-74


243-243: Use a more efficient key for table rows.

Using JSON.stringify(row) as a React key is inefficient and potentially problematic for large datasets. Consider using a unique identifier from the data.

🧹 Nitpick comments (1)
src/dashboard/Data/Views/Views.react.js (1)

26-513: Consider splitting this component for better maintainability.

This component is handling many responsibilities and has grown to over 500 lines. Consider extracting:

  1. Table rendering logic (renderRow, renderHeaders, renderColGroup) into a separate ViewsTable component
  2. Column type detection logic (duplicated in lines 133-150 and 246-263) into a utility function
  3. Dialog management into a custom hook or container component

Example of extracting column type detection:

// In lib/columnUtils.js
export function getColumnType(value) {
  if (typeof value === 'number') return 'Number';
  if (typeof value === 'boolean') return 'Boolean';
  if (value && typeof value === 'object') {
    if (value.__type === 'Date') return 'Date';
    if (value.__type === 'Pointer') return 'Pointer';
    if (value.__type === 'File') return 'File';
    if (value.__type === 'GeoPoint') return 'GeoPoint';
    return 'Object';
  }
  return 'String';
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1708ad9 and 80ef8c1.

📒 Files selected for processing (5)
  • src/dashboard/DashboardView.react.js (1 hunks)
  • src/dashboard/Data/Views/DeleteViewDialog.react.js (1 hunks)
  • src/dashboard/Data/Views/EditViewDialog.react.js (1 hunks)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
  • src/dashboard/Data/Views/Views.scss (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/dashboard/DashboardView.react.js
  • src/dashboard/Data/Views/Views.scss
🧰 Additional context used
🧠 Learnings (3)
src/dashboard/Data/Views/DeleteViewDialog.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
src/dashboard/Data/Views/EditViewDialog.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🧬 Code Graph Analysis (1)
src/dashboard/Data/Views/EditViewDialog.react.js (1)
src/dashboard/Push/PushNew.react.js (1)
  • isValidJSON (55-68)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (2)
src/dashboard/Data/Views/Views.react.js (2)

114-129: Well-implemented column width calculation.

The dynamic column width calculation using canvas text measurement is a sophisticated approach that provides accurate results. The fallback for non-browser environments and canvas caching for performance are excellent touches.


265-275: Good implementation of pointer navigation.

The clickable pointer values that navigate to filtered browser views provide excellent user experience for exploring related data.

Also applies to: 488-497

Comment on lines +7 to +45
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>
);
}
}
Copy link

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, and onConfirm props but doesn't validate them. Consider adding PropTypes validation to ensure proper usage.

Add prop validation after the component class:

DeleteViewDialog.propTypes = {
  name: PropTypes.string.isRequired,
  onCancel: PropTypes.func.isRequired,
  onConfirm: PropTypes.func.isRequired,
};

And import PropTypes at the top:

+import PropTypes from 'prop-types';
import Field from 'components/Field/Field.react';
🤖 Prompt for AI Agents
In src/dashboard/Data/Views/DeleteViewDialog.react.js from lines 7 to 45, the
component uses props name, onCancel, and onConfirm but lacks prop validation. To
fix this, import PropTypes at the top of the file and add a static propTypes
object or assign DeleteViewDialog.propTypes after the class definition,
specifying name as a required string and onCancel and onConfirm as required
functions to ensure proper prop usage and catch errors early.

Comment on lines +10 to +17
function isValidJSON(value) {
try {
JSON.parse(value);
return true;
} catch {
return false;
}
}
Copy link

Choose a reason for hiding this comment

The 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 src/dashboard/Push/PushNew.react.js. Consider extracting it to a shared utility module to follow DRY principles.

Create a new utility file src/lib/jsonUtils.js:

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
In src/dashboard/Data/Views/EditViewDialog.react.js lines 10 to 17, the
isValidJSON function is duplicated from src/dashboard/Push/PushNew.react.js.
Extract this function into a new shared utility file at src/lib/jsonUtils.js by
moving the function there and exporting it. Then, replace the local isValidJSON
function in both files with an import statement that imports isValidJSON from
the new utility module.

Comment on lines +19 to +112
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>
);
}
}
Copy link

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 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
In src/dashboard/Data/Views/EditViewDialog.react.js from lines 19 to 112, the
EditViewDialog component lacks prop validation which can lead to runtime errors.
To fix this, import PropTypes at the top of the file and add a static propTypes
object after the component class definition specifying the expected types for
classes, view, onCancel, and onConfirm props as described. This ensures proper
validation and better maintainability.

@mtrezza mtrezza changed the title feat: Add custom views page feat: Add custom data views with aggregation query Jul 13, 2025
@mtrezza mtrezza merged commit b1679db into alpha Jul 13, 2025
10 of 11 checks passed
@mtrezza mtrezza deleted the codex/add-views-feature-for-custom-queries branch July 13, 2025 01:11
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/dashboard/Data/Views/Views.react.js (2)

63-80: Replace deprecated React lifecycle methods

The code still uses deprecated lifecycle methods componentWillMount and componentWillReceiveProps that were flagged in previous reviews. These should be replaced with their modern equivalents.

Apply this diff to fix the deprecated lifecycle methods:

-  componentWillMount() {
+  componentDidMount() {
    this.props.schema.dispatch(SchemaActionTypes.FETCH).then(() => this.loadViews(this.context));
  }

-  componentWillReceiveProps(nextProps, nextContext) {
-    if (this.context !== nextContext) {
-      this.props.schema.dispatch(SchemaActionTypes.FETCH).then(() => this.loadViews(nextContext));
-    }
-    if (this.props.params.name !== nextProps.params.name || this.context !== nextContext) {
-      window.scrollTo({ top: 0 });
-      this.loadData(nextProps.params.name);
-    }
+  componentDidUpdate(prevProps, prevState, prevContext) {
+    if (this.context !== prevContext) {
+      this.props.schema.dispatch(SchemaActionTypes.FETCH).then(() => this.loadViews(this.context));
+    }
+    if (this.props.params.name !== prevProps.params.name || this.context !== prevContext) {
+      window.scrollTo({ top: 0 });
+      this.loadData(this.props.params.name);
+    }
  }

267-267: Use a more efficient React key

The table row still uses JSON.stringify(row) as a React key, which was flagged in previous reviews as inefficient for large datasets.

-      <tr key={JSON.stringify(row)} className={styles.tableRow}>
+      <tr key={row.objectId || row.id || JSON.stringify(row)} className={styles.tableRow}>
🧹 Nitpick comments (1)
src/dashboard/Data/Views/Views.react.js (1)

308-308: Use optional chaining for cleaner code

The static analysis tool suggests using optional chaining for better readability and safety.

-            content = value && value.iso ? value.iso : String(value);
+            content = value?.iso ?? String(value);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d74b4f8 and d657be1.

📒 Files selected for processing (1)
  • src/dashboard/Data/Views/Views.react.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/dashboard/Data/Views/Views.react.js (1)
Learnt from: mtrezza
PR: parse-community/parse-dashboard#2828
File: src/dashboard/Data/Browser/Browser.react.js:1605-1607
Timestamp: 2025-05-27T12:09:47.644Z
Learning: In script execution dialogs in Parse Dashboard (specifically the `confirmExecuteScriptRows` method in `src/dashboard/Data/Browser/Browser.react.js`), individual `setState` calls to update `processedScripts` counter should be kept as-is rather than batched, because this provides real-time progress feedback to users in the dialog UI.
🧬 Code Graph Analysis (1)
src/dashboard/Data/Views/Views.react.js (10)
src/lib/withRouter.js (1)
  • withRouter (4-26)
src/dashboard/TableView.react.js (1)
  • TableView (13-66)
src/components/CategoryList/CategoryList.react.js (1)
  • CategoryList (15-177)
src/components/BrowserMenu/MenuItem.react.js (1)
  • MenuItem (11-27)
src/dashboard/Data/Views/ViewValueDialog.react.js (1)
  • ViewValueDialog (7-37)
src/dashboard/Data/Views/CreateViewDialog.react.js (1)
  • CreateViewDialog (19-111)
src/dashboard/Data/Views/EditViewDialog.react.js (1)
  • EditViewDialog (19-112)
src/dashboard/Data/Views/DeleteViewDialog.react.js (1)
  • DeleteViewDialog (7-45)
src/lib/generatePath.js (2)
  • generatePath (3-34)
  • filters (8-8)
src/dashboard/Data/Browser/Notification.react.js (1)
  • Notification (13-80)
🪛 Biome (1.9.4)
src/dashboard/Data/Views/Views.react.js

[error] 308-308: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Docker linux/amd64
🔇 Additional comments (3)
src/dashboard/Data/Views/Views.react.js (3)

82-195: Well-implemented data loading with proper error handling

The data loading methods are well-structured with comprehensive error handling, proper cleanup using _isMounted checks, and sophisticated column width calculation using canvas text measurement. The type inference logic correctly handles all Parse data types.


371-471: Well-designed UI rendering with good UX patterns

The rendering methods provide excellent user experience with helpful empty states, clear navigation, and appropriate toolbar functionality. The separation of concerns between different rendering methods is well-maintained.


584-599: Proper notification handling with cleanup

The notification system is well-implemented with proper timeout management and cleanup. Good user feedback patterns are maintained throughout.

parseplatformorg pushed a commit that referenced this pull request Jul 13, 2025
# [7.3.0-alpha.11](7.3.0-alpha.10...7.3.0-alpha.11) (2025-07-13)

### Features

* Add custom data views with aggregation query ([#2888](#2888)) ([b1679db](b1679db))
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 7.3.0-alpha.11

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Jul 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released-alpha Released as alpha version
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants