0% found this document useful (0 votes)
29 views18 pages

Custom Dashboard CUrsor

The Custom Dashboard feature for the NDR platform enables users to create personalized dashboards with customizable metrics and charts. It emphasizes user personalization, reusability of existing components, seamless integration, scalability, and robust error handling. The development will adhere to established design patterns and coding standards while ensuring UI consistency with the existing theme library.

Uploaded by

paritosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views18 pages

Custom Dashboard CUrsor

The Custom Dashboard feature for the NDR platform enables users to create personalized dashboards with customizable metrics and charts. It emphasizes user personalization, reusability of existing components, seamless integration, scalability, and robust error handling. The development will adhere to established design patterns and coding standards while ensuring UI consistency with the existing theme library.

Uploaded by

paritosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 18

1.

Overview
The Custom Dashboard feature will allow users to create personalized
dashboards within the existing NDR (Network Detection and Response)
platform. Users can add, configure, and visualize different metrics from
network data in customizable charts. This functionality must reuse
existing components and adhere to established design patterns, coding
standards, and the platform theme.

2. Goals & Objectives


1. User Personalization
o Allow each user to create multiple dashboards.
o Support drag-and-drop and resizable widgets/cards (similar to
existing Alert Summary dashboard).
2. Reusable & Maintainable
o Reuse existing chart components from
src/views/charts/apex/ApexGradientChart.jsx.
o Reuse existing drag-and-drop/resizable logic from
src/views/dashboards/alert-summary/AlertSummaryView.jsx.
o Maintain a single code path for any future chart or dashboard
updates.
3. Seamless Integration
o The new dashboard feature must integrate into the existing
product without breaking existing functionalities.
o Must use only theme library components for UI consistency
(avoid custom or third-party styling that conflicts with the
theme).
4. Scalable & Extensible
o Ensure the new feature can handle large volumes of data
(breakdown range up to 10K).
o Provide an easy path for adding new metrics or chart types in
the future.
5. Error Handling & Rule Tracking
o Update and reference the cursor rule file for each bug fix to
prevent regressions.
o Provide robust error handling (catch and handle errors
gracefully).

3. Scope
In-Scope
 Navigation: A new page “Custom Dashboard” under Apps (or
relevant section) in the navigation drawer.
 Create Dashboard Flow:
1. Click “Create Dashboard” button.
2. Modal popup to capture dashboard title and description.
3. Dashboard creation confirmation and auto-navigation to the
new dashboard.
 Add Metrics Flow:
1. Configuring metrics (X-Axis, Y-Axis, optional Breakdown).
2. Selecting chart type (Stacked, Area, etc.).
3. Automatic preview of the chart.
4. Dummy API integration referencing provided JSON fields.
 Dashboard Management:
o Drag, drop, and resize cards.
o Auto-save changes.
o Delete entire dashboard (with confirmation).
o Delete individual metrics (with “More” icon, no confirmation
needed).
 UI Components: Use the existing theme library; do not create new
custom components.
Out of Scope
 Advanced data transformations beyond the breakdown or intervals.
 Real-time streaming updates of charts (current requirement is based
on the dummy API with JSON).
 Custom-coded chart components that deviate from
ApexGradientChart.jsx.

4. Figma Reference
Figma:
https://www.figma.com/design/dYGmfQ2X4ap4UMDnmA1dai/Hawk
-7.5-(Latest)?node-id=27683-99493&t=38VAqovCvcMVHkBj-0
A Figma screenshot (attached) illustrates the layout and labeling of each
screen:
1. Dashboard List Page – showing “Create Dashboard” button.
2. Create Dashboard Modal – fields for title, description.
3. Custom Dashboard – drag-and-drop grid with chart cards.
4. Add Metrics Form – X Axis, Y Axis, Breakdown, chart type, preview.
5. Delete/Confirmation – modals and toast notifications.
Key Observations / Suggested Improvements:
 Ensure the “Add Metrics” button is visually distinct and consistent
with the theme.
 Confirm that the “More” icon is placed on the top-right corner of
each chart card for a consistent user experience.
 Provide user feedback (e.g., loading/spinner) when saving or
deleting dashboards.

5. Detailed Requirements
5.1. Navigation & Page Setup
1. New Page: Create a page named Custom Dashboard under the
Apps section.
2. Navigation Drawer: Add an entry “Custom Dashboard” in the
drawer.
3. Routing: Ensure the route is protected (if needed) and consistent
with existing patterns.
5.2. Create Dashboard Workflow
1. Step 1: User opens the Custom Dashboard page.
2. Step 2: User clicks on Create Dashboard.
3. Step 3: A modal popup appears with fields:
o Dashboard Title (required)
o Description (optional)
4. Step 4: User clicks Save.
5. Step 5: On successful creation:
o Show a confirmation popup (e.g., “Dashboard created
successfully!”).
o Automatically navigate to the newly created dashboard view.
5.3. Adding Metrics
1. Open Dashboard: The user selects a newly created (or existing)
dashboard from a table/list.
2. Click on “Add Metrics”: Opens a configuration form.
3. Configuration Fields:
1. X Axis (Interval): Time range (1 day, 1 week, 1 month, 6
months, 1 year).
2. Y Axis (Count): Select from JSON keys (e.g., client.bytes,
client.packets).
3. Breakdown (Optional): Also from JSON keys.
 If a breakdown is chosen, show “Number of values”
(default: 3, range: 1–10,000).
4. Chart Type: e.g., Stacked, Area, etc.
5. Chart Preview: Auto-updates based on user selections.
6. Important: Use your deep thinking to successfully forming
chart in multiple types. Effective way to use the Breakdown
option.
4. Dummy API:
o Create a mock endpoint returning data in the shape of the
provided JSON example.
o Data from the JSON file can be used to populate the charts.
5. Save: Once metrics are configured, the card is added to the
dashboard layout.
5.4. Drag & Drop / Resizable Cards
 Reusability: Must use the existing logic from
src/views/dashboards/alert-summary/AlertSummaryView.jsx.
 Chart Component: Must reuse
src/views/charts/apex/ApexGradientChart.jsx.
 Behavior:
o Each metric card can be moved around the dashboard grid.
o Each metric card can be resized.
o Changes persist automatically (auto-save).
5.5. Deleting Dashboards & Metrics
1. Delete Dashboard:
o A “Delete” button on the dashboard page triggers a
confirmation modal.
o If confirmed, remove the dashboard from the system.
o Show toast: “Dashboard Deleted successfully.”
2. Delete Metric:
o Within each card, a “More” icon → “Delete” option.
o No confirmation needed.
o On success, remove the card from the layout.
5.6. Auto-Save & Error Handling
 Auto-Save:
o Every user action (add metric, move card, resize card) triggers
an auto-save.
o Show a brief loading spinner or “Saving…” state in the UI.
 Error Handling:
o Wrap all API calls with try/catch blocks.
o If any error occurs, show an error toast or alert.
o Log errors and update the cursor rule file to prevent repeating
the same mistakes.
5.7. Theming & Styling
1. Theme Library: Use only the existing theme library components for
all UI elements.
2. CSS: Must reside within the theme or follow the theme’s styling
guidelines.
3. No Custom Components: Do not introduce new custom or 3rd-
party libraries that conflict with existing code.
5.8. Cursor Rule File
1. Cursor Rule File:
o Review the rule file before making changes.
o Update it whenever a bug is found and fixed, so future code
generations avoid the same pitfalls.
2. Documentation:
o For each new rule or fix, add a comment or short description
explaining the issue and the resolution.

6. Data Model & JSON Reference


We have a JSON object containing the fields client.bytes, client.packets,
etc. Use your brain to pick the key values.

JSON File:

{
"_index": ".internal.alerts-security.alerts-default-000006",
"_id": "356924b74ce2273e635f03624d28cc331bdec4ea",
"_version": 1,
"_score": 0,
"_source": {
"kibana.alert.rule.execution.timestamp": "2025-03-28T06:21:24.496Z",
"kibana.alert.start": "2025-03-28T06:21:24.496Z",
"kibana.alert.last_detected": "2025-03-28T06:21:24.496Z",
"kibana.version": "8.15.0",
"kibana.alert.rule.parameters": {
"description": "testing recreation of policy 1",
"risk_score": 0,
"severity": "critical",
"author": [],
"false_positives": [],
"from": "now-5m",
"rule_id": "ab1e5b08-64d0-4faf-9348-f3b305f71642",
"max_signals": 100,
"risk_score_mapping": [],
"severity_mapping": [],
"threat": [],
"to": "now",
"references": [],
"version": 1,
"exceptions_list": [],
"immutable": false,
"rule_source": {
"type": "internal"
},
"related_integrations": [],
"required_fields": [],
"setup": "",
"type": "esql",
"language": "esql",
"query": "FROM wiresense*| EVAL minute =
DATE_EXTRACT(\"minute_of_day\", network.start) | WHERE
(CIDR_MATCH(source.ip,\"192.168.3.0/24\",\"192.168.4.0/24\",\"192.168.1.
70/32\") OR
CIDR_MATCH(destination.ip,\"192.168.3.0/24\",\"192.168.4.0/24\",\"192.16
8.1.70/32\")) AND ((source.locality==\"public\" AND server.bytes>0) OR
(destination.locality==\"public\" AND client.bytes>0)) AND
network.start>= now() - 10 minute"
},
"kibana.alert.rule.category": "ES|QL Rule",
"kibana.alert.rule.consumer": "siem",
"kibana.alert.rule.execution.uuid": "ded5424e-bd37-4ee4-bdc7-
150642c87338",
"kibana.alert.rule.name": "shantanu",
"kibana.alert.rule.producer": "siem",
"kibana.alert.rule.revision": 0,
"kibana.alert.rule.rule_type_id": "siem.esqlRule",
"kibana.alert.rule.uuid": "04e36cb7-7f20-49aa-b296-a3494780ad7b",
"kibana.space_ids": [
"default"
],
"kibana.alert.rule.tags": [
"Policy Violation"
],
"@timestamp": "2025-03-28T06:21:24.219Z",
"client.bytes": 168,
"client.packets": 1,
"destination.address": "8.8.8.8",
"destination.as.number": 15169,
"destination.as.organization.name": "GOOGLE",
"destination.geo.continent_code": "NA",
"destination.geo.continent_name": "North America",
"destination.geo.country_iso_code": "US",
"destination.geo.country_name": "United States",
"destination.geo.location": "POINT (-97.822 37.751)",
"destination.geo.timezone": "America/Chicago",
"destination.ip": "8.8.8.8",
"destination.ip_version": "IPv4",
"destination.locality": "public",
"destination.mac": "00:08:a2:0d:23:0b",
"destination.mac_oui": "adi engineering, inc.",
"destination.macs": "00:08:a2:0d:23:0b",
"destination.port": 53,
"destination.service": "dns (UDP/53)",
"event.id": "571316607360098",
"event.interface": "eno12409",
"event.timestamp": "2025-03-28T06:17:38.142Z",
"event.type": "flow",
"network.application": "dns",
"network.bytes": 252,
"network.end": "2025-03-28T06:12:34.992Z",
"network.locality": "public",
"network.packets": 2,
"network.protocol": "UDP",
"network.start": "2025-03-28T06:12:34.984Z",
"network.transport": "UDP",
"server.bytes": 84,
"server.packets": 1,
"source.address": "192.168.1.70",
"source.asset.assetType": "unknown",
"source.asset.name": "unknown",
"source.ip": "192.168.1.70",
"source.ip_version": "IPv4",
"source.locality": "private",
"source.mac": "74:56:3c:b8:50:71",
"source.mac_oui": "Unknown",
"source.port": 60717,
"source.service": "Unknown",
"minute": 372,
"event.kind": "signal",
"kibana.alert.original_time": "2025-03-28T06:17:38.493Z",
"kibana.alert.ancestors": [
{
"id": "",
"type": "event",
"index": "",
"depth": 0
}
],
"kibana.alert.status": "active",
"kibana.alert.workflow_status": "open",
"kibana.alert.depth": 1,
"kibana.alert.reason": "event created critical alert shantanu.",
"kibana.alert.severity": "critical",
"kibana.alert.risk_score": 0,
"kibana.alert.rule.actions": [],
"kibana.alert.rule.author": [],
"kibana.alert.rule.created_at": "2025-03-05T05:06:46.033Z",
"kibana.alert.rule.created_by": "elastic",
"kibana.alert.rule.description": "testing recreation of policy 1",
"kibana.alert.rule.enabled": true,
"kibana.alert.rule.exceptions_list": [],
"kibana.alert.rule.false_positives": [],
"kibana.alert.rule.from": "now-5m",
"kibana.alert.rule.immutable": false,
"kibana.alert.rule.interval": "1m",
"kibana.alert.rule.indices": [],
"kibana.alert.rule.max_signals": 100,
"kibana.alert.rule.references": [],
"kibana.alert.rule.risk_score_mapping": [],
"kibana.alert.rule.rule_id": "ab1e5b08-64d0-4faf-9348-f3b305f71642",
"kibana.alert.rule.severity_mapping": [],
"kibana.alert.rule.threat": [],
"kibana.alert.rule.to": "now",
"kibana.alert.rule.type": "esql",
"kibana.alert.rule.updated_at": "2025-03-05T05:06:48.140Z",
"kibana.alert.rule.updated_by": "elastic",
"kibana.alert.rule.version": 1,
"kibana.alert.url":
"http://kibana-02:5601/ui/kibana/02/app/security/alerts/redirect/356924b7
4ce2273e635f03624d28cc331bdec4ea?index=.alerts-security.alerts-
default&timestamp=2025-03-28T06:21:24.219Z",
"kibana.alert.uuid": "356924b74ce2273e635f03624d28cc331bdec4ea",
"kibana.alert.workflow_tags": [],
"kibana.alert.workflow_assignee_ids": [],
"kibana.alert.rule.risk_score": 0,
"kibana.alert.rule.severity": "critical",
"kibana.alert.original_event.id": "571316607360098",
"kibana.alert.original_event.interface": "eno12409",
"kibana.alert.original_event.timestamp": "2025-03-28T06:17:38.142Z",
"kibana.alert.original_event.type": "flow"
},
"fields": {
"kibana.alert.severity": [
"critical"
],
"kibana.alert.rule.updated_by": [
"elastic"
],
"signal.ancestors.depth": [
0
],
"source.mac_oui": [
"Unknown"
],
"kibana.alert.rule.tags": [
"Policy Violation"
],
"kibana.alert.reason.text": [
"event created critical alert shantanu."
],
"kibana.alert.ancestors.depth": [
0
],
"signal.rule.enabled": [
"true"
],
"signal.rule.max_signals": [
100
],
"kibana.alert.risk_score": [
0
],
"signal.rule.updated_at": [
"2025-03-05T05:06:48.140Z"
],
"source.ip": [
"192.168.1.70"
],
"destination.address": [
"8.8.8.8"
],
"kibana.alert.original_event.id": [
"571316607360098"
],
"destination.geo.continent_name": [
"North America"
],
"source.asset.assetType": [
"unknown"
],
"kibana.alert.rule.interval": [
"1m"
],
"kibana.alert.rule.type": [
"esql"
],
"event.timestamp": [
"2025-03-28T06:17:38.142Z"
],
"kibana.alert.start": [
"2025-03-28T06:21:24.496Z"
],
"kibana.alert.rule.immutable": [
"false"
],
"kibana.alert.original_event.type": [
"flow"
],
"source.port": [
60717
],
"signal.rule.from": [
"now-5m"
],
"kibana.alert.rule.enabled": [
"true"
],
"destination.geo.country_name": [
"United States"
],
"kibana.alert.rule.version": [
"1"
],
"kibana.alert.ancestors.type": [
"event"
],
"destination.port": [
53
],
"signal.ancestors.index": [
""
],
"source.mac": [
"74:56:3c:b8:50:71"
],
"network.application": [
"dns"
],
"signal.original_event.id": [
"571316607360098"
],
"network.end": [
"2025-03-28T06:12:34.992Z"
],
"signal.original_event.type": [
"flow"
],
"kibana.alert.rule.max_signals": [
100
],
"kibana.alert.rule.risk_score": [
0
],
"destination.as.organization.name.text": [
"GOOGLE"
],
"destination.ip_version": [
"IPv4"
],
"destination.ip": [
"8.8.8.8"
],
"kibana.alert.rule.consumer": [
"siem"
],
"source.locality": [
"private"
],
"destination.service": [
"dns (UDP/53)"
],
"kibana.alert.rule.category": [
"ES|QL Rule"
],
"@timestamp": [
"2025-03-28T06:21:24.219Z"
],
"signal.rule.updated_by": [
"elastic"
],
"source.ip_version": [
"IPv4"
],
"destination.geo.country_iso_code": [
"US"
],
"kibana.alert.rule.severity": [
"critical"
],
"kibana.alert.rule.execution.timestamp": [
"2025-03-28T06:21:24.496Z"
],
"kibana.alert.rule.execution.uuid": [
"ded5424e-bd37-4ee4-bdc7-150642c87338"
],
"kibana.alert.uuid": [
"356924b74ce2273e635f03624d28cc331bdec4ea"
],
"kibana.version": [
"8.15.0"
],
"source.service": [
"Unknown"
],
"event.id": [
"571316607360098"
],
"signal.ancestors.type": [
"event"
],
"event.interface": [
"eno12409"
],
"destination.as.organization.name": [
"GOOGLE"
],
"kibana.alert.rule.rule_id": [
"ab1e5b08-64d0-4faf-9348-f3b305f71642"
],
"signal.rule.type": [
"esql"
],
"kibana.alert.ancestors.id": [
""
],
"kibana.alert.url": [

"http://kibana-02:5601/ui/kibana/02/app/security/alerts/redirect/356924b7
4ce2273e635f03624d28cc331bdec4ea?index=.alerts-security.alerts-
default&timestamp=2025-03-28T06:21:24.219Z"
],
"kibana.alert.rule.description": [
"testing recreation of policy 1"
],
"kibana.alert.rule.producer": [
"siem"
],
"kibana.alert.rule.to": [
"now"
],
"signal.rule.created_by": [
"elastic"
],
"signal.rule.interval": [
"1m"
],
"destination.mac": [
"00:08:a2:0d:23:0b"
],
"kibana.alert.rule.created_by": [
"elastic"
],
"signal.rule.id": [
"04e36cb7-7f20-49aa-b296-a3494780ad7b"
],
"signal.reason": [
"event created critical alert shantanu."
],
"signal.rule.risk_score": [
0
],
"kibana.alert.rule.name": [
"shantanu"
],
"signal.status": [
"open"
],
"destination.geo.timezone": [
"America/Chicago"
],
"event.kind": [
"signal"
],
"signal.rule.created_at": [
"2025-03-05T05:06:46.033Z"
],
"signal.rule.tags": [
"Policy Violation"
],
"kibana.alert.workflow_status": [
"open"
],
"kibana.alert.rule.uuid": [
"04e36cb7-7f20-49aa-b296-a3494780ad7b"
],
"network.packets": [
2
],
"kibana.alert.reason": [
"event created critical alert shantanu."
],
"signal.ancestors.id": [
""
],
"signal.original_time": [
"2025-03-28T06:17:38.493Z"
],
"signal.rule.severity": [
"critical"
],
"kibana.alert.ancestors.index": [
""
],
"kibana.alert.depth": [
1
],
"kibana.alert.original_event.timestamp": [
"2025-03-28T06:17:38.142Z"
],
"kibana.alert.rule.from": [
"now-5m"
],
"kibana.alert.rule.parameters": [
{
"description": "testing recreation of policy 1",
"risk_score": 0,
"severity": "critical",
"author": [],
"false_positives": [],
"from": "now-5m",
"rule_id": "ab1e5b08-64d0-4faf-9348-f3b305f71642",
"max_signals": 100,
"risk_score_mapping": [],
"severity_mapping": [],
"threat": [],
"to": "now",
"references": [],
"version": 1,
"exceptions_list": [],
"immutable": false,
"rule_source": {
"type": "internal"
},
"related_integrations": [],
"required_fields": [],
"setup": "",
"type": "esql",
"language": "esql",
"query": "FROM wiresense*| EVAL minute =
DATE_EXTRACT(\"minute_of_day\", network.start) | WHERE
(CIDR_MATCH(source.ip,\"192.168.3.0/24\",\"192.168.4.0/24\",\"192.168.1.
70/32\") OR
CIDR_MATCH(destination.ip,\"192.168.3.0/24\",\"192.168.4.0/24\",\"192.16
8.1.70/32\")) AND ((source.locality==\"public\" AND server.bytes>0) OR
(destination.locality==\"public\" AND client.bytes>0)) AND
network.start>= now() - 10 minute"
}
],
"kibana.alert.rule.revision": [
0
],
"signal.rule.version": [
"1"
],
"server.bytes": [
84
],
"destination.geo.continent_code": [
"NA"
],
"kibana.alert.status": [
"active"
],
"kibana.alert.last_detected": [
"2025-03-28T06:21:24.496Z"
],
"client.packets": [
1
],
"destination.macs": [
"00:08:a2:0d:23:0b"
],
"signal.depth": [
1
],
"source.address": [
"192.168.1.70"
],
"signal.rule.immutable": [
"false"
],
"kibana.alert.original_event.interface": [
"eno12409"
],
"destination.geo.location": [
{
"coordinates": [
-97.822,
37.751
],
"type": "Point"
}
],
"destination.mac_oui": [
"adi engineering, inc."
],
"kibana.alert.rule.rule_type_id": [
"siem.esqlRule"
],
"signal.rule.name": [
"shantanu"
],
"source.asset.name": [
"unknown"
],
"network.protocol": [
"UDP"
],
"signal.rule.rule_id": [
"ab1e5b08-64d0-4faf-9348-f3b305f71642"
],
"network.bytes": [
252
],
"destination.locality": [
"public"
],
"kibana.alert.rule.updated_at": [
"2025-03-05T05:06:48.140Z"
],
"signal.rule.description": [
"testing recreation of policy 1"
],
"destination.as.number": [
15169
],
"server.packets": [
1
],
"network.locality": [
"public"
],
"network.transport": [
"UDP"
],
"minute": [
372
],
"kibana.alert.rule.created_at": [
"2025-03-05T05:06:46.033Z"
],
"signal.rule.to": [
"now"
],
"client.bytes": [
168
],
"event.type": [
"flow"
],
"kibana.space_ids": [
"default"
],
"network.start": [
"2025-03-28T06:12:34.984Z"
],
"kibana.alert.original_time": [
"2025-03-28T06:17:38.493Z"
]
}
}

When a user selects X Axis = Time Range, the system should query
data grouped by the chosen interval. When Y Axis = client.bytes, the
chart should plot that data count over time. If a Breakdown is
selected, the chart should display separate series (or stacked
segments, etc.) for each breakdown category, up to the “Number of
value” limit.

7. Acceptance Criteria
1. Create Dashboard
o A user can successfully create a new dashboard with a title
and description.
o The user is navigated automatically to the new dashboard.
2. Add Metrics
o User can select an X Axis, Y Axis, optional Breakdown, chart
type, and see a live preview.
o The metric is added to the dashboard as a new card after
saving.
3. Drag & Drop
o Cards can be repositioned and resized.
o Position changes persist automatically (no manual save
required).
4. Delete Dashboard
o Confirmation modal appears.
o Dashboard is removed from the table/list upon confirmation.
o Toast message “Dashboard Deleted successfully.” appears.
5. Delete Metric
o Clicking “Delete” from the card’s “More” menu removes the
card immediately.
o No confirmation is required.
6. Error Handling
o If an API call fails, a user-friendly error message or toast is
displayed.
o The code logs errors and updates the cursor rule file with the
fix.
7. Theme Compliance
o All UI elements are from the theme library (no raw CSS or
external libraries).

8. Implementation Guidelines
1.Reuse:
 src/views/dashboards/alert-summary/
AlertSummaryView.jsx for drag-and-drop/resizing.
 src/views/charts/apex/ApexGradientChart.jsx for
charts.
2. Dummy API
1. Provide a mock function or endpoint: GET /api/v1/mockData
that returns a subset of the JSON fields.
2. The system transforms this data into the structure required by
ApexGradientChart.jsx.
3. State Management
1. Each dashboard can store its layout (positions, sizes) and
metrics configuration in local state or Redux (depending on
existing architecture).
2. Save to server or local storage (as per product standard).
4. Cursor Rule File
1. Each time a fix or enhancement is done, add an entry in the
rule file:
js
CopyEdit
// Cursor Rule: [Short Title of Issue]
// Explanation: [What caused the issue, how we resolved it] // Example:
"Fixed an undefined reference error when the breakdown field was empty."
```

9. Testing & QA
1. Unit Tests
o Components: test AddMetricsForm with various inputs.
o Chart rendering: ensure ApexGradientChart.jsx is called with
correct props.
2. Integration Tests
o Create dashboard → add metrics → verify chart rendering.
o Drag, drop, and resize cards → refresh the page → positions
persist.
3. End-to-End Tests
o Automated flow in a QA environment to simulate user actions:
1. Create a new dashboard.
2. Add multiple metrics with breakdown.
3. Delete one metric.
4. Delete the entire dashboard.
4. Performance
o Ensure large breakdown values (up to 10K) do not crash or
severely degrade performance.
o Validate that the chart rendering is still responsive.

10. Additional Recommendations / Improvements


1. User Guidance
o Consider adding tooltips or help icons next to each form field
for clarity.
o Provide default placeholders or sample data for quick testing.
2. Responsive Design
o Ensure the drag-and-drop layout is responsive for different
screen sizes.
o Test on mobile if required by product scope.
3. Localization / i18n
o If the product is localized, ensure new strings are added to
translation files.
4. Future Chart Types
o The architecture should easily allow for new chart types (e.g.,
bar, line, donut).
5. Security
o Validate user inputs in the form (title, description) to prevent
XSS.
o Use existing authentication/authorization checks to ensure
only authorized users can create dashboards.

Final Notes
 Implementation must not break existing features. Thorough
testing is required before merging.
 Documentation must be updated (e.g., README or Confluence)
with usage instructions for the new Custom Dashboard.
 Cursor Rule File must be maintained diligently to prevent
repeating mistakes and to guide AI assistance in the future.

You might also like