@eventcatalog/core 2.19.9 → 2.20.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.
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "2.19.9";
40
+ var version = "2.20.0";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-TST4Y2ZK.js";
4
- import "../chunk-6YOAJVAK.js";
3
+ } from "../chunk-I5QQKYHU.js";
4
+ import "../chunk-PCV7T4HR.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -106,7 +106,7 @@ var import_axios = __toESM(require("axios"), 1);
106
106
  var import_os = __toESM(require("os"), 1);
107
107
 
108
108
  // package.json
109
- var version = "2.19.9";
109
+ var version = "2.20.0";
110
110
 
111
111
  // src/constants.ts
112
112
  var VERSION = version;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-V6S4JW6E.js";
4
- import "../chunk-TST4Y2ZK.js";
5
- import "../chunk-6YOAJVAK.js";
3
+ } from "../chunk-3C6KBZOA.js";
4
+ import "../chunk-I5QQKYHU.js";
5
+ import "../chunk-PCV7T4HR.js";
6
6
  import "../chunk-E7TXTI7G.js";
7
7
  export {
8
8
  log_build_default as default
@@ -34,7 +34,7 @@ __export(catalog_to_astro_content_directory_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(catalog_to_astro_content_directory_exports);
36
36
 
37
- // node_modules/.pnpm/tsup@8.3.5_jiti@1.21.7_postcss@8.4.49_typescript@5.7.3_yaml@2.7.0/node_modules/tsup/assets/cjs_shims.js
37
+ // node_modules/.pnpm/tsup@8.3.6_jiti@1.21.7_postcss@8.5.1_typescript@5.7.3_yaml@2.7.0/node_modules/tsup/assets/cjs_shims.js
38
38
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
39
39
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
40
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-TST4Y2ZK.js";
3
+ } from "./chunk-I5QQKYHU.js";
4
4
  import {
5
5
  getEventCatalogConfigFile,
6
6
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-6YOAJVAK.js";
3
+ } from "./chunk-PCV7T4HR.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "2.19.9";
2
+ var version = "2.20.0";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "2.19.9";
28
+ var version = "2.20.0";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-6YOAJVAK.js";
3
+ } from "./chunk-PCV7T4HR.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
 
25
- // node_modules/.pnpm/tsup@8.3.5_jiti@1.21.7_postcss@8.4.49_typescript@5.7.3_yaml@2.7.0/node_modules/tsup/assets/cjs_shims.js
25
+ // node_modules/.pnpm/tsup@8.3.6_jiti@1.21.7_postcss@8.5.1_typescript@5.7.3_yaml@2.7.0/node_modules/tsup/assets/cjs_shims.js
26
26
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
27
27
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
28
28
 
@@ -161,7 +161,7 @@ var import_axios = __toESM(require("axios"), 1);
161
161
  var import_os = __toESM(require("os"), 1);
162
162
 
163
163
  // package.json
164
- var version = "2.19.9";
164
+ var version = "2.20.0";
165
165
 
166
166
  // src/constants.ts
167
167
  var VERSION = version;
@@ -3,14 +3,14 @@ import {
3
3
  } from "./chunk-SHCMAL37.js";
4
4
  import {
5
5
  log_build_default
6
- } from "./chunk-V6S4JW6E.js";
7
- import "./chunk-TST4Y2ZK.js";
6
+ } from "./chunk-3C6KBZOA.js";
7
+ import "./chunk-I5QQKYHU.js";
8
8
  import {
9
9
  catalogToAstro
10
10
  } from "./chunk-WF34R5UT.js";
11
11
  import {
12
12
  VERSION
13
- } from "./chunk-6YOAJVAK.js";
13
+ } from "./chunk-PCV7T4HR.js";
14
14
  import {
15
15
  generate
16
16
  } from "./chunk-YEQVKHST.js";
@@ -20,14 +20,24 @@ import { isSameVersion } from '@utils/collections/util';
20
20
  declare module '@tanstack/react-table' {
21
21
  // @ts-ignore
22
22
  interface ColumnMeta<TData extends RowData, TValue> {
23
- filterVariant?: 'collection' | 'name' | 'badges';
24
- collectionFilterKey?: 'producers' | 'consumers' | 'sends' | 'receives' | 'services';
23
+ filterVariant?: 'collection' | 'name' | 'badges' | 'text';
24
+ collectionFilterKey?:
25
+ | 'producers'
26
+ | 'consumers'
27
+ | 'sends'
28
+ | 'receives'
29
+ | 'services'
30
+ | 'ownedCommands'
31
+ | 'ownedEvents'
32
+ | 'ownedServices'
33
+ | 'associatedTeams';
34
+ filteredItemHasVersion?: boolean;
25
35
  showFilter?: boolean;
26
36
  className?: string;
27
37
  }
28
38
  }
29
39
 
30
- export type TCollectionTypes = 'domains' | 'services' | CollectionMessageTypes | 'flows';
40
+ export type TCollectionTypes = 'domains' | 'services' | CollectionMessageTypes | 'flows' | 'users' | 'teams';
31
41
 
32
42
  export type TData<T extends TCollectionTypes> = {
33
43
  collection: T;
@@ -92,6 +102,19 @@ export type TData<T extends TCollectionTypes> = {
92
102
  };
93
103
  }>;
94
104
  // ---------------------------------------------------------------------------
105
+ // Users
106
+ avatarUrl?: string;
107
+ email?: string;
108
+ slackDirectMessageUrl?: string;
109
+ msTeamsDirectMessageUrl?: string;
110
+ role?: string;
111
+ ownedCommands: any;
112
+ ownedEvents: any;
113
+ ownedServices: any;
114
+ associatedTeams: any;
115
+
116
+ // Teams
117
+ members: any;
95
118
  };
96
119
  };
97
120
 
@@ -121,7 +144,6 @@ export const Table = <T extends TCollectionTypes>({
121
144
 
122
145
  useEffect(() => {
123
146
  const checkbox = document.getElementById(checkboxLatestId);
124
-
125
147
  function handleChange(evt: Event) {
126
148
  setShowOnlyLatest((evt.target as HTMLInputElement).checked);
127
149
  }
@@ -266,7 +288,7 @@ export const Table = <T extends TCollectionTypes>({
266
288
  };
267
289
 
268
290
  function Filter<T extends TCollectionTypes>({ column }: { column: Column<TData<T>, unknown> }) {
269
- const { filterVariant, collectionFilterKey } = column.columnDef.meta ?? {};
291
+ const { filterVariant, collectionFilterKey, filteredItemHasVersion = true } = column.columnDef.meta ?? {};
270
292
 
271
293
  const columnFilterValue = column.getFilterValue();
272
294
 
@@ -275,7 +297,9 @@ function Filter<T extends TCollectionTypes>({ column }: { column: Column<TData<T
275
297
  const rows = column.getFacetedRowModel().rows;
276
298
  const data = rows.map((row) => row.original.data?.[collectionFilterKey] ?? []).flat();
277
299
 
278
- const allItems = data.map((item) => `${item?.data.name} (v${item?.data.version})`);
300
+ const allItems = data.map((item) =>
301
+ filteredItemHasVersion ? `${item?.data.name} (v${item?.data.version})` : `${item?.data.name}`
302
+ );
279
303
  const uniqueItemsInList = Array.from(new Set(allItems));
280
304
 
281
305
  return uniqueItemsInList.sort().slice(0, 2000);
@@ -289,7 +313,9 @@ function Filter<T extends TCollectionTypes>({ column }: { column: Column<TData<T
289
313
  })
290
314
  .flat();
291
315
 
292
- const allItems = data.map((item) => `${item.data.name} (v${item.data.version})`);
316
+ const allItems = data.map((item) =>
317
+ filteredItemHasVersion ? `${item.data.name} (v${item.data.version})` : `${item.data.name}`
318
+ );
293
319
  const uniqueItemsInList = Array.from(new Set(allItems));
294
320
 
295
321
  return uniqueItemsInList.sort().slice(0, 2000);
@@ -0,0 +1,253 @@
1
+ import { MagnifyingGlassIcon } from '@heroicons/react/24/solid';
2
+ import { createColumnHelper } from '@tanstack/react-table';
3
+ import { useMemo, useState } from 'react';
4
+ import { filterByName, filterCollectionByName } from '../filters/custom-filters';
5
+ import { buildUrl } from '@utils/url-builder';
6
+ import type { TData } from '../Table';
7
+ import type { CollectionUserTypes } from '@types';
8
+ import { User, Users } from 'lucide-react';
9
+ import type { CollectionEntry } from 'astro:content';
10
+ import { ServerIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/solid';
11
+
12
+ const columnHelper = createColumnHelper<TData<CollectionUserTypes>>();
13
+
14
+ export const getColorAndIconForMessageType = (type: string) => {
15
+ switch (type) {
16
+ case 'event':
17
+ return { color: 'orange', Icon: BoltIcon };
18
+ case 'command':
19
+ return { color: 'blue', Icon: ChatBubbleLeftIcon };
20
+ case 'querie':
21
+ case 'query':
22
+ return { color: 'green', Icon: MagnifyingGlassIcon };
23
+ default:
24
+ return { color: 'gray', Icon: ChatBubbleLeftIcon };
25
+ }
26
+ };
27
+
28
+ export const columns = () => [
29
+ columnHelper.accessor('data.name', {
30
+ id: 'name',
31
+ header: () => <span>Name</span>,
32
+ cell: (info) => {
33
+ const messageRaw = info.row.original;
34
+ const type = useMemo(() => messageRaw.collection.slice(0, -1), [messageRaw.collection]);
35
+ return (
36
+ <div className=" group ">
37
+ <a
38
+ href={buildUrl(`/docs/${messageRaw.collection}/${messageRaw.data.id}`)}
39
+ className={`group-hover:text-pink-500 flex space-x-1 items-center`}
40
+ >
41
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md group-hover:border-pink-400`}>
42
+ <span className="flex items-center">
43
+ <span className={`bg-pink-500 group-hover:bg-pink-600 h-full rounded-tl rounded-bl p-1`}>
44
+ {!messageRaw.data.avatarUrl && <Users className="h-4 w-4 text-white" />}
45
+ </span>
46
+ <span className="leading-none px-2 group-hover:underline group-hover:text-primary font-light">
47
+ {messageRaw.data.name}
48
+ </span>
49
+ </span>
50
+ </div>
51
+ </a>
52
+ </div>
53
+ );
54
+ },
55
+ meta: {
56
+ filterVariant: 'name',
57
+ filteredItemHasVersion: false,
58
+ },
59
+ filterFn: filterByName,
60
+ }),
61
+
62
+ columnHelper.accessor('data.members', {
63
+ header: () => <span>Team members</span>,
64
+ meta: {
65
+ // filterVariant: 'collection',
66
+ showFilter: false,
67
+ },
68
+ cell: (info) => {
69
+ const members = info.getValue();
70
+ if (members?.length === 0 || !members)
71
+ return <div className="font-light text-sm text-gray-400/60 text-left italic">Team has no members</div>;
72
+
73
+ return <div>{members.length}</div>;
74
+ },
75
+ footer: (info) => info.column.id,
76
+ filterFn: filterByName,
77
+ }),
78
+
79
+ columnHelper.accessor('data.ownedCommands', {
80
+ header: () => <span>Owned commands</span>,
81
+ meta: {
82
+ filterVariant: 'collection',
83
+ collectionFilterKey: 'ownedCommands',
84
+ },
85
+ cell: (info) => {
86
+ const commands = info.getValue();
87
+ if (commands?.length === 0 || !commands)
88
+ return <div className="font-light text-sm text-gray-400/60 text-left italic">User owns no commands</div>;
89
+
90
+ const isExpandable = commands?.length > 10;
91
+ const isOpen = isExpandable ? commands?.length < 10 : true;
92
+ const [isExpanded, setIsExpanded] = useState(isOpen);
93
+
94
+ return (
95
+ <div>
96
+ {isExpandable && (
97
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
98
+ {isExpanded ? '▼' : '▶'} {commands.length} command{commands.length !== 1 ? 's' : ''}
99
+ </button>
100
+ )}
101
+ {isExpanded && (
102
+ <ul>
103
+ {commands.map((command: CollectionEntry<'commands'>, index: number) => (
104
+ <li key={`${command.data.id}-${index}`} className="py-1 group font-light ">
105
+ <a
106
+ href={buildUrl(`/docs/${command.collection}/${command.data.id}/${command.data.version}`)}
107
+ className="group-hover:text-primary flex space-x-1 items-center "
108
+ >
109
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
110
+ <span className="flex items-center">
111
+ <span className={`bg-blue-500 h-full rounded-tl rounded-bl p-1`}>
112
+ <ChatBubbleLeftIcon className="h-4 w-4 text-white" />
113
+ </span>
114
+ <span className="leading-none px-2 group-hover:underline ">
115
+ {command.data.name} (v{command.data.version})
116
+ </span>
117
+ </span>
118
+ </div>
119
+ </a>
120
+ </li>
121
+ ))}
122
+ </ul>
123
+ )}
124
+ </div>
125
+ );
126
+
127
+ // return commands.length;
128
+ },
129
+ footer: (info) => info.column.id,
130
+ filterFn: filterCollectionByName('ownedCommands'),
131
+ }),
132
+ columnHelper.accessor('data.ownedEvents', {
133
+ header: () => <span>Owned Events</span>,
134
+ meta: {
135
+ filterVariant: 'collection',
136
+ collectionFilterKey: 'ownedEvents',
137
+ },
138
+ cell: (info) => {
139
+ const events = info.getValue();
140
+ if (events?.length === 0 || !events)
141
+ return <div className="font-light text-sm text-gray-400/80 text-left italic">User owns no events</div>;
142
+
143
+ const isExpandable = events?.length > 10;
144
+ const isOpen = isExpandable ? events?.length < 10 : true;
145
+ const [isExpanded, setIsExpanded] = useState(isOpen);
146
+
147
+ return (
148
+ <div>
149
+ {isExpandable && (
150
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
151
+ {isExpanded ? '▼' : '▶'} {events.length} event{events.length !== 1 ? 's' : ''}
152
+ </button>
153
+ )}
154
+ {isExpanded && (
155
+ <ul>
156
+ {events.map((event: CollectionEntry<'events'>, index: number) => (
157
+ <li key={`${event.data.id}-${index}`} className="py-1 group font-light ">
158
+ <a
159
+ href={buildUrl(`/docs/${event.collection}/${event.data.id}/${event.data.version}`)}
160
+ className="group-hover:text-primary flex space-x-1 items-center "
161
+ >
162
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
163
+ <span className="flex items-center">
164
+ <span className={`bg-orange-500 h-full rounded-tl rounded-bl p-1`}>
165
+ <BoltIcon className="h-4 w-4 text-white" />
166
+ </span>
167
+ <span className="leading-none px-2 group-hover:underline ">
168
+ {event.data.name} (v{event.data.version})
169
+ </span>
170
+ </span>
171
+ </div>
172
+ </a>
173
+ </li>
174
+ ))}
175
+ </ul>
176
+ )}
177
+ </div>
178
+ );
179
+ },
180
+ footer: (info) => info.column.id,
181
+ filterFn: filterCollectionByName('ownedEvents'),
182
+ }),
183
+ columnHelper.accessor('data.ownedServices', {
184
+ header: () => <span>Owned Services</span>,
185
+ meta: {
186
+ filterVariant: 'collection',
187
+ collectionFilterKey: 'ownedServices',
188
+ },
189
+ cell: (info) => {
190
+ const services = info.getValue();
191
+ if (services?.length === 0 || !services)
192
+ return <div className="font-light text-sm text-gray-400/80 text-left italic">User owns no services</div>;
193
+
194
+ const isExpandable = services?.length > 10;
195
+ const isOpen = isExpandable ? services?.length < 10 : true;
196
+ const [isExpanded, setIsExpanded] = useState(isOpen);
197
+
198
+ return (
199
+ <div>
200
+ {isExpandable && (
201
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
202
+ {isExpanded ? '▼' : '▶'} {services.length} service{services.length !== 1 ? 's' : ''}
203
+ </button>
204
+ )}
205
+ {isExpanded && (
206
+ <ul>
207
+ {services.map((service: CollectionEntry<'services'>, index: number) => (
208
+ <li key={`${service.data.id}-${index}`} className="py-1 group font-light ">
209
+ <a
210
+ href={buildUrl(`/docs/${service.collection}/${service.data.id}/${service.data.version}`)}
211
+ className="group-hover:text-primary flex space-x-1 items-center "
212
+ >
213
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
214
+ <span className="flex items-center">
215
+ <span className={`bg-green-500 h-full rounded-tl rounded-bl p-1`}>
216
+ <ServerIcon className="h-4 w-4 text-white" />
217
+ </span>
218
+ <span className="leading-none px-2 group-hover:underline ">
219
+ {service.data.name} (v{service.data.version})
220
+ </span>
221
+ </span>
222
+ </div>
223
+ </a>
224
+ </li>
225
+ ))}
226
+ </ul>
227
+ )}
228
+ </div>
229
+ );
230
+ },
231
+ footer: (info) => info.column.id,
232
+ filterFn: filterCollectionByName('ownedServices'),
233
+ }),
234
+
235
+ columnHelper.accessor('data.name', {
236
+ header: () => <span />,
237
+ cell: (info) => {
238
+ const domain = info.row.original;
239
+ return (
240
+ <a
241
+ className="hover:text-primary hover:underline px-4 font-light"
242
+ href={buildUrl(`/docs/${domain.collection}/${domain.data.id}`)}
243
+ >
244
+ View &rarr;
245
+ </a>
246
+ );
247
+ },
248
+ id: 'actions',
249
+ meta: {
250
+ showFilter: false,
251
+ },
252
+ }),
253
+ ];
@@ -0,0 +1,289 @@
1
+ import { createColumnHelper } from '@tanstack/react-table';
2
+ import { useMemo, useState } from 'react';
3
+ import { filterByName, filterCollectionByName } from '../filters/custom-filters';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import type { TData } from '../Table';
6
+ import type { CollectionUserTypes } from '@types';
7
+ import { User, Users } from 'lucide-react';
8
+ import type { CollectionEntry } from 'astro:content';
9
+ import { ServerIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/solid';
10
+
11
+ const columnHelper = createColumnHelper<TData<CollectionUserTypes>>();
12
+
13
+ export const columns = () => [
14
+ columnHelper.accessor('data.name', {
15
+ id: 'name',
16
+ header: () => <span>Name</span>,
17
+ cell: (info) => {
18
+ const messageRaw = info.row.original;
19
+ const type = useMemo(() => messageRaw.collection.slice(0, -1), [messageRaw.collection]);
20
+ return (
21
+ <div className=" group ">
22
+ <a
23
+ href={buildUrl(`/docs/${messageRaw.collection}/${messageRaw.data.id}`)}
24
+ className={`group-hover:text-gray-500 flex space-x-1 items-center`}
25
+ >
26
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md group-hover:border-gray-400`}>
27
+ <span className="flex items-center">
28
+ {!messageRaw.data.avatarUrl && (
29
+ <span className={`bg-gray-300 group-hover:bg-gray-600 h-full rounded-tl rounded-bl p-1`}>
30
+ <User className="h-4 w-4 text-white" />
31
+ </span>
32
+ )}
33
+ {messageRaw.data.avatarUrl && (
34
+ <img src={messageRaw.data.avatarUrl} alt={messageRaw.data.name} className="h-12 w-12 rounded-md p-0.5" />
35
+ )}
36
+
37
+ <span className="leading-none px-2 group-hover:underline group-hover:text-primary font-light">
38
+ {messageRaw.data.name}
39
+ </span>
40
+ </span>
41
+ </div>
42
+ </a>
43
+ </div>
44
+ );
45
+ },
46
+ meta: {
47
+ filterVariant: 'name',
48
+ filteredItemHasVersion: false,
49
+ },
50
+ filterFn: filterByName,
51
+ }),
52
+
53
+ columnHelper.accessor('data.role', {
54
+ id: 'role',
55
+ header: () => 'Role',
56
+ cell: (info) => <span className="font-light ">{info.renderValue()}</span>,
57
+ footer: (info) => info.column.id,
58
+ meta: {
59
+ showFilter: false,
60
+ className: 'max-w-[200px]',
61
+ },
62
+ filterFn: filterCollectionByName('role'),
63
+ }),
64
+
65
+ columnHelper.accessor('data.ownedCommands', {
66
+ header: () => <span>Owned commands</span>,
67
+ meta: {
68
+ filterVariant: 'collection',
69
+ collectionFilterKey: 'ownedCommands',
70
+ },
71
+ cell: (info) => {
72
+ const commands = info.getValue();
73
+ if (commands?.length === 0 || !commands)
74
+ return <div className="font-light text-sm text-gray-400/60 text-left italic">User owns no commands</div>;
75
+
76
+ const isExpandable = commands?.length > 10;
77
+ const isOpen = isExpandable ? commands?.length < 10 : true;
78
+ const [isExpanded, setIsExpanded] = useState(isOpen);
79
+
80
+ return (
81
+ <div>
82
+ {isExpandable && (
83
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
84
+ {isExpanded ? '▼' : '▶'} {commands.length} command{commands.length !== 1 ? 's' : ''}
85
+ </button>
86
+ )}
87
+ {isExpanded && (
88
+ <ul>
89
+ {commands.map((command: CollectionEntry<'commands'>, index: number) => (
90
+ <li key={`${command.data.id}-${index}`} className="py-1 group font-light ">
91
+ <a
92
+ href={buildUrl(`/docs/${command.collection}/${command.data.id}/${command.data.version}`)}
93
+ className="group-hover:text-primary flex space-x-1 items-center "
94
+ >
95
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
96
+ <span className="flex items-center">
97
+ <span className={`bg-blue-500 h-full rounded-tl rounded-bl p-1`}>
98
+ <ChatBubbleLeftIcon className="h-4 w-4 text-white" />
99
+ </span>
100
+ <span className="leading-none px-2 group-hover:underline ">
101
+ {command.data.name} (v{command.data.version})
102
+ </span>
103
+ </span>
104
+ </div>
105
+ </a>
106
+ </li>
107
+ ))}
108
+ </ul>
109
+ )}
110
+ </div>
111
+ );
112
+
113
+ // return commands.length;
114
+ },
115
+ footer: (info) => info.column.id,
116
+ filterFn: filterCollectionByName('ownedCommands'),
117
+ }),
118
+ columnHelper.accessor('data.ownedEvents', {
119
+ header: () => <span>Owned Events</span>,
120
+ meta: {
121
+ filterVariant: 'collection',
122
+ collectionFilterKey: 'ownedEvents',
123
+ },
124
+ cell: (info) => {
125
+ const events = info.getValue();
126
+ if (events?.length === 0 || !events)
127
+ return <div className="font-light text-sm text-gray-400/80 text-left italic">User owns no events</div>;
128
+
129
+ const isExpandable = events?.length > 10;
130
+ const isOpen = isExpandable ? events?.length < 10 : true;
131
+ const [isExpanded, setIsExpanded] = useState(isOpen);
132
+
133
+ return (
134
+ <div>
135
+ {isExpandable && (
136
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
137
+ {isExpanded ? '▼' : '▶'} {events.length} event{events.length !== 1 ? 's' : ''}
138
+ </button>
139
+ )}
140
+ {isExpanded && (
141
+ <ul>
142
+ {events.map((event: CollectionEntry<'events'>, index: number) => (
143
+ <li key={`${event.data.id}-${index}`} className="py-1 group font-light ">
144
+ <a
145
+ href={buildUrl(`/docs/${event.collection}/${event.data.id}/${event.data.version}`)}
146
+ className="group-hover:text-primary flex space-x-1 items-center "
147
+ >
148
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
149
+ <span className="flex items-center">
150
+ <span className={`bg-orange-500 h-full rounded-tl rounded-bl p-1`}>
151
+ <BoltIcon className="h-4 w-4 text-white" />
152
+ </span>
153
+ <span className="leading-none px-2 group-hover:underline ">
154
+ {event.data.name} (v{event.data.version})
155
+ </span>
156
+ </span>
157
+ </div>
158
+ </a>
159
+ </li>
160
+ ))}
161
+ </ul>
162
+ )}
163
+ </div>
164
+ );
165
+ },
166
+ footer: (info) => info.column.id,
167
+ filterFn: filterCollectionByName('ownedEvents'),
168
+ }),
169
+ columnHelper.accessor('data.ownedServices', {
170
+ header: () => <span>Owned Services</span>,
171
+ meta: {
172
+ filterVariant: 'collection',
173
+ collectionFilterKey: 'ownedServices',
174
+ },
175
+ cell: (info) => {
176
+ const services = info.getValue();
177
+ if (services?.length === 0 || !services)
178
+ return <div className="font-light text-sm text-gray-400/80 text-left italic">User owns no services</div>;
179
+
180
+ const isExpandable = services?.length > 10;
181
+ const isOpen = isExpandable ? services?.length < 10 : true;
182
+ const [isExpanded, setIsExpanded] = useState(isOpen);
183
+
184
+ return (
185
+ <div>
186
+ {isExpandable && (
187
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
188
+ {isExpanded ? '▼' : '▶'} {services.length} service{services.length !== 1 ? 's' : ''}
189
+ </button>
190
+ )}
191
+ {isExpanded && (
192
+ <ul>
193
+ {services.map((service: CollectionEntry<'services'>, index: number) => (
194
+ <li key={`${service.data.id}-${index}`} className="py-1 group font-light ">
195
+ <a
196
+ href={buildUrl(`/docs/${service.collection}/${service.data.id}/${service.data.version}`)}
197
+ className="group-hover:text-primary flex space-x-1 items-center "
198
+ >
199
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
200
+ <span className="flex items-center">
201
+ <span className={`bg-green-500 h-full rounded-tl rounded-bl p-1`}>
202
+ <ServerIcon className="h-4 w-4 text-white" />
203
+ </span>
204
+ <span className="leading-none px-2 group-hover:underline ">
205
+ {service.data.name} (v{service.data.version})
206
+ </span>
207
+ </span>
208
+ </div>
209
+ </a>
210
+ </li>
211
+ ))}
212
+ </ul>
213
+ )}
214
+ </div>
215
+ );
216
+ },
217
+ footer: (info) => info.column.id,
218
+ filterFn: filterCollectionByName('ownedServices'),
219
+ }),
220
+ columnHelper.accessor('data.associatedTeams', {
221
+ header: () => <span>Teams</span>,
222
+ meta: {
223
+ filterVariant: 'collection',
224
+ collectionFilterKey: 'associatedTeams',
225
+ filteredItemHasVersion: false,
226
+ },
227
+ cell: (info) => {
228
+ const teams = info.getValue();
229
+
230
+ const isExpandable = teams?.length > 10;
231
+ const isOpen = isExpandable ? teams?.length < 10 : true;
232
+ const [isExpanded, setIsExpanded] = useState(isOpen);
233
+
234
+ if (teams?.length === 0 || !teams)
235
+ return <div className="font-light text-sm text-gray-400/80 text-left italic">User is not associated with any teams</div>;
236
+
237
+ return (
238
+ <div>
239
+ {isExpandable && (
240
+ <button onClick={() => setIsExpanded(!isExpanded)} className="mb-2 text-sm text-gray-600 hover:text-gray-900">
241
+ {isExpanded ? '▼' : '▶'} {teams.length} team{teams.length !== 1 ? 's' : ''}
242
+ </button>
243
+ )}
244
+ {isExpanded && (
245
+ <ul>
246
+ {teams.map((team: CollectionEntry<'teams'>, index: number) => (
247
+ <li key={`${team.data.id}-${index}`} className="py-1 group font-light ">
248
+ <a
249
+ href={buildUrl(`/docs/teams/${team.data.id}`)}
250
+ className="group-hover:text-primary flex space-x-1 items-center "
251
+ >
252
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md`}>
253
+ <span className="flex items-center">
254
+ <span className={`bg-pink-500 h-full rounded-tl rounded-bl p-1`}>
255
+ <Users className="h-4 w-4 text-white" />
256
+ </span>
257
+ <span className="leading-none px-2 group-hover:underline ">{team.data.name}</span>
258
+ </span>
259
+ </div>
260
+ </a>
261
+ </li>
262
+ ))}
263
+ </ul>
264
+ )}
265
+ </div>
266
+ );
267
+ },
268
+ footer: (info) => info.column.id,
269
+ filterFn: filterCollectionByName('associatedTeams'),
270
+ }),
271
+ columnHelper.accessor('data.name', {
272
+ header: () => <span />,
273
+ cell: (info) => {
274
+ const domain = info.row.original;
275
+ return (
276
+ <a
277
+ className="hover:text-primary hover:underline px-4 font-light"
278
+ href={buildUrl(`/docs/${domain.collection}/${domain.data.id}`)}
279
+ >
280
+ View &rarr;
281
+ </a>
282
+ );
283
+ },
284
+ id: 'actions',
285
+ meta: {
286
+ showFilter: false,
287
+ },
288
+ }),
289
+ ];
@@ -1,8 +1,9 @@
1
1
  import { columns as MessageTableColumns } from './MessageTableColumns';
2
+ import { columns as UserTableColumns } from './UserTableColumns';
2
3
  import { columns as ServiceTableColumns } from './ServiceTableColumns';
3
4
  import { columns as DomainTableColumns } from './DomainTableColumns';
4
5
  import { columns as FlowTableColumns } from './FlowTableColumns';
5
-
6
+ import { columns as TeamsTableColumns } from './TeamsTableColumns';
6
7
  export const getColumnsByCollection = (collection: string): any => {
7
8
  switch (collection) {
8
9
  case 'events':
@@ -15,6 +16,10 @@ export const getColumnsByCollection = (collection: string): any => {
15
16
  return DomainTableColumns();
16
17
  case 'flows':
17
18
  return FlowTableColumns();
19
+ case 'users':
20
+ return UserTableColumns();
21
+ case 'teams':
22
+ return TeamsTableColumns();
18
23
  default:
19
24
  return [];
20
25
  }
@@ -0,0 +1,110 @@
1
+ ---
2
+ import { Table, type TData, type TCollectionTypes } from '@components/Tables/Table';
3
+ import { QueueListIcon, RectangleGroupIcon, BoltIcon, ChatBubbleLeftIcon } from '@heroicons/react/24/outline';
4
+ import ServerIcon from '@heroicons/react/24/outline/ServerIcon';
5
+ import { getCommands } from '@utils/commands';
6
+ import { getDomains } from '@utils/collections/domains';
7
+ import { getFlows } from '@utils/collections/flows';
8
+ import { getEvents } from '@utils/events';
9
+ import { getServices } from '@utils/collections/services';
10
+ import { buildUrl } from '@utils/url-builder';
11
+ import { getQueries } from '@utils/queries';
12
+ import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
13
+ import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
14
+ import Checkbox from '@components/Checkbox.astro';
15
+ import { User, Users } from 'lucide-react';
16
+ import { getUsers } from '@utils/users';
17
+ import { getTeams } from '@utils/teams';
18
+
19
+ const users = await getUsers();
20
+ const teams = await getTeams();
21
+
22
+ export interface Props<T extends TCollectionTypes> {
23
+ title: string;
24
+ subtitle: string;
25
+ data: TData<T>[];
26
+ type: T;
27
+ }
28
+
29
+ const { title, subtitle, data, type } = Astro.props;
30
+ const currentPath = Astro.url.pathname;
31
+
32
+ const checkboxLatestId = 'latest-only';
33
+
34
+ const tabs = [
35
+ {
36
+ label: `Users (${users.length})`,
37
+ href: buildUrl('/directory/users'),
38
+ isActive: currentPath === '/directory/users',
39
+ icon: User,
40
+ activeColor: 'orange',
41
+ enabled: users.length > 0,
42
+ },
43
+ {
44
+ label: `Teams (${teams.length})`,
45
+ href: buildUrl('/directory/teams'),
46
+ isActive: currentPath === '/directory/teams',
47
+ icon: Users,
48
+ activeColor: 'blue',
49
+ enabled: teams.length > 0,
50
+ },
51
+ ];
52
+ ---
53
+
54
+ <VerticalSideBarLayout title={`Explore | ${title}`}>
55
+ <main class="ml-0">
56
+ <div id="discover-collection-tabs">
57
+ <div class="hidden sm:block">
58
+ <div class="border-b border-gray-200">
59
+ <nav class="flex space-x-8 -mb-0.5 pl-6" aria-label="Tabs">
60
+ {
61
+ tabs.map((tab) => (
62
+ <a
63
+ href={tab.href}
64
+ class={` text-black group inline-flex items-center py-4 px-1 text-sm font-light ${tab.isActive ? `border-${tab.activeColor}-500 border-b-[2px] text-${tab.activeColor}-500` : 'opacity-80'} ${!tab.enabled ? 'disabled' : ''}`}
65
+ aria-current="page"
66
+ >
67
+ <tab.icon
68
+ className={`w-6 h-6 -ml-0.5 mr-2 ${tab.isActive ? `text-${tab.activeColor}-500` : 'text-gray-500'}`}
69
+ />
70
+ <span>{tab.label}</span>
71
+ </a>
72
+ ))
73
+ }
74
+ </nav>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Table -->
80
+ <div class="pb-20 ml-6 md:pr-10">
81
+ <div>
82
+ <div class="sm:flex sm:items-end py-4 pb-4" id="discover-title">
83
+ <div class="sm:flex-auto space-y-2">
84
+ <h1 class="text-4xl font-semibold text-gray-900 capitalize">{title}</h1>
85
+ <p class="text-md text-gray-700">{subtitle}</p>
86
+ </div>
87
+ </div>
88
+ <div class="mt-4 flow-root">
89
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
90
+ <div class="inline-block min-w-full align-middle sm:px-6 lg:px-8">
91
+ <Table checkboxLatestId={checkboxLatestId} data={data} collection={type} client:load />
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </main>
98
+ </VerticalSideBarLayout>
99
+
100
+ <style>
101
+ .ec-align-top {
102
+ vertical-align: top !important;
103
+ }
104
+
105
+ a.disabled {
106
+ pointer-events: none;
107
+ cursor: default;
108
+ opacity: 0.25;
109
+ }
110
+ </style>
@@ -5,7 +5,7 @@ interface Props {
5
5
  description?: string;
6
6
  }
7
7
 
8
- import { BookOpenText, Workflow, TableProperties, House } from 'lucide-react';
8
+ import { BookOpenText, Workflow, TableProperties, House, BookUser } from 'lucide-react';
9
9
  import Header from '../components/Header.astro';
10
10
  import SEO from '../components/Seo.astro';
11
11
 
@@ -14,8 +14,6 @@ import { getDomains } from '@utils/collections/domains';
14
14
  import { getEvents } from '@utils/events';
15
15
  import { getServices } from '@utils/collections/services';
16
16
  import { getFlows } from '@utils/collections/flows';
17
- import { getTeams } from '@utils/teams';
18
- import { getUsers } from '@utils/users';
19
17
  import { isCollectionVisibleInCatalog } from '@eventcatalog';
20
18
  import { buildUrl } from '@utils/url-builder';
21
19
  import { getQueries } from '@utils/queries';
@@ -30,13 +28,11 @@ const services = await getServices({ getAllVersions: false });
30
28
  const domains = await getDomains({ getAllVersions: false });
31
29
  const channels = await getChannels({ getAllVersions: false });
32
30
  const flows = await getFlows({ getAllVersions: false });
33
- const teams = await getTeams();
34
- const users = await getUsers();
35
31
 
36
32
  const messages = [...events, ...commands, ...queries];
37
33
 
38
34
  // @ts-ignore for large catalogs https://github.com/event-catalog/eventcatalog/issues/552
39
- const allData = [...domains, ...services, ...messages, ...channels, ...flows, ...teams, ...users];
35
+ const allData = [...domains, ...services, ...messages, ...channels, ...flows];
40
36
 
41
37
  const currentPath = Astro.url.pathname;
42
38
 
@@ -95,6 +91,14 @@ const navigationItems = [
95
91
  current: currentPath.includes('/discover/'),
96
92
  sidebar: false,
97
93
  },
94
+ {
95
+ id: '/directory',
96
+ label: 'Directory',
97
+ icon: BookUser,
98
+ href: buildUrl('/directory/users'),
99
+ current: currentPath.includes('/directory'),
100
+ sidebar: false,
101
+ },
98
102
  ];
99
103
 
100
104
  const allDataAsSideNav = allData.reduce((acc, item) => {
@@ -103,17 +107,9 @@ const allDataAsSideNav = allData.reduce((acc, item) => {
103
107
  const currentPath = Astro.url.pathname;
104
108
  const route = currentPath.includes('visualiser') ? 'visualiser' : 'docs';
105
109
 
106
- if (
107
- currentPath.includes('visualiser') &&
108
- (item.collection === 'teams' || item.collection === 'users' || item.collection === 'channels')
109
- ) {
110
- return acc;
111
- }
112
-
113
110
  const navigationItem = {
114
111
  label: item.data.name,
115
- version: item.collection === 'teams' || item.collection === 'users' ? null : item.data.version,
116
- // items: item.collection === 'users' ? [] : item.headings,
112
+ version: item.data.version,
117
113
  visible: isCollectionVisibleInCatalog(item.collection),
118
114
  // @ts-ignore
119
115
  href: item.data.version
@@ -0,0 +1,69 @@
1
+ ---
2
+ import { getUsers } from '@utils/users';
3
+ import { getTeams } from '@utils/teams';
4
+
5
+ import DirectoryLayout, { type Props as DirectoryLayoutProps } from '@layouts/DirectoryLayout.astro';
6
+
7
+ export async function getStaticPaths() {
8
+ const loaders = {
9
+ users: getUsers,
10
+ teams: getTeams,
11
+ };
12
+
13
+ const itemTypes = ['users', 'teams'] as const;
14
+ const allItems = await Promise.all(itemTypes.map((type) => loaders[type]()));
15
+
16
+ return allItems.flatMap((items, index) => ({
17
+ params: {
18
+ type: itemTypes[index],
19
+ },
20
+ props: {
21
+ data: items,
22
+ type: itemTypes[index],
23
+ },
24
+ }));
25
+ }
26
+
27
+ const { type, data } = Astro.props;
28
+
29
+ function mapToItem(i: any) {
30
+ return {
31
+ collection: i.collection,
32
+ data: {
33
+ id: i.data.id,
34
+ name: i.data.name,
35
+ version: i.data.version,
36
+ },
37
+ };
38
+ }
39
+ ---
40
+
41
+ <DirectoryLayout
42
+ title={`${type} (${data.length})`}
43
+ subtitle={`Find, filter and search for any ${type} in your system.`}
44
+ data={data.map(
45
+ (d) =>
46
+ ({
47
+ collection: d.collection,
48
+ data: {
49
+ id: d.data.id,
50
+ name: d.data.name,
51
+ // @ts-ignore
52
+ role: d.data?.role,
53
+ // @ts-ignore
54
+ avatarUrl: d.data?.avatarUrl,
55
+ // @ts-ignore
56
+ associatedTeams: d.data?.associatedTeams?.map(mapToItem) ?? [],
57
+ // @ts-ignore
58
+ ownedCommands: d.data?.ownedCommands?.map(mapToItem) ?? [],
59
+ // @ts-ignore
60
+ ownedEvents: d.data?.ownedEvents?.map(mapToItem) ?? [],
61
+ // @ts-ignore
62
+ ownedServices: d.data?.ownedServices?.map(mapToItem) ?? [],
63
+ // @ts-ignore
64
+ members: d.data?.members,
65
+ },
66
+ }) as DirectoryLayoutProps<typeof type>['data'][0]
67
+ )}
68
+ type={type}
69
+ />
@@ -0,0 +1,242 @@
1
+ ---
2
+ import VerticalSideBarLayout from '../../layouts/VerticalSideBarLayout.astro';
3
+ import { getUsers } from '@utils/users';
4
+ import { getTeams } from '@utils/teams';
5
+
6
+ const users = await getUsers();
7
+ const teams = await getTeams();
8
+ ---
9
+
10
+ <VerticalSideBarLayout title="EventCatalog">
11
+ <body class="min-h-screen bg-gray-50">
12
+ <main>
13
+ <div class="container mx-auto px-6 py-20 max-w-[90em]">
14
+ <div class="mb-12">
15
+ <h1 class="text-3xl font-bold text-gray-900 mb-8">Users</h1>
16
+ <div class="bg-white shadow-sm rounded-lg overflow-hidden">
17
+ <table class="min-w-full divide-y divide-gray-200">
18
+ <thead class="bg-gray-50">
19
+ <tr>
20
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
21
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
22
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th
23
+ >
24
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
25
+ >Contact</th
26
+ >
27
+ </tr>
28
+ </thead>
29
+ <tbody class="bg-white divide-y divide-gray-200">
30
+ {
31
+ users.map((user: any) => (
32
+ <tr class="hover:bg-gray-50">
33
+ <td class="px-6 py-4 whitespace-nowrap">
34
+ <a href={`/docs/users/${user.data.id}`} class="flex items-center">
35
+ <div class="flex-shrink-0 h-10 w-10">
36
+ <div class="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center overflow-hidden">
37
+ {user.data.avatarUrl ? (
38
+ <img src={user.data.avatarUrl} alt={user.data.name} class="h-10 w-10 object-cover" />
39
+ ) : (
40
+ <svg
41
+ xmlns="http://www.w3.org/2000/svg"
42
+ class="h-5 w-5 text-blue-600"
43
+ fill="none"
44
+ viewBox="0 0 24 24"
45
+ stroke="currentColor"
46
+ >
47
+ <path
48
+ stroke-linecap="round"
49
+ stroke-linejoin="round"
50
+ stroke-width="2"
51
+ d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
52
+ />
53
+ </svg>
54
+ )}
55
+ </div>
56
+ </div>
57
+ <div class="ml-4">
58
+ <div class="text-sm font-medium text-gray-900">{user.data.name}</div>
59
+ </div>
60
+ </a>
61
+ </td>
62
+ <td class="px-6 py-4">
63
+ <div class="text-sm text-gray-900">{user.data.role}</div>
64
+ </td>
65
+ <td class="px-6 py-4 whitespace-nowrap">
66
+ {user.data.email && <div class="text-sm text-gray-900">{user.data.email}</div>}
67
+ </td>
68
+ <td class="px-6 py-4 whitespace-nowrap">
69
+ <div class="flex items-center space-x-2">
70
+ {user.data.slackDirectMessageUrl && (
71
+ <a
72
+ href={user.data.slackDirectMessageUrl}
73
+ target="_blank"
74
+ rel="noopener"
75
+ class="text-gray-500 hover:text-gray-700"
76
+ >
77
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
78
+ <path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
79
+ </svg>
80
+ </a>
81
+ )}
82
+ {user.data.msTeamsDirectMessageUrl && (
83
+ <a
84
+ href={user.data.msTeamsDirectMessageUrl}
85
+ target="_blank"
86
+ rel="noopener"
87
+ class="text-gray-500 hover:text-gray-700"
88
+ >
89
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
90
+ <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z" />
91
+ </svg>
92
+ </a>
93
+ )}
94
+ </div>
95
+ </td>
96
+ </tr>
97
+ ))
98
+ }
99
+ </tbody>
100
+ </table>
101
+ </div>
102
+ </div>
103
+
104
+ <div class="mb-12">
105
+ <h1 class="text-3xl font-bold text-gray-900 mb-8">Teams</h1>
106
+ <div class="bg-white shadow-sm rounded-lg overflow-hidden">
107
+ <table class="min-w-full divide-y divide-gray-200">
108
+ <thead class="bg-gray-50">
109
+ <tr>
110
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
111
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
112
+ >Summary</th
113
+ >
114
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
115
+ >Members</th
116
+ >
117
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
118
+ >Contact</th
119
+ >
120
+ </tr>
121
+ </thead>
122
+ <tbody class="bg-white divide-y divide-gray-200">
123
+ {
124
+ teams.map((team: any) => (
125
+ <tr class="hover:bg-gray-50">
126
+ <td class="px-6 py-4 whitespace-nowrap">
127
+ <a href={`/docs/teams/${team.data.id}`} class="flex items-center">
128
+ <div class="flex-shrink-0 h-10 w-10">
129
+ <div class="h-10 w-10 bg-green-100 rounded-full flex items-center justify-center overflow-hidden">
130
+ {team.data.avatarUrl ? (
131
+ <img src={team.data.avatarUrl} alt={team.data.name} class="h-10 w-10 object-cover" />
132
+ ) : (
133
+ <svg
134
+ xmlns="http://www.w3.org/2000/svg"
135
+ class="h-5 w-5 text-green-600"
136
+ fill="none"
137
+ viewBox="0 0 24 24"
138
+ stroke="currentColor"
139
+ >
140
+ <path
141
+ stroke-linecap="round"
142
+ stroke-linejoin="round"
143
+ stroke-width="2"
144
+ d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
145
+ />
146
+ </svg>
147
+ )}
148
+ </div>
149
+ </div>
150
+ <div class="ml-4">
151
+ <div class="text-sm font-medium text-gray-900">{team.data.name}</div>
152
+ </div>
153
+ </a>
154
+ </td>
155
+ <td class="px-6 py-4">
156
+ <div class="text-sm text-gray-900">{team.data.summary}</div>
157
+ </td>
158
+ <td class="px-6 py-4 whitespace-nowrap">
159
+ {team.data.members && <div class="text-sm text-gray-900">{team.data.members.length} members</div>}
160
+ </td>
161
+ <td class="px-6 py-4 whitespace-nowrap">
162
+ <div class="flex items-center space-x-2">
163
+ {team.data.slackDirectMessageUrl && (
164
+ <a
165
+ href={team.data.slackDirectMessageUrl}
166
+ target="_blank"
167
+ rel="noopener"
168
+ class="text-gray-500 hover:text-gray-700"
169
+ >
170
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
171
+ <path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
172
+ </svg>
173
+ </a>
174
+ )}
175
+ {team.data.msTeamsDirectMessageUrl && (
176
+ <a
177
+ href={team.data.msTeamsDirectMessageUrl}
178
+ target="_blank"
179
+ rel="noopener"
180
+ class="text-gray-500 hover:text-gray-700"
181
+ >
182
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
183
+ <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z" />
184
+ </svg>
185
+ </a>
186
+ )}
187
+ </div>
188
+ </td>
189
+ </tr>
190
+ ))
191
+ }
192
+ </tbody>
193
+ </table>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </main>
198
+ </body>
199
+ </VerticalSideBarLayout>
200
+
201
+ <style>
202
+ .filter-btn.active {
203
+ background-color: #f3f4f6;
204
+ color: #111827;
205
+ }
206
+
207
+ .filter-btn {
208
+ color: #6b7280;
209
+ }
210
+
211
+ .filter-btn:hover {
212
+ color: #111827;
213
+ }
214
+ </style>
215
+
216
+ <script>
217
+ const filterButtons = document.querySelectorAll('.filter-btn');
218
+ const rows = document.querySelectorAll('tr[data-type]');
219
+
220
+ filterButtons.forEach((button) => {
221
+ button.addEventListener('click', () => {
222
+ filterButtons.forEach((btn) => btn.classList.remove('active'));
223
+ button.classList.add('active');
224
+
225
+ const filter = button.getAttribute('data-filter');
226
+
227
+ rows.forEach((row) => {
228
+ if (filter === 'all') {
229
+ row.style.display = 'table-row';
230
+ } else {
231
+ const rowType = row.getAttribute('data-type');
232
+ row.style.display = rowType === filter ? 'table-row' : 'none';
233
+ }
234
+ });
235
+ });
236
+ });
237
+
238
+ const defaultButton = document.querySelector('[data-filter="all"]');
239
+ if (defaultButton) {
240
+ defaultButton.classList.add('active');
241
+ }
242
+ </script>
@@ -1,4 +1,4 @@
1
1
  export type CollectionTypes = 'commands' | 'events' | 'queries' | 'domains' | 'services' | 'flows' | 'channels';
2
2
  export type CollectionMessageTypes = 'commands' | 'events' | 'queries';
3
-
3
+ export type CollectionUserTypes = 'users';
4
4
  export type PageTypes = 'events' | 'commands' | 'queries' | 'services' | 'domains' | 'channels';
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/event-catalog/eventcatalog.git"
7
7
  },
8
8
  "type": "module",
9
- "version": "2.19.9",
9
+ "version": "2.20.0",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },