@schandlergarcia/sf-web-components 1.9.41 → 1.9.42
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/INSTALL.md +200 -0
- package/package.json +3 -1
- package/src/templates/config/routes.tsx.template +33 -0
- package/src/templates/pages/BlankDashboard.tsx.template +15 -0
- package/src/templates/pages/Home.tsx.template +58 -0
- package/src/templates/pages/NotFound.tsx.template +19 -0
- package/src/templates/pages/Search.tsx.template +13 -0
- package/src/templates/workspace/CommandCenter.tsx.template +15 -0
- package/src/types/conversation.ts +48 -0
package/INSTALL.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Installation Guide
|
|
2
|
+
|
|
3
|
+
Quick start guide for installing `@schandlergarcia/sf-web-components` in your Salesforce web application.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Salesforce web application created with `sf webapp generate --template react-ts`
|
|
8
|
+
- Node.js 18+ and npm installed
|
|
9
|
+
- Tailwind CSS v4 configured in your project
|
|
10
|
+
|
|
11
|
+
## Installation Steps
|
|
12
|
+
|
|
13
|
+
### 1. Navigate to Your Web App
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd force-app/main/default/webapplications/YOUR_APP_NAME
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Install the Package
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @schandlergarcia/sf-web-components@latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The postinstall script runs automatically and:
|
|
26
|
+
- ✅ Copies 102+ component files to `src/components/library/`
|
|
27
|
+
- ✅ Installs page templates to `src/pages/`
|
|
28
|
+
- ✅ Installs workspace files to `src/components/workspace/`
|
|
29
|
+
- ✅ Copies AI assistant rules to project root `.a4drules/`
|
|
30
|
+
- ✅ Adds `reset:command-center` and `validate:dashboard` scripts
|
|
31
|
+
- ✅ Updates imports in existing files
|
|
32
|
+
|
|
33
|
+
### 3. Install Type Definitions
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install --save-dev @types/d3 @types/topojson-client
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 4. Verify Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm run build
|
|
43
|
+
npm run dev
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Visit `http://localhost:5173` - you should see the CommandCenter with BlankDashboard.
|
|
47
|
+
|
|
48
|
+
## What You Get
|
|
49
|
+
|
|
50
|
+
After installation:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
src/
|
|
54
|
+
├── components/
|
|
55
|
+
│ ├── library/ # 102+ UI components
|
|
56
|
+
│ │ ├── ui/ # Buttons, inputs, cards, alerts
|
|
57
|
+
│ │ ├── cards/ # MetricCard, ChartCard, TableCard, etc.
|
|
58
|
+
│ │ ├── charts/ # D3Chart, GeoMap
|
|
59
|
+
│ │ ├── forms/ # FormRenderer, FormField
|
|
60
|
+
│ │ ├── filters/ # SearchFilter, SelectFilter
|
|
61
|
+
│ │ └── ...
|
|
62
|
+
│ └── workspace/
|
|
63
|
+
│ └── CommandCenter.tsx # Dashboard wrapper
|
|
64
|
+
├── pages/
|
|
65
|
+
│ ├── Home.tsx # Renders CommandCenter
|
|
66
|
+
│ ├── BlankDashboard.tsx # Empty template
|
|
67
|
+
│ ├── Search.tsx # Search interface
|
|
68
|
+
│ └── NotFound.tsx # 404 page
|
|
69
|
+
├── lib/
|
|
70
|
+
│ └── utils.ts # Utility functions
|
|
71
|
+
└── types/
|
|
72
|
+
└── conversation.ts # AgentforceConversationClient types
|
|
73
|
+
|
|
74
|
+
package.json # Added scripts:
|
|
75
|
+
# - reset:command-center
|
|
76
|
+
# - validate:dashboard
|
|
77
|
+
|
|
78
|
+
.a4drules/ # AI assistant rules (at project root)
|
|
79
|
+
├── skills/
|
|
80
|
+
│ └── command-center-builder/
|
|
81
|
+
│ └── SKILL.md
|
|
82
|
+
└── features/
|
|
83
|
+
└── command-center-dashboard-rule.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Next Steps
|
|
87
|
+
|
|
88
|
+
### Option 1: Build a Dashboard with AI
|
|
89
|
+
|
|
90
|
+
Have an AI assistant (Agentforce, Claude, etc.) build your dashboard:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Build me a Sales Dashboard with KPI metrics, revenue chart, and top accounts table
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The AI will:
|
|
97
|
+
1. Load the command-center-builder skill
|
|
98
|
+
2. Create your dashboard file in `src/pages/`
|
|
99
|
+
3. Wire it to CommandCenter
|
|
100
|
+
4. Update Home.tsx
|
|
101
|
+
5. Validate with `npm run validate:dashboard`
|
|
102
|
+
|
|
103
|
+
### Option 2: Build Manually
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# 1. Create dashboard file
|
|
107
|
+
cat > src/pages/MyDashboard.tsx << 'EOF'
|
|
108
|
+
import { MetricCard, ChartCard } from '@/components/library';
|
|
109
|
+
|
|
110
|
+
export default function MyDashboard() {
|
|
111
|
+
return (
|
|
112
|
+
<div className="space-y-6 p-6">
|
|
113
|
+
<div className="grid grid-cols-3 gap-6">
|
|
114
|
+
<MetricCard title="Revenue" value="$1.2M" change="+12%" changeType="positive" />
|
|
115
|
+
<MetricCard title="Users" value={1284} change="+8%" changeType="positive" />
|
|
116
|
+
<MetricCard title="Conversion" value="3.2%" change="-0.5%" changeType="negative" />
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
EOF
|
|
122
|
+
|
|
123
|
+
# 2. Wire to CommandCenter
|
|
124
|
+
# Edit src/components/workspace/CommandCenter.tsx
|
|
125
|
+
# Change: import BlankDashboard from "../../pages/BlankDashboard";
|
|
126
|
+
# To: import MyDashboard from "../../pages/MyDashboard";
|
|
127
|
+
# Change: <BlankDashboard />
|
|
128
|
+
# To: <MyDashboard />
|
|
129
|
+
|
|
130
|
+
# 3. Update Home
|
|
131
|
+
# Home.tsx should already render CommandCenter
|
|
132
|
+
|
|
133
|
+
# 4. Validate
|
|
134
|
+
npm run validate:dashboard
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Useful Commands
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Development
|
|
141
|
+
npm run dev # Start dev server
|
|
142
|
+
npm run build # Build for production
|
|
143
|
+
|
|
144
|
+
# Dashboard Management
|
|
145
|
+
npm run reset:command-center # Reset to blank state
|
|
146
|
+
npm run validate:dashboard # Check dashboard rules
|
|
147
|
+
|
|
148
|
+
# GraphQL (if using Salesforce data)
|
|
149
|
+
npm run graphql:schema # Fetch schema
|
|
150
|
+
npm run graphql:codegen # Generate types
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Troubleshooting
|
|
154
|
+
|
|
155
|
+
### Postinstall didn't run
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
node node_modules/@schandlergarcia/sf-web-components/scripts/postinstall.mjs
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Build errors about missing D3 types
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm install --save-dev @types/d3 @types/topojson-client
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Home page shows blank/wrong content
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Re-run postinstall to restore templates
|
|
171
|
+
node node_modules/@schandlergarcia/sf-web-components/scripts/postinstall.mjs
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### AI assistant not following rules
|
|
175
|
+
|
|
176
|
+
Check that `.a4drules/` exists at your **project root** (not in the webapp directory):
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# From webapp directory
|
|
180
|
+
ls ../../../../../.a4drules/skills/command-center-builder/SKILL.md
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
If missing, re-run postinstall:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
node node_modules/@schandlergarcia/sf-web-components/scripts/postinstall.mjs
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Support
|
|
190
|
+
|
|
191
|
+
- **Documentation:** [README.md](./README.md)
|
|
192
|
+
- **Issues:** [GitHub Issues](https://github.com/schandlergarcia/sf-web-components/issues)
|
|
193
|
+
- **Package:** [npm](https://www.npmjs.com/package/@schandlergarcia/sf-web-components)
|
|
194
|
+
|
|
195
|
+
## Next Steps
|
|
196
|
+
|
|
197
|
+
- Read the [Component Documentation](./README.md#using-the-dashboard-framework)
|
|
198
|
+
- Check [AI Assistant Rules](./.a4drules/skills/command-center-builder/SKILL.md) (after install)
|
|
199
|
+
- Browse example pages in `src/pages/`
|
|
200
|
+
- Explore component library in `src/components/library/`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schandlergarcia/sf-web-components",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.42",
|
|
4
4
|
"description": "Reusable Salesforce web components library with Tailwind CSS v4 and shadcn/ui",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
"dist",
|
|
29
29
|
"scripts",
|
|
30
30
|
"data",
|
|
31
|
+
"src/templates",
|
|
31
32
|
"src/components",
|
|
32
33
|
"src/lib",
|
|
33
34
|
"src/types",
|
|
34
35
|
"README.md",
|
|
36
|
+
"INSTALL.md",
|
|
35
37
|
".a4drules"
|
|
36
38
|
],
|
|
37
39
|
"scripts": {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RouteObject } from 'react-router';
|
|
2
|
+
import AppLayout from './appLayout';
|
|
3
|
+
import Home from './pages/Home';
|
|
4
|
+
import AccountSearch from './features/object-search/__examples__/pages/AccountSearch';
|
|
5
|
+
import AccountObjectDetailPage from './features/object-search/__examples__/pages/AccountObjectDetailPage';
|
|
6
|
+
import NotFound from './pages/NotFound';
|
|
7
|
+
|
|
8
|
+
export const routes: RouteObject[] = [
|
|
9
|
+
{
|
|
10
|
+
path: "/",
|
|
11
|
+
element: <AppLayout />,
|
|
12
|
+
children: [
|
|
13
|
+
{
|
|
14
|
+
index: true,
|
|
15
|
+
element: <Home />,
|
|
16
|
+
handle: { showInNavigation: true, label: "Home" }
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
path: 'accounts',
|
|
20
|
+
element: <AccountSearch />,
|
|
21
|
+
handle: { showInNavigation: true, label: "Accounts" }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: 'accounts/:recordId',
|
|
25
|
+
element: <AccountObjectDetailPage />
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: '*',
|
|
29
|
+
element: <NotFound />
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RocketLaunchIcon } from "@heroicons/react/24/outline";
|
|
2
|
+
import { EmptyState } from "@/components/library";
|
|
3
|
+
|
|
4
|
+
export default function BlankDashboard() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="flex min-h-screen items-center justify-center bg-slate-50 dark:bg-slate-950 transition-colors">
|
|
7
|
+
<EmptyState
|
|
8
|
+
size="lg"
|
|
9
|
+
icon={<RocketLaunchIcon className="h-14 w-14" />}
|
|
10
|
+
heading="Bespoke App Template"
|
|
11
|
+
body="Component library loaded and ready to go."
|
|
12
|
+
/>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { useNavigate } from "react-router";
|
|
3
|
+
import UIInput from '@/components/library/ui/UIInput';
|
|
4
|
+
import UIButton from '@/components/library/ui/UIButton';
|
|
5
|
+
import { Search } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export default function HomePage() {
|
|
8
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
9
|
+
const navigate = useNavigate();
|
|
10
|
+
|
|
11
|
+
const handleSearch = () => {
|
|
12
|
+
const trimmed = searchQuery.trim();
|
|
13
|
+
if (trimmed) {
|
|
14
|
+
navigate(`/accounts?search=${encodeURIComponent(trimmed)}`);
|
|
15
|
+
} else {
|
|
16
|
+
navigate('/accounts');
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
21
|
+
if (e.key === "Enter") {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
handleSearch();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex min-h-[80vh] items-center justify-center px-4 sm:px-6 lg:px-8 bg-slate-50 dark:bg-slate-950 transition-colors">
|
|
29
|
+
<div className="w-full max-w-4xl">
|
|
30
|
+
<div className="text-center mb-8">
|
|
31
|
+
<h1 className="text-4xl font-bold text-slate-900 dark:text-slate-50 mb-4">Search</h1>
|
|
32
|
+
<p className="text-lg text-slate-600 dark:text-slate-300">Find records across your organization.</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="flex flex-col gap-4">
|
|
35
|
+
<div className="relative">
|
|
36
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-slate-400" />
|
|
37
|
+
<UIInput
|
|
38
|
+
type="text"
|
|
39
|
+
value={searchQuery}
|
|
40
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
41
|
+
onKeyDown={handleKeyDown}
|
|
42
|
+
placeholder="Search..."
|
|
43
|
+
className="pl-10 w-full"
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="flex gap-3 justify-center">
|
|
47
|
+
<UIButton onClick={handleSearch} variant="primary">
|
|
48
|
+
Search
|
|
49
|
+
</UIButton>
|
|
50
|
+
<UIButton onClick={() => navigate('/accounts')} variant="secondary">
|
|
51
|
+
Browse All
|
|
52
|
+
</UIButton>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useNavigate } from "react-router";
|
|
2
|
+
import UIButton from '@/components/library/ui/UIButton';
|
|
3
|
+
|
|
4
|
+
export default function NotFound() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex min-h-[80vh] items-center justify-center px-4 sm:px-6 lg:px-8">
|
|
9
|
+
<div className="w-full max-w-2xl text-center">
|
|
10
|
+
<h1 className="text-6xl font-bold text-slate-900 dark:text-slate-50 mb-4">404</h1>
|
|
11
|
+
<h2 className="text-2xl font-semibold text-slate-700 dark:text-slate-300 mb-4">Page Not Found</h2>
|
|
12
|
+
<p className="text-lg text-slate-600 dark:text-slate-400 mb-8">
|
|
13
|
+
The page you're looking for doesn't exist.
|
|
14
|
+
</p>
|
|
15
|
+
<UIButton onClick={() => navigate("/")}>Go Home</UIButton>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default function Search() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="flex min-h-[80vh] items-center justify-center px-4 sm:px-6 lg:px-8 bg-slate-50 dark:bg-slate-950 transition-colors">
|
|
4
|
+
<div className="w-full max-w-4xl">
|
|
5
|
+
<div className="text-center mb-8">
|
|
6
|
+
<h1 className="text-4xl font-bold text-slate-900 dark:text-slate-50 mb-4">Search</h1>
|
|
7
|
+
<p className="text-lg text-slate-600 dark:text-slate-300">Find records across your organization.</p>
|
|
8
|
+
</div>
|
|
9
|
+
<p className="text-center text-slate-500 dark:text-slate-400">Search functionality coming soon...</p>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import AppThemeProvider from "@/components/library/theme/AppThemeProvider";
|
|
2
|
+
import DataModeProvider from "@/components/library/data/DataModeProvider";
|
|
3
|
+
import { Toaster } from "sonner";
|
|
4
|
+
import BlankDashboard from "../pages/BlankDashboard";
|
|
5
|
+
|
|
6
|
+
export default function CommandCenter() {
|
|
7
|
+
return (
|
|
8
|
+
<AppThemeProvider initialMode="light">
|
|
9
|
+
<DataModeProvider initialMode="sample">
|
|
10
|
+
<BlankDashboard />
|
|
11
|
+
<Toaster position="bottom-right" />
|
|
12
|
+
</DataModeProvider>
|
|
13
|
+
</AppThemeProvider>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Agentforce Conversation Client
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ResolvedEmbedOptions {
|
|
6
|
+
salesforceOrigin?: string;
|
|
7
|
+
frontdoorUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface RenderingConfig {
|
|
11
|
+
mode?: 'inline' | 'modal' | 'sidebar' | 'floating';
|
|
12
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
13
|
+
width?: string | number;
|
|
14
|
+
height?: string | number;
|
|
15
|
+
headerEnabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AgentforceClientConfig {
|
|
19
|
+
agentId?: string;
|
|
20
|
+
devName?: string;
|
|
21
|
+
renderingConfig?: RenderingConfig;
|
|
22
|
+
locale?: string;
|
|
23
|
+
styleTokens?: Record<string, string>;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Props interface for AgentforceConversationClient component.
|
|
29
|
+
* Uses flat props that get normalized to AgentforceClientConfig internally.
|
|
30
|
+
*/
|
|
31
|
+
export interface AgentforceConversationClientProps {
|
|
32
|
+
/** Agent ID to load */
|
|
33
|
+
agentId?: string;
|
|
34
|
+
/** Whether to render inline (true) or floating (false) */
|
|
35
|
+
inline?: boolean;
|
|
36
|
+
/** Whether to show the header in the conversation UI */
|
|
37
|
+
headerEnabled?: boolean;
|
|
38
|
+
/** Width of the conversation UI */
|
|
39
|
+
width?: string | number;
|
|
40
|
+
/** Height of the conversation UI */
|
|
41
|
+
height?: string | number;
|
|
42
|
+
/** Custom style tokens for theming */
|
|
43
|
+
styleTokens?: Record<string, string>;
|
|
44
|
+
/** Salesforce origin URL */
|
|
45
|
+
salesforceOrigin?: string;
|
|
46
|
+
/** Frontdoor URL for authentication */
|
|
47
|
+
frontdoorUrl?: string;
|
|
48
|
+
}
|