@schandlergarcia/sf-web-components 1.9.38 โ 1.9.40
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/package.json +4 -1
- package/scripts/postinstall.mjs +319 -99
- package/src/components/library/cards/ActionList.jsx +38 -0
- package/src/components/library/cards/ActivityCard.jsx +56 -0
- package/src/components/library/cards/BaseCard.jsx +109 -0
- package/src/components/library/cards/CalloutCard.jsx +37 -0
- package/src/components/library/cards/ChartCard.jsx +105 -0
- package/src/components/library/cards/FeedPanel.jsx +39 -0
- package/src/components/library/cards/ListCard.jsx +193 -0
- package/src/components/library/cards/MetricCard.jsx +109 -0
- package/src/components/library/cards/MetricsStrip.jsx +78 -0
- package/src/components/library/cards/SectionCard.jsx +83 -0
- package/src/components/library/cards/SemanticMetricCard.jsx +52 -0
- package/src/components/library/cards/SemanticMetricCardWithLoading.jsx +23 -0
- package/src/components/library/cards/SemanticTableCard.jsx +48 -0
- package/src/components/library/cards/SemanticTableCardWithLoading.jsx +22 -0
- package/src/components/library/cards/StatusCard.jsx +220 -0
- package/src/components/library/cards/TableCard.jsx +337 -0
- package/src/components/library/cards/WidgetCard.jsx +90 -0
- package/src/components/library/charts/D3Chart.jsx +109 -0
- package/src/components/library/charts/D3ChartTemplates.jsx +126 -0
- package/src/components/library/charts/GeoMap.jsx +293 -0
- package/src/components/library/chat/ChatBar.jsx +256 -0
- package/src/components/library/chat/ChatInput.jsx +89 -0
- package/src/components/library/chat/ChatMessage.jsx +178 -0
- package/src/components/library/chat/ChatMessageList.jsx +73 -0
- package/src/components/library/chat/ChatPanel.jsx +97 -0
- package/src/components/library/chat/ChatSuggestions.jsx +28 -0
- package/src/components/library/chat/ChatToolCall.jsx +100 -0
- package/src/components/library/chat/ChatTypingIndicator.jsx +23 -0
- package/src/components/library/chat/ChatWelcome.jsx +43 -0
- package/src/components/library/chat/index.jsx +10 -0
- package/src/components/library/chat/useChatState.jsx +130 -0
- package/src/components/library/data/DataModeProvider.jsx +67 -0
- package/src/components/library/data/DataModeToggle.jsx +36 -0
- package/src/components/library/data/chartDataProvider.jsx +61 -0
- package/src/components/library/data/filterUtils.jsx +141 -0
- package/src/components/library/data/useDataSource.jsx +33 -0
- package/src/components/library/data/usePageFilters.jsx +99 -0
- package/src/components/library/filters/FilterBar.jsx +95 -0
- package/src/components/library/filters/SearchFilter.jsx +36 -0
- package/src/components/library/filters/SelectFilter.jsx +55 -0
- package/src/components/library/filters/ToggleFilter.jsx +52 -0
- package/src/components/library/filters/index.jsx +4 -0
- package/src/components/library/forms/FormField.jsx +291 -0
- package/src/components/library/forms/FormModal.jsx +201 -0
- package/src/components/library/forms/FormRenderer.jsx +46 -0
- package/src/components/library/forms/FormSection.jsx +69 -0
- package/src/components/library/forms/index.jsx +5 -0
- package/src/components/library/forms/useFormState.jsx +165 -0
- package/src/components/library/heroui/Accordion.jsx +26 -0
- package/src/components/library/heroui/Alert.jsx +8 -0
- package/src/components/library/heroui/Badge.jsx +8 -0
- package/src/components/library/heroui/Breadcrumbs.jsx +22 -0
- package/src/components/library/heroui/Button.jsx +58 -0
- package/src/components/library/heroui/Card.jsx +8 -0
- package/src/components/library/heroui/Collapsible.jsx +42 -0
- package/src/components/library/heroui/DatePicker.jsx +34 -0
- package/src/components/library/heroui/Dialog.jsx +37 -0
- package/src/components/library/heroui/Drawer.jsx +32 -0
- package/src/components/library/heroui/Dropdown.jsx +28 -0
- package/src/components/library/heroui/Field.jsx +51 -0
- package/src/components/library/heroui/Input.jsx +6 -0
- package/src/components/library/heroui/Kbd.jsx +8 -0
- package/src/components/library/heroui/Meter.jsx +8 -0
- package/src/components/library/heroui/Modal.jsx +32 -0
- package/src/components/library/heroui/Pagination.jsx +8 -0
- package/src/components/library/heroui/Popover.jsx +64 -0
- package/src/components/library/heroui/ProgressBar.jsx +8 -0
- package/src/components/library/heroui/ProgressCircle.jsx +8 -0
- package/src/components/library/heroui/ScrollShadow.jsx +8 -0
- package/src/components/library/heroui/Select.jsx +37 -0
- package/src/components/library/heroui/Separator.jsx +8 -0
- package/src/components/library/heroui/Skeleton.jsx +8 -0
- package/src/components/library/heroui/Tabs.jsx +26 -0
- package/src/components/library/heroui/Toast.jsx +25 -0
- package/src/components/library/heroui/Toggle.jsx +14 -0
- package/src/components/library/heroui/Tooltip.jsx +21 -0
- package/src/components/library/index.jsx +146 -0
- package/src/components/library/layout/PageContainer.jsx +11 -0
- package/src/components/library/skeletons/CardSkeleton.jsx +30 -0
- package/src/components/library/theme/AppThemeProvider.jsx +67 -0
- package/src/components/library/theme/tokens.jsx +72 -0
- package/src/components/library/ui/Alert.jsx +80 -0
- package/src/components/library/ui/Avatar.jsx +44 -0
- package/src/components/library/ui/BreadcrumbExtras.tsx +120 -0
- package/src/components/library/ui/Button.jsx +61 -0
- package/src/components/library/ui/Card.jsx +117 -0
- package/src/components/library/ui/Checkbox.jsx +17 -0
- package/src/components/library/ui/Chip.jsx +38 -0
- package/src/components/library/ui/Collapsible.tsx +31 -0
- package/src/components/library/ui/Container.jsx +56 -0
- package/src/components/library/ui/DatePicker.tsx +34 -0
- package/src/components/library/ui/Dialog.tsx +141 -0
- package/src/components/library/ui/EmptyState.jsx +46 -0
- package/src/components/library/ui/Field.tsx +82 -0
- package/src/components/library/ui/FieldGroup.jsx +17 -0
- package/src/components/library/ui/Input.jsx +21 -0
- package/src/components/library/ui/Label.jsx +22 -0
- package/src/components/library/ui/PaginationExtras.tsx +142 -0
- package/src/components/library/ui/Popover.tsx +39 -0
- package/src/components/library/ui/Select.tsx +113 -0
- package/src/components/library/ui/Spinner.d.ts +10 -0
- package/src/components/library/ui/Spinner.jsx +64 -0
- package/src/components/library/ui/Text.jsx +46 -0
- package/src/components/library/ui/Toggle.jsx +42 -0
- package/src/components/workspace/ComponentRegistry.jsx +297 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
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.40",
|
|
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,6 +28,9 @@
|
|
|
28
28
|
"dist",
|
|
29
29
|
"scripts",
|
|
30
30
|
"data",
|
|
31
|
+
"src/components",
|
|
32
|
+
"src/lib",
|
|
33
|
+
"src/types",
|
|
31
34
|
"README.md",
|
|
32
35
|
".a4drules"
|
|
33
36
|
],
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,148 +1,368 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Post-install script
|
|
4
|
+
* Post-install script
|
|
5
5
|
*
|
|
6
|
-
* Automatically
|
|
7
|
-
*
|
|
8
|
-
* - Skills (.a4drules/)
|
|
9
|
-
* - Scripts (scripts/)
|
|
6
|
+
* Automatically runs after installing @schandlergarcia/sf-web-components
|
|
7
|
+
* Copies UI components into the consuming project so Tailwind can properly scan them
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
import fs from 'fs';
|
|
13
11
|
import path from 'path';
|
|
14
12
|
import { fileURLToPath } from 'url';
|
|
13
|
+
import { glob } from 'glob';
|
|
15
14
|
|
|
16
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
16
|
const __dirname = path.dirname(__filename);
|
|
18
17
|
|
|
19
|
-
//
|
|
20
|
-
const
|
|
18
|
+
// Only run if we're in a consuming project (not the package itself)
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
const isPackageItself = cwd.includes('sf-web-components') && fs.existsSync(path.join(cwd, 'scripts/postinstall.mjs'));
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const PROJECT_ROOT = path.resolve(PACKAGE_ROOT, '../../..');
|
|
25
|
-
|
|
26
|
-
console.log('\n๐ฆ @schandlergarcia/sf-web-components post-install\n');
|
|
27
|
-
|
|
28
|
-
// Check if we're in a node_modules context (not during package development)
|
|
29
|
-
if (!PACKAGE_ROOT.includes('node_modules')) {
|
|
30
|
-
console.log(' โน๏ธ Running in development mode, skipping setup\n');
|
|
22
|
+
if (isPackageItself) {
|
|
23
|
+
console.log('Skipping postinstall - running in package directory');
|
|
31
24
|
process.exit(0);
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
console.log('๐ Running @schandlergarcia/sf-web-components setup...\n');
|
|
28
|
+
|
|
29
|
+
const PACKAGE_NAME = '@schandlergarcia/sf-web-components';
|
|
30
|
+
let filesUpdated = 0;
|
|
31
|
+
let componentsCopied = 0;
|
|
32
|
+
|
|
33
|
+
// Copy entire library directory
|
|
34
|
+
const packageRoot = path.join(cwd, 'node_modules', PACKAGE_NAME);
|
|
35
|
+
const sourceLibraryDir = path.join(packageRoot, 'src/components/library');
|
|
36
|
+
const targetLibraryDir = path.join(cwd, 'src/components/library');
|
|
37
|
+
|
|
38
|
+
console.log('๐ฆ Copying component library...\n');
|
|
39
|
+
|
|
40
|
+
function copyDirectoryRecursive(source, target) {
|
|
41
|
+
if (!fs.existsSync(source)) return 0;
|
|
42
|
+
|
|
43
|
+
let count = 0;
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(target)) {
|
|
46
|
+
fs.mkdirSync(target, { recursive: true });
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const items = fs.readdirSync(source);
|
|
50
|
+
|
|
51
|
+
for (const item of items) {
|
|
52
|
+
// Skip hidden directories and files (like .sfdx, .git, etc.)
|
|
53
|
+
if (item.startsWith('.')) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const sourcePath = path.join(source, item);
|
|
58
|
+
const targetPath = path.join(target, item);
|
|
59
|
+
const stat = fs.statSync(sourcePath);
|
|
60
|
+
|
|
61
|
+
if (stat.isDirectory()) {
|
|
62
|
+
count += copyDirectoryRecursive(sourcePath, targetPath);
|
|
63
|
+
} else if (item.match(/\.(jsx|tsx|js|ts|css|md)$/)) {
|
|
64
|
+
try {
|
|
65
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
66
|
+
count++;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(` โ Failed to copy ${item}: ${error.message}`);
|
|
51
69
|
}
|
|
52
70
|
}
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
return
|
|
73
|
+
return count;
|
|
56
74
|
}
|
|
57
75
|
|
|
58
|
-
|
|
76
|
+
if (fs.existsSync(sourceLibraryDir)) {
|
|
77
|
+
componentsCopied = copyDirectoryRecursive(sourceLibraryDir, targetLibraryDir);
|
|
78
|
+
console.log(` โ Copied ${componentsCopied} component files\n`);
|
|
79
|
+
}
|
|
59
80
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
81
|
+
// Also copy lib directory (utils, etc.)
|
|
82
|
+
const sourceLibDir = path.join(packageRoot, 'src/lib');
|
|
83
|
+
const targetLibDir = path.join(cwd, 'src/lib');
|
|
84
|
+
|
|
85
|
+
if (fs.existsSync(sourceLibDir)) {
|
|
86
|
+
const libFilesCopied = copyDirectoryRecursive(sourceLibDir, targetLibDir);
|
|
87
|
+
console.log(` โ Copied ${libFilesCopied} lib files\n`);
|
|
64
88
|
}
|
|
65
89
|
|
|
66
|
-
|
|
90
|
+
// Copy types directory
|
|
91
|
+
const sourceTypesDir = path.join(packageRoot, 'src/types');
|
|
92
|
+
const targetTypesDir = path.join(cwd, 'src/types');
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(sourceTypesDir)) {
|
|
95
|
+
const typesFilesCopied = copyDirectoryRecursive(sourceTypesDir, targetTypesDir);
|
|
96
|
+
console.log(` โ Copied ${typesFilesCopied} type files\n`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Copy workspace directory (ComponentRegistry, etc.)
|
|
100
|
+
const sourceWorkspaceDir = path.join(packageRoot, 'src/components/workspace');
|
|
101
|
+
const targetWorkspaceDir = path.join(cwd, 'src/components/workspace');
|
|
102
|
+
|
|
103
|
+
if (fs.existsSync(sourceWorkspaceDir)) {
|
|
104
|
+
const workspaceFilesCopied = copyDirectoryRecursive(sourceWorkspaceDir, targetWorkspaceDir);
|
|
105
|
+
console.log(` โ Copied ${workspaceFilesCopied} workspace files\n`);
|
|
106
|
+
}
|
|
67
107
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!fs.existsSync(
|
|
72
|
-
fs.mkdirSync(
|
|
108
|
+
// Copy workspace templates (CommandCenter, etc.)
|
|
109
|
+
const workspaceTemplatesDir = path.join(packageRoot, 'src/templates/workspace');
|
|
110
|
+
if (fs.existsSync(workspaceTemplatesDir)) {
|
|
111
|
+
if (!fs.existsSync(targetWorkspaceDir)) {
|
|
112
|
+
fs.mkdirSync(targetWorkspaceDir, { recursive: true });
|
|
73
113
|
}
|
|
74
114
|
|
|
75
|
-
const
|
|
76
|
-
|
|
115
|
+
const workspaceTemplates = fs.readdirSync(workspaceTemplatesDir).filter(f => f.endsWith('.template'));
|
|
116
|
+
let workspaceTemplatesCopied = 0;
|
|
77
117
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
118
|
+
for (const template of workspaceTemplates) {
|
|
119
|
+
const sourcePath = path.join(workspaceTemplatesDir, template);
|
|
120
|
+
const targetFileName = template.replace('.template', '');
|
|
121
|
+
const targetPath = path.join(targetWorkspaceDir, targetFileName);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
125
|
+
fs.writeFileSync(targetPath, content, 'utf-8');
|
|
126
|
+
workspaceTemplatesCopied++;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(` โ Failed to copy ${targetFileName}: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (workspaceTemplatesCopied > 0) {
|
|
133
|
+
console.log(` โ Copied ${workspaceTemplatesCopied} workspace template files\n`);
|
|
82
134
|
}
|
|
83
|
-
} catch (err) {
|
|
84
|
-
console.log(' โ ๏ธ Could not copy sample data:', err.message);
|
|
85
135
|
}
|
|
86
136
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
// Copy pages directory (sample pages, etc.)
|
|
138
|
+
const sourcePagesDir = path.join(packageRoot, 'src/components/pages');
|
|
139
|
+
const targetComponentPagesDir = path.join(cwd, 'src/components/pages');
|
|
140
|
+
|
|
141
|
+
if (fs.existsSync(sourcePagesDir)) {
|
|
142
|
+
const pagesFilesCopied = copyDirectoryRecursive(sourcePagesDir, targetComponentPagesDir);
|
|
143
|
+
console.log(` โ Copied ${pagesFilesCopied} component pages\n`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Update imports in existing files to use local library
|
|
147
|
+
const files = glob.sync('src/**/*.{ts,tsx,js,jsx}', {
|
|
148
|
+
cwd,
|
|
149
|
+
absolute: true,
|
|
150
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/components/library/**']
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
console.log(`\n๐ Updating imports in ${files.length} files...\n`);
|
|
154
|
+
|
|
155
|
+
for (const file of files) {
|
|
156
|
+
try {
|
|
157
|
+
let content = fs.readFileSync(file, 'utf-8');
|
|
158
|
+
let modified = false;
|
|
159
|
+
|
|
160
|
+
// Replace package imports with local library imports
|
|
161
|
+
// Match: import { UIButton, Card, MetricCard } from '@schandlergarcia/sf-web-components';
|
|
162
|
+
// Replace: import { UIButton, Card, MetricCard } from '@/components/library';
|
|
163
|
+
const packageImportRegex = new RegExp(
|
|
164
|
+
`from\\s+['"]${PACKAGE_NAME}['"]`,
|
|
165
|
+
'g'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (packageImportRegex.test(content)) {
|
|
169
|
+
content = content.replace(packageImportRegex, `from '@/components/library'`);
|
|
170
|
+
modified = true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Also replace specific subpath imports
|
|
174
|
+
const subpathRegex = new RegExp(
|
|
175
|
+
`from\\s+['"]${PACKAGE_NAME}/lib['"]`,
|
|
176
|
+
'g'
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (subpathRegex.test(content)) {
|
|
180
|
+
content = content.replace(subpathRegex, `from '@/lib'`);
|
|
181
|
+
modified = true;
|
|
105
182
|
}
|
|
106
183
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
if (modified) {
|
|
185
|
+
fs.writeFileSync(file, content, 'utf-8');
|
|
186
|
+
filesUpdated++;
|
|
187
|
+
console.log(` โ ${path.relative(cwd, file)}`);
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(` โ Error updating ${file}: ${error.message}`);
|
|
110
191
|
}
|
|
111
|
-
} catch (err) {
|
|
112
|
-
console.log(' โ ๏ธ Could not copy skills:', err.message);
|
|
113
192
|
}
|
|
114
193
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
194
|
+
// Copy page templates
|
|
195
|
+
const templatesDir = path.join(path.dirname(__dirname), 'src/templates/pages');
|
|
196
|
+
const targetPagesDir = path.join(cwd, 'src/pages');
|
|
197
|
+
|
|
198
|
+
console.log('\n๐ Installing page templates...\n');
|
|
199
|
+
|
|
200
|
+
let templatesInstalled = 0;
|
|
201
|
+
const installedTemplates = [];
|
|
202
|
+
|
|
203
|
+
if (fs.existsSync(templatesDir)) {
|
|
204
|
+
// Create target pages directory if it doesn't exist
|
|
205
|
+
if (!fs.existsSync(targetPagesDir)) {
|
|
206
|
+
fs.mkdirSync(targetPagesDir, { recursive: true });
|
|
120
207
|
}
|
|
121
208
|
|
|
122
|
-
const
|
|
123
|
-
for (const script of scriptsToCopy) {
|
|
124
|
-
const sourceFile = path.join(PACKAGE_ROOT, 'scripts', script);
|
|
125
|
-
const targetFile = path.join(scriptsDir, script);
|
|
209
|
+
const templates = fs.readdirSync(templatesDir).filter(f => f.endsWith('.template'));
|
|
126
210
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
211
|
+
for (const template of templates) {
|
|
212
|
+
const sourcePath = path.join(templatesDir, template);
|
|
213
|
+
const targetFileName = template.replace('.template', '');
|
|
214
|
+
const targetPath = path.join(targetPagesDir, targetFileName);
|
|
215
|
+
const pageName = targetFileName.replace('.tsx', '');
|
|
216
|
+
|
|
217
|
+
// Always overwrite to ensure latest templates
|
|
218
|
+
try {
|
|
219
|
+
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
220
|
+
fs.writeFileSync(targetPath, content, 'utf-8');
|
|
221
|
+
console.log(` โ Installed ${targetFileName}`);
|
|
222
|
+
templatesInstalled++;
|
|
223
|
+
installedTemplates.push(pageName);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error(` โ Failed to install ${targetFileName}: ${error.message}`);
|
|
132
226
|
}
|
|
133
227
|
}
|
|
134
|
-
} catch (err) {
|
|
135
|
-
console.log(' โ ๏ธ Could not copy scripts:', err.message);
|
|
136
228
|
}
|
|
137
229
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
console.log('
|
|
144
|
-
|
|
145
|
-
|
|
230
|
+
// Copy routes.tsx template with full configuration
|
|
231
|
+
const routesTemplatePath = path.join(packageRoot, 'src/templates/config/routes.tsx.template');
|
|
232
|
+
const routesPath = path.join(cwd, 'src/routes.tsx');
|
|
233
|
+
|
|
234
|
+
if (fs.existsSync(routesTemplatePath) && fs.existsSync(path.join(cwd, 'src'))) {
|
|
235
|
+
console.log('\n๐ Installing routes configuration...\n');
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const routesContent = fs.readFileSync(routesTemplatePath, 'utf-8');
|
|
239
|
+
fs.writeFileSync(routesPath, routesContent, 'utf-8');
|
|
240
|
+
console.log(' โ Installed complete routes.tsx with Account search and detail pages');
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error(` โ Failed to install routes.tsx: ${error.message}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add reset:command-center script to package.json
|
|
247
|
+
const packageJsonPath = path.join(cwd, 'package.json');
|
|
248
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
249
|
+
console.log('\n๐ง Adding reset script to package.json...\n');
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
253
|
+
|
|
254
|
+
if (!packageJson.scripts) {
|
|
255
|
+
packageJson.scripts = {};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Add the reset:command-center script if it doesn't exist
|
|
259
|
+
let scriptsAdded = [];
|
|
260
|
+
if (!packageJson.scripts['reset:command-center']) {
|
|
261
|
+
packageJson.scripts['reset:command-center'] = 'bash node_modules/@schandlergarcia/sf-web-components/scripts/reset-command-center.sh';
|
|
262
|
+
scriptsAdded.push('reset:command-center');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Add the validate:dashboard script if it doesn't exist
|
|
266
|
+
if (!packageJson.scripts['validate:dashboard']) {
|
|
267
|
+
packageJson.scripts['validate:dashboard'] = 'bash node_modules/@schandlergarcia/sf-web-components/scripts/validate-dashboard.sh';
|
|
268
|
+
scriptsAdded.push('validate:dashboard');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (scriptsAdded.length > 0) {
|
|
272
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
|
|
273
|
+
scriptsAdded.forEach(script => {
|
|
274
|
+
console.log(` โ Added "${script}" script to package.json`);
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
console.log(' โน Scripts already exist');
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error(` โ Failed to update package.json: ${error.message}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Copy .a4drules from package to project root (so AI assistants can discover them)
|
|
285
|
+
const packageA4dRules = path.join(packageRoot, '.a4drules');
|
|
286
|
+
const projectRootA4dRules = path.join(cwd, '../../../../../.a4drules'); // Go up from webapp to project root
|
|
287
|
+
|
|
288
|
+
if (fs.existsSync(packageA4dRules)) {
|
|
289
|
+
console.log('\n๐ Copying AI assistant rules to project root...\n');
|
|
290
|
+
|
|
291
|
+
// Resolve to absolute path
|
|
292
|
+
const projectRootPath = path.resolve(cwd, '../../../../../');
|
|
293
|
+
const targetA4dRules = path.join(projectRootPath, '.a4drules');
|
|
294
|
+
|
|
295
|
+
// Create .a4drules if it doesn't exist
|
|
296
|
+
if (!fs.existsSync(targetA4dRules)) {
|
|
297
|
+
fs.mkdirSync(targetA4dRules, { recursive: true });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Copy skills directory
|
|
301
|
+
const skillsSource = path.join(packageA4dRules, 'skills');
|
|
302
|
+
const skillsTarget = path.join(targetA4dRules, 'skills');
|
|
303
|
+
if (fs.existsSync(skillsSource)) {
|
|
304
|
+
const skillsCopied = copyDirectoryRecursive(skillsSource, skillsTarget);
|
|
305
|
+
console.log(` โ Copied ${skillsCopied} skill files`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Copy features directory
|
|
309
|
+
const featuresSource = path.join(packageA4dRules, 'features');
|
|
310
|
+
const featuresTarget = path.join(targetA4dRules, 'features');
|
|
311
|
+
if (fs.existsSync(featuresSource)) {
|
|
312
|
+
const featuresCopied = copyDirectoryRecursive(featuresSource, featuresTarget);
|
|
313
|
+
console.log(` โ Copied ${featuresCopied} feature rule files`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Migrate any dashboards from old location (src/components/pages/) to new location (src/pages/)
|
|
318
|
+
const oldPagesDir = path.join(cwd, 'src/components/pages');
|
|
319
|
+
const newPagesDir = path.join(cwd, 'src/pages');
|
|
320
|
+
let migratedFiles = 0;
|
|
321
|
+
|
|
322
|
+
if (fs.existsSync(oldPagesDir)) {
|
|
323
|
+
console.log('\n๐ Migrating dashboards from old location...\n');
|
|
324
|
+
|
|
325
|
+
const oldFiles = fs.readdirSync(oldPagesDir).filter(f =>
|
|
326
|
+
(f.endsWith('.jsx') || f.endsWith('.tsx')) &&
|
|
327
|
+
f.includes('Dashboard') &&
|
|
328
|
+
f !== 'BlankDashboard.jsx' &&
|
|
329
|
+
f !== 'BlankDashboard.tsx'
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
for (const file of oldFiles) {
|
|
333
|
+
const oldPath = path.join(oldPagesDir, file);
|
|
334
|
+
const newFile = file.replace('.jsx', '.tsx'); // Also convert .jsx to .tsx
|
|
335
|
+
const newPath = path.join(newPagesDir, newFile);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
fs.renameSync(oldPath, newPath);
|
|
339
|
+
console.log(` โ Migrated ${file} โ src/pages/${newFile}`);
|
|
340
|
+
migratedFiles++;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.error(` โ Failed to migrate ${file}: ${error.message}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Try to remove old directory if empty
|
|
347
|
+
try {
|
|
348
|
+
const remaining = fs.readdirSync(oldPagesDir);
|
|
349
|
+
if (remaining.length === 0) {
|
|
350
|
+
fs.rmdirSync(oldPagesDir);
|
|
351
|
+
console.log(' โ Removed empty src/components/pages directory');
|
|
352
|
+
}
|
|
353
|
+
} catch {
|
|
354
|
+
// Ignore errors
|
|
355
|
+
}
|
|
146
356
|
}
|
|
147
357
|
|
|
148
|
-
|
|
358
|
+
console.log('\n๐ Summary:');
|
|
359
|
+
console.log(` - Copied ${componentsCopied} UI components`);
|
|
360
|
+
console.log(` - Updated ${filesUpdated} files`);
|
|
361
|
+
console.log(` - Installed ${templatesInstalled} page templates`);
|
|
362
|
+
console.log(` - Installed CommandCenter.tsx for dashboard management`);
|
|
363
|
+
console.log(` - Added "npm run reset:command-center" script`);
|
|
364
|
+
console.log(` - Installed AI assistant rules for command center building`);
|
|
365
|
+
if (migratedFiles > 0) {
|
|
366
|
+
console.log(` - Migrated ${migratedFiles} dashboard files to correct location`);
|
|
367
|
+
}
|
|
368
|
+
console.log('\nโ
Setup complete! UI components are now local for optimal Tailwind scanning\n');
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import UIButton from "../ui/Button";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Row of action buttons โ typically used at the bottom of a dashboard section.
|
|
6
|
+
*
|
|
7
|
+
* @param {{ label: string, [key]: any }[] | string[]} actions
|
|
8
|
+
* @param {string} title
|
|
9
|
+
* @param {Function} onAction Called with the action object/string when clicked
|
|
10
|
+
*/
|
|
11
|
+
export default function ActionList({
|
|
12
|
+
actions = [],
|
|
13
|
+
title,
|
|
14
|
+
onAction,
|
|
15
|
+
className = "",
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<div className={`rounded-2xl border border-slate-200 bg-white p-4 dark:border-slate-800 dark:bg-slate-900 ${className}`}>
|
|
19
|
+
{title && (
|
|
20
|
+
<div className="mb-3 text-sm font-medium text-slate-900 dark:text-slate-50">
|
|
21
|
+
{title}
|
|
22
|
+
</div>
|
|
23
|
+
)}
|
|
24
|
+
<div className="flex flex-wrap gap-2">
|
|
25
|
+
{actions.map((action, i) => (
|
|
26
|
+
<UIButton
|
|
27
|
+
key={i}
|
|
28
|
+
size="sm"
|
|
29
|
+
variant={i === 0 ? "primary" : "outline"}
|
|
30
|
+
onClick={() => onAction?.(action)}
|
|
31
|
+
>
|
|
32
|
+
{typeof action === "string" ? action : action.label}
|
|
33
|
+
</UIButton>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
3
|
+
import { ArrowPathIcon, CheckCircleIcon, ExclamationCircleIcon, ClockIcon } from "@heroicons/react/24/outline";
|
|
4
|
+
import UIText from "../ui/Text";
|
|
5
|
+
|
|
6
|
+
const STATUS_ICON = {
|
|
7
|
+
working: { Icon: ArrowPathIcon, color: "text-indigo-500", spin: true },
|
|
8
|
+
pending: { Icon: ClockIcon, color: "text-slate-400", spin: false },
|
|
9
|
+
complete: { Icon: CheckCircleIcon, color: "text-emerald-500", spin: false },
|
|
10
|
+
error: { Icon: ExclamationCircleIcon, color: "text-red-500", spin: false },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function ActionItem({ action }) {
|
|
14
|
+
const s = STATUS_ICON[action.status] ?? STATUS_ICON.pending;
|
|
15
|
+
return (
|
|
16
|
+
<motion.div
|
|
17
|
+
initial={{ y: 12, opacity: 0 }}
|
|
18
|
+
animate={{ y: 0, opacity: 1 }}
|
|
19
|
+
exit={{ y: -12, opacity: 0 }}
|
|
20
|
+
className="rounded-lg border border-slate-100 bg-slate-50 p-3 dark:border-slate-800 dark:bg-slate-950/40"
|
|
21
|
+
>
|
|
22
|
+
<div className="flex items-start gap-2">
|
|
23
|
+
<s.Icon className={`mt-0.5 h-4 w-4 shrink-0 ${s.color} ${s.spin ? "animate-spin" : ""}`} />
|
|
24
|
+
<div className="min-w-0">
|
|
25
|
+
<div className="text-xs font-medium text-slate-700 dark:text-slate-200">{action.title ?? action.action}</div>
|
|
26
|
+
{(action.subtitle ?? action.traveler ?? action.timestamp ?? action.startedAt) && (
|
|
27
|
+
<div className="mt-0.5 text-[10px] text-slate-400">
|
|
28
|
+
{[action.subtitle, action.traveler, action.timestamp ?? action.startedAt].filter(Boolean).join(" ยท ")}
|
|
29
|
+
</div>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</motion.div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function ActivityCard({ title = "Activity", actions = [], className = "" }) {
|
|
38
|
+
if (actions.length === 0) return null;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className={className}>
|
|
42
|
+
{title && (
|
|
43
|
+
<UIText as="div" size="xs" weight="semibold" muted className="mb-2 uppercase tracking-wider">
|
|
44
|
+
{title}
|
|
45
|
+
</UIText>
|
|
46
|
+
)}
|
|
47
|
+
<div className="space-y-2">
|
|
48
|
+
<AnimatePresence>
|
|
49
|
+
{actions.map(a => (
|
|
50
|
+
<ActionItem key={a.id} action={a} />
|
|
51
|
+
))}
|
|
52
|
+
</AnimatePresence>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|