@salesforce/afv-skills 1.5.0 → 1.5.2
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/README.md +16 -415
- package/package.json +5 -3
- package/skills/building-ui-bundle-app/SKILL.md +325 -0
- package/skills/building-ui-bundle-frontend/SKILL.md +122 -0
- package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/component.md +1 -1
- package/skills/creating-b2b-commerce-store/SKILL.md +169 -0
- package/skills/creating-b2b-commerce-store/references/store-vs-storefront.md +169 -0
- package/skills/deploying-ui-bundle/SKILL.md +77 -0
- package/skills/generating-apex/CREDITS.md +30 -0
- package/skills/generating-apex/SKILL.md +399 -0
- package/skills/generating-apex/assets/abstract.cls +132 -0
- package/skills/generating-apex/assets/batch.cls +125 -0
- package/skills/generating-apex/assets/domain.cls +102 -0
- package/skills/generating-apex/assets/dto.cls +108 -0
- package/skills/generating-apex/assets/exception.cls +51 -0
- package/skills/generating-apex/assets/interface.cls +25 -0
- package/skills/generating-apex/assets/invocable.cls +115 -0
- package/skills/generating-apex/assets/queueable.cls +92 -0
- package/skills/generating-apex/assets/rest-resource.cls +300 -0
- package/skills/generating-apex/assets/schedulable.cls +75 -0
- package/skills/generating-apex/assets/selector.cls +92 -0
- package/skills/generating-apex/assets/service.cls +69 -0
- package/skills/generating-apex/assets/trigger.cls +45 -0
- package/skills/generating-apex/assets/utility.cls +97 -0
- package/skills/generating-apex/references/AccountDeduplicationBatch.cls +148 -0
- package/skills/generating-apex/references/AccountSelector.cls +193 -0
- package/skills/generating-apex/references/AccountService.cls +201 -0
- package/skills/generating-apex-test/CREDITS.md +30 -0
- package/skills/generating-apex-test/SKILL.md +199 -0
- package/skills/generating-apex-test/assets/test-class-template.cls +93 -0
- package/skills/generating-apex-test/assets/test-data-factory-template.cls +111 -0
- package/skills/generating-apex-test/references/assertion-patterns.md +108 -0
- package/skills/generating-apex-test/references/async-testing.md +193 -0
- package/skills/generating-apex-test/references/mocking-patterns.md +220 -0
- package/skills/generating-apex-test/references/test-data-factory.md +75 -0
- package/skills/generating-experience-react-site/SKILL.md +20 -9
- package/skills/generating-experience-react-site/docs/configure-metadata-digital-experience.md +1 -1
- package/skills/generating-flexipage/SKILL.md +58 -60
- package/skills/generating-ui-bundle-features/SKILL.md +45 -0
- package/skills/generating-ui-bundle-metadata/SKILL.md +106 -0
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/SKILL.md +5 -5
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/constraints.md +2 -2
- package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/examples.md +1 -1
- package/skills/{implementing-webapp-file-upload → implementing-ui-bundle-file-upload}/SKILL.md +11 -11
- package/skills/searching-media/SKILL.md +342 -0
- package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data}/SKILL.md +52 -25
- package/skills/using-ui-bundle-salesforce-data/references/mutation-query-generation.md +140 -0
- package/skills/using-ui-bundle-salesforce-data/references/query-testing.md +78 -0
- package/skills/using-ui-bundle-salesforce-data/references/read-query-generation.md +307 -0
- package/skills/using-ui-bundle-salesforce-data/references/schema-introspection.md +53 -0
- package/skills/using-ui-bundle-salesforce-data/references/ui-bundle-integration.md +221 -0
- package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data/scripts}/graphql-search.sh +75 -23
- package/skills/building-webapp-data-visualization/SKILL.md +0 -72
- package/skills/building-webapp-data-visualization/implementation/bar-line-chart.md +0 -316
- package/skills/building-webapp-data-visualization/implementation/dashboard-layout.md +0 -189
- package/skills/building-webapp-data-visualization/implementation/donut-chart.md +0 -181
- package/skills/building-webapp-data-visualization/implementation/stat-card.md +0 -150
- package/skills/building-webapp-react-components/SKILL.md +0 -96
- package/skills/configuring-webapp-csp-trusted-sites/SKILL.md +0 -90
- package/skills/configuring-webapp-metadata/SKILL.md +0 -158
- package/skills/creating-webapp/SKILL.md +0 -140
- package/skills/deploying-webapp-to-salesforce/SKILL.md +0 -226
- package/skills/installing-webapp-features/SKILL.md +0 -210
- /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/header-footer.md +0 -0
- /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/page.md +0 -0
- /package/skills/{configuring-webapp-csp-trusted-sites/implementation/metadata-format.md → generating-ui-bundle-metadata/implementation/csp-metadata-format.md} +0 -0
- /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/style-tokens.md +0 -0
- /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/troubleshooting.md +0 -0
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail)
|
|
2
3
|
# graphql-search.sh — Look up one or more Salesforce entities in schema.graphql.
|
|
3
4
|
#
|
|
4
5
|
# Run from the SFDX project root (where schema.graphql lives):
|
|
5
|
-
# bash
|
|
6
|
-
# bash
|
|
6
|
+
# bash scripts/graphql-search.sh Account
|
|
7
|
+
# bash scripts/graphql-search.sh Account Contact Opportunity
|
|
7
8
|
#
|
|
8
9
|
# Pass a custom schema path with -s / --schema:
|
|
9
|
-
# bash
|
|
10
|
-
# bash
|
|
10
|
+
# bash scripts/graphql-search.sh -s /path/to/schema.graphql Account
|
|
11
|
+
# bash scripts/graphql-search.sh --schema ./other/schema.graphql Account Contact
|
|
11
12
|
#
|
|
12
13
|
# Output sections per entity:
|
|
13
|
-
# 1. Type definition
|
|
14
|
-
# 2. Filter options
|
|
15
|
-
# 3. Sort options
|
|
16
|
-
# 4. Create
|
|
17
|
-
# 5.
|
|
14
|
+
# 1. Type definition — all fields and relationships
|
|
15
|
+
# 2. Filter options — <Entity>_Filter input (for `where:`)
|
|
16
|
+
# 3. Sort options — <Entity>_OrderBy input (for `orderBy:`)
|
|
17
|
+
# 4. Create mutation wrapper — <Entity>CreateInput
|
|
18
|
+
# 5. Create mutation fields — <Entity>CreateRepresentation (for create mutations)
|
|
19
|
+
# 6. Update mutation wrapper — <Entity>UpdateInput
|
|
20
|
+
# 7. Update mutation fields — <Entity>UpdateRepresentation (for update mutations)
|
|
18
21
|
|
|
19
22
|
SCHEMA="./schema.graphql"
|
|
20
23
|
|
|
@@ -57,8 +60,21 @@ if [ ! -f "$SCHEMA" ]; then
|
|
|
57
60
|
echo "ERROR: schema.graphql not found at $SCHEMA"
|
|
58
61
|
echo " Make sure you are running from the SFDX project root, or pass the path explicitly:"
|
|
59
62
|
echo " bash $0 --schema <path/to/schema.graphql> <EntityName>"
|
|
60
|
-
echo " If the file is missing entirely, generate it from the
|
|
61
|
-
echo " cd force-app/main/default/
|
|
63
|
+
echo " If the file is missing entirely, generate it from the UI bundle dir:"
|
|
64
|
+
echo " cd force-app/main/default/uiBundles/<app-name> && npm run graphql:schema"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [ ! -r "$SCHEMA" ]; then
|
|
69
|
+
echo "ERROR: schema.graphql is not readable at $SCHEMA"
|
|
70
|
+
echo " Check file permissions: ls -la $SCHEMA"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if [ ! -s "$SCHEMA" ]; then
|
|
75
|
+
echo "ERROR: schema.graphql is empty at $SCHEMA"
|
|
76
|
+
echo " Regenerate it from the UI bundle dir:"
|
|
77
|
+
echo " cd force-app/main/default/uiBundles/<app-name> && npm run graphql:schema"
|
|
62
78
|
exit 1
|
|
63
79
|
fi
|
|
64
80
|
|
|
@@ -72,68 +88,104 @@ extract_block() {
|
|
|
72
88
|
local max_lines="$3"
|
|
73
89
|
|
|
74
90
|
local match
|
|
75
|
-
|
|
91
|
+
local grep_exit=0
|
|
92
|
+
match=$(grep -nE "$pattern" "$SCHEMA" | head -1) || grep_exit=$?
|
|
93
|
+
|
|
94
|
+
if [ "$grep_exit" -eq 2 ]; then
|
|
95
|
+
echo " ERROR: grep failed on pattern: $pattern" >&2
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
76
98
|
|
|
77
99
|
if [ -z "$match" ]; then
|
|
78
100
|
echo " (not found: $pattern)"
|
|
79
|
-
return
|
|
101
|
+
return 3
|
|
80
102
|
fi
|
|
81
103
|
|
|
82
104
|
echo "### $label"
|
|
83
105
|
grep -E "$pattern" "$SCHEMA" -A "$max_lines" | \
|
|
84
106
|
awk '/^\}$/{print; exit} {print}' | \
|
|
85
|
-
head -n "$max_lines"
|
|
107
|
+
head -n "$max_lines" || true
|
|
86
108
|
echo ""
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
# ── Main loop ────────────────────────────────────────────────────────────────
|
|
90
112
|
|
|
91
113
|
for ENTITY in "$@"; do
|
|
114
|
+
# Validate entity name: must be a valid PascalCase identifier (letters, digits, underscores)
|
|
115
|
+
if [[ ! "$ENTITY" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
|
116
|
+
echo "ERROR: Invalid entity name: '$ENTITY'"
|
|
117
|
+
echo " Entity names must start with a letter or underscore, followed by letters, digits, or underscores."
|
|
118
|
+
echo " Examples: Account, My_Custom_Object__c"
|
|
119
|
+
continue
|
|
120
|
+
fi
|
|
121
|
+
|
|
92
122
|
echo ""
|
|
93
123
|
echo "======================================================================"
|
|
94
124
|
echo " SCHEMA LOOKUP: $ENTITY"
|
|
95
125
|
echo "======================================================================"
|
|
96
126
|
echo ""
|
|
97
127
|
|
|
128
|
+
found=0
|
|
129
|
+
|
|
130
|
+
# Helper: call extract_block, track matches, surface errors
|
|
131
|
+
try_extract() {
|
|
132
|
+
local rc=0
|
|
133
|
+
extract_block "$@" || rc=$?
|
|
134
|
+
if [ "$rc" -eq 0 ]; then
|
|
135
|
+
found=$((found + 1))
|
|
136
|
+
elif [ "$rc" -eq 1 ]; then
|
|
137
|
+
echo " Aborting lookup for '$ENTITY' due to grep error" >&2
|
|
138
|
+
fi
|
|
139
|
+
# rc=3 is not-found — continue silently (already printed by extract_block)
|
|
140
|
+
}
|
|
141
|
+
|
|
98
142
|
# 1. Type definition — all fields and relationships
|
|
99
|
-
|
|
143
|
+
try_extract \
|
|
100
144
|
"Type definition — fields and relationships" \
|
|
101
145
|
"^type ${ENTITY} implements Record" \
|
|
102
146
|
200
|
|
103
147
|
|
|
104
148
|
# 2. Filter input — used in `where:` arguments
|
|
105
|
-
|
|
149
|
+
try_extract \
|
|
106
150
|
"Filter options — use in where: { ... }" \
|
|
107
151
|
"^input ${ENTITY}_Filter" \
|
|
108
152
|
100
|
|
109
153
|
|
|
110
154
|
# 3. OrderBy input — used in `orderBy:` arguments
|
|
111
|
-
|
|
155
|
+
try_extract \
|
|
112
156
|
"Sort options — use in orderBy: { ... }" \
|
|
113
157
|
"^input ${ENTITY}_OrderBy" \
|
|
114
158
|
60
|
|
115
159
|
|
|
116
|
-
# 4. Create mutation
|
|
117
|
-
|
|
160
|
+
# 4. Create mutation wrapper
|
|
161
|
+
try_extract \
|
|
118
162
|
"Create mutation wrapper — ${ENTITY}CreateInput" \
|
|
119
163
|
"^input ${ENTITY}CreateInput" \
|
|
120
164
|
10
|
|
121
165
|
|
|
122
|
-
|
|
166
|
+
# 5. Create mutation fields
|
|
167
|
+
try_extract \
|
|
123
168
|
"Create mutation fields — ${ENTITY}CreateRepresentation" \
|
|
124
169
|
"^input ${ENTITY}CreateRepresentation" \
|
|
125
170
|
100
|
|
126
171
|
|
|
127
|
-
#
|
|
128
|
-
|
|
172
|
+
# 6. Update mutation wrapper
|
|
173
|
+
try_extract \
|
|
129
174
|
"Update mutation wrapper — ${ENTITY}UpdateInput" \
|
|
130
175
|
"^input ${ENTITY}UpdateInput" \
|
|
131
176
|
10
|
|
132
177
|
|
|
133
|
-
|
|
178
|
+
# 7. Update mutation fields
|
|
179
|
+
try_extract \
|
|
134
180
|
"Update mutation fields — ${ENTITY}UpdateRepresentation" \
|
|
135
181
|
"^input ${ENTITY}UpdateRepresentation" \
|
|
136
182
|
100
|
|
137
183
|
|
|
184
|
+
if [ "$found" -eq 0 ]; then
|
|
185
|
+
echo "WARNING: No schema entries found for '$ENTITY'."
|
|
186
|
+
echo " - Names are PascalCase (e.g., 'Account' not 'account')"
|
|
187
|
+
echo " - Custom objects may need deployment first"
|
|
188
|
+
fi
|
|
189
|
+
|
|
138
190
|
echo ""
|
|
139
191
|
done
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: building-webapp-data-visualization
|
|
3
|
-
description: "Adds data visualization components (charts, stat cards, KPI metrics) to React pages using Recharts. Use when the user asks to add a chart, graph, donut chart, pie chart, bar chart, stat card, KPI metric, dashboard visualization, or analytics component to the web application."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Data Visualization
|
|
7
|
-
|
|
8
|
-
## When to Use
|
|
9
|
-
|
|
10
|
-
Use this skill when:
|
|
11
|
-
- Adding charts (donut, pie, bar, line, area) to a dashboard or analytics page
|
|
12
|
-
- Displaying KPI/metric stat cards with trend indicators
|
|
13
|
-
- Building a dashboard layout with mixed chart types and summary cards
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Step 1 — Determine the visualization type
|
|
18
|
-
|
|
19
|
-
Identify what the user needs:
|
|
20
|
-
|
|
21
|
-
- **Donut / pie chart** — categorical breakdown (e.g. issue types, status distribution)
|
|
22
|
-
- **Bar chart** — comparison across categories or time periods
|
|
23
|
-
- **Line / area chart** — trends over time
|
|
24
|
-
- **Stat card** — single KPI metric with optional trend indicator
|
|
25
|
-
- **Combined dashboard** — stat cards + one or more charts
|
|
26
|
-
|
|
27
|
-
If unclear, ask:
|
|
28
|
-
|
|
29
|
-
> "What data should the chart display, and would a donut chart, bar chart, line chart, or stat cards work best?"
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Step 2 — Install dependencies
|
|
34
|
-
|
|
35
|
-
All chart types in this skill use **recharts**. Install once from the web app directory:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm install recharts
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Recharts is built on D3 and provides declarative React components. No additional CSS is needed.
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
## Step 3 — Choose implementation path
|
|
46
|
-
|
|
47
|
-
Read the corresponding guide:
|
|
48
|
-
|
|
49
|
-
- **Bar chart** — read `implementation/bar-line-chart.md` (categorical data)
|
|
50
|
-
- **Line / area chart** — read `implementation/bar-line-chart.md` (time-series data)
|
|
51
|
-
- **Donut / pie chart** — read `implementation/donut-chart.md`
|
|
52
|
-
- **Stat card with trend** — read `implementation/stat-card.md`
|
|
53
|
-
- **Dashboard layout** — read `implementation/dashboard-layout.md`
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Verification
|
|
58
|
-
|
|
59
|
-
Before completing:
|
|
60
|
-
|
|
61
|
-
1. Chart renders with correct data and colors.
|
|
62
|
-
2. Chart is responsive (resizes with container).
|
|
63
|
-
3. Legend labels match the data categories.
|
|
64
|
-
4. Stat card trends display correct positive/negative indicators.
|
|
65
|
-
5. Run from the web app directory:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
cd force-app/main/default/webapplications/<appName> && npm run lint && npm run build
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
- **Lint:** MUST result in 0 errors.
|
|
72
|
-
- **Build:** MUST succeed.
|
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
# Bar & Line / Area Chart — Implementation Guide
|
|
2
|
-
|
|
3
|
-
Requires **recharts** (install from the web app directory; see SKILL.md Step 2).
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Data shapes
|
|
8
|
-
|
|
9
|
-
### Time-series (line / area chart)
|
|
10
|
-
|
|
11
|
-
Use when data represents a trend over time or ordered sequence.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
interface TimeSeriesDataPoint {
|
|
15
|
-
x: string; // date or label on the x-axis
|
|
16
|
-
y: number; // numeric value
|
|
17
|
-
}
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Map raw fields to this shape: e.g. `date` → `x`, `revenue` → `y`.
|
|
21
|
-
|
|
22
|
-
### Categorical (bar chart)
|
|
23
|
-
|
|
24
|
-
Use when data compares discrete categories.
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
interface CategoricalDataPoint {
|
|
28
|
-
name: string; // category label
|
|
29
|
-
value: number; // numeric value
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Map raw fields to this shape: e.g. `product` → `name`, `sales` → `value`.
|
|
34
|
-
|
|
35
|
-
### How to decide
|
|
36
|
-
|
|
37
|
-
| Signal | Type |
|
|
38
|
-
|--------|------|
|
|
39
|
-
| "over time", "trend", date-like keys | Time-series → line chart |
|
|
40
|
-
| "by category", "by X", label-like keys | Categorical → bar chart |
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Theme colors
|
|
45
|
-
|
|
46
|
-
Pick a theme based on the data's sentiment:
|
|
47
|
-
|
|
48
|
-
| Theme | Stroke / Fill | When to use |
|
|
49
|
-
|-------|---------------|-------------|
|
|
50
|
-
| `green` | `#22c55e` | Growth, gain, positive trend |
|
|
51
|
-
| `red` | `#ef4444` | Decline, loss, negative trend |
|
|
52
|
-
| `neutral` | `#6366f1` | Default or mixed data |
|
|
53
|
-
|
|
54
|
-
Define colors as constants — do not use inline hex values.
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
const THEME_COLORS = {
|
|
58
|
-
red: "#ef4444",
|
|
59
|
-
green: "#22c55e",
|
|
60
|
-
neutral: "#6366f1",
|
|
61
|
-
} as const;
|
|
62
|
-
|
|
63
|
-
type ChartTheme = keyof typeof THEME_COLORS;
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Line chart component
|
|
69
|
-
|
|
70
|
-
Create at `components/LineChart.tsx` (or colocate with the page):
|
|
71
|
-
|
|
72
|
-
```tsx
|
|
73
|
-
import React from "react";
|
|
74
|
-
import {
|
|
75
|
-
LineChart as RechartsLineChart,
|
|
76
|
-
Line,
|
|
77
|
-
XAxis,
|
|
78
|
-
YAxis,
|
|
79
|
-
CartesianGrid,
|
|
80
|
-
Tooltip,
|
|
81
|
-
Legend,
|
|
82
|
-
ResponsiveContainer,
|
|
83
|
-
} from "recharts";
|
|
84
|
-
|
|
85
|
-
const THEME_COLORS = {
|
|
86
|
-
red: "#ef4444",
|
|
87
|
-
green: "#22c55e",
|
|
88
|
-
neutral: "#6366f1",
|
|
89
|
-
} as const;
|
|
90
|
-
|
|
91
|
-
type ChartTheme = keyof typeof THEME_COLORS;
|
|
92
|
-
|
|
93
|
-
interface TimeSeriesDataPoint {
|
|
94
|
-
x: string;
|
|
95
|
-
y: number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
interface TimeSeriesChartProps {
|
|
99
|
-
data: TimeSeriesDataPoint[];
|
|
100
|
-
theme?: ChartTheme;
|
|
101
|
-
title?: string;
|
|
102
|
-
className?: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function TimeSeriesChart({
|
|
106
|
-
data,
|
|
107
|
-
theme = "neutral",
|
|
108
|
-
title,
|
|
109
|
-
className = "",
|
|
110
|
-
}: TimeSeriesChartProps) {
|
|
111
|
-
if (data.length === 0) {
|
|
112
|
-
return <p className="text-muted-foreground text-center py-8">No data to display</p>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const color = THEME_COLORS[theme];
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<div className={className}>
|
|
119
|
-
{title && (
|
|
120
|
-
<h3 className="text-sm font-medium text-primary mb-2 uppercase tracking-wide">
|
|
121
|
-
{title}
|
|
122
|
-
</h3>
|
|
123
|
-
)}
|
|
124
|
-
<ResponsiveContainer width="100%" height={300}>
|
|
125
|
-
<RechartsLineChart data={data}>
|
|
126
|
-
<CartesianGrid strokeDasharray="3 3" />
|
|
127
|
-
<XAxis dataKey="x" />
|
|
128
|
-
<YAxis />
|
|
129
|
-
<Tooltip />
|
|
130
|
-
<Legend />
|
|
131
|
-
<Line type="monotone" dataKey="y" stroke={color} strokeWidth={2} dot={false} />
|
|
132
|
-
</RechartsLineChart>
|
|
133
|
-
</ResponsiveContainer>
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Bar chart component
|
|
142
|
-
|
|
143
|
-
Create at `components/BarChart.tsx` (or colocate with the page):
|
|
144
|
-
|
|
145
|
-
```tsx
|
|
146
|
-
import React from "react";
|
|
147
|
-
import {
|
|
148
|
-
BarChart as RechartsBarChart,
|
|
149
|
-
Bar,
|
|
150
|
-
XAxis,
|
|
151
|
-
YAxis,
|
|
152
|
-
CartesianGrid,
|
|
153
|
-
Tooltip,
|
|
154
|
-
Legend,
|
|
155
|
-
ResponsiveContainer,
|
|
156
|
-
} from "recharts";
|
|
157
|
-
|
|
158
|
-
const THEME_COLORS = {
|
|
159
|
-
red: "#ef4444",
|
|
160
|
-
green: "#22c55e",
|
|
161
|
-
neutral: "#6366f1",
|
|
162
|
-
} as const;
|
|
163
|
-
|
|
164
|
-
type ChartTheme = keyof typeof THEME_COLORS;
|
|
165
|
-
|
|
166
|
-
interface CategoricalDataPoint {
|
|
167
|
-
name: string;
|
|
168
|
-
value: number;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
interface CategoricalChartProps {
|
|
172
|
-
data: CategoricalDataPoint[];
|
|
173
|
-
theme?: ChartTheme;
|
|
174
|
-
title?: string;
|
|
175
|
-
className?: string;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function CategoricalChart({
|
|
179
|
-
data,
|
|
180
|
-
theme = "neutral",
|
|
181
|
-
title,
|
|
182
|
-
className = "",
|
|
183
|
-
}: CategoricalChartProps) {
|
|
184
|
-
if (data.length === 0) {
|
|
185
|
-
return <p className="text-muted-foreground text-center py-8">No data to display</p>;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const color = THEME_COLORS[theme];
|
|
189
|
-
|
|
190
|
-
return (
|
|
191
|
-
<div className={className}>
|
|
192
|
-
{title && (
|
|
193
|
-
<h3 className="text-sm font-medium text-primary mb-2 uppercase tracking-wide">
|
|
194
|
-
{title}
|
|
195
|
-
</h3>
|
|
196
|
-
)}
|
|
197
|
-
<ResponsiveContainer width="100%" height={300}>
|
|
198
|
-
<RechartsBarChart data={data}>
|
|
199
|
-
<CartesianGrid strokeDasharray="3 3" />
|
|
200
|
-
<XAxis dataKey="name" />
|
|
201
|
-
<YAxis />
|
|
202
|
-
<Tooltip />
|
|
203
|
-
<Legend />
|
|
204
|
-
<Bar dataKey="value" fill={color} radius={[4, 4, 0, 0]} />
|
|
205
|
-
</RechartsBarChart>
|
|
206
|
-
</ResponsiveContainer>
|
|
207
|
-
</div>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## Area chart variant
|
|
215
|
-
|
|
216
|
-
For a filled area chart (useful for volume-over-time), swap `Line` for `Area`:
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
|
|
220
|
-
|
|
221
|
-
<ResponsiveContainer width="100%" height={300}>
|
|
222
|
-
<AreaChart data={data}>
|
|
223
|
-
<CartesianGrid strokeDasharray="3 3" />
|
|
224
|
-
<XAxis dataKey="x" />
|
|
225
|
-
<YAxis />
|
|
226
|
-
<Tooltip />
|
|
227
|
-
<Area type="monotone" dataKey="y" stroke={color} fill={color} fillOpacity={0.2} />
|
|
228
|
-
</AreaChart>
|
|
229
|
-
</ResponsiveContainer>
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## Chart container wrapper
|
|
235
|
-
|
|
236
|
-
Wrap any chart in a styled card for consistent spacing:
|
|
237
|
-
|
|
238
|
-
```tsx
|
|
239
|
-
import { Card } from "@/components/ui/card";
|
|
240
|
-
|
|
241
|
-
interface ChartContainerProps {
|
|
242
|
-
children: React.ReactNode;
|
|
243
|
-
className?: string;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export function ChartContainer({ children, className = "" }: ChartContainerProps) {
|
|
247
|
-
return (
|
|
248
|
-
<Card className={`p-4 border-gray-200 shadow-sm ${className}`}>
|
|
249
|
-
{children}
|
|
250
|
-
</Card>
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
Usage:
|
|
256
|
-
|
|
257
|
-
```tsx
|
|
258
|
-
<ChartContainer>
|
|
259
|
-
<TimeSeriesChart data={monthlyData} theme="green" title="Monthly Revenue" />
|
|
260
|
-
</ChartContainer>
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
---
|
|
264
|
-
|
|
265
|
-
## Preparing raw data
|
|
266
|
-
|
|
267
|
-
Map API responses to the expected shape before passing to the chart:
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
const timeSeriesData = useMemo(
|
|
271
|
-
() => apiRecords.map((r) => ({ x: r.date, y: r.revenue })),
|
|
272
|
-
[apiRecords],
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
const categoricalData = useMemo(
|
|
276
|
-
() => apiRecords.map((r) => ({ name: r.product, value: r.sales })),
|
|
277
|
-
[apiRecords],
|
|
278
|
-
);
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## Key Recharts concepts
|
|
284
|
-
|
|
285
|
-
| Component | Purpose |
|
|
286
|
-
|-----------|---------|
|
|
287
|
-
| `ResponsiveContainer` | Wraps chart to fill parent width |
|
|
288
|
-
| `CartesianGrid` | Background grid lines |
|
|
289
|
-
| `XAxis` / `YAxis` | Axis labels; `dataKey` maps to the data field |
|
|
290
|
-
| `Tooltip` | Hover info |
|
|
291
|
-
| `Legend` | Series labels |
|
|
292
|
-
| `Line` | Line series; `type="monotone"` for smooth curves |
|
|
293
|
-
| `Bar` | Bar series; `radius` rounds top corners |
|
|
294
|
-
| `Area` | Filled area; `fillOpacity` controls transparency |
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## Accessibility
|
|
299
|
-
|
|
300
|
-
- Always include a text legend (not just colors).
|
|
301
|
-
- Chart should be wrapped in a section with a visible heading.
|
|
302
|
-
- For critical data, provide a text summary or table alternative.
|
|
303
|
-
- Use sufficient color contrast between the chart stroke/fill and background.
|
|
304
|
-
- Consider `prefers-reduced-motion` for chart animations.
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
## Common mistakes
|
|
309
|
-
|
|
310
|
-
| Mistake | Fix |
|
|
311
|
-
|---------|-----|
|
|
312
|
-
| Missing `ResponsiveContainer` | Chart won't resize; always wrap |
|
|
313
|
-
| Fixed width/height on chart | Let `ResponsiveContainer` control sizing |
|
|
314
|
-
| No empty-data handling | Show "No data" message when `data.length === 0` |
|
|
315
|
-
| Inline colors | Extract to `THEME_COLORS` constant |
|
|
316
|
-
| Using raw Recharts for every chart type | Use `DonutChart` (see `donut-chart.md`) for pie/donut |
|