@mostrom/service-catalog 0.1.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/README.md +22 -0
- package/package.json +12 -0
- package/src/adapter.ts +103 -0
- package/src/index.ts +15 -0
- package/src/mock/catalog.ts +496 -0
- package/src/source.ts +21 -0
- package/src/types.ts +27 -0
- package/test-search.mjs +6 -0
- package/test-search.ts +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Service Catalog
|
|
2
|
+
|
|
3
|
+
Shared catalog data for platform services. This package provides the data contract and mock dataset used by the App Shell and other apps.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { getServiceCatalogSync, searchServices } from "@platform/service-catalog";
|
|
9
|
+
|
|
10
|
+
const catalog = getServiceCatalogSync();
|
|
11
|
+
const results = catalog ? searchServices(catalog, "crm") : [];
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Swap mock data for API
|
|
15
|
+
|
|
16
|
+
The default export uses the mock catalog in `src/mock/catalog.ts`.
|
|
17
|
+
To replace it with a real API later:
|
|
18
|
+
|
|
19
|
+
1. Create a fetcher that returns `ServiceCatalog`.
|
|
20
|
+
2. Replace `defaultSource` in `src/adapter.ts` with `createRemoteCatalogSource(fetcher)`.
|
|
21
|
+
|
|
22
|
+
The UI will continue to work without any changes because it only relies on the shared contract.
|
package/package.json
ADDED
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { mockServiceCatalog } from "./mock/catalog";
|
|
2
|
+
import { createMockCatalogSource } from "./source";
|
|
3
|
+
import type { ServiceCatalog, ServiceGroup, ServiceItem } from "./types";
|
|
4
|
+
import type { ServiceCatalogSource } from "./source";
|
|
5
|
+
|
|
6
|
+
const defaultSource = createMockCatalogSource(mockServiceCatalog);
|
|
7
|
+
|
|
8
|
+
export function getServiceCatalog(): Promise<ServiceCatalog> {
|
|
9
|
+
return defaultSource.getCatalog();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getServiceCatalogSync(): ServiceCatalog | null {
|
|
13
|
+
return defaultSource.getCatalogSync?.() ?? null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function groupServicesByCategory(catalog: ServiceCatalog): ServiceGroup[] {
|
|
17
|
+
const servicesByCategory = new Map<string, ServiceItem[]>();
|
|
18
|
+
|
|
19
|
+
for (const service of catalog.services) {
|
|
20
|
+
const bucket = servicesByCategory.get(service.categoryId) ?? [];
|
|
21
|
+
bucket.push(service);
|
|
22
|
+
servicesByCategory.set(service.categoryId, bucket);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const categories = [...catalog.categories].sort((a, b) => a.order - b.order);
|
|
26
|
+
|
|
27
|
+
return categories.map((category) => ({
|
|
28
|
+
category,
|
|
29
|
+
services: (servicesByCategory.get(category.id) ?? []).sort((a, b) =>
|
|
30
|
+
a.name.localeCompare(b.name),
|
|
31
|
+
),
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const normalize = (value: string) =>
|
|
36
|
+
value
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
39
|
+
.replace(/\s+/g, " ")
|
|
40
|
+
.trim();
|
|
41
|
+
|
|
42
|
+
const scoreMatch = (text: string, query: string) => {
|
|
43
|
+
if (!text) return 0;
|
|
44
|
+
if (text.startsWith(query)) return 6;
|
|
45
|
+
if (text.includes(query)) return 3;
|
|
46
|
+
return 0;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function searchServices(
|
|
50
|
+
catalog: ServiceCatalog,
|
|
51
|
+
query: string,
|
|
52
|
+
limit = 12,
|
|
53
|
+
): ServiceItem[] {
|
|
54
|
+
const normalizedQuery = normalize(query);
|
|
55
|
+
if (!normalizedQuery) return [];
|
|
56
|
+
|
|
57
|
+
const tokens = normalizedQuery.split(" ");
|
|
58
|
+
|
|
59
|
+
// Build category lookup map for including category names in search
|
|
60
|
+
const categoryMap = new Map(
|
|
61
|
+
catalog.categories.map((cat) => [cat.id, normalize(cat.label)])
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const scored = catalog.services
|
|
65
|
+
.map((service) => {
|
|
66
|
+
const name = normalize(service.name);
|
|
67
|
+
const summary = normalize(service.summary);
|
|
68
|
+
const keywords = (service.keywords ?? []).map(normalize).join(" ");
|
|
69
|
+
const categoryName = categoryMap.get(service.categoryId) ?? "";
|
|
70
|
+
|
|
71
|
+
let score = 0;
|
|
72
|
+
score += scoreMatch(name, normalizedQuery) * 3;
|
|
73
|
+
score += scoreMatch(summary, normalizedQuery) * 2;
|
|
74
|
+
score += scoreMatch(keywords, normalizedQuery);
|
|
75
|
+
score += scoreMatch(categoryName, normalizedQuery);
|
|
76
|
+
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
if (name.includes(token)) score += 2;
|
|
79
|
+
if (summary.includes(token)) score += 1;
|
|
80
|
+
if (keywords.includes(token)) score += 1;
|
|
81
|
+
if (categoryName.includes(token)) score += 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { service, score };
|
|
85
|
+
})
|
|
86
|
+
.filter((item) => item.score > 0)
|
|
87
|
+
.sort((a, b) => {
|
|
88
|
+
if (a.score !== b.score) return b.score - a.score;
|
|
89
|
+
return a.service.name.localeCompare(b.service.name);
|
|
90
|
+
})
|
|
91
|
+
.map((item) => item.service);
|
|
92
|
+
|
|
93
|
+
return scored.slice(0, limit);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function createServiceCatalogClient(source: ServiceCatalogSource = defaultSource) {
|
|
97
|
+
return {
|
|
98
|
+
getCatalog: () => source.getCatalog(),
|
|
99
|
+
getCatalogSync: () => source.getCatalogSync?.() ?? null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const serviceCatalogClient = createServiceCatalogClient(defaultSource);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type { ServiceCatalog, ServiceCategory, ServiceGroup, ServiceItem } from "./types";
|
|
2
|
+
export type { ServiceCatalogSource } from "./source";
|
|
3
|
+
export {
|
|
4
|
+
createRemoteCatalogSource,
|
|
5
|
+
createMockCatalogSource,
|
|
6
|
+
} from "./source";
|
|
7
|
+
export {
|
|
8
|
+
getServiceCatalog,
|
|
9
|
+
getServiceCatalogSync,
|
|
10
|
+
groupServicesByCategory,
|
|
11
|
+
searchServices,
|
|
12
|
+
createServiceCatalogClient,
|
|
13
|
+
serviceCatalogClient,
|
|
14
|
+
} from "./adapter";
|
|
15
|
+
export { mockServiceCatalog } from "./mock/catalog";
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import type { ServiceCatalog } from "../types";
|
|
2
|
+
|
|
3
|
+
export const mockServiceCatalog: ServiceCatalog = {
|
|
4
|
+
updatedAt: "2026-02-08T00:00:00.000Z",
|
|
5
|
+
categories: [
|
|
6
|
+
{
|
|
7
|
+
id: "client-management",
|
|
8
|
+
label: "Client Management",
|
|
9
|
+
description: "Manage clients, pipeline, and customer workstreams.",
|
|
10
|
+
order: 1,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "project-management",
|
|
14
|
+
label: "Project Management",
|
|
15
|
+
description: "Plan, track, and deliver projects and tasks.",
|
|
16
|
+
order: 2,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "finance-management",
|
|
20
|
+
label: "Finance Management",
|
|
21
|
+
description: "Bill, collect, and track financial performance.",
|
|
22
|
+
order: 3,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "knowledge-management",
|
|
26
|
+
label: "Knowledge Management",
|
|
27
|
+
description: "Wikis, knowledge bases, and internal documentation.",
|
|
28
|
+
order: 4,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "people-and-hr",
|
|
32
|
+
label: "People & HR",
|
|
33
|
+
description: "Hiring, payroll, and HR operations.",
|
|
34
|
+
order: 5,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "growth-and-marketing",
|
|
38
|
+
label: "Growth & Marketing",
|
|
39
|
+
description: "Campaigns, automation, and audience engagement.",
|
|
40
|
+
order: 6,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "operations-and-automation",
|
|
44
|
+
label: "Operations & Automation",
|
|
45
|
+
description: "Workflow automation and system integrations.",
|
|
46
|
+
order: 7,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "analytics",
|
|
50
|
+
label: "Analytics",
|
|
51
|
+
description: "Dashboards, reporting, and performance insights.",
|
|
52
|
+
order: 8,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "administration",
|
|
56
|
+
label: "Administration",
|
|
57
|
+
description: "Billing, spending governance, and access control.",
|
|
58
|
+
order: 9,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
services: [
|
|
62
|
+
// Client Management
|
|
63
|
+
{
|
|
64
|
+
id: "clients",
|
|
65
|
+
name: "Clients",
|
|
66
|
+
summary: "Track client accounts and relationship history.",
|
|
67
|
+
categoryId: "client-management",
|
|
68
|
+
href: "/client-management/clients",
|
|
69
|
+
keywords: ["crm", "accounts", "customers"],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "contacts",
|
|
73
|
+
name: "Contacts",
|
|
74
|
+
summary: "Manage people, roles, and contact details.",
|
|
75
|
+
categoryId: "client-management",
|
|
76
|
+
href: "/client-management/contacts",
|
|
77
|
+
keywords: ["people", "stakeholders", "directory"],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "deals",
|
|
81
|
+
name: "Deals",
|
|
82
|
+
summary: "Monitor pipeline stages, forecasts, and close dates.",
|
|
83
|
+
categoryId: "client-management",
|
|
84
|
+
href: "/client-management/deals",
|
|
85
|
+
keywords: ["pipeline", "sales", "opportunities"],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "estimates",
|
|
89
|
+
name: "Estimates",
|
|
90
|
+
summary: "Create estimates and quotes tied to client work.",
|
|
91
|
+
categoryId: "client-management",
|
|
92
|
+
href: "/client-management/estimates",
|
|
93
|
+
keywords: ["quotes", "pricing", "scope"],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "proposals",
|
|
97
|
+
name: "Proposals",
|
|
98
|
+
summary: "Build proposals with scope, pricing, and approvals.",
|
|
99
|
+
categoryId: "client-management",
|
|
100
|
+
href: "/client-management/proposals",
|
|
101
|
+
keywords: ["rfp", "scope", "approvals"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "contracts",
|
|
105
|
+
name: "Contracts",
|
|
106
|
+
summary: "Generate agreements and track signatures.",
|
|
107
|
+
categoryId: "client-management",
|
|
108
|
+
href: "/client-management/contracts",
|
|
109
|
+
keywords: ["agreements", "signatures", "legal"],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "cm-forms",
|
|
113
|
+
name: "Forms",
|
|
114
|
+
summary: "Create contact forms and intake surveys.",
|
|
115
|
+
categoryId: "client-management",
|
|
116
|
+
href: "/client-management/forms",
|
|
117
|
+
keywords: ["surveys", "intake", "questionnaires"],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "scheduling",
|
|
121
|
+
name: "Scheduling",
|
|
122
|
+
summary: "Book meetings and coordinate availability.",
|
|
123
|
+
categoryId: "client-management",
|
|
124
|
+
href: "/client-management/scheduling",
|
|
125
|
+
keywords: ["calendar", "meetings", "availability"],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: "services",
|
|
129
|
+
name: "Services",
|
|
130
|
+
summary: "Define service offerings and standard packages.",
|
|
131
|
+
categoryId: "client-management",
|
|
132
|
+
href: "/client-management/services",
|
|
133
|
+
keywords: ["offerings", "packages", "catalog"],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "client-portal",
|
|
137
|
+
name: "Client Portal",
|
|
138
|
+
summary: "Give clients a secure space to view work and files.",
|
|
139
|
+
categoryId: "client-management",
|
|
140
|
+
href: "/client-management/client-portal",
|
|
141
|
+
keywords: ["portal", "sharing", "client access"],
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Project Management
|
|
145
|
+
{
|
|
146
|
+
id: "projects",
|
|
147
|
+
name: "Projects",
|
|
148
|
+
summary: "Create and manage projects with tasks, milestones, and team assignments.",
|
|
149
|
+
categoryId: "project-management",
|
|
150
|
+
href: "/project-management/projects",
|
|
151
|
+
keywords: ["workstreams", "delivery", "planning"],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "tasks",
|
|
155
|
+
name: "Tasks",
|
|
156
|
+
summary: "Manage individual work items across all projects.",
|
|
157
|
+
categoryId: "project-management",
|
|
158
|
+
href: "/project-management/tasks",
|
|
159
|
+
keywords: ["kanban", "backlog", "workflow", "todo"],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "milestones",
|
|
163
|
+
name: "Milestones",
|
|
164
|
+
summary: "Define and track key project milestones and deliverables.",
|
|
165
|
+
categoryId: "project-management",
|
|
166
|
+
href: "/project-management/milestones",
|
|
167
|
+
keywords: ["deadlines", "deliverables", "checkpoints"],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "time-tracking",
|
|
171
|
+
name: "Time Tracking",
|
|
172
|
+
summary: "Log and track time spent on tasks and projects.",
|
|
173
|
+
categoryId: "project-management",
|
|
174
|
+
href: "/project-management/time-tracking",
|
|
175
|
+
keywords: ["hours", "utilization", "billing", "logging"],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "timesheets",
|
|
179
|
+
name: "Timesheets",
|
|
180
|
+
summary: "Review and approve weekly timesheets.",
|
|
181
|
+
categoryId: "project-management",
|
|
182
|
+
href: "/project-management/timesheets",
|
|
183
|
+
keywords: ["timesheets", "approvals", "payroll"],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
id: "resources",
|
|
187
|
+
name: "Resources",
|
|
188
|
+
summary: "Manage team members and their availability.",
|
|
189
|
+
categoryId: "project-management",
|
|
190
|
+
href: "/project-management/resources",
|
|
191
|
+
keywords: ["capacity", "staffing", "allocation", "team"],
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "pm-reports",
|
|
195
|
+
name: "Reports",
|
|
196
|
+
summary: "Generate and view project reports.",
|
|
197
|
+
categoryId: "project-management",
|
|
198
|
+
href: "/project-management/reports",
|
|
199
|
+
keywords: ["status", "insights", "performance", "analytics"],
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// Finance Management
|
|
203
|
+
{
|
|
204
|
+
id: "invoicing",
|
|
205
|
+
name: "Invoicing",
|
|
206
|
+
summary: "Create and manage invoices and recurring billing.",
|
|
207
|
+
categoryId: "finance-management",
|
|
208
|
+
href: "/finance-management/invoicing",
|
|
209
|
+
keywords: ["billing", "invoices", "accounts receivable"],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: "payments",
|
|
213
|
+
name: "Payments",
|
|
214
|
+
summary: "Track received payments and reconcile transactions.",
|
|
215
|
+
categoryId: "finance-management",
|
|
216
|
+
href: "/finance-management/payments",
|
|
217
|
+
keywords: ["collections", "payments", "reconciliation"],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: "transactions",
|
|
221
|
+
name: "Transactions",
|
|
222
|
+
summary: "Track income and expenses in one place.",
|
|
223
|
+
categoryId: "finance-management",
|
|
224
|
+
href: "/finance-management/transactions",
|
|
225
|
+
keywords: ["income", "expenses", "money movement"],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "bookkeeping",
|
|
229
|
+
name: "Bookkeeping",
|
|
230
|
+
summary: "Maintain chart of accounts and journal entries.",
|
|
231
|
+
categoryId: "finance-management",
|
|
232
|
+
href: "/finance-management/bookkeeping",
|
|
233
|
+
keywords: ["general ledger", "accounts", "journals"],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: "rate-cards",
|
|
237
|
+
name: "Rate Cards",
|
|
238
|
+
summary: "Standardize pricing by role, service, or region.",
|
|
239
|
+
categoryId: "finance-management",
|
|
240
|
+
href: "/finance-management/rate-cards",
|
|
241
|
+
keywords: ["pricing", "rates", "billing"],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "fm-reports",
|
|
245
|
+
name: "Reports",
|
|
246
|
+
summary: "Generate financial reports and track budgets.",
|
|
247
|
+
categoryId: "finance-management",
|
|
248
|
+
href: "/finance-management/reports",
|
|
249
|
+
keywords: ["p&l", "balance sheet", "budgets", "analytics"],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
id: "fm-integrations",
|
|
253
|
+
name: "Integrations",
|
|
254
|
+
summary: "Connect to QuickBooks, Xero, Stripe, and more.",
|
|
255
|
+
categoryId: "finance-management",
|
|
256
|
+
href: "/finance-management/integrations",
|
|
257
|
+
keywords: ["quickbooks", "xero", "stripe", "sync"],
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
// Knowledge Management
|
|
261
|
+
{
|
|
262
|
+
id: "wikis-knowledge",
|
|
263
|
+
name: "Wiki",
|
|
264
|
+
summary: "Organize internal knowledge and documentation.",
|
|
265
|
+
categoryId: "knowledge-management",
|
|
266
|
+
href: "/knowledge-management/wiki",
|
|
267
|
+
keywords: ["confluence", "docs", "knowledge"],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "knowledge-base",
|
|
271
|
+
name: "Knowledge Base",
|
|
272
|
+
summary: "Publish help articles and FAQs for teams.",
|
|
273
|
+
categoryId: "knowledge-management",
|
|
274
|
+
href: "/knowledge-management/knowledge-base",
|
|
275
|
+
keywords: ["faq", "help center", "support"],
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: "policies-sops",
|
|
279
|
+
name: "Policies & SOPs",
|
|
280
|
+
summary: "Store policies, procedures, and playbooks.",
|
|
281
|
+
categoryId: "knowledge-management",
|
|
282
|
+
href: "/knowledge-management/policies-sops",
|
|
283
|
+
keywords: ["policies", "sops", "governance"],
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
// People & HR
|
|
287
|
+
{
|
|
288
|
+
id: "people",
|
|
289
|
+
name: "People",
|
|
290
|
+
summary: "Employee directory and lifecycle management.",
|
|
291
|
+
categoryId: "people-and-hr",
|
|
292
|
+
href: "/people-and-hr/people",
|
|
293
|
+
keywords: ["employees", "directory", "onboarding", "offboarding"],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: "recruiting",
|
|
297
|
+
name: "Recruiting",
|
|
298
|
+
summary: "Job postings, candidate pipeline, and hiring workflow.",
|
|
299
|
+
categoryId: "people-and-hr",
|
|
300
|
+
href: "/people-and-hr/recruiting",
|
|
301
|
+
keywords: ["candidates", "jobs", "hiring", "ats"],
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "payroll",
|
|
305
|
+
name: "Payroll",
|
|
306
|
+
summary: "Pay runs, compensation, and payslips.",
|
|
307
|
+
categoryId: "people-and-hr",
|
|
308
|
+
href: "/people-and-hr/payroll",
|
|
309
|
+
keywords: ["pay", "compensation", "salary"],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: "hr-expenses",
|
|
313
|
+
name: "Expenses",
|
|
314
|
+
summary: "Employee expense tracking and reimbursements.",
|
|
315
|
+
categoryId: "people-and-hr",
|
|
316
|
+
href: "/people-and-hr/expenses",
|
|
317
|
+
keywords: ["reimbursements", "receipts", "expense reports"],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: "time-off",
|
|
321
|
+
name: "Time Off",
|
|
322
|
+
summary: "PTO, leave requests, and balances.",
|
|
323
|
+
categoryId: "people-and-hr",
|
|
324
|
+
href: "/people-and-hr/time-off",
|
|
325
|
+
keywords: ["pto", "vacation", "leave", "attendance"],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: "benefits",
|
|
329
|
+
name: "Benefits",
|
|
330
|
+
summary: "Health insurance, retirement, and perks administration.",
|
|
331
|
+
categoryId: "people-and-hr",
|
|
332
|
+
href: "/people-and-hr/benefits",
|
|
333
|
+
keywords: ["health", "insurance", "401k", "perks"],
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: "compliance",
|
|
337
|
+
name: "Compliance",
|
|
338
|
+
summary: "Tax documents and regulatory compliance.",
|
|
339
|
+
categoryId: "people-and-hr",
|
|
340
|
+
href: "/people-and-hr/compliance",
|
|
341
|
+
keywords: ["taxes", "w2", "1099", "audits"],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
id: "hr-documents",
|
|
345
|
+
name: "Documents",
|
|
346
|
+
summary: "HR documents, offer letters, and policies.",
|
|
347
|
+
categoryId: "people-and-hr",
|
|
348
|
+
href: "/people-and-hr/documents",
|
|
349
|
+
keywords: ["templates", "offer letters", "policies", "handbook"],
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
id: "hr-reports",
|
|
353
|
+
name: "Reports",
|
|
354
|
+
summary: "HR analytics and dashboards.",
|
|
355
|
+
categoryId: "people-and-hr",
|
|
356
|
+
href: "/people-and-hr/reports",
|
|
357
|
+
keywords: ["headcount", "turnover", "compensation", "analytics"],
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// Growth & Marketing
|
|
361
|
+
{
|
|
362
|
+
id: "campaigns",
|
|
363
|
+
name: "Campaigns",
|
|
364
|
+
summary: "Create and manage marketing campaigns across channels.",
|
|
365
|
+
categoryId: "growth-and-marketing",
|
|
366
|
+
href: "/growth-and-marketing/campaigns",
|
|
367
|
+
keywords: ["email", "social", "marketing", "outreach"],
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: "automations",
|
|
371
|
+
name: "Automations",
|
|
372
|
+
summary: "Build automated workflows triggered by user actions.",
|
|
373
|
+
categoryId: "growth-and-marketing",
|
|
374
|
+
href: "/growth-and-marketing/automations",
|
|
375
|
+
keywords: ["workflows", "triggers", "nurture", "drip"],
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: "sms",
|
|
379
|
+
name: "SMS",
|
|
380
|
+
summary: "Send targeted SMS messages to your audience.",
|
|
381
|
+
categoryId: "growth-and-marketing",
|
|
382
|
+
href: "/growth-and-marketing/sms",
|
|
383
|
+
keywords: ["text", "messaging", "mobile"],
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
id: "gm-forms",
|
|
387
|
+
name: "Forms",
|
|
388
|
+
summary: "Create lead capture and registration forms.",
|
|
389
|
+
categoryId: "growth-and-marketing",
|
|
390
|
+
href: "/growth-and-marketing/forms",
|
|
391
|
+
keywords: ["lead capture", "registration", "data collection"],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
id: "polls",
|
|
395
|
+
name: "Polls",
|
|
396
|
+
summary: "Create quick polls to gather instant feedback.",
|
|
397
|
+
categoryId: "growth-and-marketing",
|
|
398
|
+
href: "/growth-and-marketing/polls",
|
|
399
|
+
keywords: ["voting", "feedback", "questions"],
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: "surveys",
|
|
403
|
+
name: "Surveys",
|
|
404
|
+
summary: "Create multi-question surveys for detailed feedback.",
|
|
405
|
+
categoryId: "growth-and-marketing",
|
|
406
|
+
href: "/growth-and-marketing/surveys",
|
|
407
|
+
keywords: ["questionnaires", "research", "feedback"],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: "subscribe-forms",
|
|
411
|
+
name: "Subscribe Forms",
|
|
412
|
+
summary: "Create embeddable email subscribe forms for websites.",
|
|
413
|
+
categoryId: "growth-and-marketing",
|
|
414
|
+
href: "/growth-and-marketing/subscribe-forms",
|
|
415
|
+
keywords: ["opt-in", "newsletter", "email list", "embed"],
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: "gm-contacts",
|
|
419
|
+
name: "Contacts",
|
|
420
|
+
summary: "Manage your marketing contact list and segments.",
|
|
421
|
+
categoryId: "growth-and-marketing",
|
|
422
|
+
href: "/growth-and-marketing/contacts",
|
|
423
|
+
keywords: ["subscribers", "audience", "segments", "lists"],
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: "gm-analytics",
|
|
427
|
+
name: "Analytics",
|
|
428
|
+
summary: "Track and analyze marketing performance.",
|
|
429
|
+
categoryId: "growth-and-marketing",
|
|
430
|
+
href: "/growth-and-marketing/analytics",
|
|
431
|
+
keywords: ["metrics", "reports", "roi", "performance"],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
id: "content",
|
|
435
|
+
name: "Content",
|
|
436
|
+
summary: "Manage marketing assets and content library.",
|
|
437
|
+
categoryId: "growth-and-marketing",
|
|
438
|
+
href: "/growth-and-marketing/content",
|
|
439
|
+
keywords: ["assets", "media", "storage", "files"],
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: "gm-integrations",
|
|
443
|
+
name: "Integrations",
|
|
444
|
+
summary: "Connect marketing tools and third-party platforms.",
|
|
445
|
+
categoryId: "growth-and-marketing",
|
|
446
|
+
href: "/growth-and-marketing/integrations",
|
|
447
|
+
keywords: ["mailchimp", "hubspot", "salesforce", "api"],
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
// Operations & Automation
|
|
451
|
+
{
|
|
452
|
+
id: "workflow-automation",
|
|
453
|
+
name: "Workflow Automation",
|
|
454
|
+
summary: "Automate workflows across teams and tools.",
|
|
455
|
+
categoryId: "operations-and-automation",
|
|
456
|
+
href: "/operations-and-automation/workflow-automation",
|
|
457
|
+
keywords: ["zapier", "n8n", "automation"],
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
id: "integrations",
|
|
461
|
+
name: "Integrations",
|
|
462
|
+
summary: "Connect services to share data seamlessly.",
|
|
463
|
+
categoryId: "operations-and-automation",
|
|
464
|
+
href: "/operations-and-automation/integrations",
|
|
465
|
+
keywords: ["apis", "connectors", "workato"],
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
// Analytics
|
|
469
|
+
{
|
|
470
|
+
id: "dashboards-reporting",
|
|
471
|
+
name: "Dashboards & Reporting",
|
|
472
|
+
summary: "Visualize metrics and track KPIs.",
|
|
473
|
+
categoryId: "analytics",
|
|
474
|
+
href: "/analytics/dashboards-reporting",
|
|
475
|
+
keywords: ["dashboards", "analytics", "insights"],
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
// Administration
|
|
479
|
+
{
|
|
480
|
+
id: "billing",
|
|
481
|
+
name: "Billing",
|
|
482
|
+
summary: "View and pay bills, analyze and govern your spending, and optimize your costs.",
|
|
483
|
+
categoryId: "administration",
|
|
484
|
+
href: "/billing",
|
|
485
|
+
keywords: ["cost", "spending", "invoices", "payments", "budgets"],
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: "iam",
|
|
489
|
+
name: "IAM",
|
|
490
|
+
summary: "Manage access.",
|
|
491
|
+
categoryId: "administration",
|
|
492
|
+
href: "/iam",
|
|
493
|
+
keywords: ["identity", "access", "permissions", "roles", "users"],
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
};
|
package/src/source.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ServiceCatalog } from "./types";
|
|
2
|
+
|
|
3
|
+
export type ServiceCatalogSource = {
|
|
4
|
+
getCatalog: () => Promise<ServiceCatalog>;
|
|
5
|
+
getCatalogSync?: () => ServiceCatalog | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function createMockCatalogSource(catalog: ServiceCatalog): ServiceCatalogSource {
|
|
9
|
+
return {
|
|
10
|
+
getCatalog: async () => catalog,
|
|
11
|
+
getCatalogSync: () => catalog,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createRemoteCatalogSource(
|
|
16
|
+
fetchCatalog: () => Promise<ServiceCatalog>,
|
|
17
|
+
): ServiceCatalogSource {
|
|
18
|
+
return {
|
|
19
|
+
getCatalog: fetchCatalog,
|
|
20
|
+
};
|
|
21
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type ServiceCategory = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
order: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type ServiceItem = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
summary: string;
|
|
12
|
+
categoryId: string;
|
|
13
|
+
href: string;
|
|
14
|
+
keywords?: string[];
|
|
15
|
+
tags?: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ServiceCatalog = {
|
|
19
|
+
categories: ServiceCategory[];
|
|
20
|
+
services: ServiceItem[];
|
|
21
|
+
updatedAt: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ServiceGroup = {
|
|
25
|
+
category: ServiceCategory;
|
|
26
|
+
services: ServiceItem[];
|
|
27
|
+
};
|
package/test-search.mjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { searchServices } from "./dist/adapter.js";
|
|
2
|
+
import { mockServiceCatalog } from "./dist/mock/catalog.js";
|
|
3
|
+
|
|
4
|
+
const results = searchServices(mockServiceCatalog, "project");
|
|
5
|
+
console.log(`Search "project" returned ${results.length} results:`);
|
|
6
|
+
results.forEach(r => console.log(` - ${r.name} (${r.categoryId})`));
|
package/test-search.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { searchServices } from "./src/adapter.ts";
|
|
2
|
+
import { mockServiceCatalog } from "./src/mock/catalog.ts";
|
|
3
|
+
|
|
4
|
+
const results = searchServices(mockServiceCatalog, "project");
|
|
5
|
+
console.log(`Search "project" returned ${results.length} results:`);
|
|
6
|
+
results.forEach(r => console.log(` - ${r.name} (${r.categoryId})`));
|