@j-256/ccam 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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/dist/auth/browser-login.d.ts +14 -0
  4. package/dist/auth/browser-login.js +72 -0
  5. package/dist/auth/manual-login.d.ts +10 -0
  6. package/dist/auth/manual-login.js +33 -0
  7. package/dist/auth/paths.d.ts +4 -0
  8. package/dist/auth/paths.js +15 -0
  9. package/dist/auth/profile-resolver.d.ts +26 -0
  10. package/dist/auth/profile-resolver.js +42 -0
  11. package/dist/auth/profile-store.d.ts +38 -0
  12. package/dist/auth/profile-store.js +125 -0
  13. package/dist/auth/prompt.d.ts +9 -0
  14. package/dist/auth/prompt.js +70 -0
  15. package/dist/bin.d.ts +3 -0
  16. package/dist/bin.js +4 -0
  17. package/dist/client-factory.d.ts +6 -0
  18. package/dist/client-factory.js +40 -0
  19. package/dist/commands/auth.d.ts +77 -0
  20. package/dist/commands/auth.js +387 -0
  21. package/dist/commands/client.d.ts +3 -0
  22. package/dist/commands/client.js +365 -0
  23. package/dist/commands/instance.d.ts +11 -0
  24. package/dist/commands/instance.js +128 -0
  25. package/dist/commands/org-config.d.ts +3 -0
  26. package/dist/commands/org-config.js +31 -0
  27. package/dist/commands/org.d.ts +11 -0
  28. package/dist/commands/org.js +234 -0
  29. package/dist/commands/permission.d.ts +3 -0
  30. package/dist/commands/permission.js +60 -0
  31. package/dist/commands/realm.d.ts +3 -0
  32. package/dist/commands/realm.js +58 -0
  33. package/dist/commands/role.d.ts +3 -0
  34. package/dist/commands/role.js +77 -0
  35. package/dist/commands/service-type.d.ts +3 -0
  36. package/dist/commands/service-type.js +57 -0
  37. package/dist/commands/user.d.ts +14 -0
  38. package/dist/commands/user.js +573 -0
  39. package/dist/error-handler.d.ts +2 -0
  40. package/dist/error-handler.js +28 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +2 -0
  43. package/dist/output/csv.d.ts +3 -0
  44. package/dist/output/csv.js +57 -0
  45. package/dist/output/default-columns.d.ts +2 -0
  46. package/dist/output/default-columns.js +16 -0
  47. package/dist/output/detect.d.ts +4 -0
  48. package/dist/output/detect.js +6 -0
  49. package/dist/output/index.d.ts +15 -0
  50. package/dist/output/index.js +34 -0
  51. package/dist/output/json.d.ts +2 -0
  52. package/dist/output/json.js +10 -0
  53. package/dist/output/shared.d.ts +13 -0
  54. package/dist/output/shared.js +41 -0
  55. package/dist/output/table.d.ts +2 -0
  56. package/dist/output/table.js +72 -0
  57. package/dist/output/types.d.ts +2 -0
  58. package/dist/output/types.js +2 -0
  59. package/dist/output/yaml-fmt.d.ts +2 -0
  60. package/dist/output/yaml-fmt.js +11 -0
  61. package/dist/program.d.ts +3 -0
  62. package/dist/program.js +37 -0
  63. package/dist/shared.d.ts +46 -0
  64. package/dist/shared.js +96 -0
  65. package/dist/tui/App.d.ts +7 -0
  66. package/dist/tui/App.js +30 -0
  67. package/dist/tui/components/AuditTab.d.ts +8 -0
  68. package/dist/tui/components/AuditTab.js +80 -0
  69. package/dist/tui/components/FooterBar.d.ts +17 -0
  70. package/dist/tui/components/FooterBar.js +23 -0
  71. package/dist/tui/components/FullScreenLayout.d.ts +6 -0
  72. package/dist/tui/components/FullScreenLayout.js +11 -0
  73. package/dist/tui/components/HeaderBar.d.ts +5 -0
  74. package/dist/tui/components/HeaderBar.js +10 -0
  75. package/dist/tui/components/InfoTab.d.ts +8 -0
  76. package/dist/tui/components/InfoTab.js +70 -0
  77. package/dist/tui/components/ResourcePicker.d.ts +10 -0
  78. package/dist/tui/components/ResourcePicker.js +36 -0
  79. package/dist/tui/components/SubResourceTab.d.ts +7 -0
  80. package/dist/tui/components/SubResourceTab.js +193 -0
  81. package/dist/tui/components/TabBar.d.ts +11 -0
  82. package/dist/tui/components/TabBar.js +13 -0
  83. package/dist/tui/components/Table.d.ts +32 -0
  84. package/dist/tui/components/Table.js +175 -0
  85. package/dist/tui/context/client.d.ts +7 -0
  86. package/dist/tui/context/client.js +14 -0
  87. package/dist/tui/context/navigation.d.ts +14 -0
  88. package/dist/tui/context/navigation.js +25 -0
  89. package/dist/tui/context/terminal-size.d.ts +9 -0
  90. package/dist/tui/context/terminal-size.js +26 -0
  91. package/dist/tui/format.d.ts +20 -0
  92. package/dist/tui/format.js +57 -0
  93. package/dist/tui/hooks/use-audit-log.d.ts +12 -0
  94. package/dist/tui/hooks/use-audit-log.js +71 -0
  95. package/dist/tui/hooks/use-local-collection.d.ts +8 -0
  96. package/dist/tui/hooks/use-local-collection.js +30 -0
  97. package/dist/tui/hooks/use-paginated-resource.d.ts +23 -0
  98. package/dist/tui/hooks/use-paginated-resource.js +115 -0
  99. package/dist/tui/hooks/use-resource-detail.d.ts +7 -0
  100. package/dist/tui/hooks/use-resource-detail.js +30 -0
  101. package/dist/tui/hooks/use-scroll-window.d.ts +7 -0
  102. package/dist/tui/hooks/use-scroll-window.js +29 -0
  103. package/dist/tui/index.d.ts +2 -0
  104. package/dist/tui/index.js +22 -0
  105. package/dist/tui/navigation.d.ts +11 -0
  106. package/dist/tui/navigation.js +29 -0
  107. package/dist/tui/resource-configs/api-clients.d.ts +3 -0
  108. package/dist/tui/resource-configs/api-clients.js +118 -0
  109. package/dist/tui/resource-configs/index.d.ts +14 -0
  110. package/dist/tui/resource-configs/index.js +28 -0
  111. package/dist/tui/resource-configs/instances.d.ts +3 -0
  112. package/dist/tui/resource-configs/instances.js +24 -0
  113. package/dist/tui/resource-configs/org-configuration.d.ts +3 -0
  114. package/dist/tui/resource-configs/org-configuration.js +28 -0
  115. package/dist/tui/resource-configs/organizations.d.ts +3 -0
  116. package/dist/tui/resource-configs/organizations.js +104 -0
  117. package/dist/tui/resource-configs/permissions.d.ts +3 -0
  118. package/dist/tui/resource-configs/permissions.js +25 -0
  119. package/dist/tui/resource-configs/realms.d.ts +3 -0
  120. package/dist/tui/resource-configs/realms.js +36 -0
  121. package/dist/tui/resource-configs/roles.d.ts +3 -0
  122. package/dist/tui/resource-configs/roles.js +56 -0
  123. package/dist/tui/resource-configs/service-types.d.ts +3 -0
  124. package/dist/tui/resource-configs/service-types.js +24 -0
  125. package/dist/tui/resource-configs/users.d.ts +3 -0
  126. package/dist/tui/resource-configs/users.js +126 -0
  127. package/dist/tui/types.d.ts +99 -0
  128. package/dist/tui/types.js +23 -0
  129. package/dist/tui/views/ResourceDetailView.d.ts +7 -0
  130. package/dist/tui/views/ResourceDetailView.js +123 -0
  131. package/dist/tui/views/ResourceListView.d.ts +6 -0
  132. package/dist/tui/views/ResourceListView.js +140 -0
  133. package/dist/tui/views/ViewRouter.d.ts +2 -0
  134. package/dist/tui/views/ViewRouter.js +60 -0
  135. package/package.json +62 -0
@@ -0,0 +1,28 @@
1
+ import { usersConfig } from './users.js';
2
+ import { organizationsConfig } from './organizations.js';
3
+ import { apiClientsConfig } from './api-clients.js';
4
+ import { rolesConfig } from './roles.js';
5
+ import { realmsConfig } from './realms.js';
6
+ import { instancesConfig } from './instances.js';
7
+ import { permissionsConfig } from './permissions.js';
8
+ import { serviceTypesConfig } from './service-types.js';
9
+ import { orgConfigurationConfig } from './org-configuration.js';
10
+ export const RESOURCE_CONFIGS = {
11
+ user: usersConfig,
12
+ org: organizationsConfig,
13
+ client: apiClientsConfig,
14
+ role: rolesConfig,
15
+ realm: realmsConfig,
16
+ instance: instancesConfig,
17
+ permission: permissionsConfig,
18
+ 'service-type': serviceTypesConfig,
19
+ 'org-configuration': orgConfigurationConfig,
20
+ };
21
+ export function getResourceConfig(name) {
22
+ const config = RESOURCE_CONFIGS[name];
23
+ if (!config)
24
+ throw new Error(`Unknown resource: ${name}`);
25
+ return config;
26
+ }
27
+ export { usersConfig, organizationsConfig, apiClientsConfig, rolesConfig, realmsConfig, instancesConfig, permissionsConfig, serviceTypesConfig, orgConfigurationConfig, };
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const instancesConfig: ResourceConfig;
3
+ //# sourceMappingURL=instances.d.ts.map
@@ -0,0 +1,24 @@
1
+ export const instancesConfig = {
2
+ name: 'instance',
3
+ displayName: 'Instances',
4
+ idField: 'id',
5
+ // -- List view --
6
+ columns: [
7
+ { key: 'id', label: 'ID', width: 2, color: 'white' },
8
+ { key: 'description', label: 'Description', width: 5 },
9
+ ],
10
+ listFn: (c, opts) => c.instances.list({
11
+ page: opts.page,
12
+ size: opts.size,
13
+ }),
14
+ labelFn: (item) => item.id || 'Instance',
15
+ detailFn: (c, id) => c.instances.get(id),
16
+ // -- Detail view --
17
+ fields: [
18
+ { key: 'id', label: 'ID' },
19
+ { key: 'description', label: 'Description' },
20
+ ],
21
+ tabs: [],
22
+ crossLinks: [],
23
+ };
24
+ //# sourceMappingURL=instances.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const orgConfigurationConfig: ResourceConfig;
3
+ //# sourceMappingURL=org-configuration.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { formatArray } from '../format.js';
2
+ export const orgConfigurationConfig = {
3
+ name: 'org-configuration',
4
+ displayName: 'Org Configuration',
5
+ idField: 'id',
6
+ // -- List view (singleton -- wraps single item in ContentResponse shape) --
7
+ columns: [
8
+ { key: 'allowedSfMyDomainSuffixes', label: 'Allowed SF My Domain Suffixes', width: 5, format: formatArray },
9
+ ],
10
+ /**
11
+ * OrganizationConfiguration is a singleton with no `id` field. The TUI
12
+ * list view expects an `id`, so we synthesize one here (`'organization-configuration'`)
13
+ * for display purposes only. Callers of the SDK see no such field.
14
+ */
15
+ listFn: (c) => c.organizationConfiguration.get().then((config) => ({
16
+ content: [{ id: 'organization-configuration', ...config }],
17
+ links: [],
18
+ })),
19
+ labelFn: () => 'Org Configuration',
20
+ detailFn: (c) => c.organizationConfiguration.get(),
21
+ // -- Detail view --
22
+ fields: [
23
+ { key: 'allowedSfMyDomainSuffixes', label: 'Allowed SF My Domain Suffixes', format: formatArray },
24
+ ],
25
+ tabs: [],
26
+ crossLinks: [],
27
+ };
28
+ //# sourceMappingURL=org-configuration.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const organizationsConfig: ResourceConfig;
3
+ //# sourceMappingURL=organizations.d.ts.map
@@ -0,0 +1,104 @@
1
+ import { OrganizationSortField } from 'ccam-sdk';
2
+ import { formatBoolean, formatDate, formatArray, formatCount, formatUserState } from '../format.js';
3
+ export const organizationsConfig = {
4
+ name: 'org',
5
+ displayName: 'Organizations',
6
+ idField: 'id',
7
+ // -- List view --
8
+ columns: [
9
+ { key: 'name', label: 'Name', width: 5, color: 'white', sort: { mode: 'remote', field: OrganizationSortField.NAME } },
10
+ { key: 'type', label: 'Type', width: 2, color: 'magenta' },
11
+ { key: 'twoFAEnabled', label: '2FA', width: 1, format: formatBoolean, priority: 5 },
12
+ { key: 'realms', label: 'Realms', width: 2, format: formatCount, priority: 3, color: 'yellow', align: 'right' },
13
+ ],
14
+ listFn: (c, opts) => c.organizations.list({
15
+ page: opts.page,
16
+ size: opts.size,
17
+ sort: opts.sort ? { field: opts.sort.field, direction: opts.sort.direction } : undefined,
18
+ }),
19
+ labelFn: (item) => item.name || 'Organization',
20
+ detailFn: (c, id) => c.organizations.get(id),
21
+ // -- Detail view --
22
+ fields: [
23
+ { key: 'name', label: 'Name', group: 'identity' },
24
+ { key: 'type', label: 'Type', group: 'identity' },
25
+ { key: 'emailDomains', label: 'Email Domains', format: formatArray, group: 'identity' },
26
+ { key: 'sfAccountIds', label: 'SF Account IDs', format: formatArray, group: 'identity' },
27
+ { key: 'contactUsers', label: 'Contact Users', format: formatArray, group: 'identity' },
28
+ { key: 'passwordMinEntropy', label: 'Password Min Entropy', group: 'password' },
29
+ { key: 'passwordHistorySize', label: 'Password History Size', group: 'password' },
30
+ { key: 'passwordDaysExpiration', label: 'Password Days Expiration', group: 'password' },
31
+ { key: 'twoFAEnabled', label: '2FA Enabled', format: formatBoolean, group: 'security' },
32
+ { key: 'twoFARoles', label: '2FA Roles', format: formatArray, group: 'security' },
33
+ { key: 'sfMyDomain', label: 'SF My Domain', group: 'security' },
34
+ { key: 'sfMyDomainSuffix', label: 'SF My Domain Suffix', group: 'security' },
35
+ { key: 'sfMyDomainVerified', label: 'SF My Domain Verified', format: formatBoolean, group: 'security' },
36
+ { key: 'sfIdentityFederation', label: 'SF Identity Federation', group: 'security' },
37
+ {
38
+ key: 'justInTimeUserProvisioningEnabled',
39
+ label: 'JIT Provisioning',
40
+ format: formatBoolean,
41
+ group: 'security',
42
+ },
43
+ { key: 'allowedVerifierTypes', label: 'Allowed Verifier Types', format: formatArray, group: 'status' },
44
+ { key: 'disableInactiveUsers', label: 'Disable Inactive Users', format: formatBoolean, group: 'status' },
45
+ { key: 'inactiveUserDays', label: 'Inactive User Days', group: 'status' },
46
+ {
47
+ key: 'supportTicketRequiredForAccessModification',
48
+ label: 'Support Ticket Required',
49
+ format: formatBoolean,
50
+ group: 'status',
51
+ },
52
+ ],
53
+ tabs: [
54
+ {
55
+ key: 'realms',
56
+ label: 'Realms',
57
+ type: 'local',
58
+ fetchFn: (c, id) => c.organizations.realms(id, { expand: 'instance' }),
59
+ columns: [
60
+ { key: 'id', label: 'ID', width: 1 },
61
+ { key: 'description', label: 'Description', width: 3 },
62
+ { key: 'customerName', label: 'Customer', width: 3 },
63
+ ],
64
+ crossLinkTo: 'realm-detail',
65
+ },
66
+ {
67
+ key: 'instances',
68
+ label: 'Instances',
69
+ type: 'local',
70
+ fetchFn: (c, id) => c.organizations.instances(id),
71
+ columns: [
72
+ { key: 'id', label: 'ID', width: 2 },
73
+ { key: 'description', label: 'Description', width: 5 },
74
+ ],
75
+ crossLinkTo: 'instance-detail',
76
+ },
77
+ {
78
+ key: 'users',
79
+ label: 'Users',
80
+ type: 'paginated',
81
+ fetchFn: (c, id, page, size) => c.users.search.findByOrg({ organization: id, page, size }),
82
+ columns: [
83
+ { key: 'mail', label: 'Email', width: 5 },
84
+ { key: 'displayName', label: 'Display Name', width: 3 },
85
+ { key: 'userState', label: 'State', width: 2, format: formatUserState },
86
+ ],
87
+ crossLinkTo: 'user-detail',
88
+ },
89
+ {
90
+ key: 'audit',
91
+ label: 'Audit',
92
+ type: 'audit',
93
+ fetchFn: (c, id, querySize) => c.organizations.auditLogs(id, querySize !== undefined ? { querySize } : undefined),
94
+ columns: [
95
+ { key: 'timestamp', label: 'Time', width: 2, format: formatDate },
96
+ { key: 'eventType', label: 'Event', width: 2 },
97
+ { key: 'eventMessage', label: 'Message', width: 4 },
98
+ { key: 'authorDisplayName', label: 'Author', width: 2 },
99
+ ],
100
+ },
101
+ ],
102
+ crossLinks: [],
103
+ };
104
+ //# sourceMappingURL=organizations.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const permissionsConfig: ResourceConfig;
3
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { formatBoolean } from '../format.js';
2
+ export const permissionsConfig = {
3
+ name: 'permission',
4
+ displayName: 'Permissions',
5
+ idField: 'name',
6
+ // -- List view --
7
+ columns: [
8
+ { key: 'name', label: 'Name', width: 4, color: 'white' },
9
+ { key: 'adminPermission', label: 'Admin', width: 1, format: formatBoolean, priority: 5 },
10
+ ],
11
+ listFn: (c, opts) => c.permissions.list({
12
+ page: opts.page,
13
+ size: opts.size,
14
+ }),
15
+ labelFn: (item) => item.name || 'Permission',
16
+ detailFn: (c, id) => c.permissions.get(id),
17
+ // -- Detail view --
18
+ fields: [
19
+ { key: 'name', label: 'Name' },
20
+ { key: 'adminPermission', label: 'Admin Permission', format: formatBoolean },
21
+ ],
22
+ tabs: [],
23
+ crossLinks: [],
24
+ };
25
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const realmsConfig: ResourceConfig;
3
+ //# sourceMappingURL=realms.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { RealmSortField } from 'ccam-sdk';
2
+ export const realmsConfig = {
3
+ name: 'realm',
4
+ displayName: 'Realms',
5
+ idField: 'id',
6
+ // -- List view --
7
+ columns: [
8
+ { key: 'id', label: 'ID', width: 1, color: 'white', sort: { mode: 'remote', field: RealmSortField.ID } },
9
+ { key: 'description', label: 'Description', width: 3 },
10
+ { key: 'customerName', label: 'Customer', width: 3 },
11
+ { key: 'organizationId', label: 'Organization', width: 3, priority: 5, color: 'gray', sort: { mode: 'remote', field: RealmSortField.ORGANIZATION_ID } },
12
+ ],
13
+ listFn: (c, opts) => c.realms.list({
14
+ page: opts.page,
15
+ size: opts.size,
16
+ sort: opts.sort ? { field: opts.sort.field, direction: opts.sort.direction } : undefined,
17
+ }),
18
+ labelFn: (item) => item.id || 'Realm',
19
+ detailFn: (c, id) => c.realms.get(id),
20
+ // -- Detail view --
21
+ fields: [
22
+ { key: 'id', label: 'ID', group: 'identity' },
23
+ { key: 'description', label: 'Description', group: 'identity' },
24
+ { key: 'customerName', label: 'Customer', group: 'identity' },
25
+ {
26
+ key: 'organizationId',
27
+ label: 'Organization',
28
+ crossLink: { field: 'organizationId', targetView: 'org-detail' },
29
+ group: 'config',
30
+ },
31
+ { key: 'sfAccountId', label: 'SF Account ID', group: 'config' },
32
+ ],
33
+ tabs: [],
34
+ crossLinks: [{ field: 'organizationId', targetView: 'org-detail' }],
35
+ };
36
+ //# sourceMappingURL=realms.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const rolesConfig: ResourceConfig;
3
+ //# sourceMappingURL=roles.d.ts.map
@@ -0,0 +1,56 @@
1
+ import { RoleSortField } from 'ccam-sdk';
2
+ import { formatBoolean, formatArray, formatUserState } from '../format.js';
3
+ export const rolesConfig = {
4
+ name: 'role',
5
+ displayName: 'Roles',
6
+ idField: 'id',
7
+ // -- List view --
8
+ columns: [
9
+ { key: 'id', label: 'ID', width: 3, color: 'white', sort: { mode: 'remote', field: RoleSortField.ID } },
10
+ { key: 'description', label: 'Description', width: 5, sort: { mode: 'remote', field: RoleSortField.DESCRIPTION } },
11
+ { key: 'scope', label: 'Scope', width: 1, color: 'magenta', sort: { mode: 'remote', field: RoleSortField.SCOPE } },
12
+ { key: 'targetType', label: 'Target', width: 1, priority: 5, color: 'magenta', sort: { mode: 'remote', field: RoleSortField.TARGET_TYPE } },
13
+ { key: 'serviceType', label: 'Service Type', width: 2, priority: 3, color: 'magenta', sort: { mode: 'remote', field: RoleSortField.SERVICE_TYPE } },
14
+ ],
15
+ listFn: (c, opts) => c.roles.list({
16
+ page: opts.page,
17
+ size: opts.size,
18
+ sort: opts.sort ? { field: opts.sort.field, direction: opts.sort.direction } : undefined,
19
+ }),
20
+ labelFn: (item) => item.id || item.description || 'Role',
21
+ detailFn: (c, id) => c.roles.get(id),
22
+ // -- Detail view --
23
+ fields: [
24
+ { key: 'id', label: 'ID', group: 'identity' },
25
+ { key: 'description', label: 'Description', group: 'identity' },
26
+ { key: 'roleEnumName', label: 'Enum Name', group: 'identity' },
27
+ { key: 'internalRole', label: 'Internal Role', format: formatBoolean, group: 'identity' },
28
+ {
29
+ key: 'serviceType',
30
+ label: 'Service Type',
31
+ crossLink: { field: 'serviceType', targetView: 'service-type-detail' },
32
+ group: 'config',
33
+ },
34
+ { key: 'permissions', label: 'Permissions', format: formatArray, group: 'config' },
35
+ { key: 'scope', label: 'Scope', group: 'config' },
36
+ { key: 'targetType', label: 'Target Type', group: 'config' },
37
+ { key: 'twoFAEnabled', label: '2FA Enabled', format: formatBoolean, group: 'config' },
38
+ { key: 'privileged', label: 'Privileged', format: formatBoolean, group: 'config' },
39
+ ],
40
+ tabs: [
41
+ {
42
+ key: 'usersWithRole',
43
+ label: 'Users with Role',
44
+ type: 'paginated',
45
+ fetchFn: (c, id, page, size) => c.users.search.findByRole({ role: id, page, size }),
46
+ columns: [
47
+ { key: 'mail', label: 'Email', width: 5 },
48
+ { key: 'displayName', label: 'Display Name', width: 3 },
49
+ { key: 'userState', label: 'State', width: 2, format: formatUserState },
50
+ ],
51
+ crossLinkTo: 'user-detail',
52
+ },
53
+ ],
54
+ crossLinks: [{ field: 'serviceType', targetView: 'service-type-detail' }],
55
+ };
56
+ //# sourceMappingURL=roles.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const serviceTypesConfig: ResourceConfig;
3
+ //# sourceMappingURL=service-types.d.ts.map
@@ -0,0 +1,24 @@
1
+ export const serviceTypesConfig = {
2
+ name: 'service-type',
3
+ displayName: 'Service Types',
4
+ idField: 'id',
5
+ // -- List view --
6
+ columns: [
7
+ { key: 'id', label: 'ID', width: 1, color: 'white' },
8
+ { key: 'description', label: 'Description', width: 5 },
9
+ ],
10
+ listFn: (c, opts) => c.serviceTypes.list({
11
+ page: opts.page,
12
+ size: opts.size,
13
+ }),
14
+ labelFn: (item) => item.id || 'Service Type',
15
+ detailFn: (c, id) => c.serviceTypes.get(id),
16
+ // -- Detail view --
17
+ fields: [
18
+ { key: 'id', label: 'ID' },
19
+ { key: 'description', label: 'Description' },
20
+ ],
21
+ tabs: [],
22
+ crossLinks: [],
23
+ };
24
+ //# sourceMappingURL=service-types.js.map
@@ -0,0 +1,3 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export declare const usersConfig: ResourceConfig;
3
+ //# sourceMappingURL=users.d.ts.map
@@ -0,0 +1,126 @@
1
+ import { UserSortField } from 'ccam-sdk';
2
+ import { formatUserState, formatDate, formatBoolean, formatArray } from '../format.js';
3
+ export const usersConfig = {
4
+ name: 'user',
5
+ displayName: 'Users',
6
+ idField: 'id',
7
+ // -- List view --
8
+ columns: [
9
+ { key: 'mail', label: 'Email', width: 5, color: 'white', sort: { mode: 'remote', field: UserSortField.EMAIL } },
10
+ { key: 'displayName', label: 'Display Name', width: 3, sort: { mode: 'remote', field: UserSortField.DISPLAY_NAME } },
11
+ { key: 'userState', label: 'State', width: 2, format: formatUserState },
12
+ { key: 'lastLoginDate', label: 'Last Login', width: 2, format: formatDate, priority: 5, color: 'blue' },
13
+ { key: 'createdAt', label: 'Created', width: 2, format: formatDate, priority: 3, color: 'blue', sort: { mode: 'remote', field: UserSortField.CREATED_AT } },
14
+ ],
15
+ listFn: (c, opts) => c.users.list({
16
+ page: opts.page,
17
+ size: opts.size,
18
+ sort: opts.sort ? { field: opts.sort.field, direction: opts.sort.direction } : undefined,
19
+ }),
20
+ labelFn: (item) => item.displayName || item.mail || 'User',
21
+ detailFn: (c, id) => c.users.get(id),
22
+ // -- Detail view --
23
+ fields: [
24
+ { key: 'displayName', label: 'Display Name', group: 'identity' },
25
+ { key: 'mail', label: 'Email', group: 'identity' },
26
+ { key: 'firstName', label: 'First Name', group: 'identity' },
27
+ { key: 'lastName', label: 'Last Name', group: 'identity' },
28
+ { key: 'userState', label: 'State', format: formatUserState, group: 'identity' },
29
+ { key: 'businessPhone', label: 'Business Phone', group: 'contact' },
30
+ { key: 'homePhone', label: 'Home Phone', group: 'contact' },
31
+ { key: 'mobilePhone', label: 'Mobile Phone', group: 'contact' },
32
+ { key: 'preferredLocale', label: 'Preferred Locale', group: 'contact' },
33
+ {
34
+ key: 'primaryOrganization',
35
+ label: 'Primary Organization',
36
+ crossLink: { field: 'primaryOrganization', targetView: 'org-detail' },
37
+ group: 'organization',
38
+ },
39
+ { key: 'roleTenantFilter', label: 'Role Tenant Filter', group: 'organization' },
40
+ { key: 'passwordExpirationTimestamp', label: 'Password Expires', group: 'status' },
41
+ { key: 'passwordModificationTimestamp', label: 'Password Modified', group: 'status' },
42
+ { key: 'createdAt', label: 'Created', format: formatDate, group: 'status' },
43
+ { key: 'lastModified', label: 'Last Modified', format: formatDate, group: 'status' },
44
+ { key: 'lastLoginDate', label: 'Last Login', format: formatDate, group: 'status' },
45
+ { key: 'sfUserId', label: 'SF User ID', group: 'status' },
46
+ { key: 'verifiers', label: 'Verifiers', format: formatArray, group: 'status' },
47
+ ],
48
+ tabs: [
49
+ {
50
+ key: 'roles',
51
+ label: 'Roles',
52
+ type: 'local',
53
+ fetchFn: (c, id) => c.users.roles(id),
54
+ columns: [
55
+ { key: 'id', label: 'ID', width: 3 },
56
+ { key: 'description', label: 'Description', width: 5 },
57
+ { key: 'scope', label: 'Scope', width: 1 },
58
+ { key: 'targetType', label: 'Target', width: 1 },
59
+ ],
60
+ crossLinkTo: 'role-detail',
61
+ },
62
+ {
63
+ key: 'organizations',
64
+ label: 'Organizations',
65
+ type: 'local',
66
+ fetchFn: (c, id) => c.users.get(id, { expand: 'organizations' }).then((u) => ({
67
+ content: u.organizations || [],
68
+ links: [],
69
+ })),
70
+ columns: [
71
+ { key: 'name', label: 'Name', width: 5 },
72
+ { key: 'type', label: 'Type', width: 2 },
73
+ { key: 'twoFAEnabled', label: '2FA', width: 1, format: formatBoolean },
74
+ ],
75
+ crossLinkTo: 'org-detail',
76
+ },
77
+ {
78
+ key: 'instances',
79
+ label: 'Instances',
80
+ type: 'local',
81
+ fetchFn: (c, id) => c.users.instances(id),
82
+ columns: [
83
+ { key: 'id', label: 'ID', width: 2 },
84
+ { key: 'description', label: 'Description', width: 5 },
85
+ ],
86
+ crossLinkTo: 'instance-detail',
87
+ },
88
+ {
89
+ key: 'assignedRealms',
90
+ label: 'Assigned Realms',
91
+ type: 'local',
92
+ fetchFn: (c, id) => c.users.assignedRealms(id),
93
+ columns: [
94
+ { key: 'id', label: 'ID', width: 1 },
95
+ { key: 'description', label: 'Description', width: 3 },
96
+ { key: 'customerName', label: 'Customer', width: 3 },
97
+ ],
98
+ crossLinkTo: 'realm-detail',
99
+ },
100
+ {
101
+ key: 'assignedInstances',
102
+ label: 'Assigned Instances',
103
+ type: 'local',
104
+ fetchFn: (c, id) => c.users.assignedInstances(id),
105
+ columns: [
106
+ { key: 'id', label: 'ID', width: 2 },
107
+ { key: 'description', label: 'Description', width: 5 },
108
+ ],
109
+ crossLinkTo: 'instance-detail',
110
+ },
111
+ {
112
+ key: 'audit',
113
+ label: 'Audit',
114
+ type: 'audit',
115
+ fetchFn: (c, id, querySize) => c.users.auditLogs(id, querySize !== undefined ? { querySize } : undefined),
116
+ columns: [
117
+ { key: 'timestamp', label: 'Time', width: 2, format: formatDate },
118
+ { key: 'eventType', label: 'Event', width: 2 },
119
+ { key: 'eventMessage', label: 'Message', width: 4 },
120
+ { key: 'authorDisplayName', label: 'Author', width: 2 },
121
+ ],
122
+ },
123
+ ],
124
+ crossLinks: [{ field: 'primaryOrganization', targetView: 'org-detail' }],
125
+ };
126
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1,99 @@
1
+ import type { CcamClient, ContentResponse, PagedResponse } from 'ccam-sdk';
2
+ export type ViewType = 'resource-picker' | 'user-list' | 'org-list' | 'client-list' | 'role-list' | 'realm-list' | 'instance-list' | 'permission-list' | 'service-type-list' | 'user-detail' | 'org-detail' | 'client-detail' | 'role-detail' | 'realm-detail' | 'instance-detail' | 'permission-detail' | 'service-type-detail' | 'org-config-detail' | 'audit-log';
3
+ export interface ViewEntry {
4
+ view: ViewType;
5
+ label: string;
6
+ params?: Record<string, string>;
7
+ }
8
+ export interface ColumnDef {
9
+ key: string;
10
+ label: string;
11
+ width: number;
12
+ minWidth?: number;
13
+ priority?: number;
14
+ format?: (value: unknown) => string;
15
+ color?: 'red' | 'green' | 'yellow' | 'blue' | 'cyan' | 'magenta' | 'white' | 'gray';
16
+ align?: 'left' | 'right';
17
+ sort?: ColumnSortDef;
18
+ }
19
+ export interface FieldDef {
20
+ key: string;
21
+ label: string;
22
+ format?: (value: unknown) => string;
23
+ crossLink?: CrossLinkDef;
24
+ group?: string;
25
+ }
26
+ export interface ColumnSortDef {
27
+ mode: 'remote' | 'local';
28
+ field?: string;
29
+ label?: string;
30
+ }
31
+ export interface SortFieldDef {
32
+ key: string;
33
+ field: string;
34
+ label: string;
35
+ }
36
+ export interface CrossLinkDef {
37
+ field: string;
38
+ targetView: ViewType;
39
+ }
40
+ interface TabConfigBase {
41
+ key: string;
42
+ label: string;
43
+ columns: ColumnDef[];
44
+ crossLinkTo?: ViewType;
45
+ }
46
+ /**
47
+ * Tab that fetches a single batch of records from the server and sorts/filters
48
+ * client-side. fetchFn takes only the parent id.
49
+ */
50
+ export interface LocalTabConfig extends TabConfigBase {
51
+ type: 'local';
52
+ fetchFn: (client: CcamClient, id: string) => Promise<ContentResponse<Record<string, unknown>>>;
53
+ }
54
+ /**
55
+ * Tab that pages through results server-side. fetchFn takes the parent id plus
56
+ * page/size. Return type is a PagedResponse when the backing endpoint paginates
57
+ * and a ContentResponse otherwise -- both shapes are handled by the consumer
58
+ * hook.
59
+ */
60
+ export interface PaginatedTabConfig extends TabConfigBase {
61
+ type: 'paginated';
62
+ fetchFn: (client: CcamClient, id: string, page?: number, size?: number) => Promise<PagedResponse<Record<string, unknown>> | ContentResponse<Record<string, unknown>>>;
63
+ }
64
+ /**
65
+ * Tab that fetches an audit log. fetchFn takes the parent id plus an optional
66
+ * querySize (initial window; the consumer can expand via `load more`).
67
+ */
68
+ export interface AuditTabConfig extends TabConfigBase {
69
+ type: 'audit';
70
+ fetchFn: (client: CcamClient, id: string, querySize?: number) => Promise<ContentResponse<Record<string, unknown>>>;
71
+ }
72
+ export type TabConfig = LocalTabConfig | PaginatedTabConfig | AuditTabConfig;
73
+ export interface ResourceConfig {
74
+ name: string;
75
+ displayName: string;
76
+ idField: string;
77
+ columns: ColumnDef[];
78
+ listFn: (client: CcamClient, opts: {
79
+ page?: number;
80
+ size?: number;
81
+ sort?: {
82
+ field: string;
83
+ direction: 'asc' | 'desc';
84
+ };
85
+ }) => Promise<ContentResponse<Record<string, unknown>> | PagedResponse<Record<string, unknown>>>;
86
+ labelFn: (item: Record<string, unknown>) => string;
87
+ detailFn: (client: CcamClient, id: string) => Promise<Record<string, unknown>>;
88
+ fields: FieldDef[];
89
+ tabs: TabConfig[];
90
+ crossLinks: CrossLinkDef[];
91
+ }
92
+ export interface FooterHints {
93
+ hints: string;
94
+ pageInfo?: string;
95
+ count?: string;
96
+ }
97
+ export declare function getSortableColumns(columns: ColumnDef[], mode: 'remote' | 'local'): SortFieldDef[];
98
+ export {};
99
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,23 @@
1
+ export function getSortableColumns(columns, mode) {
2
+ return columns.flatMap((column) => {
3
+ if (mode === 'local') {
4
+ if (column.sort?.mode === 'remote') {
5
+ return [];
6
+ }
7
+ return [{
8
+ key: column.key,
9
+ field: column.sort?.field ?? column.key,
10
+ label: column.sort?.label ?? column.label,
11
+ }];
12
+ }
13
+ if (column.sort?.mode !== 'remote') {
14
+ return [];
15
+ }
16
+ return [{
17
+ key: column.key,
18
+ field: column.sort.field ?? column.key,
19
+ label: column.sort.label ?? column.label,
20
+ }];
21
+ });
22
+ }
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ import type { ResourceConfig } from '../types.js';
2
+ export interface ResourceDetailViewProps {
3
+ config: ResourceConfig;
4
+ id: string;
5
+ }
6
+ export declare function ResourceDetailView({ config, id }: ResourceDetailViewProps): import("react/jsx-runtime").JSX.Element | null;
7
+ //# sourceMappingURL=ResourceDetailView.d.ts.map