@schandlergarcia/sf-web-components 2.2.0 → 2.3.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/CHANGELOG.md +15 -0
- package/brands/engine/app/api/graphql-operations-types.ts +11260 -0
- package/brands/engine/app/api/graphqlClient.ts +25 -0
- package/brands/engine/app/api/partnerQueries.ts +212 -0
- package/brands/engine/app/appLayout.tsx +13 -0
- package/brands/engine/app/components/AgentforceConversationClient.tsx +201 -0
- package/brands/engine/app/components/__inherit_AgentforceConversationClient.tsx +3 -0
- package/brands/engine/app/components/alerts/status-alert.tsx +49 -0
- package/brands/engine/app/components/layouts/card-layout.tsx +29 -0
- package/brands/engine/app/components/workspace/CommandCenter.tsx +16 -0
- package/brands/engine/app/config/agentApi.ts +36 -0
- package/brands/engine/app/features/object-search/__examples__/api/accountSearchService.ts +46 -0
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountIndustries.graphql +19 -0
- package/brands/engine/app/features/object-search/__examples__/api/query/distinctAccountTypes.graphql +19 -0
- package/brands/engine/app/features/object-search/__examples__/api/query/getAccountDetail.graphql +121 -0
- package/brands/engine/app/features/object-search/__examples__/api/query/searchAccounts.graphql +51 -0
- package/brands/engine/app/features/object-search/__examples__/pages/AccountObjectDetailPage.tsx +357 -0
- package/brands/engine/app/features/object-search/__examples__/pages/AccountSearch.tsx +312 -0
- package/brands/engine/app/features/object-search/__examples__/pages/Home.tsx +34 -0
- package/brands/engine/app/features/object-search/api/objectSearchService.ts +84 -0
- package/brands/engine/app/features/object-search/components/ActiveFilters.tsx +89 -0
- package/brands/engine/app/features/object-search/components/FilterContext.tsx +83 -0
- package/brands/engine/app/features/object-search/components/ObjectBreadcrumb.tsx +66 -0
- package/brands/engine/app/features/object-search/components/PaginationControls.tsx +109 -0
- package/brands/engine/app/features/object-search/components/SearchBar.tsx +41 -0
- package/brands/engine/app/features/object-search/components/SortControl.tsx +143 -0
- package/brands/engine/app/features/object-search/components/filters/BooleanFilter.tsx +78 -0
- package/brands/engine/app/features/object-search/components/filters/DateFilter.tsx +128 -0
- package/brands/engine/app/features/object-search/components/filters/DateRangeFilter.tsx +70 -0
- package/brands/engine/app/features/object-search/components/filters/FilterFieldWrapper.tsx +33 -0
- package/brands/engine/app/features/object-search/components/filters/MultiSelectFilter.tsx +97 -0
- package/brands/engine/app/features/object-search/components/filters/NumericRangeFilter.tsx +163 -0
- package/brands/engine/app/features/object-search/components/filters/SearchFilter.tsx +50 -0
- package/brands/engine/app/features/object-search/components/filters/SelectFilter.tsx +97 -0
- package/brands/engine/app/features/object-search/components/filters/TextFilter.tsx +91 -0
- package/brands/engine/app/features/object-search/hooks/useAsyncData.ts +54 -0
- package/brands/engine/app/features/object-search/hooks/useCachedAsyncData.ts +184 -0
- package/brands/engine/app/features/object-search/hooks/useDebouncedCallback.ts +34 -0
- package/brands/engine/app/features/object-search/hooks/useObjectSearchParams.ts +252 -0
- package/brands/engine/app/features/object-search/utils/debounce.ts +25 -0
- package/brands/engine/app/features/object-search/utils/fieldUtils.ts +29 -0
- package/brands/engine/app/features/object-search/utils/filterUtils.ts +404 -0
- package/brands/engine/app/features/object-search/utils/sortUtils.ts +38 -0
- package/brands/engine/app/hooks/useEngineLiveData.ts +49 -0
- package/brands/engine/app/hooks/useEvaAgent.ts +288 -0
- package/brands/engine/app/hooks/usePartnerDashboardData.ts +141 -0
- package/brands/engine/app/navigationMenu.tsx +80 -0
- package/brands/engine/app/pages/AccountObjectDetailPage.tsx +361 -0
- package/brands/engine/app/pages/AccountSearch.tsx +305 -0
- package/brands/engine/app/pages/BlankDashboard.tsx +15 -0
- package/brands/engine/app/pages/DataTest.tsx +78 -0
- package/brands/engine/app/pages/Home.tsx +5 -0
- package/brands/engine/app/pages/NotFound.tsx +19 -0
- package/brands/engine/app/pages/PartnerHubDashboard.tsx +2010 -0
- package/brands/engine/app/pages/Search.tsx +13 -0
- package/brands/engine/app/router-utils.tsx +35 -0
- package/brands/engine/app/routes.tsx +39 -0
- package/brands/engine/app/styles/global.css +270 -0
- package/package.json +1 -1
- package/scripts/apply-brand.mjs +159 -76
- package/scripts/postinstall.mjs +24 -5
|
@@ -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,35 @@
|
|
|
1
|
+
import type { RouteObject } from 'react-router';
|
|
2
|
+
import { routes } from './routes';
|
|
3
|
+
|
|
4
|
+
export type RouteWithFullPath = RouteObject & { fullPath: string };
|
|
5
|
+
|
|
6
|
+
const flatMapRoutes = (
|
|
7
|
+
route: RouteObject,
|
|
8
|
+
parentPath: string = ''
|
|
9
|
+
): RouteWithFullPath[] => {
|
|
10
|
+
let fullPath: string;
|
|
11
|
+
|
|
12
|
+
if (route.index) {
|
|
13
|
+
fullPath = parentPath || '/';
|
|
14
|
+
} else if (route.path) {
|
|
15
|
+
if (route.path.startsWith('/')) {
|
|
16
|
+
fullPath = route.path;
|
|
17
|
+
} else {
|
|
18
|
+
fullPath =
|
|
19
|
+
parentPath === '/' ? `/${route.path}` : `${parentPath}/${route.path}`;
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
fullPath = parentPath;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const routeWithPath = { ...route, fullPath };
|
|
26
|
+
|
|
27
|
+
const childRoutes =
|
|
28
|
+
route.children?.flatMap(child => flatMapRoutes(child, fullPath)) || [];
|
|
29
|
+
|
|
30
|
+
return [routeWithPath, ...childRoutes];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const getAllRoutes = (): RouteWithFullPath[] => {
|
|
34
|
+
return routes.flatMap(route => flatMapRoutes(route));
|
|
35
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { RouteObject } from 'react-router';
|
|
2
|
+
import AppLayout from './appLayout';
|
|
3
|
+
import Home from './pages/Home';
|
|
4
|
+
import DataTest from './pages/DataTest';
|
|
5
|
+
import AccountSearch from './features/object-search/__examples__/pages/AccountSearch';
|
|
6
|
+
import AccountObjectDetailPage from './features/object-search/__examples__/pages/AccountObjectDetailPage';
|
|
7
|
+
import NotFound from './pages/NotFound';
|
|
8
|
+
|
|
9
|
+
export const routes: RouteObject[] = [
|
|
10
|
+
{
|
|
11
|
+
path: "/",
|
|
12
|
+
element: <AppLayout />,
|
|
13
|
+
children: [
|
|
14
|
+
{
|
|
15
|
+
index: true,
|
|
16
|
+
element: <Home />,
|
|
17
|
+
handle: { showInNavigation: true, showNavBar: true, label: "Partner Hub" }
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
path: 'data-test',
|
|
21
|
+
element: <DataTest />,
|
|
22
|
+
handle: { showInNavigation: true, showNavBar: true, label: "Data Test" }
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
path: 'accounts',
|
|
26
|
+
element: <AccountSearch />,
|
|
27
|
+
handle: { showInNavigation: true, showNavBar: true, label: "Accounts" }
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
path: 'accounts/:recordId',
|
|
31
|
+
element: <AccountObjectDetailPage />
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: '*',
|
|
35
|
+
element: <NotFound />
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
];
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
@import '@heroui/styles';
|
|
2
|
+
|
|
3
|
+
@layer base {
|
|
4
|
+
html,
|
|
5
|
+
body,
|
|
6
|
+
#root {
|
|
7
|
+
@apply min-h-screen;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
body {
|
|
11
|
+
@apply antialiased bg-white;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Smooth scrolling */
|
|
15
|
+
html {
|
|
16
|
+
scroll-behavior: smooth;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Custom animations */
|
|
20
|
+
@keyframes fade-in {
|
|
21
|
+
from {
|
|
22
|
+
opacity: 0;
|
|
23
|
+
transform: translateY(10px);
|
|
24
|
+
}
|
|
25
|
+
to {
|
|
26
|
+
opacity: 1;
|
|
27
|
+
transform: translateY(0);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes slide-up {
|
|
32
|
+
from {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(20px);
|
|
35
|
+
}
|
|
36
|
+
to {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
transform: translateY(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.animate-fade-in {
|
|
43
|
+
animation: fade-in 0.6s ease-out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.animate-slide-up {
|
|
47
|
+
animation: slide-up 0.5s ease-out;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@import 'tw-animate-css';
|
|
52
|
+
@import 'shadcn/tailwind.css';
|
|
53
|
+
|
|
54
|
+
@custom-variant dark (&:is(.dark *));
|
|
55
|
+
|
|
56
|
+
@source "../components/library";
|
|
57
|
+
@source "../components/pages";
|
|
58
|
+
|
|
59
|
+
@theme inline {
|
|
60
|
+
--color-background: var(--background);
|
|
61
|
+
--color-foreground: var(--foreground);
|
|
62
|
+
--color-card: var(--card);
|
|
63
|
+
--color-card-foreground: var(--card-foreground);
|
|
64
|
+
--color-popover: var(--popover);
|
|
65
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
66
|
+
--color-primary: var(--primary);
|
|
67
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
68
|
+
--color-secondary: var(--secondary);
|
|
69
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
70
|
+
--color-muted: var(--muted);
|
|
71
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
72
|
+
--color-accent: var(--accent);
|
|
73
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
74
|
+
--color-destructive: var(--destructive);
|
|
75
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
76
|
+
--color-border: var(--border);
|
|
77
|
+
--color-input: var(--input);
|
|
78
|
+
--color-ring: var(--ring);
|
|
79
|
+
--color-chart-1: var(--chart-1);
|
|
80
|
+
--color-chart-2: var(--chart-2);
|
|
81
|
+
--color-chart-3: var(--chart-3);
|
|
82
|
+
--color-chart-4: var(--chart-4);
|
|
83
|
+
--color-chart-5: var(--chart-5);
|
|
84
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
85
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
86
|
+
--radius-lg: var(--radius);
|
|
87
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
88
|
+
--color-sidebar: var(--sidebar);
|
|
89
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
90
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
91
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
92
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
93
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
94
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
95
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
96
|
+
|
|
97
|
+
/* Engine brand palette — official guidelines */
|
|
98
|
+
--color-engine-black: #0D1117;
|
|
99
|
+
--color-engine-cyan: #7DCBD9;
|
|
100
|
+
--color-engine-green: #1E9D6D;
|
|
101
|
+
--color-engine-blue: #157DE5;
|
|
102
|
+
--color-engine-orange: #FD4B23;
|
|
103
|
+
--color-engine-amber: #FFB200;
|
|
104
|
+
--color-engine-bg: #F3F3F4;
|
|
105
|
+
--color-engine-border: #B0B1B3;
|
|
106
|
+
--color-engine-text: #0D1117;
|
|
107
|
+
--color-engine-muted: #616368;
|
|
108
|
+
--color-engine-label: #B0B1B3;
|
|
109
|
+
|
|
110
|
+
/* Engine Cyan brand ramp */
|
|
111
|
+
--color-brand-50: #F0FAFB;
|
|
112
|
+
--color-brand-100: #D9F2F5;
|
|
113
|
+
--color-brand-200: #B3E5EB;
|
|
114
|
+
--color-brand-300: #7DCBD9;
|
|
115
|
+
--color-brand-400: #5BB8CA;
|
|
116
|
+
--color-brand-500: #3AA0B5;
|
|
117
|
+
--color-brand-600: #2D849A;
|
|
118
|
+
--color-brand-700: #266B7E;
|
|
119
|
+
--color-brand-800: #235768;
|
|
120
|
+
--color-brand-900: #1F4858;
|
|
121
|
+
--color-brand-950: #112E3A;
|
|
122
|
+
|
|
123
|
+
--font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
124
|
+
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
|
|
125
|
+
|
|
126
|
+
--radius-pill: 9999px;
|
|
127
|
+
--radius-card: 10px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
:root {
|
|
131
|
+
--radius: 0.625rem;
|
|
132
|
+
--background: oklch(1 0 0);
|
|
133
|
+
--foreground: oklch(0.145 0 0);
|
|
134
|
+
--card: oklch(1 0 0);
|
|
135
|
+
--card-foreground: oklch(0.145 0 0);
|
|
136
|
+
--popover: oklch(1 0 0);
|
|
137
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
138
|
+
--primary: oklch(0.205 0 0);
|
|
139
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
140
|
+
--secondary: oklch(0.97 0 0);
|
|
141
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
142
|
+
--muted: oklch(0.97 0 0);
|
|
143
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
144
|
+
--accent: oklch(0.97 0 0);
|
|
145
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
146
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
147
|
+
--border: oklch(0.922 0 0);
|
|
148
|
+
--input: oklch(0.922 0 0);
|
|
149
|
+
--ring: oklch(0.708 0 0);
|
|
150
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
151
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
152
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
153
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
154
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
155
|
+
--sidebar: oklch(0.985 0 0);
|
|
156
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
157
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
158
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
159
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
160
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
161
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
162
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.dark {
|
|
166
|
+
--background: oklch(0.145 0 0);
|
|
167
|
+
--foreground: oklch(0.985 0 0);
|
|
168
|
+
--card: oklch(0.205 0 0);
|
|
169
|
+
--card-foreground: oklch(0.985 0 0);
|
|
170
|
+
--popover: oklch(0.205 0 0);
|
|
171
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
172
|
+
--primary: oklch(0.922 0 0);
|
|
173
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
174
|
+
--secondary: oklch(0.269 0 0);
|
|
175
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
176
|
+
--muted: oklch(0.269 0 0);
|
|
177
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
178
|
+
--accent: oklch(0.269 0 0);
|
|
179
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
180
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
181
|
+
--border: oklch(1 0 0 / 10%);
|
|
182
|
+
--input: oklch(1 0 0 / 15%);
|
|
183
|
+
--ring: oklch(0.556 0 0);
|
|
184
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
185
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
186
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
187
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
188
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
189
|
+
--sidebar: oklch(0.205 0 0);
|
|
190
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
191
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
192
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
193
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
194
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
195
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
196
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@layer base {
|
|
200
|
+
* {
|
|
201
|
+
@apply border-border outline-ring/50;
|
|
202
|
+
}
|
|
203
|
+
body {
|
|
204
|
+
@apply bg-background text-foreground;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
* Restore HeroUI theme variables inside the Command Center scope.
|
|
210
|
+
* Uses Engine brand colors: Cyan #7DCBD9 as secondary, Orange-Red #FD4B23 as danger.
|
|
211
|
+
*/
|
|
212
|
+
.heroui-scope {
|
|
213
|
+
--primary: #0D1117;
|
|
214
|
+
--primary-foreground: oklch(0.9911 0 0);
|
|
215
|
+
--secondary: #7DCBD9;
|
|
216
|
+
--secondary-foreground: #0D1117;
|
|
217
|
+
--success: #1E9D6D;
|
|
218
|
+
--success-foreground: oklch(0.9911 0 0);
|
|
219
|
+
--warning: #FFB200;
|
|
220
|
+
--warning-foreground: #0D1117;
|
|
221
|
+
--danger: #FD4B23;
|
|
222
|
+
--danger-foreground: oklch(0.9911 0 0);
|
|
223
|
+
|
|
224
|
+
--muted: oklch(0.5517 0.0138 285.94);
|
|
225
|
+
--accent: oklch(0.6204 0.195 253.83);
|
|
226
|
+
--accent-foreground: oklch(0.9911 0 0);
|
|
227
|
+
--background: #F3F3F4;
|
|
228
|
+
--foreground: #0D1117;
|
|
229
|
+
--default: oklch(94% 0.001 286.375);
|
|
230
|
+
--default-foreground: #0D1117;
|
|
231
|
+
--border: #B0B1B3;
|
|
232
|
+
--separator: oklch(92% 0.004 286.32);
|
|
233
|
+
--segment: oklch(100% 0 0);
|
|
234
|
+
--segment-foreground: #0D1117;
|
|
235
|
+
--surface: oklch(100% 0 0);
|
|
236
|
+
--surface-foreground: #0D1117;
|
|
237
|
+
--overlay: oklch(100% 0 0);
|
|
238
|
+
--overlay-foreground: #0D1117;
|
|
239
|
+
--focus: #157DE5;
|
|
240
|
+
--link: #0D1117;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ChatBar expanded overlay — horizontal padding so it doesn't hit window edges */
|
|
244
|
+
body > .fixed.inset-x-0.rounded-2xl {
|
|
245
|
+
left: 1.5rem !important;
|
|
246
|
+
right: 1.5rem !important;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.dark .heroui-scope,
|
|
250
|
+
.heroui-scope.dark {
|
|
251
|
+
--muted: oklch(70.5% 0.015 286.067);
|
|
252
|
+
--background: oklch(12% 0.005 285.823);
|
|
253
|
+
--foreground: oklch(0.9911 0 0);
|
|
254
|
+
--default: oklch(27.4% 0.006 286.033);
|
|
255
|
+
--default-foreground: oklch(0.9911 0 0);
|
|
256
|
+
--border: oklch(28% 0.006 286.033);
|
|
257
|
+
--separator: oklch(25% 0.006 286.033);
|
|
258
|
+
--segment: oklch(0.3964 0.01 285.93);
|
|
259
|
+
--segment-foreground: oklch(0.9911 0 0);
|
|
260
|
+
--surface: oklch(0.2103 0.0059 285.89);
|
|
261
|
+
--surface-foreground: oklch(0.9911 0 0);
|
|
262
|
+
--overlay: oklch(0.2103 0.0059 285.89);
|
|
263
|
+
--overlay-foreground: oklch(0.9911 0 0);
|
|
264
|
+
--warning: oklch(0.8203 0.1388 76.34);
|
|
265
|
+
--warning-foreground: #0D1117;
|
|
266
|
+
--danger: oklch(0.594 0.1967 24.63);
|
|
267
|
+
--danger-foreground: oklch(0.9911 0 0);
|
|
268
|
+
--focus: #157DE5;
|
|
269
|
+
--link: oklch(0.9911 0 0);
|
|
270
|
+
}
|
package/package.json
CHANGED
package/scripts/apply-brand.mjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* apply-brand.mjs — Apply a brand theme
|
|
4
|
+
* apply-brand.mjs — Apply a brand theme or install a full demo app.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* node scripts/apply-brand.mjs engine
|
|
8
|
-
* node scripts/apply-brand.mjs --
|
|
9
|
-
* node scripts/apply-brand.mjs --
|
|
7
|
+
* node scripts/apply-brand.mjs engine # Apply Engine brand colors only
|
|
8
|
+
* node scripts/apply-brand.mjs --demo engine # Install full Engine demo app (neutral theme)
|
|
9
|
+
* node scripts/apply-brand.mjs --list # List available brands
|
|
10
|
+
* node scripts/apply-brand.mjs --reset # Remove brand, restore neutral theme
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* The demo command installs the full app (dashboard, routes, hooks, queries,
|
|
13
|
+
* features) but keeps the neutral color palette. Run brand:engine afterwards
|
|
14
|
+
* to switch to Engine colors.
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
import fs from 'fs';
|
|
@@ -19,10 +21,17 @@ import { fileURLToPath } from 'url';
|
|
|
19
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
22
|
const __dirname = path.dirname(__filename);
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
const isDemo = args.includes('--demo');
|
|
26
|
+
const positionalArgs = args.filter(a => !a.startsWith('--'));
|
|
27
|
+
const flagArgs = args.filter(a => a.startsWith('--'));
|
|
23
28
|
|
|
24
|
-
if (
|
|
25
|
-
console.error('Usage:
|
|
29
|
+
if (args.length === 0) {
|
|
30
|
+
console.error('Usage:');
|
|
31
|
+
console.error(' node scripts/apply-brand.mjs <brand> # Apply brand colors');
|
|
32
|
+
console.error(' node scripts/apply-brand.mjs --demo <brand> # Install full demo app');
|
|
33
|
+
console.error(' node scripts/apply-brand.mjs --list # List brands');
|
|
34
|
+
console.error(' node scripts/apply-brand.mjs --reset # Reset to neutral');
|
|
26
35
|
process.exit(1);
|
|
27
36
|
}
|
|
28
37
|
|
|
@@ -47,7 +56,30 @@ if (!packageRoot) {
|
|
|
47
56
|
|
|
48
57
|
const brandsDir = path.join(packageRoot, 'brands');
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
function copyDirectoryRecursive(source, target) {
|
|
62
|
+
if (!fs.existsSync(source)) return 0;
|
|
63
|
+
let count = 0;
|
|
64
|
+
if (!fs.existsSync(target)) fs.mkdirSync(target, { recursive: true });
|
|
65
|
+
|
|
66
|
+
for (const item of fs.readdirSync(source)) {
|
|
67
|
+
if (item.startsWith('.')) continue;
|
|
68
|
+
const srcPath = path.join(source, item);
|
|
69
|
+
const dstPath = path.join(target, item);
|
|
70
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
71
|
+
count += copyDirectoryRecursive(srcPath, dstPath);
|
|
72
|
+
} else {
|
|
73
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
74
|
+
count++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return count;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── --list ──────────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
if (flagArgs.includes('--list')) {
|
|
51
83
|
if (!fs.existsSync(brandsDir)) {
|
|
52
84
|
console.log('No brands available.');
|
|
53
85
|
process.exit(0);
|
|
@@ -56,10 +88,16 @@ if (arg === '--list') {
|
|
|
56
88
|
fs.statSync(path.join(brandsDir, d)).isDirectory()
|
|
57
89
|
);
|
|
58
90
|
console.log(`Available brands: ${brands.join(', ') || '(none)'}`);
|
|
91
|
+
for (const b of brands) {
|
|
92
|
+
const hasApp = fs.existsSync(path.join(brandsDir, b, 'app'));
|
|
93
|
+
console.log(` ${b}${hasApp ? ' (has demo app)' : ''}`);
|
|
94
|
+
}
|
|
59
95
|
process.exit(0);
|
|
60
96
|
}
|
|
61
97
|
|
|
62
|
-
|
|
98
|
+
// ── --reset ─────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
if (flagArgs.includes('--reset')) {
|
|
63
101
|
const neutralCSS = path.join(packageRoot, 'src/styles/global.css');
|
|
64
102
|
const targetCSS = path.join(cwd, 'src/styles/global.css');
|
|
65
103
|
if (fs.existsSync(neutralCSS)) {
|
|
@@ -72,7 +110,14 @@ if (arg === '--reset') {
|
|
|
72
110
|
process.exit(0);
|
|
73
111
|
}
|
|
74
112
|
|
|
75
|
-
|
|
113
|
+
// ── Resolve brand name ──────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
const brandName = positionalArgs[0];
|
|
116
|
+
if (!brandName) {
|
|
117
|
+
console.error('Brand name required. Run with --list to see available brands.');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
76
121
|
const brandDir = path.join(brandsDir, brandName);
|
|
77
122
|
|
|
78
123
|
if (!fs.existsSync(brandDir)) {
|
|
@@ -84,85 +129,123 @@ if (!fs.existsSync(brandDir)) {
|
|
|
84
129
|
process.exit(1);
|
|
85
130
|
}
|
|
86
131
|
|
|
87
|
-
|
|
132
|
+
// ── --demo: Install the full demo app (with NEUTRAL theme) ──────────────────
|
|
88
133
|
|
|
89
|
-
|
|
134
|
+
if (isDemo) {
|
|
135
|
+
const appDir = path.join(brandDir, 'app');
|
|
136
|
+
if (!fs.existsSync(appDir)) {
|
|
137
|
+
console.error(`Brand "${brandName}" does not have a demo app (no app/ directory).`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
90
140
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
141
|
+
console.log(`\n🚀 Installing "${brandName}" demo app...\n`);
|
|
142
|
+
let installed = 0;
|
|
143
|
+
|
|
144
|
+
// Copy the full app directory tree into src/
|
|
145
|
+
const appSubdirs = ['pages', 'hooks', 'api', 'config', 'features', 'components/workspace', 'components/alerts', 'components/layouts', 'styles'];
|
|
146
|
+
|
|
147
|
+
for (const sub of appSubdirs) {
|
|
148
|
+
const src = path.join(appDir, sub);
|
|
149
|
+
const dst = path.join(cwd, 'src', sub);
|
|
150
|
+
if (fs.existsSync(src)) {
|
|
151
|
+
const count = copyDirectoryRecursive(src, dst);
|
|
152
|
+
if (count > 0) {
|
|
153
|
+
console.log(` ✓ Installed ${count} files in src/${sub}/`);
|
|
154
|
+
installed += count;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
100
158
|
|
|
101
|
-
//
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
159
|
+
// Copy AgentforceConversationClient to src/components/
|
|
160
|
+
const agentClient = path.join(appDir, 'components/AgentforceConversationClient.tsx');
|
|
161
|
+
const agentClientInherit = path.join(appDir, 'components/__inherit_AgentforceConversationClient.tsx');
|
|
162
|
+
const componentsDir = path.join(cwd, 'src/components');
|
|
163
|
+
if (!fs.existsSync(componentsDir)) fs.mkdirSync(componentsDir, { recursive: true });
|
|
164
|
+
|
|
165
|
+
if (fs.existsSync(agentClient)) {
|
|
166
|
+
fs.copyFileSync(agentClient, path.join(componentsDir, 'AgentforceConversationClient.tsx'));
|
|
167
|
+
console.log(' ✓ Installed src/components/AgentforceConversationClient.tsx');
|
|
168
|
+
installed++;
|
|
169
|
+
}
|
|
170
|
+
if (fs.existsSync(agentClientInherit)) {
|
|
171
|
+
fs.copyFileSync(agentClientInherit, path.join(componentsDir, '__inherit_AgentforceConversationClient.tsx'));
|
|
114
172
|
installed++;
|
|
115
173
|
}
|
|
116
|
-
}
|
|
117
174
|
|
|
118
|
-
//
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
175
|
+
// Copy root-level app files (routes, appLayout, etc.)
|
|
176
|
+
const rootFiles = ['routes.tsx', 'appLayout.tsx', 'navigationMenu.tsx', 'router-utils.tsx'];
|
|
177
|
+
for (const file of rootFiles) {
|
|
178
|
+
const src = path.join(appDir, file);
|
|
179
|
+
const dst = path.join(cwd, 'src', file);
|
|
180
|
+
if (fs.existsSync(src)) {
|
|
181
|
+
fs.copyFileSync(src, dst);
|
|
182
|
+
console.log(` ✓ Installed src/${file}`);
|
|
183
|
+
installed++;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Copy sample data files
|
|
188
|
+
const dataFiles = ['engine-sample-data.js', 'engine-live-data.js', 'partner-hub-sample-data.js'];
|
|
189
|
+
for (const file of dataFiles) {
|
|
190
|
+
const src = path.join(brandDir, file);
|
|
191
|
+
const dst = path.join(cwd, 'src/data', file);
|
|
192
|
+
if (fs.existsSync(src)) {
|
|
193
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
194
|
+
fs.copyFileSync(src, dst);
|
|
195
|
+
console.log(` ✓ Installed src/data/${file}`);
|
|
196
|
+
installed++;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Copy PRDs to project root
|
|
201
|
+
const prds = fs.readdirSync(brandDir).filter(f => f.endsWith('_PRD.md') || f.endsWith('-prd.md'));
|
|
202
|
+
for (const prdFile of prds) {
|
|
203
|
+
fs.copyFileSync(path.join(brandDir, prdFile), path.join(cwd, prdFile));
|
|
204
|
+
console.log(` ✓ Installed ${prdFile}`);
|
|
130
205
|
installed++;
|
|
131
206
|
}
|
|
132
|
-
}
|
|
133
207
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const srcPath = path.join(brandDir, src);
|
|
140
|
-
const dstPath = path.join(cwd, dst);
|
|
141
|
-
if (fs.existsSync(srcPath)) {
|
|
142
|
-
fs.mkdirSync(path.dirname(dstPath), { recursive: true });
|
|
143
|
-
fs.copyFileSync(srcPath, dstPath);
|
|
144
|
-
console.log(` ✓ Installed ${dst}`);
|
|
208
|
+
// Copy schema
|
|
209
|
+
const schema = path.join(brandDir, 'schema.graphql');
|
|
210
|
+
if (fs.existsSync(schema)) {
|
|
211
|
+
fs.copyFileSync(schema, path.join(cwd, 'schema.graphql'));
|
|
212
|
+
console.log(' ✓ Installed schema.graphql');
|
|
145
213
|
installed++;
|
|
146
214
|
}
|
|
147
|
-
}
|
|
148
215
|
|
|
149
|
-
//
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
216
|
+
// Copy logo
|
|
217
|
+
const logo = path.join(brandDir, 'engine_logo.png');
|
|
218
|
+
if (fs.existsSync(logo)) {
|
|
219
|
+
const logoTarget = path.join(cwd, 'src/assets/images/engine_logo.png');
|
|
220
|
+
fs.mkdirSync(path.dirname(logoTarget), { recursive: true });
|
|
221
|
+
fs.copyFileSync(logo, logoTarget);
|
|
222
|
+
console.log(' ✓ Installed engine_logo.png');
|
|
223
|
+
installed++;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`\n✅ "${brandName}" demo app installed (${installed} files).`);
|
|
227
|
+
console.log(' The app uses the NEUTRAL color palette.');
|
|
228
|
+
console.log(` Run "npm run brand:${brandName}" to switch to ${brandName} colors.\n`);
|
|
229
|
+
process.exit(0);
|
|
155
230
|
}
|
|
156
231
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
// ── Default: Apply brand colors only ────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
console.log(`\n🎨 Applying "${brandName}" brand colors...\n`);
|
|
235
|
+
|
|
236
|
+
let installed = 0;
|
|
237
|
+
|
|
238
|
+
// 1. global.css → src/styles/global.css (always overwrite)
|
|
239
|
+
const brandCSS = path.join(brandDir, 'global.css');
|
|
240
|
+
const targetCSS = path.join(cwd, 'src/styles/global.css');
|
|
241
|
+
if (fs.existsSync(brandCSS)) {
|
|
242
|
+
fs.mkdirSync(path.dirname(targetCSS), { recursive: true });
|
|
243
|
+
fs.copyFileSync(brandCSS, targetCSS);
|
|
244
|
+
console.log(' ✓ Brand theme applied (global.css)');
|
|
162
245
|
installed++;
|
|
163
246
|
}
|
|
164
247
|
|
|
165
|
-
//
|
|
248
|
+
// 2. Logo → src/assets/images/
|
|
166
249
|
const logo = path.join(brandDir, 'engine_logo.png');
|
|
167
250
|
if (fs.existsSync(logo)) {
|
|
168
251
|
const logoTarget = path.join(cwd, 'src/assets/images/engine_logo.png');
|
|
@@ -172,8 +255,8 @@ if (fs.existsSync(logo)) {
|
|
|
172
255
|
installed++;
|
|
173
256
|
}
|
|
174
257
|
|
|
175
|
-
//
|
|
258
|
+
// 3. Write .brand marker
|
|
176
259
|
fs.writeFileSync(path.join(cwd, '.brand'), brandName + '\n', 'utf-8');
|
|
177
260
|
|
|
178
|
-
console.log(`\n✅ "${brandName}" brand applied (${installed} files
|
|
261
|
+
console.log(`\n✅ "${brandName}" brand colors applied (${installed} files).`);
|
|
179
262
|
console.log(' Run "npm run brand:reset" to revert to neutral theme.\n');
|