@salesforce/webapp-template-feature-react-chart-experimental 1.25.2 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +2 -1
- package/dist/.a4drules/skills/install-feature/SKILL.md +14 -6
- package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +36 -0
- package/dist/CHANGELOG.md +19 -0
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/appLayout.tsx +8 -6
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/navigationMenu.tsx +81 -0
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/pages/about.tsx +10 -0
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/pages/new.tsx +10 -0
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/router-utils.tsx +34 -0
- package/dist/force-app/main/default/webapplications/feature-react-chart/src/routes.tsx +14 -3
- package/dist/package.json +1 -1
- package/package.json +6 -4
- package/rules/analytics-charts-rule.md +27 -0
- package/skills/analytics-charts/SKILL.md +41 -0
- package/skills/analytics-charts/docs/schema-mapping.md +4 -0
|
@@ -123,7 +123,8 @@ mutation mutateEntityName(
|
|
|
123
123
|
## Mutation Standalone (Default) Output Format - CLEAN CODE ONLY
|
|
124
124
|
|
|
125
125
|
```javascript
|
|
126
|
-
|
|
126
|
+
import { gql } from 'api/graphql.ts';
|
|
127
|
+
const QUERY_NAME = gql`
|
|
127
128
|
mutation mutateEntity($input: EntityNameOperationInput!) {
|
|
128
129
|
uiapi {
|
|
129
130
|
EntityNameOperation(input: $input) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: install-feature
|
|
3
|
-
description: Install a feature into the current app. Use when the user asks to add a feature, capability, or functionality like authentication, search, charts, or navigation to their React web app.
|
|
3
|
+
description: Install a feature into the current app. Use when the user asks to add a feature, capability, or functionality like authentication, search, charts, or navigation to their React web app. Do this when user wants to do _anything_ to webapplications/webapps.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Install feature
|
|
@@ -17,10 +17,12 @@ Available features (npm packages):
|
|
|
17
17
|
| **Authentication** | `@salesforce/webapp-template-feature-react-authentication-experimental` | Login, register, password reset, protected routes |
|
|
18
18
|
| **Global search** | `@salesforce/webapp-template-feature-react-global-search-experimental` | Search Salesforce objects with filters and pagination |
|
|
19
19
|
| **Navigation menu** | `@salesforce/webapp-template-feature-react-nav-menu-experimental` | App layout with responsive navigation menu |
|
|
20
|
-
| **Analytics charts** | `@salesforce/webapp-template-feature-react-chart-experimental`
|
|
20
|
+
| **Analytics charts** | `@salesforce/webapp-template-feature-react-chart-experimental` | Recharts line/bar charts with theming (AnalyticsChart, ChartContainer) |
|
|
21
|
+
| **GraphQL data access**| `@salesforce/webapp-template-feature-graphql-experimental` | executeGraphQL utilities, codegen tooling, and example AccountsTable |
|
|
21
22
|
| **Shared UI (shadcn)** | `@salesforce/webapp-template-feature-react-shadcn-experimental` | Button, Card, Input, Select, Table, Tabs, etc. |
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
|
|
24
26
|
If no feature matches, tell the user and offer to build it from scratch following the project's existing patterns.
|
|
25
27
|
|
|
26
28
|
## 2. Install the npm package
|
|
@@ -31,12 +33,18 @@ npm install <package-name>
|
|
|
31
33
|
|
|
32
34
|
## 3. Copy skills and rules from the package
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
Run the script so skills and rules from the package are copied into your project. Pass `[skills-dir]` and `[rules-dir]` for your environment; the script reports what it copied.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Default (e.g. Cline): .cline/skills, .clinerules
|
|
40
|
+
bash <this-skill>/scripts/copy-feature-assets.sh <package-name>
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
-
|
|
42
|
+
# Agentforce: .a4drules/ ([docs](https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-rules.html))
|
|
43
|
+
bash <this-skill>/scripts/copy-feature-assets.sh <package-name> .a4drules/skills .a4drules
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
# Cursor: .cursor/skills, .cursor/rules
|
|
46
|
+
bash <this-skill>/scripts/copy-feature-assets.sh <package-name> .cursor/skills .cursor/rules
|
|
47
|
+
```
|
|
40
48
|
|
|
41
49
|
## 4. Read the feature's exports and components
|
|
42
50
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Copies skills/ and rules/ from an installed npm package into the current project.
|
|
3
|
+
# Usage: bash <this-script> <package-name> [skills-dir] [rules-dir]
|
|
4
|
+
#
|
|
5
|
+
# Examples:
|
|
6
|
+
# bash copy-feature-assets.sh @salesforce/webapp-template-feature-graphql-experimental
|
|
7
|
+
# bash copy-feature-assets.sh @salesforce/webapp-template-feature-graphql-experimental .cursor/skills .cursor/rules
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
PKG="${1:?Usage: copy-feature-assets.sh <package-name> [skills-dir] [rules-dir]}"
|
|
11
|
+
SKILLS_DIR="${2:-.cline/skills}"
|
|
12
|
+
RULES_DIR="${3:-.clinerules}"
|
|
13
|
+
|
|
14
|
+
PKG_DIR="node_modules/$PKG"
|
|
15
|
+
|
|
16
|
+
if [ ! -d "$PKG_DIR" ]; then
|
|
17
|
+
echo "Error: Package not found at $PKG_DIR" >&2
|
|
18
|
+
echo "Run 'npm install $PKG' first." >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ -d "$PKG_DIR/skills" ] && [ "$(ls -A "$PKG_DIR/skills")" ]; then
|
|
23
|
+
mkdir -p "$SKILLS_DIR"
|
|
24
|
+
cp -r "$PKG_DIR"/skills/* "$SKILLS_DIR"/
|
|
25
|
+
echo "Copied skills → $SKILLS_DIR/"
|
|
26
|
+
else
|
|
27
|
+
echo "No skills/ found in $PKG_DIR — nothing to copy."
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ -d "$PKG_DIR/rules" ] && [ "$(ls -A "$PKG_DIR/rules")" ]; then
|
|
31
|
+
mkdir -p "$RULES_DIR"
|
|
32
|
+
cp -r "$PKG_DIR"/rules/* "$RULES_DIR"/
|
|
33
|
+
echo "Copied rules → $RULES_DIR/"
|
|
34
|
+
else
|
|
35
|
+
echo "No rules/ found in $PKG_DIR — nothing to copy."
|
|
36
|
+
fi
|
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.27.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.26.0...v1.27.0) (2026-02-12)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* defining graphql feature @W-20226218@ ([#83](https://github.com/salesforce-experience-platform-emu/webapps/issues/83)) ([51a9273](https://github.com/salesforce-experience-platform-emu/webapps/commit/51a92733063e1adf0c6e44ceab37162baddc2ffb))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [1.26.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.25.2...v1.26.0) (2026-02-11)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [1.25.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.25.1...v1.25.2) (2026-02-11)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Outlet } from
|
|
1
|
+
import { Outlet } from "react-router";
|
|
2
|
+
import NavigationMenu from "./navigationMenu";
|
|
2
3
|
|
|
3
4
|
export default function AppLayout() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<NavigationMenu />
|
|
8
|
+
<Outlet />
|
|
9
|
+
</>
|
|
10
|
+
);
|
|
9
11
|
}
|
package/dist/force-app/main/default/webapplications/feature-react-chart/src/navigationMenu.tsx
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Link, useLocation } from "react-router";
|
|
2
|
+
import { getAllRoutes } from "./router-utils";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
export default function NavigationMenu() {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const location = useLocation();
|
|
8
|
+
|
|
9
|
+
const isActive = (path: string) => location.pathname === path;
|
|
10
|
+
|
|
11
|
+
const toggleMenu = () => setIsOpen(!isOpen);
|
|
12
|
+
|
|
13
|
+
// Get all route configs and filter by showInNavigation
|
|
14
|
+
const navigationRoutes: { path: string; label: string }[] = getAllRoutes()
|
|
15
|
+
.filter(
|
|
16
|
+
(route) =>
|
|
17
|
+
route.handle?.showInNavigation === true &&
|
|
18
|
+
route.fullPath !== undefined &&
|
|
19
|
+
route.handle?.label !== undefined,
|
|
20
|
+
)
|
|
21
|
+
.map(
|
|
22
|
+
(route) =>
|
|
23
|
+
({
|
|
24
|
+
path: route.fullPath,
|
|
25
|
+
label: route.handle?.label,
|
|
26
|
+
}) as { path: string; label: string },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<nav className="bg-white border-b border-gray-200">
|
|
31
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
32
|
+
<div className="flex justify-between items-center h-16">
|
|
33
|
+
<Link to="/" className="text-xl font-semibold text-gray-900">
|
|
34
|
+
React App
|
|
35
|
+
</Link>
|
|
36
|
+
<button
|
|
37
|
+
onClick={toggleMenu}
|
|
38
|
+
className="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
39
|
+
aria-label="Toggle menu"
|
|
40
|
+
>
|
|
41
|
+
<div className="w-6 h-6 flex flex-col justify-center space-y-1.5">
|
|
42
|
+
<span
|
|
43
|
+
className={`block h-0.5 w-6 bg-current transition-all ${
|
|
44
|
+
isOpen ? "rotate-45 translate-y-2" : ""
|
|
45
|
+
}`}
|
|
46
|
+
/>
|
|
47
|
+
<span
|
|
48
|
+
className={`block h-0.5 w-6 bg-current transition-all ${isOpen ? "opacity-0" : ""}`}
|
|
49
|
+
/>
|
|
50
|
+
<span
|
|
51
|
+
className={`block h-0.5 w-6 bg-current transition-all ${
|
|
52
|
+
isOpen ? "-rotate-45 -translate-y-2" : ""
|
|
53
|
+
}`}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
{isOpen && (
|
|
59
|
+
<div className="pb-4">
|
|
60
|
+
<div className="flex flex-col space-y-2">
|
|
61
|
+
{navigationRoutes.map((item) => (
|
|
62
|
+
<Link
|
|
63
|
+
key={item.path}
|
|
64
|
+
to={item.path}
|
|
65
|
+
onClick={() => setIsOpen(false)}
|
|
66
|
+
className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
67
|
+
isActive(item.path)
|
|
68
|
+
? "bg-blue-100 text-blue-700"
|
|
69
|
+
: "text-gray-700 hover:bg-gray-100"
|
|
70
|
+
}`}
|
|
71
|
+
>
|
|
72
|
+
{item.label}
|
|
73
|
+
</Link>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</nav>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function About() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
4
|
+
<div className="text-center">
|
|
5
|
+
<h1 className="text-4xl font-bold text-gray-900 mb-4">About</h1>
|
|
6
|
+
<p className="text-lg text-gray-600">This is the about page.</p>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function New() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
4
|
+
<div className="text-center">
|
|
5
|
+
<h1 className="text-4xl font-bold text-gray-900 mb-4">New</h1>
|
|
6
|
+
<p className="text-lg text-gray-600">This is the new page.</p>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RouteObject } from "react-router";
|
|
2
|
+
import { routes } from "./routes";
|
|
3
|
+
|
|
4
|
+
export type RouteWithFullPath = RouteObject & { fullPath: string };
|
|
5
|
+
|
|
6
|
+
const flatMapRoutes = (route: RouteObject, parentPath: string = ""): RouteWithFullPath[] => {
|
|
7
|
+
// Construct the full path for this route
|
|
8
|
+
let fullPath: string;
|
|
9
|
+
|
|
10
|
+
if (route.index) {
|
|
11
|
+
// Index routes use the parent path
|
|
12
|
+
fullPath = parentPath || "/";
|
|
13
|
+
} else if (route.path) {
|
|
14
|
+
// Combine parent path with current path
|
|
15
|
+
if (route.path.startsWith("/")) {
|
|
16
|
+
fullPath = route.path;
|
|
17
|
+
} else {
|
|
18
|
+
fullPath = parentPath === "/" ? `/${route.path}` : `${parentPath}/${route.path}`;
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
fullPath = parentPath;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const routeWithPath = { ...route, fullPath };
|
|
25
|
+
|
|
26
|
+
// Recursively process children
|
|
27
|
+
const childRoutes = route.children?.flatMap((child) => flatMapRoutes(child, fullPath)) || [];
|
|
28
|
+
|
|
29
|
+
return [routeWithPath, ...childRoutes];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getAllRoutes = (): RouteWithFullPath[] => {
|
|
33
|
+
return routes.flatMap((route) => flatMapRoutes(route));
|
|
34
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { RouteObject } from 'react-router';
|
|
2
2
|
import AppLayout from './appLayout';
|
|
3
3
|
import Home from '@/pages/Home';
|
|
4
|
-
import About from './pages/
|
|
4
|
+
import About from './pages/about';
|
|
5
5
|
import NotFound from './pages/NotFound';
|
|
6
|
+
import New from "./pages/new";
|
|
6
7
|
import ChartPage from "./pages/ChartPage";
|
|
7
8
|
|
|
8
9
|
export const routes: RouteObject[] = [
|
|
@@ -16,14 +17,24 @@ export const routes: RouteObject[] = [
|
|
|
16
17
|
handle: { showInNavigation: true, label: 'Home' }
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
|
-
path:
|
|
20
|
+
path: "about",
|
|
20
21
|
element: <About />,
|
|
21
|
-
handle: { showInNavigation: true, label:
|
|
22
|
+
handle: { showInNavigation: true, label: "About" }
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
path: '*',
|
|
25
26
|
element: <NotFound />
|
|
26
27
|
},
|
|
28
|
+
{
|
|
29
|
+
path: "contact",
|
|
30
|
+
element: <h1>Hello World from Contact Page</h1>,
|
|
31
|
+
handle: { showInNavigation: false }
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: "new",
|
|
35
|
+
element: <New />,
|
|
36
|
+
handle: { showInNavigation: true, label: "New Page" }
|
|
37
|
+
},
|
|
27
38
|
{
|
|
28
39
|
path: "chart",
|
|
29
40
|
element: <ChartPage />,
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-template-feature-react-chart-experimental",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"description": "Chart feature with analytics chart components, agent skills, and rules (Recharts)",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"author": "",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"src"
|
|
19
|
+
"src",
|
|
20
|
+
"rules",
|
|
21
|
+
"skills"
|
|
20
22
|
],
|
|
21
23
|
"publishConfig": {
|
|
22
24
|
"access": "public"
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
"watch": "npx tsx ../../cli/src/index.ts watch-patches packages/template/feature/feature-react-chart packages/template/base-app/base-react-app packages/template/feature/feature-react-chart/dist"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
|
-
"@salesforce/webapp-experimental": "^1.
|
|
33
|
+
"@salesforce/webapp-experimental": "^1.27.0",
|
|
32
34
|
"@types/react": "^19.2.7",
|
|
33
35
|
"@types/react-dom": "^19.2.3",
|
|
34
36
|
"react-dom": "^19.2.1",
|
|
@@ -36,5 +38,5 @@
|
|
|
36
38
|
"recharts": "^2.15.0",
|
|
37
39
|
"vite": "^7.3.1"
|
|
38
40
|
},
|
|
39
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b162807f5062f6aee52605127b787de508035fc9"
|
|
40
42
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.tsx"
|
|
4
|
+
- "**/components/**/*.ts"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Analytics charts (standards)
|
|
8
|
+
|
|
9
|
+
When adding or editing chart UI in this project, follow these conventions.
|
|
10
|
+
|
|
11
|
+
## Components and library
|
|
12
|
+
|
|
13
|
+
- Use the shared **AnalyticsChart** component (and **ChartContainer** when a framed block is needed). Do not use raw Recharts components for standard line or bar charts.
|
|
14
|
+
- The project must have **recharts** installed (`npm install recharts`).
|
|
15
|
+
|
|
16
|
+
## Data shape
|
|
17
|
+
|
|
18
|
+
- **Time-series** (line): data must be an array of `{ x: string, y: number }`. Map raw fields (e.g. `date`, `value`) to these keys before passing to the chart.
|
|
19
|
+
- **Categorical** (bar): data must be an array of `{ name: string, value: number }`. Map raw fields (e.g. `category`, `total`) accordingly.
|
|
20
|
+
|
|
21
|
+
## Theming
|
|
22
|
+
|
|
23
|
+
- Use only the **theme** prop on AnalyticsChart: `red` (decline/loss), `green` (growth/gain), `neutral` (default or mixed). Do not introduce ad-hoc color schemes for these semantics.
|
|
24
|
+
|
|
25
|
+
## Placement
|
|
26
|
+
|
|
27
|
+
- Render charts inside the existing application frame (e.g. main content or a route). Do not replace the full app shell with a single chart.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analytics-charts
|
|
3
|
+
description: Add or change charts from raw data. Use when the user asks for a chart, graph, or analytics visualization from JSON/data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Analytics charts (workflow)
|
|
7
|
+
|
|
8
|
+
When the user wants a chart or visualization from data, follow this workflow. Charts use **Recharts** via the **AnalyticsChart** component.
|
|
9
|
+
|
|
10
|
+
## Dependencies
|
|
11
|
+
|
|
12
|
+
Ensure the following package is installed in the project:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install recharts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 1. Interpret data type
|
|
19
|
+
|
|
20
|
+
- **Time-series**: data over time or ordered (dates, timestamps). Use a line chart. Raw shape often has date-like keys and a numeric value.
|
|
21
|
+
- **Categorical**: data by category (labels, segments). Use a bar chart. Raw shape often has a category name and a numeric value.
|
|
22
|
+
|
|
23
|
+
If the user says "over time", "trend", or uses date-like keys → time-series. If they say "by category", "by X", or use label-like keys → categorical.
|
|
24
|
+
|
|
25
|
+
## 2. Map data to chart shape
|
|
26
|
+
|
|
27
|
+
- **Time-series**: produce `[{ x: string, y: number }, ...]` (e.g. map `date`→`x`, `value`→`y`).
|
|
28
|
+
- **Categorical**: produce `[{ name: string, value: number }, ...]` (e.g. map `category`→`name`, `total`→`value`).
|
|
29
|
+
|
|
30
|
+
See [schema-mapping.md](docs/schema-mapping.md) for examples.
|
|
31
|
+
|
|
32
|
+
## 3. Choose theme
|
|
33
|
+
|
|
34
|
+
- **red**: declining, loss, negative trend.
|
|
35
|
+
- **green**: growth, gain, positive trend.
|
|
36
|
+
- **neutral**: default or mixed.
|
|
37
|
+
|
|
38
|
+
## 4. Generate and place the chart
|
|
39
|
+
|
|
40
|
+
- Use **AnalyticsChart** with `chartType` (`"time-series"` or `"categorical"`), `data` (mapped array), `theme`, and optional `title`. Wrap in **ChartContainer** if the app uses it for chart blocks.
|
|
41
|
+
- Insert the chart inside the existing app (e.g. main content or a route), not as the entire page.
|