# Chart Configuration Manager

This example demonstrates a dynamic chart system with configuration management using Chart.js and the BuzzyFrameAPI for data persistence.

## Key Features

* **Interactive Chart.js integration** with dynamic data visualization
* **Configuration persistence** storing chart settings as child data
* **Real-time chart updates** when configurations change
* **Multiple chart configurations** with version management
* **Chart destruction and recreation** for smooth transitions
* **Sample data generation** for demonstration purposes

## Use Cases

Perfect for applications requiring:

* Business intelligence dashboards
* Data analytics platforms
* Reporting systems with customizable charts
* Configuration management interfaces
* A/B testing for chart presentations
* Historical chart configuration tracking

## Implementation Overview

This widget uses the **Child Table Data** pattern to store chart configurations as JSON objects. Key features include:

* Chart.js library integration for professional visualizations
* Configuration versioning and management
* Real-time chart updates without page refresh
* Clean chart lifecycle management (destroy/recreate)
* Sample data for immediate demonstration

## Code Example

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Chart Configuration Manager</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script
      src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"
      crossorigin
    ></script>
    <script
      src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"
      crossorigin
    ></script>
    <script
      src="https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js"
      crossorigin
    ></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const buzzyFrameAPI = new BuzzyFrameAPI();
      let appInitData = null;
      let appConfigs = [];

      async function initApp() {
        appInitData = await buzzyFrameAPI.initialise();
        await fetchAndUpdateConfigs();

        buzzyFrameAPI.addMicroappListener({
          microAppID: appInitData.rowJSON._id,
          listener: async () => {
            console.log('Config data updated, refreshing');
            await fetchAndUpdateConfigs();
          },
        });
      }

      async function fetchAndUpdateConfigs() {
        try {
          const { rowJSON = {} } = appInitData || {};
          const children = await buzzyFrameAPI.getChildItemsByField({
            appID: rowJSON._id,
            fieldID: rowJSON.fieldID,
          });

          appConfigs = children;

          if (window.updateConfigs) {
            window.updateConfigs(appConfigs);
          }
        } catch (error) {
          console.error('Error fetching configs:', error);
          if (window.updateConfigs) {
            window.updateConfigs([]);
          }
        }
      }

      function App() {
        const { useEffect, useState, useRef } = React;
        const [configs, setConfigs] = useState([]);
        const [loading, setLoading] = useState(true);
        const chartRef = useRef(null);
        const canvasRef = useRef(null);

        useEffect(() => {
          window.updateConfigs = newConfigs => {
            setConfigs(newConfigs);
            setLoading(false);
          };

          if (appConfigs.length > 0) {
            setConfigs(appConfigs);
            setLoading(false);
          }

          return () => {
            window.updateConfigs = null;
          };
        }, []);

        useEffect(() => {
          if (configs.length > 0 && canvasRef.current) {
            const latestConfig = configs[configs.length - 1].content;

            if (chartRef.current) {
              chartRef.current.destroy();
            }

            const ctx = canvasRef.current.getContext('2d');
            chartRef.current = new Chart(ctx, {
              type: latestConfig.type || 'bar',
              data: latestConfig.data || {},
              options: latestConfig.options || {},
            });
          }
        }, [configs]);

        const handleSaveConfig = async () => {
          try {
            const { rowJSON } = appInitData;

            const newConfig = {
              type: 'bar',
              data: {
                labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                datasets: [
                  {
                    label: 'Sample Data',
                    data: [12, 19, 3, 5, 2, 3],
                    backgroundColor: [
                      'rgba(255, 99, 132, 0.2)',
                      'rgba(54, 162, 235, 0.2)',
                      'rgba(255, 206, 86, 0.2)',
                      'rgba(75, 192, 192, 0.2)',
                      'rgba(153, 102, 255, 0.2)',
                      'rgba(255, 159, 64, 0.2)',
                    ],
                    borderColor: [
                      'rgba(255, 99, 132, 1)',
                      'rgba(54, 162, 235, 1)',
                      'rgba(255, 206, 86, 1)',
                      'rgba(75, 192, 192, 1)',
                      'rgba(153, 102, 255, 1)',
                      'rgba(255, 159, 64, 1)',
                    ],
                    borderWidth: 1,
                  },
                ],
              },
              options: {
                scales: {
                  y: {
                    beginAtZero: true,
                  },
                },
              },
            };

            await buzzyFrameAPI.createMicroappChild({
              microAppResourceID: rowJSON.parentResourceID,
              appID: rowJSON._id,
              fieldID: rowJSON.fieldID,
              content: newConfig,
            });
          } catch (error) {
            console.error('Error saving config:', error);
          }
        };

        const handleDeleteConfig = async childID => {
          if (!confirm('Are you sure you want to delete this configuration?'))
            return;

          try {
            await buzzyFrameAPI.removeMicroappChild({ childID });
          } catch (error) {
            console.error('Error deleting config:', error);
          }
        };

        if (loading)
          return <div className="has-text-centered p-4">Loading...</div>;

        return (
          <div className="container">
            <div className="box">
              <canvas ref={canvasRef}></canvas>
            </div>

            <div className="box">
              <h3 className="title is-4 mb-4">Chart Configurations</h3>
              <button
                className="button is-primary mb-4"
                onClick={handleSaveConfig}>
                <span className="icon">
                  <i className="fas fa-save"></i>
                </span>
                <span>Save Current Config</span>
              </button>

              <div className="content">
                {configs.map((config, index) => (
                  <div key={config._id} className="level is-mobile">
                    <div className="level-left">
                      <div className="level-item">
                        <span className="has-text-weight-medium">
                          Config {index + 1}
                        </span>
                      </div>
                    </div>
                    <div className="level-right">
                      <div className="level-item">
                        <button
                          className="button is-danger is-small"
                          onClick={() => handleDeleteConfig(config._id)}>
                          <span className="icon is-small">
                            <i className="fas fa-trash"></i>
                          </span>
                          <span>Delete</span>
                        </button>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        );
      }

      initApp().then(() => {
        ReactDOM.render(<App />, document.getElementById('root'));
      });
    </script>
  </body>
</html>
```

## Key Concepts

### Chart.js Integration

* **External Library**: Demonstrates how to integrate popular JavaScript libraries
* **Chart Lifecycle**: Proper chart destruction and recreation for smooth updates
* **Canvas Management**: Direct canvas manipulation with React refs
* **Configuration Objects**: Complete Chart.js configuration stored as JSON

### Configuration Management

* **Persistence**: Chart configurations stored as child data for long-term access
* **Versioning**: Multiple configurations can be saved and managed
* **Real-time Updates**: Charts update immediately when configurations change
* **JSON Storage**: Complex configuration objects stored efficiently

### Data Visualization Patterns

* **Sample Data**: Includes sample datasets for immediate demonstration
* **Color Schemes**: Professional color palettes for visual appeal
* **Responsive Design**: Charts adapt to container size automatically
* **Interactive Elements**: Hover effects and tooltips built into Chart.js

### Advanced Features

* **Memory Management**: Proper cleanup of chart instances to prevent memory leaks
* **Error Handling**: Graceful handling of configuration errors
* **User Feedback**: Loading states and confirmation dialogs
* **Extensibility**: Easy to extend with additional chart types and options

This example showcases how code widgets can integrate sophisticated charting libraries while maintaining data persistence and real-time updates, making it perfect for business intelligence and analytics applications.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.buzzy.buzz/the-building-blocks/code-widget-custom-code/examples/chart-configuration-manager.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
